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: releng/10.2/share/examples/scsi_target/scsi_cmds.c 229997 2012-01-12 00:34:33Z ken $
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>
38162704Smjacob#include <unistd.h>
39107178Snjl#include <assert.h>
40121184Ssimokawa#include <sys/param.h>
41107178Snjl#include <sys/types.h>
42107178Snjl
43107178Snjl#include <cam/cam.h>
44107178Snjl#include <cam/cam_ccb.h>
45107178Snjl#include <cam/scsi/scsi_all.h>
46107178Snjl#include <cam/scsi/scsi_targetio.h>
47107178Snjl#include "scsi_target.h"
48107178Snjl
49107178Snjltypedef int targ_start_func(struct ccb_accept_tio *, struct ccb_scsiio *);
50107178Snjltypedef void targ_done_func(struct ccb_accept_tio *, struct ccb_scsiio *,
51107178Snjl			      io_ops);
52156981Smjacob#ifndef	REPORT_LUNS
53156981Smjacob#define	REPORT_LUNS	0xa0
54156981Smjacob#endif
55107178Snjl
56107178Snjlstruct targ_cdb_handlers {
57107178Snjl	u_int8_t	  cmd;
58107178Snjl	targ_start_func  *start;
59107178Snjl	targ_done_func	 *done;
60107178Snjl#define ILLEGAL_CDB	  0xFF
61107178Snjl};
62107178Snjl
63107178Snjlstatic targ_start_func		tcmd_inquiry;
64107178Snjlstatic targ_start_func		tcmd_req_sense;
65107178Snjlstatic targ_start_func		tcmd_rd_cap;
66121184Ssimokawa#ifdef READ_16
67121184Ssimokawastatic targ_start_func		tcmd_rd_cap16;
68121184Ssimokawa#endif
69107178Snjlstatic targ_start_func		tcmd_rdwr;
70107178Snjlstatic targ_start_func		tcmd_rdwr_decode;
71107178Snjlstatic targ_done_func		tcmd_rdwr_done;
72107178Snjlstatic targ_start_func		tcmd_null_ok;
73107178Snjlstatic targ_start_func		tcmd_illegal_req;
74107178Snjlstatic int			start_io(struct ccb_accept_tio *atio,
75107178Snjl					 struct ccb_scsiio *ctio, int dir);
76107178Snjlstatic int init_inquiry(u_int16_t req_flags, u_int16_t sim_flags);
77107178Snjlstatic struct initiator_state *
78107178Snjl			tcmd_get_istate(u_int init_id);
79107178Snjlstatic void cdb_debug(u_int8_t *cdb, const char *msg, ...);
80107178Snjl
81107178Snjlstatic struct targ_cdb_handlers cdb_handlers[] = {
82107178Snjl	{ READ_10,		tcmd_rdwr,		tcmd_rdwr_done },
83107178Snjl	{ WRITE_10,		tcmd_rdwr,		tcmd_rdwr_done },
84107178Snjl	{ READ_6,		tcmd_rdwr,		tcmd_rdwr_done },
85107178Snjl	{ WRITE_6,		tcmd_rdwr,		tcmd_rdwr_done },
86107178Snjl	{ INQUIRY,		tcmd_inquiry,		NULL },
87107178Snjl	{ REQUEST_SENSE,	tcmd_req_sense,		NULL },
88107178Snjl	{ READ_CAPACITY,	tcmd_rd_cap,		NULL },
89107178Snjl	{ TEST_UNIT_READY,	tcmd_null_ok,		NULL },
90107178Snjl	{ START_STOP_UNIT,	tcmd_null_ok,		NULL },
91107178Snjl	{ SYNCHRONIZE_CACHE,	tcmd_null_ok,		NULL },
92107178Snjl	{ MODE_SENSE_6,		tcmd_illegal_req,	NULL },
93107178Snjl	{ MODE_SELECT_6,	tcmd_illegal_req,	NULL },
94156981Smjacob	{ REPORT_LUNS,		tcmd_illegal_req,	NULL },
95121184Ssimokawa#ifdef READ_16
96121184Ssimokawa	{ READ_16,		tcmd_rdwr,		tcmd_rdwr_done },
97121184Ssimokawa	{ WRITE_16,		tcmd_rdwr,		tcmd_rdwr_done },
98121184Ssimokawa	{ SERVICE_ACTION_IN,	tcmd_rd_cap16,		NULL },
99121184Ssimokawa#endif
100107178Snjl	{ ILLEGAL_CDB,		NULL,			NULL }
101107178Snjl};
102107178Snjl
103107178Snjlstatic struct scsi_inquiry_data inq_data;
104107178Snjlstatic struct initiator_state istates[MAX_INITIATORS];
105107178Snjlextern int		debug;
106228462Smavextern off_t		volume_size;
107228462Smavextern u_int		sector_size;
108107178Snjlextern size_t		buf_size;
109107178Snjl
110107178Snjlcam_status
111107178Snjltcmd_init(u_int16_t req_inq_flags, u_int16_t sim_inq_flags)
112107178Snjl{
113107178Snjl	struct initiator_state *istate;
114107178Snjl	int i, ret;
115107178Snjl
116107178Snjl	/* Initialize our inquiry data */
117107178Snjl	ret = init_inquiry(req_inq_flags, sim_inq_flags);
118107178Snjl	if (ret != 0)
119107178Snjl        	return (ret);
120107178Snjl
121107178Snjl	/* We start out life with a UA to indicate power-on/reset. */
122107178Snjl	for (i = 0; i < MAX_INITIATORS; i++) {
123107178Snjl		istate = tcmd_get_istate(i);
124107178Snjl		bzero(istate, sizeof(*istate));
125107178Snjl		istate->pending_ua = UA_POWER_ON;
126107178Snjl	}
127107178Snjl
128107178Snjl	return (0);
129107178Snjl}
130107178Snjl
131107178Snjl/* Caller allocates CTIO, sets its init_id
132107178Snjlreturn 0 if done, 1 if more processing needed
133107178Snjlon 0, caller sets SEND_STATUS */
134107178Snjlint
135107178Snjltcmd_handle(struct ccb_accept_tio *atio, struct ccb_scsiio *ctio, io_ops event)
136107178Snjl{
137107178Snjl	static struct targ_cdb_handlers *last_cmd;
138107178Snjl	struct initiator_state *istate;
139107178Snjl	struct atio_descr *a_descr;
140107178Snjl	int ret;
141107178Snjl
142109345Snjl	if (debug) {
143109345Snjl		warnx("tcmd_handle atio %p ctio %p atioflags %#x", atio, ctio,
144109345Snjl		      atio->ccb_h.flags);
145109345Snjl	}
146107178Snjl	ret = 0;
147107178Snjl	a_descr = (struct atio_descr *)atio->ccb_h.targ_descr;
148107178Snjl
149107178Snjl	/* Do a full lookup if one-behind cache failed */
150107178Snjl	if (last_cmd == NULL || last_cmd->cmd != a_descr->cdb[0]) {
151107178Snjl		struct targ_cdb_handlers *h;
152107178Snjl
153107178Snjl		for (h = cdb_handlers; h->cmd != ILLEGAL_CDB; h++) {
154107178Snjl			if (a_descr->cdb[0] == h->cmd)
155107178Snjl				break;
156107178Snjl		}
157107178Snjl		last_cmd = h;
158107178Snjl	}
159157129Smjacob
160157129Smjacob	/* call completion and exit */
161157129Smjacob	if (event != ATIO_WORK) {
162157129Smjacob		if (last_cmd->done != NULL)
163157129Smjacob			last_cmd->done(atio, ctio, event);
164157129Smjacob		else
165157129Smjacob			free_ccb((union ccb *)ctio);
166157129Smjacob		return (1);
167157129Smjacob	}
168157129Smjacob
169107178Snjl	if (last_cmd->cmd == ILLEGAL_CDB) {
170107178Snjl		if (event != ATIO_WORK) {
171107178Snjl			warnx("no done func for %#x???", a_descr->cdb[0]);
172107178Snjl			abort();
173107178Snjl		}
174107178Snjl		/* Not found, return illegal request */
175107178Snjl		warnx("cdb %#x not handled", a_descr->cdb[0]);
176107178Snjl		tcmd_illegal_req(atio, ctio);
177107178Snjl		send_ccb((union ccb *)ctio, /*priority*/1);
178107178Snjl		return (0);
179107178Snjl	}
180107178Snjl
181107178Snjl	istate = tcmd_get_istate(ctio->init_id);
182107178Snjl	if (istate == NULL) {
183107178Snjl		tcmd_illegal_req(atio, ctio);
184107178Snjl		send_ccb((union ccb *)ctio, /*priority*/1);
185107178Snjl		return (0);
186107178Snjl	}
187107178Snjl
188107178Snjl	if (istate->pending_ca == 0 && istate->pending_ua != 0 &&
189107178Snjl	    a_descr->cdb[0] != INQUIRY) {
190107178Snjl		tcmd_sense(ctio->init_id, ctio, SSD_KEY_UNIT_ATTENTION,
191107178Snjl			   0x29, istate->pending_ua == UA_POWER_ON ? 1 : 2);
192107178Snjl		istate->pending_ca = CA_UNIT_ATTN;
193107178Snjl		if (debug) {
194107178Snjl			cdb_debug(a_descr->cdb, "UA active for %u: ",
195107178Snjl				  atio->init_id);
196107178Snjl		}
197107178Snjl		send_ccb((union ccb *)ctio, /*priority*/1);
198107178Snjl		return (0);
199107178Snjl	}
200107178Snjl
201107178Snjl	/* Store current CA and UA for later */
202107178Snjl	istate->orig_ua = istate->pending_ua;
203107178Snjl	istate->orig_ca = istate->pending_ca;
204107178Snjl
205107178Snjl	/*
206107178Snjl	 * As per SAM2, any command that occurs
207107178Snjl	 * after a CA is reported, clears the CA.  We must
208107178Snjl	 * also clear the UA condition, if any, that caused
209107178Snjl	 * the CA to occur assuming the UA is not for a
210107178Snjl	 * persistent condition.
211107178Snjl	 */
212107178Snjl	istate->pending_ca = CA_NONE;
213107178Snjl	if (istate->orig_ca == CA_UNIT_ATTN)
214107178Snjl		istate->pending_ua = UA_NONE;
215107178Snjl
216107178Snjl	/* If we have a valid handler, call start or completion function */
217107178Snjl	if (last_cmd->cmd != ILLEGAL_CDB) {
218107178Snjl		ret = last_cmd->start(atio, ctio);
219107178Snjl		/* XXX hack */
220107178Snjl		if (last_cmd->start != tcmd_rdwr) {
221107178Snjl			a_descr->init_req += ctio->dxfer_len;
222107178Snjl			send_ccb((union ccb *)ctio, /*priority*/1);
223107178Snjl		}
224107178Snjl	}
225107178Snjl
226107178Snjl	return (ret);
227107178Snjl}
228107178Snjl
229107178Snjlstatic struct initiator_state *
230107178Snjltcmd_get_istate(u_int init_id)
231107178Snjl{
232107178Snjl	if (init_id >= MAX_INITIATORS) {
233107178Snjl		warnx("illegal init_id %d, max %d", init_id, MAX_INITIATORS - 1);
234107178Snjl		return (NULL);
235107178Snjl	} else {
236107178Snjl		return (&istates[init_id]);
237107178Snjl	}
238107178Snjl}
239107178Snjl
240107178Snjlvoid
241107178Snjltcmd_sense(u_int init_id, struct ccb_scsiio *ctio, u_int8_t flags,
242107178Snjl	       u_int8_t asc, u_int8_t ascq)
243107178Snjl{
244107178Snjl	struct initiator_state *istate;
245225950Sken	struct scsi_sense_data_fixed *sense;
246107178Snjl
247107178Snjl	/* Set our initiator's istate */
248107178Snjl	istate = tcmd_get_istate(init_id);
249107178Snjl	if (istate == NULL)
250107178Snjl		return;
251107178Snjl	istate->pending_ca |= CA_CMD_SENSE; /* XXX set instead of or? */
252225950Sken	sense = (struct scsi_sense_data_fixed *)&istate->sense_data;
253107178Snjl	bzero(sense, sizeof(*sense));
254107178Snjl	sense->error_code = SSD_CURRENT_ERROR;
255107178Snjl	sense->flags = flags;
256107178Snjl	sense->add_sense_code = asc;
257107178Snjl	sense->add_sense_code_qual = ascq;
258107178Snjl	sense->extra_len =
259225950Sken		offsetof(struct scsi_sense_data_fixed, sense_key_spec[2]) -
260225950Sken		offsetof(struct scsi_sense_data_fixed, extra_len);
261107178Snjl
262107178Snjl	/* Fill out the supplied CTIO */
263107178Snjl	if (ctio != NULL) {
264107178Snjl		bcopy(sense, &ctio->sense_data, sizeof(*sense));
265120428Ssimokawa		ctio->sense_len = sizeof(*sense);  /* XXX */
266107178Snjl		ctio->ccb_h.flags &= ~CAM_DIR_MASK;
267124308Snjl		ctio->ccb_h.flags |= CAM_DIR_NONE | CAM_SEND_SENSE |
268107178Snjl				     CAM_SEND_STATUS;
269107178Snjl		ctio->dxfer_len = 0;
270107178Snjl		ctio->scsi_status = SCSI_STATUS_CHECK_COND;
271107178Snjl	}
272107178Snjl}
273107178Snjl
274107178Snjlvoid
275107178Snjltcmd_ua(u_int init_id, ua_types new_ua)
276107178Snjl{
277107178Snjl	struct initiator_state *istate;
278107178Snjl	u_int start, end;
279107178Snjl
280107178Snjl	if (init_id == CAM_TARGET_WILDCARD) {
281107178Snjl		start = 0;
282107178Snjl		end = MAX_INITIATORS - 1;
283107178Snjl	} else {
284107178Snjl		start = end = init_id;
285107178Snjl	}
286107178Snjl
287107178Snjl	for (; start <= end; start++) {
288107178Snjl		istate = tcmd_get_istate(start);
289107178Snjl		if (istate == NULL)
290107178Snjl			break;
291107178Snjl		istate->pending_ua = new_ua;
292107178Snjl	}
293107178Snjl}
294107178Snjl
295107178Snjlstatic int
296107178Snjltcmd_inquiry(struct ccb_accept_tio *atio, struct ccb_scsiio *ctio)
297107178Snjl{
298107178Snjl	struct scsi_inquiry *inq;
299107178Snjl	struct atio_descr *a_descr;
300107178Snjl	struct initiator_state *istate;
301225950Sken	struct scsi_sense_data_fixed *sense;
302107178Snjl
303107178Snjl	a_descr = (struct atio_descr *)atio->ccb_h.targ_descr;
304107178Snjl	inq = (struct scsi_inquiry *)a_descr->cdb;
305107178Snjl
306107178Snjl	if (debug)
307107178Snjl		cdb_debug(a_descr->cdb, "INQUIRY from %u: ", atio->init_id);
308107178Snjl	/*
309107178Snjl	 * Validate the command.  We don't support any VPD pages, so
310107178Snjl	 * complain if EVPD or CMDDT is set.
311107178Snjl	 */
312107178Snjl	istate = tcmd_get_istate(ctio->init_id);
313225950Sken	sense = (struct scsi_sense_data_fixed *)&istate->sense_data;
314107178Snjl	if ((inq->byte2 & SI_EVPD) != 0) {
315107178Snjl		tcmd_illegal_req(atio, ctio);
316107178Snjl		sense->sense_key_spec[0] = SSD_SCS_VALID | SSD_FIELDPTR_CMD |
317107178Snjl			SSD_BITPTR_VALID | /*bit value*/1;
318107178Snjl		sense->sense_key_spec[1] = 0;
319107178Snjl		sense->sense_key_spec[2] =
320107178Snjl			offsetof(struct scsi_inquiry, byte2);
321107178Snjl	} else if (inq->page_code != 0) {
322107178Snjl		tcmd_illegal_req(atio, ctio);
323107178Snjl		sense->sense_key_spec[0] = SSD_SCS_VALID | SSD_FIELDPTR_CMD;
324107178Snjl		sense->sense_key_spec[1] = 0;
325107178Snjl		sense->sense_key_spec[2] =
326107178Snjl			offsetof(struct scsi_inquiry, page_code);
327107178Snjl	} else {
328107178Snjl		bcopy(&inq_data, ctio->data_ptr, sizeof(inq_data));
329107178Snjl		ctio->dxfer_len = inq_data.additional_length + 4;
330107178Snjl		ctio->dxfer_len = min(ctio->dxfer_len,
331229997Sken				      scsi_2btoul(inq->length));
332107178Snjl		ctio->ccb_h.flags |= CAM_DIR_IN | CAM_SEND_STATUS;
333107178Snjl		ctio->scsi_status = SCSI_STATUS_OK;
334107178Snjl	}
335107178Snjl	return (0);
336107178Snjl}
337107178Snjl
338107178Snjl/* Initialize the inquiry response structure with the requested flags */
339107178Snjlstatic int
340107178Snjlinit_inquiry(u_int16_t req_flags, u_int16_t sim_flags)
341107178Snjl{
342107178Snjl	struct scsi_inquiry_data *inq;
343107178Snjl
344107178Snjl	inq = &inq_data;
345107178Snjl	bzero(inq, sizeof(*inq));
346107178Snjl	inq->device = T_DIRECT | (SID_QUAL_LU_CONNECTED << 5);
347120428Ssimokawa#ifdef SCSI_REV_SPC
348107178Snjl	inq->version = SCSI_REV_SPC; /* was 2 */
349120428Ssimokawa#else
350120428Ssimokawa	inq->version = SCSI_REV_3; /* was 2 */
351120428Ssimokawa#endif
352107178Snjl
353107178Snjl	/*
354107178Snjl	 * XXX cpi.hba_inquiry doesn't support Addr16 so we give the
355107178Snjl	 * user what they want if they ask for it.
356107178Snjl	 */
357107178Snjl	if ((req_flags & SID_Addr16) != 0) {
358107178Snjl		sim_flags |= SID_Addr16;
359107178Snjl		warnx("Not sure SIM supports Addr16 but enabling it anyway");
360107178Snjl	}
361107178Snjl
362107178Snjl	/* Advertise only what the SIM can actually support */
363107178Snjl	req_flags &= sim_flags;
364162704Smjacob	scsi_ulto2b(req_flags, &inq->spc2_flags);
365107178Snjl
366107178Snjl	inq->response_format = 2; /* SCSI2 Inquiry Format */
367107178Snjl	inq->additional_length = SHORT_INQUIRY_LENGTH -
368107178Snjl		offsetof(struct scsi_inquiry_data, additional_length);
369107178Snjl	bcopy("FreeBSD ", inq->vendor, SID_VENDOR_SIZE);
370107178Snjl	bcopy("Emulated Disk   ", inq->product, SID_PRODUCT_SIZE);
371107178Snjl	bcopy("0.1 ", inq->revision, SID_REVISION_SIZE);
372107178Snjl	return (0);
373107178Snjl}
374107178Snjl
375107178Snjlstatic int
376107178Snjltcmd_req_sense(struct ccb_accept_tio *atio, struct ccb_scsiio *ctio)
377107178Snjl{
378107178Snjl	struct scsi_request_sense *rsense;
379225950Sken	struct scsi_sense_data_fixed *sense;
380107178Snjl	struct initiator_state *istate;
381107178Snjl	size_t dlen;
382107178Snjl	struct atio_descr *a_descr;
383107178Snjl
384107178Snjl	a_descr = (struct atio_descr *)atio->ccb_h.targ_descr;
385107178Snjl	rsense = (struct scsi_request_sense *)a_descr->cdb;
386107178Snjl
387107178Snjl	istate = tcmd_get_istate(ctio->init_id);
388225950Sken	sense = (struct scsi_sense_data_fixed *)&istate->sense_data;
389107178Snjl
390107178Snjl	if (debug) {
391107178Snjl		cdb_debug(a_descr->cdb, "REQ SENSE from %u: ", atio->init_id);
392107178Snjl		warnx("Sending sense: %#x %#x %#x", sense->flags,
393107178Snjl		      sense->add_sense_code, sense->add_sense_code_qual);
394107178Snjl	}
395107178Snjl
396107178Snjl	if (istate->orig_ca == 0) {
397107178Snjl		tcmd_sense(ctio->init_id, NULL, SSD_KEY_NO_SENSE, 0, 0);
398107178Snjl		warnx("REQUEST SENSE from %u but no pending CA!",
399107178Snjl		      ctio->init_id);
400107178Snjl	}
401107178Snjl
402107178Snjl	bcopy(sense, ctio->data_ptr, sizeof(struct scsi_sense_data));
403225950Sken	dlen = offsetof(struct scsi_sense_data_fixed, extra_len) +
404107178Snjl			sense->extra_len + 1;
405107178Snjl	ctio->dxfer_len = min(dlen, SCSI_CDB6_LEN(rsense->length));
406107178Snjl	ctio->ccb_h.flags |= CAM_DIR_IN | CAM_SEND_STATUS;
407107178Snjl	ctio->scsi_status = SCSI_STATUS_OK;
408107178Snjl	return (0);
409107178Snjl}
410107178Snjl
411107178Snjlstatic int
412107178Snjltcmd_rd_cap(struct ccb_accept_tio *atio, struct ccb_scsiio *ctio)
413107178Snjl{
414107178Snjl	struct scsi_read_capacity_data *srp;
415107178Snjl	struct atio_descr *a_descr;
416121184Ssimokawa	uint32_t vsize;
417107178Snjl
418107178Snjl	a_descr = (struct atio_descr *)atio->ccb_h.targ_descr;
419107178Snjl	srp = (struct scsi_read_capacity_data *)ctio->data_ptr;
420107178Snjl
421121184Ssimokawa	if (volume_size > 0xffffffff)
422121184Ssimokawa		vsize = 0xffffffff;
423121184Ssimokawa	else
424121184Ssimokawa		vsize = (uint32_t)(volume_size - 1);
425121184Ssimokawa
426107178Snjl	if (debug) {
427107178Snjl		cdb_debug(a_descr->cdb, "READ CAP from %u (%u, %u): ",
428121184Ssimokawa			  atio->init_id, vsize, sector_size);
429121184Ssimokawa	}
430121184Ssimokawa
431121184Ssimokawa	bzero(srp, sizeof(*srp));
432121184Ssimokawa	scsi_ulto4b(vsize, srp->addr);
433121184Ssimokawa	scsi_ulto4b(sector_size, srp->length);
434121184Ssimokawa
435121184Ssimokawa	ctio->dxfer_len = sizeof(*srp);
436121184Ssimokawa	ctio->ccb_h.flags |= CAM_DIR_IN | CAM_SEND_STATUS;
437121184Ssimokawa	ctio->scsi_status = SCSI_STATUS_OK;
438121184Ssimokawa	return (0);
439121184Ssimokawa}
440121184Ssimokawa
441121184Ssimokawa#ifdef READ_16
442121184Ssimokawastatic int
443121184Ssimokawatcmd_rd_cap16(struct ccb_accept_tio *atio, struct ccb_scsiio *ctio)
444121184Ssimokawa{
445121184Ssimokawa	struct scsi_read_capacity_16 *scsi_cmd;
446121184Ssimokawa	struct scsi_read_capacity_data_long *srp;
447121184Ssimokawa	struct atio_descr *a_descr;
448121184Ssimokawa
449121184Ssimokawa	a_descr = (struct atio_descr *)atio->ccb_h.targ_descr;
450121184Ssimokawa	scsi_cmd = (struct scsi_read_capacity_16 *)a_descr->cdb;
451121184Ssimokawa	srp = (struct scsi_read_capacity_data_long *)ctio->data_ptr;
452121184Ssimokawa
453121184Ssimokawa	if (scsi_cmd->service_action != SRC16_SERVICE_ACTION) {
454121184Ssimokawa		tcmd_illegal_req(atio, ctio);
455121184Ssimokawa		return (0);
456121184Ssimokawa	}
457121184Ssimokawa
458121184Ssimokawa	if (debug) {
459121184Ssimokawa		cdb_debug(a_descr->cdb, "READ CAP16 from %u (%u, %u): ",
460107178Snjl			  atio->init_id, volume_size - 1, sector_size);
461107178Snjl	}
462107178Snjl
463107178Snjl	bzero(srp, sizeof(*srp));
464121184Ssimokawa	scsi_u64to8b(volume_size - 1, srp->addr);
465107178Snjl	scsi_ulto4b(sector_size, srp->length);
466107178Snjl
467107178Snjl	ctio->dxfer_len = sizeof(*srp);
468107178Snjl	ctio->ccb_h.flags |= CAM_DIR_IN | CAM_SEND_STATUS;
469107178Snjl	ctio->scsi_status = SCSI_STATUS_OK;
470107178Snjl	return (0);
471107178Snjl}
472121184Ssimokawa#endif
473107178Snjl
474107178Snjlstatic int
475107178Snjltcmd_rdwr(struct ccb_accept_tio *atio, struct ccb_scsiio *ctio)
476107178Snjl{
477107178Snjl	struct atio_descr *a_descr;
478107178Snjl	struct ctio_descr *c_descr;
479107178Snjl	int ret;
480107178Snjl
481107178Snjl	a_descr = (struct atio_descr *)atio->ccb_h.targ_descr;
482107178Snjl	c_descr = (struct ctio_descr *)ctio->ccb_h.targ_descr;
483107178Snjl
484107178Snjl	/* Command needs to be decoded */
485225950Sken	if ((a_descr->flags & CAM_DIR_MASK) == CAM_DIR_BOTH) {
486107178Snjl		if (debug)
487107178Snjl			warnx("Calling rdwr_decode");
488107178Snjl		ret = tcmd_rdwr_decode(atio, ctio);
489107178Snjl		if (ret == 0) {
490107178Snjl			send_ccb((union ccb *)ctio, /*priority*/1);
491107178Snjl			return (0);
492107178Snjl		}
493107178Snjl	}
494107178Snjl	ctio->ccb_h.flags |= a_descr->flags;
495107178Snjl
496107178Snjl	/* Call appropriate work function */
497107178Snjl	if ((a_descr->flags & CAM_DIR_IN) != 0) {
498107178Snjl		ret = start_io(atio, ctio, CAM_DIR_IN);
499107178Snjl		if (debug)
500162704Smjacob			warnx("Starting %p DIR_IN @" OFF_FMT ":%u",
501162704Smjacob			    a_descr, c_descr->offset, a_descr->targ_req);
502107178Snjl	} else {
503107178Snjl		ret = start_io(atio, ctio, CAM_DIR_OUT);
504107178Snjl		if (debug)
505162704Smjacob			warnx("Starting %p DIR_OUT @" OFF_FMT ":%u",
506162704Smjacob			    a_descr, c_descr->offset, a_descr->init_req);
507107178Snjl	}
508107178Snjl
509107178Snjl	return (ret);
510107178Snjl}
511107178Snjl
512107178Snjlstatic int
513107178Snjltcmd_rdwr_decode(struct ccb_accept_tio *atio, struct ccb_scsiio *ctio)
514107178Snjl{
515121184Ssimokawa	uint64_t blkno;
516121184Ssimokawa	uint32_t count;
517107178Snjl	struct atio_descr *a_descr;
518107178Snjl	u_int8_t *cdb;
519107178Snjl
520107178Snjl	a_descr = (struct atio_descr *)atio->ccb_h.targ_descr;
521107178Snjl	cdb = a_descr->cdb;
522107178Snjl	if (debug)
523107178Snjl		cdb_debug(cdb, "R/W from %u: ", atio->init_id);
524107178Snjl
525121184Ssimokawa	switch (cdb[0]) {
526121184Ssimokawa	case READ_6:
527121184Ssimokawa	case WRITE_6:
528121184Ssimokawa	{
529107178Snjl		struct scsi_rw_6 *rw_6 = (struct scsi_rw_6 *)cdb;
530107178Snjl		blkno = scsi_3btoul(rw_6->addr);
531107178Snjl		count = rw_6->length;
532121184Ssimokawa		break;
533121184Ssimokawa	}
534121184Ssimokawa	case READ_10:
535121184Ssimokawa	case WRITE_10:
536121184Ssimokawa	{
537107178Snjl		struct scsi_rw_10 *rw_10 = (struct scsi_rw_10 *)cdb;
538107178Snjl		blkno = scsi_4btoul(rw_10->addr);
539107178Snjl		count = scsi_2btoul(rw_10->length);
540121184Ssimokawa		break;
541107178Snjl	}
542121184Ssimokawa#ifdef READ_16
543121184Ssimokawa	case READ_16:
544121184Ssimokawa	case WRITE_16:
545121184Ssimokawa	{
546121184Ssimokawa		struct scsi_rw_16 *rw_16 = (struct scsi_rw_16 *)cdb;
547121184Ssimokawa		blkno = scsi_8btou64(rw_16->addr);
548121184Ssimokawa		count = scsi_4btoul(rw_16->length);
549121184Ssimokawa		break;
550121184Ssimokawa	}
551121184Ssimokawa#endif
552121184Ssimokawa	default:
553121184Ssimokawa		tcmd_illegal_req(atio, ctio);
554121184Ssimokawa		return (0);
555121184Ssimokawa	}
556107178Snjl	if (blkno + count > volume_size) {
557107178Snjl		warnx("Attempt to access past end of volume");
558107178Snjl		tcmd_sense(ctio->init_id, ctio,
559107178Snjl			   SSD_KEY_ILLEGAL_REQUEST, 0x21, 0);
560107178Snjl		return (0);
561107178Snjl	}
562107178Snjl
563107178Snjl	/* Get an (overall) data length and set direction */
564107178Snjl	a_descr->base_off = ((off_t)blkno) * sector_size;
565107178Snjl	a_descr->total_len = count * sector_size;
566107178Snjl	if (a_descr->total_len == 0) {
567107178Snjl		if (debug)
568162704Smjacob			warnx("r/w 0 blocks @ blkno " OFF_FMT, blkno);
569107178Snjl		tcmd_null_ok(atio, ctio);
570107178Snjl		return (0);
571107178Snjl	} else if (cdb[0] == WRITE_6 || cdb[0] == WRITE_10) {
572107178Snjl		a_descr->flags |= CAM_DIR_OUT;
573107178Snjl		if (debug)
574162704Smjacob			warnx("write %u blocks @ blkno " OFF_FMT, count, blkno);
575107178Snjl	} else {
576107178Snjl		a_descr->flags |= CAM_DIR_IN;
577107178Snjl		if (debug)
578162704Smjacob			warnx("read %u blocks @ blkno " OFF_FMT,  count, blkno);
579107178Snjl	}
580107178Snjl	return (1);
581107178Snjl}
582107178Snjl
583107178Snjlstatic int
584107178Snjlstart_io(struct ccb_accept_tio *atio, struct ccb_scsiio *ctio, int dir)
585107178Snjl{
586107178Snjl	struct atio_descr *a_descr;
587107178Snjl	struct ctio_descr *c_descr;
588107178Snjl	int ret;
589107178Snjl
590107178Snjl	/* Set up common structures */
591107178Snjl	a_descr = (struct atio_descr *)atio->ccb_h.targ_descr;
592107178Snjl	c_descr = (struct ctio_descr *)ctio->ccb_h.targ_descr;
593107178Snjl
594107178Snjl	if (dir == CAM_DIR_IN) {
595107178Snjl		c_descr->offset = a_descr->base_off + a_descr->targ_req;
596107178Snjl		ctio->dxfer_len = a_descr->total_len - a_descr->targ_req;
597107178Snjl	} else {
598107178Snjl		c_descr->offset = a_descr->base_off + a_descr->init_req;
599107178Snjl		ctio->dxfer_len = a_descr->total_len - a_descr->init_req;
600107178Snjl	}
601107178Snjl	ctio->dxfer_len = min(ctio->dxfer_len, buf_size);
602107178Snjl	assert(ctio->dxfer_len >= 0);
603107178Snjl
604107178Snjl	c_descr->aiocb.aio_offset = c_descr->offset;
605107178Snjl	c_descr->aiocb.aio_nbytes = ctio->dxfer_len;
606107178Snjl
607107178Snjl	/* If DIR_IN, start read from target, otherwise begin CTIO xfer. */
608107178Snjl	ret = 1;
609107178Snjl	if (dir == CAM_DIR_IN) {
610162704Smjacob		if (notaio) {
611162704Smjacob			if (debug)
612228462Smav				warnx("read sync %lu @ block " OFF_FMT,
613162704Smjacob				    (unsigned long)
614162704Smjacob				    (ctio->dxfer_len / sector_size),
615162704Smjacob				    c_descr->offset / sector_size);
616162704Smjacob			if (lseek(c_descr->aiocb.aio_fildes,
617162704Smjacob			    c_descr->aiocb.aio_offset, SEEK_SET) < 0) {
618162704Smjacob				perror("lseek");
619162704Smjacob				err(1, "lseek");
620162704Smjacob			}
621162704Smjacob			if (read(c_descr->aiocb.aio_fildes,
622162704Smjacob			    (void *)c_descr->aiocb.aio_buf,
623162704Smjacob			    ctio->dxfer_len) != ctio->dxfer_len) {
624162704Smjacob				err(1, "read");
625162704Smjacob			}
626162704Smjacob		} else {
627162704Smjacob			if (debug)
628228462Smav				warnx("read async %lu @ block " OFF_FMT,
629162704Smjacob				    (unsigned long)
630162704Smjacob				    (ctio->dxfer_len / sector_size),
631162704Smjacob				    c_descr->offset / sector_size);
632162704Smjacob			if (aio_read(&c_descr->aiocb) < 0) {
633162704Smjacob				err(1, "aio_read"); /* XXX */
634162704Smjacob			}
635162704Smjacob		}
636107178Snjl		a_descr->targ_req += ctio->dxfer_len;
637162704Smjacob		/* if we're done, we can mark the CCB as to send status */
638107178Snjl		if (a_descr->targ_req == a_descr->total_len) {
639107178Snjl			ctio->ccb_h.flags |= CAM_SEND_STATUS;
640107178Snjl			ctio->scsi_status = SCSI_STATUS_OK;
641107178Snjl			ret = 0;
642107178Snjl		}
643162704Smjacob		if (notaio)
644162704Smjacob			tcmd_rdwr_done(atio, ctio, AIO_DONE);
645107178Snjl	} else {
646107178Snjl		if (a_descr->targ_ack == a_descr->total_len)
647107178Snjl			tcmd_null_ok(atio, ctio);
648107178Snjl		a_descr->init_req += ctio->dxfer_len;
649107178Snjl		if (a_descr->init_req == a_descr->total_len &&
650107178Snjl		    ctio->dxfer_len > 0) {
651107178Snjl			/*
652107178Snjl			 * If data phase done, remove atio from workq.
653107178Snjl			 * The completion handler will call work_atio to
654107178Snjl			 * send the final status.
655107178Snjl			 */
656107178Snjl			ret = 0;
657107178Snjl		}
658107178Snjl		send_ccb((union ccb *)ctio, /*priority*/1);
659107178Snjl	}
660107178Snjl
661107178Snjl	return (ret);
662107178Snjl}
663107178Snjl
664107178Snjlstatic void
665107178Snjltcmd_rdwr_done(struct ccb_accept_tio *atio, struct ccb_scsiio *ctio,
666107178Snjl	       io_ops event)
667107178Snjl{
668107178Snjl	struct atio_descr *a_descr;
669107178Snjl	struct ctio_descr *c_descr;
670107178Snjl
671107178Snjl	a_descr = (struct atio_descr *)atio->ccb_h.targ_descr;
672107178Snjl	c_descr = (struct ctio_descr *)ctio->ccb_h.targ_descr;
673107178Snjl
674107178Snjl	switch (event) {
675107178Snjl	case AIO_DONE:
676162704Smjacob		if (!notaio && aio_return(&c_descr->aiocb) < 0) {
677107178Snjl			warn("aio_return error");
678107178Snjl			/* XXX */
679107178Snjl			tcmd_sense(ctio->init_id, ctio,
680107178Snjl				   SSD_KEY_MEDIUM_ERROR, 0, 0);
681107178Snjl			send_ccb((union ccb *)ctio, /*priority*/1);
682107178Snjl			break;
683107178Snjl		}
684107178Snjl		a_descr->targ_ack += ctio->dxfer_len;
685107178Snjl		if ((a_descr->flags & CAM_DIR_IN) != 0) {
686162704Smjacob			if (debug) {
687162704Smjacob				if (notaio)
688162704Smjacob					warnx("sending CTIO for AIO read");
689162704Smjacob				else
690162704Smjacob					warnx("sending CTIO for sync read");
691162704Smjacob			}
692107178Snjl			a_descr->init_req += ctio->dxfer_len;
693107178Snjl			send_ccb((union ccb *)ctio, /*priority*/1);
694107178Snjl		} else {
695107178Snjl			/* Use work function to send final status */
696107178Snjl			if (a_descr->init_req == a_descr->total_len)
697107178Snjl				work_atio(atio);
698107178Snjl			if (debug)
699107178Snjl				warnx("AIO done freeing CTIO");
700107178Snjl			free_ccb((union ccb *)ctio);
701107178Snjl		}
702107178Snjl		break;
703107178Snjl	case CTIO_DONE:
704157670Smjacob		switch (ctio->ccb_h.status & CAM_STATUS_MASK) {
705157670Smjacob		case CAM_REQ_CMP:
706157670Smjacob			break;
707157670Smjacob		case CAM_REQUEUE_REQ:
708157670Smjacob			warnx("requeueing request");
709157670Smjacob			if ((a_descr->flags & CAM_DIR_MASK) == CAM_DIR_OUT) {
710157670Smjacob				if (aio_write(&c_descr->aiocb) < 0) {
711157670Smjacob					err(1, "aio_write"); /* XXX */
712157670Smjacob				}
713157670Smjacob			} else {
714157670Smjacob				if (aio_read(&c_descr->aiocb) < 0) {
715157670Smjacob					err(1, "aio_read"); /* XXX */
716157670Smjacob				}
717157670Smjacob			}
718157670Smjacob			return;
719157670Smjacob		default:
720107178Snjl			errx(1, "CTIO failed, status %#x", ctio->ccb_h.status);
721107178Snjl		}
722107178Snjl		a_descr->init_ack += ctio->dxfer_len;
723107178Snjl		if ((a_descr->flags & CAM_DIR_MASK) == CAM_DIR_OUT &&
724107178Snjl		    ctio->dxfer_len > 0) {
725107178Snjl			a_descr->targ_req += ctio->dxfer_len;
726162704Smjacob			if (notaio) {
727162704Smjacob				if (debug)
728228462Smav					warnx("write sync %lu @ block "
729162704Smjacob					    OFF_FMT, (unsigned long)
730162704Smjacob					    (ctio->dxfer_len / sector_size),
731162704Smjacob					    c_descr->offset / sector_size);
732162704Smjacob				if (lseek(c_descr->aiocb.aio_fildes,
733162704Smjacob				    c_descr->aiocb.aio_offset, SEEK_SET) < 0) {
734162704Smjacob					perror("lseek");
735162704Smjacob					err(1, "lseek");
736162704Smjacob				}
737162704Smjacob				if (write(c_descr->aiocb.aio_fildes,
738162704Smjacob				    (void *) c_descr->aiocb.aio_buf,
739162704Smjacob				    ctio->dxfer_len) != ctio->dxfer_len) {
740162704Smjacob					err(1, "write");
741162704Smjacob				}
742162704Smjacob				tcmd_rdwr_done(atio, ctio, AIO_DONE);
743162704Smjacob			} else {
744162704Smjacob				if (debug)
745228462Smav					warnx("write async %lu @ block "
746162704Smjacob					    OFF_FMT, (unsigned long)
747162704Smjacob					    (ctio->dxfer_len / sector_size),
748162704Smjacob					    c_descr->offset / sector_size);
749162704Smjacob				if (aio_write(&c_descr->aiocb) < 0) {
750162704Smjacob					err(1, "aio_write"); /* XXX */
751162704Smjacob				}
752162704Smjacob			}
753107178Snjl		} else {
754107178Snjl			if (debug)
755107178Snjl				warnx("CTIO done freeing CTIO");
756107178Snjl			free_ccb((union ccb *)ctio);
757107178Snjl		}
758107178Snjl		break;
759107178Snjl	default:
760107178Snjl		warnx("Unknown completion code %d", event);
761107178Snjl		abort();
762107178Snjl		/* NOTREACHED */
763107178Snjl	}
764107178Snjl}
765107178Snjl
766107178Snjl/* Simple ok message used by TUR, SYNC_CACHE, etc. */
767107178Snjlstatic int
768107178Snjltcmd_null_ok(struct ccb_accept_tio *atio, struct ccb_scsiio *ctio)
769107178Snjl{
770107178Snjl	if (debug) {
771107178Snjl		struct atio_descr *a_descr;
772107178Snjl
773107178Snjl		a_descr = (struct atio_descr *)atio->ccb_h.targ_descr;
774107178Snjl		cdb_debug(a_descr->cdb, "Sending null ok to %u : ", atio->init_id);
775107178Snjl	}
776107178Snjl
777107178Snjl	ctio->dxfer_len = 0;
778107178Snjl	ctio->ccb_h.flags &= ~CAM_DIR_MASK;
779107178Snjl	ctio->ccb_h.flags |= CAM_DIR_NONE | CAM_SEND_STATUS;
780107178Snjl	ctio->scsi_status = SCSI_STATUS_OK;
781107178Snjl	return (0);
782107178Snjl}
783107178Snjl
784107178Snjl/* Simple illegal request message used by MODE SENSE, etc. */
785107178Snjlstatic int
786107178Snjltcmd_illegal_req(struct ccb_accept_tio *atio, struct ccb_scsiio *ctio)
787107178Snjl{
788107178Snjl	if (debug) {
789107178Snjl		struct atio_descr *a_descr;
790107178Snjl
791107178Snjl		a_descr = (struct atio_descr *)atio->ccb_h.targ_descr;
792107178Snjl		cdb_debug(a_descr->cdb, "Sending ill req to %u: ", atio->init_id);
793107178Snjl	}
794107178Snjl
795107178Snjl	tcmd_sense(atio->init_id, ctio, SSD_KEY_ILLEGAL_REQUEST,
796107178Snjl		   /*asc*/0x24, /*ascq*/0);
797107178Snjl	return (0);
798107178Snjl}
799107178Snjl
800107178Snjlstatic void
801107178Snjlcdb_debug(u_int8_t *cdb, const char *msg, ...)
802107178Snjl{
803107178Snjl	char msg_buf[512];
804107178Snjl	int len;
805107178Snjl	va_list ap;
806107178Snjl
807107178Snjl	va_start(ap, msg);
808107178Snjl	vsnprintf(msg_buf, sizeof(msg_buf), msg, ap);
809107178Snjl	va_end(ap);
810107178Snjl	len = strlen(msg_buf);
811107178Snjl	scsi_cdb_string(cdb, msg_buf + len, sizeof(msg_buf) - len);
812107178Snjl	warnx("%s", msg_buf);
813107178Snjl}
814