scsi_cmds.c revision 107178
1107178Snjl/* 2107178Snjl * SCSI Disk Emulator 3107178Snjl * 4107178Snjl * Copyright (c) 2002 Nate Lawson. 5107178Snjl * All rights reserved. 6107178Snjl * 7107178Snjl * Redistribution and use in source and binary forms, with or without 8107178Snjl * modification, are permitted provided that the following conditions 9107178Snjl * are met: 10107178Snjl * 1. Redistributions of source code must retain the above copyright 11107178Snjl * notice, this list of conditions, and the following disclaimer, 12107178Snjl * without modification, immediately at the beginning of the file. 13107178Snjl * 2. The name of the author may not be used to endorse or promote products 14107178Snjl * derived from this software without specific prior written permission. 15107178Snjl * 16107178Snjl * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17107178Snjl * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18107178Snjl * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19107178Snjl * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 20107178Snjl * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21107178Snjl * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22107178Snjl * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23107178Snjl * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24107178Snjl * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25107178Snjl * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26107178Snjl * SUCH DAMAGE. 27107178Snjl * 28107178Snjl * $FreeBSD: head/share/examples/scsi_target/scsi_cmds.c 107178 2002-11-22 22:55:51Z njl $ 29107178Snjl */ 30107178Snjl 31107178Snjl#include <stdio.h> 32107178Snjl#include <stddef.h> 33107178Snjl#include <stdarg.h> 34107178Snjl#include <stdlib.h> 35107178Snjl#include <string.h> 36107178Snjl#include <err.h> 37107178Snjl#include <aio.h> 38107178Snjl#include <assert.h> 39107178Snjl#include <sys/types.h> 40107178Snjl 41107178Snjl#include <cam/cam.h> 42107178Snjl#include <cam/cam_ccb.h> 43107178Snjl#include <cam/scsi/scsi_all.h> 44107178Snjl#include <cam/scsi/scsi_targetio.h> 45107178Snjl#include "scsi_target.h" 46107178Snjl 47107178Snjltypedef int targ_start_func(struct ccb_accept_tio *, struct ccb_scsiio *); 48107178Snjltypedef void targ_done_func(struct ccb_accept_tio *, struct ccb_scsiio *, 49107178Snjl io_ops); 50107178Snjl 51107178Snjlstruct targ_cdb_handlers { 52107178Snjl u_int8_t cmd; 53107178Snjl targ_start_func *start; 54107178Snjl targ_done_func *done; 55107178Snjl#define ILLEGAL_CDB 0xFF 56107178Snjl}; 57107178Snjl 58107178Snjlstatic targ_start_func tcmd_inquiry; 59107178Snjlstatic targ_start_func tcmd_req_sense; 60107178Snjlstatic targ_start_func tcmd_rd_cap; 61107178Snjlstatic targ_start_func tcmd_rdwr; 62107178Snjlstatic targ_start_func tcmd_rdwr_decode; 63107178Snjlstatic targ_done_func tcmd_rdwr_done; 64107178Snjlstatic targ_start_func tcmd_null_ok; 65107178Snjlstatic targ_start_func tcmd_illegal_req; 66107178Snjlstatic int start_io(struct ccb_accept_tio *atio, 67107178Snjl struct ccb_scsiio *ctio, int dir); 68107178Snjlstatic int init_inquiry(u_int16_t req_flags, u_int16_t sim_flags); 69107178Snjlstatic struct initiator_state * 70107178Snjl tcmd_get_istate(u_int init_id); 71107178Snjlstatic void cdb_debug(u_int8_t *cdb, const char *msg, ...); 72107178Snjl 73107178Snjlstatic struct targ_cdb_handlers cdb_handlers[] = { 74107178Snjl { READ_10, tcmd_rdwr, tcmd_rdwr_done }, 75107178Snjl { WRITE_10, tcmd_rdwr, tcmd_rdwr_done }, 76107178Snjl { READ_6, tcmd_rdwr, tcmd_rdwr_done }, 77107178Snjl { WRITE_6, tcmd_rdwr, tcmd_rdwr_done }, 78107178Snjl { INQUIRY, tcmd_inquiry, NULL }, 79107178Snjl { REQUEST_SENSE, tcmd_req_sense, NULL }, 80107178Snjl { READ_CAPACITY, tcmd_rd_cap, NULL }, 81107178Snjl { TEST_UNIT_READY, tcmd_null_ok, NULL }, 82107178Snjl { START_STOP_UNIT, tcmd_null_ok, NULL }, 83107178Snjl { SYNCHRONIZE_CACHE, tcmd_null_ok, NULL }, 84107178Snjl { MODE_SENSE_6, tcmd_illegal_req, NULL }, 85107178Snjl { MODE_SELECT_6, tcmd_illegal_req, NULL }, 86107178Snjl { ILLEGAL_CDB, NULL, NULL } 87107178Snjl}; 88107178Snjl 89107178Snjlstatic struct scsi_inquiry_data inq_data; 90107178Snjlstatic struct initiator_state istates[MAX_INITIATORS]; 91107178Snjlextern int debug; 92107178Snjlextern u_int32_t volume_size; 93107178Snjlextern size_t sector_size; 94107178Snjlextern size_t buf_size; 95107178Snjl 96107178Snjlcam_status 97107178Snjltcmd_init(u_int16_t req_inq_flags, u_int16_t sim_inq_flags) 98107178Snjl{ 99107178Snjl struct initiator_state *istate; 100107178Snjl int i, ret; 101107178Snjl 102107178Snjl /* Initialize our inquiry data */ 103107178Snjl ret = init_inquiry(req_inq_flags, sim_inq_flags); 104107178Snjl if (ret != 0) 105107178Snjl return (ret); 106107178Snjl 107107178Snjl /* We start out life with a UA to indicate power-on/reset. */ 108107178Snjl for (i = 0; i < MAX_INITIATORS; i++) { 109107178Snjl istate = tcmd_get_istate(i); 110107178Snjl bzero(istate, sizeof(*istate)); 111107178Snjl istate->pending_ua = UA_POWER_ON; 112107178Snjl } 113107178Snjl 114107178Snjl return (0); 115107178Snjl} 116107178Snjl 117107178Snjl/* Caller allocates CTIO, sets its init_id 118107178Snjlreturn 0 if done, 1 if more processing needed 119107178Snjlon 0, caller sets SEND_STATUS */ 120107178Snjlint 121107178Snjltcmd_handle(struct ccb_accept_tio *atio, struct ccb_scsiio *ctio, io_ops event) 122107178Snjl{ 123107178Snjl static struct targ_cdb_handlers *last_cmd; 124107178Snjl struct initiator_state *istate; 125107178Snjl struct atio_descr *a_descr; 126107178Snjl int ret; 127107178Snjl 128107178Snjl warnx("tcmd_handle atio %p ctio %p atioflags %#x", atio, ctio, 129107178Snjl atio->ccb_h.flags); 130107178Snjl ret = 0; 131107178Snjl a_descr = (struct atio_descr *)atio->ccb_h.targ_descr; 132107178Snjl 133107178Snjl /* Do a full lookup if one-behind cache failed */ 134107178Snjl if (last_cmd == NULL || last_cmd->cmd != a_descr->cdb[0]) { 135107178Snjl struct targ_cdb_handlers *h; 136107178Snjl 137107178Snjl for (h = cdb_handlers; h->cmd != ILLEGAL_CDB; h++) { 138107178Snjl if (a_descr->cdb[0] == h->cmd) 139107178Snjl break; 140107178Snjl } 141107178Snjl last_cmd = h; 142107178Snjl } 143107178Snjl if (last_cmd->cmd == ILLEGAL_CDB) { 144107178Snjl if (event != ATIO_WORK) { 145107178Snjl warnx("no done func for %#x???", a_descr->cdb[0]); 146107178Snjl abort(); 147107178Snjl } 148107178Snjl /* Not found, return illegal request */ 149107178Snjl warnx("cdb %#x not handled", a_descr->cdb[0]); 150107178Snjl tcmd_illegal_req(atio, ctio); 151107178Snjl send_ccb((union ccb *)ctio, /*priority*/1); 152107178Snjl return (0); 153107178Snjl } 154107178Snjl 155107178Snjl /* call completion and exit */ 156107178Snjl if (event != ATIO_WORK) { 157107178Snjl if (last_cmd->done != NULL) 158107178Snjl last_cmd->done(atio, ctio, event); 159107178Snjl else 160107178Snjl free_ccb((union ccb *)ctio); 161107178Snjl return (1); 162107178Snjl } 163107178Snjl 164107178Snjl istate = tcmd_get_istate(ctio->init_id); 165107178Snjl if (istate == NULL) { 166107178Snjl tcmd_illegal_req(atio, ctio); 167107178Snjl send_ccb((union ccb *)ctio, /*priority*/1); 168107178Snjl return (0); 169107178Snjl } 170107178Snjl 171107178Snjl if (istate->pending_ca == 0 && istate->pending_ua != 0 && 172107178Snjl a_descr->cdb[0] != INQUIRY) { 173107178Snjl tcmd_sense(ctio->init_id, ctio, SSD_KEY_UNIT_ATTENTION, 174107178Snjl 0x29, istate->pending_ua == UA_POWER_ON ? 1 : 2); 175107178Snjl istate->pending_ca = CA_UNIT_ATTN; 176107178Snjl if (debug) { 177107178Snjl cdb_debug(a_descr->cdb, "UA active for %u: ", 178107178Snjl atio->init_id); 179107178Snjl } 180107178Snjl send_ccb((union ccb *)ctio, /*priority*/1); 181107178Snjl return (0); 182107178Snjl } 183107178Snjl 184107178Snjl /* Store current CA and UA for later */ 185107178Snjl istate->orig_ua = istate->pending_ua; 186107178Snjl istate->orig_ca = istate->pending_ca; 187107178Snjl 188107178Snjl /* 189107178Snjl * As per SAM2, any command that occurs 190107178Snjl * after a CA is reported, clears the CA. We must 191107178Snjl * also clear the UA condition, if any, that caused 192107178Snjl * the CA to occur assuming the UA is not for a 193107178Snjl * persistent condition. 194107178Snjl */ 195107178Snjl istate->pending_ca = CA_NONE; 196107178Snjl if (istate->orig_ca == CA_UNIT_ATTN) 197107178Snjl istate->pending_ua = UA_NONE; 198107178Snjl 199107178Snjl /* If we have a valid handler, call start or completion function */ 200107178Snjl if (last_cmd->cmd != ILLEGAL_CDB) { 201107178Snjl ret = last_cmd->start(atio, ctio); 202107178Snjl /* XXX hack */ 203107178Snjl if (last_cmd->start != tcmd_rdwr) { 204107178Snjl a_descr->init_req += ctio->dxfer_len; 205107178Snjl send_ccb((union ccb *)ctio, /*priority*/1); 206107178Snjl } 207107178Snjl } 208107178Snjl 209107178Snjl return (ret); 210107178Snjl} 211107178Snjl 212107178Snjlstatic struct initiator_state * 213107178Snjltcmd_get_istate(u_int init_id) 214107178Snjl{ 215107178Snjl if (init_id >= MAX_INITIATORS) { 216107178Snjl warnx("illegal init_id %d, max %d", init_id, MAX_INITIATORS - 1); 217107178Snjl return (NULL); 218107178Snjl } else { 219107178Snjl return (&istates[init_id]); 220107178Snjl } 221107178Snjl} 222107178Snjl 223107178Snjlvoid 224107178Snjltcmd_sense(u_int init_id, struct ccb_scsiio *ctio, u_int8_t flags, 225107178Snjl u_int8_t asc, u_int8_t ascq) 226107178Snjl{ 227107178Snjl struct initiator_state *istate; 228107178Snjl struct scsi_sense_data *sense; 229107178Snjl 230107178Snjl /* Set our initiator's istate */ 231107178Snjl istate = tcmd_get_istate(init_id); 232107178Snjl if (istate == NULL) 233107178Snjl return; 234107178Snjl istate->pending_ca |= CA_CMD_SENSE; /* XXX set instead of or? */ 235107178Snjl sense = &istate->sense_data; 236107178Snjl bzero(sense, sizeof(*sense)); 237107178Snjl sense->error_code = SSD_CURRENT_ERROR; 238107178Snjl sense->flags = flags; 239107178Snjl sense->add_sense_code = asc; 240107178Snjl sense->add_sense_code_qual = ascq; 241107178Snjl sense->extra_len = 242107178Snjl offsetof(struct scsi_sense_data, sense_key_spec[2]) - 243107178Snjl offsetof(struct scsi_sense_data, extra_len); 244107178Snjl 245107178Snjl /* Fill out the supplied CTIO */ 246107178Snjl if (ctio != NULL) { 247107178Snjl /* No autosense yet 248107178Snjl bcopy(sense, &ctio->sense_data, sizeof(*sense)); 249107178Snjl ctio->sense_len = sizeof(*sense); XXX 250107178Snjl */ 251107178Snjl ctio->ccb_h.flags &= ~CAM_DIR_MASK; 252107178Snjl ctio->ccb_h.flags |= CAM_DIR_NONE | /* CAM_SEND_SENSE | */ 253107178Snjl CAM_SEND_STATUS; 254107178Snjl ctio->dxfer_len = 0; 255107178Snjl ctio->scsi_status = SCSI_STATUS_CHECK_COND; 256107178Snjl } 257107178Snjl} 258107178Snjl 259107178Snjlvoid 260107178Snjltcmd_ua(u_int init_id, ua_types new_ua) 261107178Snjl{ 262107178Snjl struct initiator_state *istate; 263107178Snjl u_int start, end; 264107178Snjl 265107178Snjl if (init_id == CAM_TARGET_WILDCARD) { 266107178Snjl start = 0; 267107178Snjl end = MAX_INITIATORS - 1; 268107178Snjl } else { 269107178Snjl start = end = init_id; 270107178Snjl } 271107178Snjl 272107178Snjl for (; start <= end; start++) { 273107178Snjl istate = tcmd_get_istate(start); 274107178Snjl if (istate == NULL) 275107178Snjl break; 276107178Snjl istate->pending_ua = new_ua; 277107178Snjl } 278107178Snjl} 279107178Snjl 280107178Snjlstatic int 281107178Snjltcmd_inquiry(struct ccb_accept_tio *atio, struct ccb_scsiio *ctio) 282107178Snjl{ 283107178Snjl struct scsi_inquiry *inq; 284107178Snjl struct atio_descr *a_descr; 285107178Snjl struct initiator_state *istate; 286107178Snjl struct scsi_sense_data *sense; 287107178Snjl 288107178Snjl a_descr = (struct atio_descr *)atio->ccb_h.targ_descr; 289107178Snjl inq = (struct scsi_inquiry *)a_descr->cdb; 290107178Snjl 291107178Snjl if (debug) 292107178Snjl cdb_debug(a_descr->cdb, "INQUIRY from %u: ", atio->init_id); 293107178Snjl /* 294107178Snjl * Validate the command. We don't support any VPD pages, so 295107178Snjl * complain if EVPD or CMDDT is set. 296107178Snjl */ 297107178Snjl istate = tcmd_get_istate(ctio->init_id); 298107178Snjl sense = &istate->sense_data; 299107178Snjl if ((inq->byte2 & SI_EVPD) != 0) { 300107178Snjl tcmd_illegal_req(atio, ctio); 301107178Snjl sense->sense_key_spec[0] = SSD_SCS_VALID | SSD_FIELDPTR_CMD | 302107178Snjl SSD_BITPTR_VALID | /*bit value*/1; 303107178Snjl sense->sense_key_spec[1] = 0; 304107178Snjl sense->sense_key_spec[2] = 305107178Snjl offsetof(struct scsi_inquiry, byte2); 306107178Snjl } else if (inq->page_code != 0) { 307107178Snjl tcmd_illegal_req(atio, ctio); 308107178Snjl sense->sense_key_spec[0] = SSD_SCS_VALID | SSD_FIELDPTR_CMD; 309107178Snjl sense->sense_key_spec[1] = 0; 310107178Snjl sense->sense_key_spec[2] = 311107178Snjl offsetof(struct scsi_inquiry, page_code); 312107178Snjl } else { 313107178Snjl bcopy(&inq_data, ctio->data_ptr, sizeof(inq_data)); 314107178Snjl ctio->dxfer_len = inq_data.additional_length + 4; 315107178Snjl ctio->dxfer_len = min(ctio->dxfer_len, 316107178Snjl SCSI_CDB6_LEN(inq->length)); 317107178Snjl ctio->ccb_h.flags |= CAM_DIR_IN | CAM_SEND_STATUS; 318107178Snjl ctio->scsi_status = SCSI_STATUS_OK; 319107178Snjl } 320107178Snjl return (0); 321107178Snjl} 322107178Snjl 323107178Snjl/* Initialize the inquiry response structure with the requested flags */ 324107178Snjlstatic int 325107178Snjlinit_inquiry(u_int16_t req_flags, u_int16_t sim_flags) 326107178Snjl{ 327107178Snjl struct scsi_inquiry_data *inq; 328107178Snjl 329107178Snjl inq = &inq_data; 330107178Snjl bzero(inq, sizeof(*inq)); 331107178Snjl inq->device = T_DIRECT | (SID_QUAL_LU_CONNECTED << 5); 332107178Snjl inq->version = SCSI_REV_SPC; /* was 2 */ 333107178Snjl 334107178Snjl /* 335107178Snjl * XXX cpi.hba_inquiry doesn't support Addr16 so we give the 336107178Snjl * user what they want if they ask for it. 337107178Snjl */ 338107178Snjl if ((req_flags & SID_Addr16) != 0) { 339107178Snjl sim_flags |= SID_Addr16; 340107178Snjl warnx("Not sure SIM supports Addr16 but enabling it anyway"); 341107178Snjl } 342107178Snjl 343107178Snjl /* Advertise only what the SIM can actually support */ 344107178Snjl req_flags &= sim_flags; 345107178Snjl scsi_ulto2b(req_flags, &inq->reserved[1]); 346107178Snjl 347107178Snjl inq->response_format = 2; /* SCSI2 Inquiry Format */ 348107178Snjl inq->additional_length = SHORT_INQUIRY_LENGTH - 349107178Snjl offsetof(struct scsi_inquiry_data, additional_length); 350107178Snjl bcopy("FreeBSD ", inq->vendor, SID_VENDOR_SIZE); 351107178Snjl bcopy("Emulated Disk ", inq->product, SID_PRODUCT_SIZE); 352107178Snjl bcopy("0.1 ", inq->revision, SID_REVISION_SIZE); 353107178Snjl return (0); 354107178Snjl} 355107178Snjl 356107178Snjlstatic int 357107178Snjltcmd_req_sense(struct ccb_accept_tio *atio, struct ccb_scsiio *ctio) 358107178Snjl{ 359107178Snjl struct scsi_request_sense *rsense; 360107178Snjl struct scsi_sense_data *sense; 361107178Snjl struct initiator_state *istate; 362107178Snjl size_t dlen; 363107178Snjl struct atio_descr *a_descr; 364107178Snjl 365107178Snjl a_descr = (struct atio_descr *)atio->ccb_h.targ_descr; 366107178Snjl rsense = (struct scsi_request_sense *)a_descr->cdb; 367107178Snjl 368107178Snjl istate = tcmd_get_istate(ctio->init_id); 369107178Snjl sense = &istate->sense_data; 370107178Snjl 371107178Snjl if (debug) { 372107178Snjl cdb_debug(a_descr->cdb, "REQ SENSE from %u: ", atio->init_id); 373107178Snjl warnx("Sending sense: %#x %#x %#x", sense->flags, 374107178Snjl sense->add_sense_code, sense->add_sense_code_qual); 375107178Snjl } 376107178Snjl 377107178Snjl if (istate->orig_ca == 0) { 378107178Snjl tcmd_sense(ctio->init_id, NULL, SSD_KEY_NO_SENSE, 0, 0); 379107178Snjl warnx("REQUEST SENSE from %u but no pending CA!", 380107178Snjl ctio->init_id); 381107178Snjl } 382107178Snjl 383107178Snjl bcopy(sense, ctio->data_ptr, sizeof(struct scsi_sense_data)); 384107178Snjl dlen = offsetof(struct scsi_sense_data, extra_len) + 385107178Snjl sense->extra_len + 1; 386107178Snjl ctio->dxfer_len = min(dlen, SCSI_CDB6_LEN(rsense->length)); 387107178Snjl ctio->ccb_h.flags |= CAM_DIR_IN | CAM_SEND_STATUS; 388107178Snjl ctio->scsi_status = SCSI_STATUS_OK; 389107178Snjl return (0); 390107178Snjl} 391107178Snjl 392107178Snjlstatic int 393107178Snjltcmd_rd_cap(struct ccb_accept_tio *atio, struct ccb_scsiio *ctio) 394107178Snjl{ 395107178Snjl struct scsi_read_capacity_data *srp; 396107178Snjl struct atio_descr *a_descr; 397107178Snjl 398107178Snjl a_descr = (struct atio_descr *)atio->ccb_h.targ_descr; 399107178Snjl srp = (struct scsi_read_capacity_data *)ctio->data_ptr; 400107178Snjl 401107178Snjl if (debug) { 402107178Snjl cdb_debug(a_descr->cdb, "READ CAP from %u (%u, %u): ", 403107178Snjl atio->init_id, volume_size - 1, sector_size); 404107178Snjl } 405107178Snjl 406107178Snjl bzero(srp, sizeof(*srp)); 407107178Snjl scsi_ulto4b(volume_size - 1, srp->addr); 408107178Snjl scsi_ulto4b(sector_size, srp->length); 409107178Snjl 410107178Snjl ctio->dxfer_len = sizeof(*srp); 411107178Snjl ctio->ccb_h.flags |= CAM_DIR_IN | CAM_SEND_STATUS; 412107178Snjl ctio->scsi_status = SCSI_STATUS_OK; 413107178Snjl return (0); 414107178Snjl} 415107178Snjl 416107178Snjlstatic int 417107178Snjltcmd_rdwr(struct ccb_accept_tio *atio, struct ccb_scsiio *ctio) 418107178Snjl{ 419107178Snjl struct atio_descr *a_descr; 420107178Snjl struct ctio_descr *c_descr; 421107178Snjl int ret; 422107178Snjl 423107178Snjl a_descr = (struct atio_descr *)atio->ccb_h.targ_descr; 424107178Snjl c_descr = (struct ctio_descr *)ctio->ccb_h.targ_descr; 425107178Snjl 426107178Snjl /* Command needs to be decoded */ 427107178Snjl if ((a_descr->flags & CAM_DIR_MASK) == CAM_DIR_RESV) { 428107178Snjl if (debug) 429107178Snjl warnx("Calling rdwr_decode"); 430107178Snjl ret = tcmd_rdwr_decode(atio, ctio); 431107178Snjl if (ret == 0) { 432107178Snjl send_ccb((union ccb *)ctio, /*priority*/1); 433107178Snjl return (0); 434107178Snjl } 435107178Snjl } 436107178Snjl ctio->ccb_h.flags |= a_descr->flags; 437107178Snjl 438107178Snjl /* Call appropriate work function */ 439107178Snjl if ((a_descr->flags & CAM_DIR_IN) != 0) { 440107178Snjl ret = start_io(atio, ctio, CAM_DIR_IN); 441107178Snjl if (debug) 442107178Snjl warnx("Starting DIR_IN @%lld:%u", c_descr->offset, 443107178Snjl a_descr->targ_req); 444107178Snjl } else { 445107178Snjl ret = start_io(atio, ctio, CAM_DIR_OUT); 446107178Snjl if (debug) 447107178Snjl warnx("Starting DIR_OUT @%lld:%u", c_descr->offset, 448107178Snjl a_descr->init_req); 449107178Snjl } 450107178Snjl 451107178Snjl return (ret); 452107178Snjl} 453107178Snjl 454107178Snjlstatic int 455107178Snjltcmd_rdwr_decode(struct ccb_accept_tio *atio, struct ccb_scsiio *ctio) 456107178Snjl{ 457107178Snjl u_int32_t blkno, count; 458107178Snjl struct atio_descr *a_descr; 459107178Snjl u_int8_t *cdb; 460107178Snjl 461107178Snjl a_descr = (struct atio_descr *)atio->ccb_h.targ_descr; 462107178Snjl cdb = a_descr->cdb; 463107178Snjl if (debug) 464107178Snjl cdb_debug(cdb, "R/W from %u: ", atio->init_id); 465107178Snjl 466107178Snjl if (cdb[0] == READ_6 || cdb[0] == WRITE_6) { 467107178Snjl struct scsi_rw_6 *rw_6 = (struct scsi_rw_6 *)cdb; 468107178Snjl blkno = scsi_3btoul(rw_6->addr); 469107178Snjl count = rw_6->length; 470107178Snjl } else { 471107178Snjl struct scsi_rw_10 *rw_10 = (struct scsi_rw_10 *)cdb; 472107178Snjl blkno = scsi_4btoul(rw_10->addr); 473107178Snjl count = scsi_2btoul(rw_10->length); 474107178Snjl } 475107178Snjl if (blkno + count > volume_size) { 476107178Snjl warnx("Attempt to access past end of volume"); 477107178Snjl tcmd_sense(ctio->init_id, ctio, 478107178Snjl SSD_KEY_ILLEGAL_REQUEST, 0x21, 0); 479107178Snjl return (0); 480107178Snjl } 481107178Snjl 482107178Snjl /* Get an (overall) data length and set direction */ 483107178Snjl a_descr->base_off = ((off_t)blkno) * sector_size; 484107178Snjl a_descr->total_len = count * sector_size; 485107178Snjl if (a_descr->total_len == 0) { 486107178Snjl if (debug) 487107178Snjl warnx("r/w 0 blocks @ blkno %u", blkno); 488107178Snjl tcmd_null_ok(atio, ctio); 489107178Snjl return (0); 490107178Snjl } else if (cdb[0] == WRITE_6 || cdb[0] == WRITE_10) { 491107178Snjl a_descr->flags |= CAM_DIR_OUT; 492107178Snjl if (debug) 493107178Snjl warnx("write %u blocks @ blkno %u", count, blkno); 494107178Snjl } else { 495107178Snjl a_descr->flags |= CAM_DIR_IN; 496107178Snjl if (debug) 497107178Snjl warnx("read %u blocks @ blkno %u", count, blkno); 498107178Snjl } 499107178Snjl return (1); 500107178Snjl} 501107178Snjl 502107178Snjlstatic int 503107178Snjlstart_io(struct ccb_accept_tio *atio, struct ccb_scsiio *ctio, int dir) 504107178Snjl{ 505107178Snjl struct atio_descr *a_descr; 506107178Snjl struct ctio_descr *c_descr; 507107178Snjl int ret; 508107178Snjl 509107178Snjl /* Set up common structures */ 510107178Snjl a_descr = (struct atio_descr *)atio->ccb_h.targ_descr; 511107178Snjl c_descr = (struct ctio_descr *)ctio->ccb_h.targ_descr; 512107178Snjl 513107178Snjl if (dir == CAM_DIR_IN) { 514107178Snjl c_descr->offset = a_descr->base_off + a_descr->targ_req; 515107178Snjl ctio->dxfer_len = a_descr->total_len - a_descr->targ_req; 516107178Snjl } else { 517107178Snjl c_descr->offset = a_descr->base_off + a_descr->init_req; 518107178Snjl ctio->dxfer_len = a_descr->total_len - a_descr->init_req; 519107178Snjl } 520107178Snjl ctio->dxfer_len = min(ctio->dxfer_len, buf_size); 521107178Snjl assert(ctio->dxfer_len >= 0); 522107178Snjl 523107178Snjl c_descr->aiocb.aio_offset = c_descr->offset; 524107178Snjl c_descr->aiocb.aio_nbytes = ctio->dxfer_len; 525107178Snjl 526107178Snjl /* If DIR_IN, start read from target, otherwise begin CTIO xfer. */ 527107178Snjl ret = 1; 528107178Snjl if (dir == CAM_DIR_IN) { 529107178Snjl if (aio_read(&c_descr->aiocb) < 0) 530107178Snjl err(1, "aio_read"); /* XXX */ 531107178Snjl a_descr->targ_req += ctio->dxfer_len; 532107178Snjl if (a_descr->targ_req == a_descr->total_len) { 533107178Snjl ctio->ccb_h.flags |= CAM_SEND_STATUS; 534107178Snjl ctio->scsi_status = SCSI_STATUS_OK; 535107178Snjl ret = 0; 536107178Snjl } 537107178Snjl } else { 538107178Snjl if (a_descr->targ_ack == a_descr->total_len) 539107178Snjl tcmd_null_ok(atio, ctio); 540107178Snjl a_descr->init_req += ctio->dxfer_len; 541107178Snjl if (a_descr->init_req == a_descr->total_len && 542107178Snjl ctio->dxfer_len > 0) { 543107178Snjl /* 544107178Snjl * If data phase done, remove atio from workq. 545107178Snjl * The completion handler will call work_atio to 546107178Snjl * send the final status. 547107178Snjl */ 548107178Snjl ret = 0; 549107178Snjl } 550107178Snjl send_ccb((union ccb *)ctio, /*priority*/1); 551107178Snjl } 552107178Snjl 553107178Snjl return (ret); 554107178Snjl} 555107178Snjl 556107178Snjlstatic void 557107178Snjltcmd_rdwr_done(struct ccb_accept_tio *atio, struct ccb_scsiio *ctio, 558107178Snjl io_ops event) 559107178Snjl{ 560107178Snjl struct atio_descr *a_descr; 561107178Snjl struct ctio_descr *c_descr; 562107178Snjl 563107178Snjl a_descr = (struct atio_descr *)atio->ccb_h.targ_descr; 564107178Snjl c_descr = (struct ctio_descr *)ctio->ccb_h.targ_descr; 565107178Snjl 566107178Snjl switch (event) { 567107178Snjl case AIO_DONE: 568107178Snjl if (aio_return(&c_descr->aiocb) < 0) { 569107178Snjl warn("aio_return error"); 570107178Snjl /* XXX */ 571107178Snjl tcmd_sense(ctio->init_id, ctio, 572107178Snjl SSD_KEY_MEDIUM_ERROR, 0, 0); 573107178Snjl send_ccb((union ccb *)ctio, /*priority*/1); 574107178Snjl break; 575107178Snjl } 576107178Snjl a_descr->targ_ack += ctio->dxfer_len; 577107178Snjl if ((a_descr->flags & CAM_DIR_IN) != 0) { 578107178Snjl if (debug) 579107178Snjl warnx("sending CTIO for AIO read"); 580107178Snjl a_descr->init_req += ctio->dxfer_len; 581107178Snjl send_ccb((union ccb *)ctio, /*priority*/1); 582107178Snjl } else { 583107178Snjl /* Use work function to send final status */ 584107178Snjl if (a_descr->init_req == a_descr->total_len) 585107178Snjl work_atio(atio); 586107178Snjl if (debug) 587107178Snjl warnx("AIO done freeing CTIO"); 588107178Snjl free_ccb((union ccb *)ctio); 589107178Snjl } 590107178Snjl break; 591107178Snjl case CTIO_DONE: 592107178Snjl if (ctio->ccb_h.status != CAM_REQ_CMP) { 593107178Snjl /* XXX */ 594107178Snjl errx(1, "CTIO failed, status %#x", ctio->ccb_h.status); 595107178Snjl } 596107178Snjl a_descr->init_ack += ctio->dxfer_len; 597107178Snjl if ((a_descr->flags & CAM_DIR_MASK) == CAM_DIR_OUT && 598107178Snjl ctio->dxfer_len > 0) { 599107178Snjl if (debug) 600107178Snjl warnx("sending AIO for CTIO write"); 601107178Snjl a_descr->targ_req += ctio->dxfer_len; 602107178Snjl if (aio_write(&c_descr->aiocb) < 0) 603107178Snjl err(1, "aio_write"); /* XXX */ 604107178Snjl } else { 605107178Snjl if (debug) 606107178Snjl warnx("CTIO done freeing CTIO"); 607107178Snjl free_ccb((union ccb *)ctio); 608107178Snjl } 609107178Snjl break; 610107178Snjl default: 611107178Snjl warnx("Unknown completion code %d", event); 612107178Snjl abort(); 613107178Snjl /* NOTREACHED */ 614107178Snjl } 615107178Snjl} 616107178Snjl 617107178Snjl/* Simple ok message used by TUR, SYNC_CACHE, etc. */ 618107178Snjlstatic int 619107178Snjltcmd_null_ok(struct ccb_accept_tio *atio, struct ccb_scsiio *ctio) 620107178Snjl{ 621107178Snjl if (debug) { 622107178Snjl struct atio_descr *a_descr; 623107178Snjl 624107178Snjl a_descr = (struct atio_descr *)atio->ccb_h.targ_descr; 625107178Snjl cdb_debug(a_descr->cdb, "Sending null ok to %u : ", atio->init_id); 626107178Snjl } 627107178Snjl 628107178Snjl ctio->dxfer_len = 0; 629107178Snjl ctio->ccb_h.flags &= ~CAM_DIR_MASK; 630107178Snjl ctio->ccb_h.flags |= CAM_DIR_NONE | CAM_SEND_STATUS; 631107178Snjl ctio->scsi_status = SCSI_STATUS_OK; 632107178Snjl return (0); 633107178Snjl} 634107178Snjl 635107178Snjl/* Simple illegal request message used by MODE SENSE, etc. */ 636107178Snjlstatic int 637107178Snjltcmd_illegal_req(struct ccb_accept_tio *atio, struct ccb_scsiio *ctio) 638107178Snjl{ 639107178Snjl if (debug) { 640107178Snjl struct atio_descr *a_descr; 641107178Snjl 642107178Snjl a_descr = (struct atio_descr *)atio->ccb_h.targ_descr; 643107178Snjl cdb_debug(a_descr->cdb, "Sending ill req to %u: ", atio->init_id); 644107178Snjl } 645107178Snjl 646107178Snjl tcmd_sense(atio->init_id, ctio, SSD_KEY_ILLEGAL_REQUEST, 647107178Snjl /*asc*/0x24, /*ascq*/0); 648107178Snjl return (0); 649107178Snjl} 650107178Snjl 651107178Snjlstatic void 652107178Snjlcdb_debug(u_int8_t *cdb, const char *msg, ...) 653107178Snjl{ 654107178Snjl char msg_buf[512]; 655107178Snjl int len; 656107178Snjl va_list ap; 657107178Snjl 658107178Snjl va_start(ap, msg); 659107178Snjl vsnprintf(msg_buf, sizeof(msg_buf), msg, ap); 660107178Snjl va_end(ap); 661107178Snjl len = strlen(msg_buf); 662107178Snjl scsi_cdb_string(cdb, msg_buf + len, sizeof(msg_buf) - len); 663107178Snjl warnx("%s", msg_buf); 664107178Snjl} 665