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