scsi_cmds.c revision 121184
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 121184 2003-10-18 04:54:08Z simokawa $
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>
39121184Ssimokawa#include <sys/param.h>
40107178Snjl#include <sys/types.h>
41107178Snjl
42107178Snjl#include <cam/cam.h>
43107178Snjl#include <cam/cam_ccb.h>
44107178Snjl#include <cam/scsi/scsi_all.h>
45107178Snjl#include <cam/scsi/scsi_targetio.h>
46107178Snjl#include "scsi_target.h"
47107178Snjl
48107178Snjltypedef int targ_start_func(struct ccb_accept_tio *, struct ccb_scsiio *);
49107178Snjltypedef void targ_done_func(struct ccb_accept_tio *, struct ccb_scsiio *,
50107178Snjl			      io_ops);
51107178Snjl
52107178Snjlstruct targ_cdb_handlers {
53107178Snjl	u_int8_t	  cmd;
54107178Snjl	targ_start_func  *start;
55107178Snjl	targ_done_func	 *done;
56107178Snjl#define ILLEGAL_CDB	  0xFF
57107178Snjl};
58107178Snjl
59107178Snjlstatic targ_start_func		tcmd_inquiry;
60107178Snjlstatic targ_start_func		tcmd_req_sense;
61107178Snjlstatic targ_start_func		tcmd_rd_cap;
62121184Ssimokawa#ifdef READ_16
63121184Ssimokawastatic targ_start_func		tcmd_rd_cap16;
64121184Ssimokawa#endif
65107178Snjlstatic targ_start_func		tcmd_rdwr;
66107178Snjlstatic targ_start_func		tcmd_rdwr_decode;
67107178Snjlstatic targ_done_func		tcmd_rdwr_done;
68107178Snjlstatic targ_start_func		tcmd_null_ok;
69107178Snjlstatic targ_start_func		tcmd_illegal_req;
70107178Snjlstatic int			start_io(struct ccb_accept_tio *atio,
71107178Snjl					 struct ccb_scsiio *ctio, int dir);
72107178Snjlstatic int init_inquiry(u_int16_t req_flags, u_int16_t sim_flags);
73107178Snjlstatic struct initiator_state *
74107178Snjl			tcmd_get_istate(u_int init_id);
75107178Snjlstatic void cdb_debug(u_int8_t *cdb, const char *msg, ...);
76107178Snjl
77107178Snjlstatic struct targ_cdb_handlers cdb_handlers[] = {
78107178Snjl	{ READ_10,		tcmd_rdwr,		tcmd_rdwr_done },
79107178Snjl	{ WRITE_10,		tcmd_rdwr,		tcmd_rdwr_done },
80107178Snjl	{ READ_6,		tcmd_rdwr,		tcmd_rdwr_done },
81107178Snjl	{ WRITE_6,		tcmd_rdwr,		tcmd_rdwr_done },
82107178Snjl	{ INQUIRY,		tcmd_inquiry,		NULL },
83107178Snjl	{ REQUEST_SENSE,	tcmd_req_sense,		NULL },
84107178Snjl	{ READ_CAPACITY,	tcmd_rd_cap,		NULL },
85107178Snjl	{ TEST_UNIT_READY,	tcmd_null_ok,		NULL },
86107178Snjl	{ START_STOP_UNIT,	tcmd_null_ok,		NULL },
87107178Snjl	{ SYNCHRONIZE_CACHE,	tcmd_null_ok,		NULL },
88107178Snjl	{ MODE_SENSE_6,		tcmd_illegal_req,	NULL },
89107178Snjl	{ MODE_SELECT_6,	tcmd_illegal_req,	NULL },
90121184Ssimokawa#ifdef READ_16
91121184Ssimokawa	{ READ_16,		tcmd_rdwr,		tcmd_rdwr_done },
92121184Ssimokawa	{ WRITE_16,		tcmd_rdwr,		tcmd_rdwr_done },
93121184Ssimokawa	{ SERVICE_ACTION_IN,	tcmd_rd_cap16,		NULL },
94121184Ssimokawa#endif
95107178Snjl	{ ILLEGAL_CDB,		NULL,			NULL }
96107178Snjl};
97107178Snjl
98107178Snjlstatic struct scsi_inquiry_data inq_data;
99107178Snjlstatic struct initiator_state istates[MAX_INITIATORS];
100107178Snjlextern int		debug;
101121184Ssimokawaextern uint64_t		volume_size;
102107178Snjlextern size_t		sector_size;
103107178Snjlextern size_t		buf_size;
104107178Snjl
105107178Snjlcam_status
106107178Snjltcmd_init(u_int16_t req_inq_flags, u_int16_t sim_inq_flags)
107107178Snjl{
108107178Snjl	struct initiator_state *istate;
109107178Snjl	int i, ret;
110107178Snjl
111107178Snjl	/* Initialize our inquiry data */
112107178Snjl	ret = init_inquiry(req_inq_flags, sim_inq_flags);
113107178Snjl	if (ret != 0)
114107178Snjl        	return (ret);
115107178Snjl
116107178Snjl	/* We start out life with a UA to indicate power-on/reset. */
117107178Snjl	for (i = 0; i < MAX_INITIATORS; i++) {
118107178Snjl		istate = tcmd_get_istate(i);
119107178Snjl		bzero(istate, sizeof(*istate));
120107178Snjl		istate->pending_ua = UA_POWER_ON;
121107178Snjl	}
122107178Snjl
123107178Snjl	return (0);
124107178Snjl}
125107178Snjl
126107178Snjl/* Caller allocates CTIO, sets its init_id
127107178Snjlreturn 0 if done, 1 if more processing needed
128107178Snjlon 0, caller sets SEND_STATUS */
129107178Snjlint
130107178Snjltcmd_handle(struct ccb_accept_tio *atio, struct ccb_scsiio *ctio, io_ops event)
131107178Snjl{
132107178Snjl	static struct targ_cdb_handlers *last_cmd;
133107178Snjl	struct initiator_state *istate;
134107178Snjl	struct atio_descr *a_descr;
135107178Snjl	int ret;
136107178Snjl
137109345Snjl	if (debug) {
138109345Snjl		warnx("tcmd_handle atio %p ctio %p atioflags %#x", atio, ctio,
139109345Snjl		      atio->ccb_h.flags);
140109345Snjl	}
141107178Snjl	ret = 0;
142107178Snjl	a_descr = (struct atio_descr *)atio->ccb_h.targ_descr;
143107178Snjl
144107178Snjl	/* Do a full lookup if one-behind cache failed */
145107178Snjl	if (last_cmd == NULL || last_cmd->cmd != a_descr->cdb[0]) {
146107178Snjl		struct targ_cdb_handlers *h;
147107178Snjl
148107178Snjl		for (h = cdb_handlers; h->cmd != ILLEGAL_CDB; h++) {
149107178Snjl			if (a_descr->cdb[0] == h->cmd)
150107178Snjl				break;
151107178Snjl		}
152107178Snjl		last_cmd = h;
153107178Snjl	}
154107178Snjl	if (last_cmd->cmd == ILLEGAL_CDB) {
155107178Snjl		if (event != ATIO_WORK) {
156107178Snjl			warnx("no done func for %#x???", a_descr->cdb[0]);
157107178Snjl			abort();
158107178Snjl		}
159107178Snjl		/* Not found, return illegal request */
160107178Snjl		warnx("cdb %#x not handled", a_descr->cdb[0]);
161107178Snjl		tcmd_illegal_req(atio, ctio);
162107178Snjl		send_ccb((union ccb *)ctio, /*priority*/1);
163107178Snjl		return (0);
164107178Snjl	}
165107178Snjl
166107178Snjl	/* call completion and exit */
167107178Snjl	if (event != ATIO_WORK) {
168107178Snjl		if (last_cmd->done != NULL)
169107178Snjl			last_cmd->done(atio, ctio, event);
170107178Snjl		else
171107178Snjl			free_ccb((union ccb *)ctio);
172107178Snjl		return (1);
173107178Snjl	}
174107178Snjl
175107178Snjl	istate = tcmd_get_istate(ctio->init_id);
176107178Snjl	if (istate == NULL) {
177107178Snjl		tcmd_illegal_req(atio, ctio);
178107178Snjl		send_ccb((union ccb *)ctio, /*priority*/1);
179107178Snjl		return (0);
180107178Snjl	}
181107178Snjl
182107178Snjl	if (istate->pending_ca == 0 && istate->pending_ua != 0 &&
183107178Snjl	    a_descr->cdb[0] != INQUIRY) {
184107178Snjl		tcmd_sense(ctio->init_id, ctio, SSD_KEY_UNIT_ATTENTION,
185107178Snjl			   0x29, istate->pending_ua == UA_POWER_ON ? 1 : 2);
186107178Snjl		istate->pending_ca = CA_UNIT_ATTN;
187107178Snjl		if (debug) {
188107178Snjl			cdb_debug(a_descr->cdb, "UA active for %u: ",
189107178Snjl				  atio->init_id);
190107178Snjl		}
191107178Snjl		send_ccb((union ccb *)ctio, /*priority*/1);
192107178Snjl		return (0);
193107178Snjl	}
194107178Snjl
195107178Snjl	/* Store current CA and UA for later */
196107178Snjl	istate->orig_ua = istate->pending_ua;
197107178Snjl	istate->orig_ca = istate->pending_ca;
198107178Snjl
199107178Snjl	/*
200107178Snjl	 * As per SAM2, any command that occurs
201107178Snjl	 * after a CA is reported, clears the CA.  We must
202107178Snjl	 * also clear the UA condition, if any, that caused
203107178Snjl	 * the CA to occur assuming the UA is not for a
204107178Snjl	 * persistent condition.
205107178Snjl	 */
206107178Snjl	istate->pending_ca = CA_NONE;
207107178Snjl	if (istate->orig_ca == CA_UNIT_ATTN)
208107178Snjl		istate->pending_ua = UA_NONE;
209107178Snjl
210107178Snjl	/* If we have a valid handler, call start or completion function */
211107178Snjl	if (last_cmd->cmd != ILLEGAL_CDB) {
212107178Snjl		ret = last_cmd->start(atio, ctio);
213107178Snjl		/* XXX hack */
214107178Snjl		if (last_cmd->start != tcmd_rdwr) {
215107178Snjl			a_descr->init_req += ctio->dxfer_len;
216107178Snjl			send_ccb((union ccb *)ctio, /*priority*/1);
217107178Snjl		}
218107178Snjl	}
219107178Snjl
220107178Snjl	return (ret);
221107178Snjl}
222107178Snjl
223107178Snjlstatic struct initiator_state *
224107178Snjltcmd_get_istate(u_int init_id)
225107178Snjl{
226107178Snjl	if (init_id >= MAX_INITIATORS) {
227107178Snjl		warnx("illegal init_id %d, max %d", init_id, MAX_INITIATORS - 1);
228107178Snjl		return (NULL);
229107178Snjl	} else {
230107178Snjl		return (&istates[init_id]);
231107178Snjl	}
232107178Snjl}
233107178Snjl
234107178Snjlvoid
235107178Snjltcmd_sense(u_int init_id, struct ccb_scsiio *ctio, u_int8_t flags,
236107178Snjl	       u_int8_t asc, u_int8_t ascq)
237107178Snjl{
238107178Snjl	struct initiator_state *istate;
239107178Snjl	struct scsi_sense_data *sense;
240107178Snjl
241107178Snjl	/* Set our initiator's istate */
242107178Snjl	istate = tcmd_get_istate(init_id);
243107178Snjl	if (istate == NULL)
244107178Snjl		return;
245107178Snjl	istate->pending_ca |= CA_CMD_SENSE; /* XXX set instead of or? */
246107178Snjl	sense = &istate->sense_data;
247107178Snjl	bzero(sense, sizeof(*sense));
248107178Snjl	sense->error_code = SSD_CURRENT_ERROR;
249107178Snjl	sense->flags = flags;
250107178Snjl	sense->add_sense_code = asc;
251107178Snjl	sense->add_sense_code_qual = ascq;
252107178Snjl	sense->extra_len =
253107178Snjl		offsetof(struct scsi_sense_data, sense_key_spec[2]) -
254107178Snjl		offsetof(struct scsi_sense_data, extra_len);
255107178Snjl
256107178Snjl	/* Fill out the supplied CTIO */
257107178Snjl	if (ctio != NULL) {
258107178Snjl		bcopy(sense, &ctio->sense_data, sizeof(*sense));
259120428Ssimokawa		ctio->sense_len = sizeof(*sense);  /* XXX */
260107178Snjl		ctio->ccb_h.flags &= ~CAM_DIR_MASK;
261107178Snjl		ctio->ccb_h.flags |= CAM_DIR_NONE | /* CAM_SEND_SENSE | */
262107178Snjl				     CAM_SEND_STATUS;
263107178Snjl		ctio->dxfer_len = 0;
264107178Snjl		ctio->scsi_status = SCSI_STATUS_CHECK_COND;
265107178Snjl	}
266107178Snjl}
267107178Snjl
268107178Snjlvoid
269107178Snjltcmd_ua(u_int init_id, ua_types new_ua)
270107178Snjl{
271107178Snjl	struct initiator_state *istate;
272107178Snjl	u_int start, end;
273107178Snjl
274107178Snjl	if (init_id == CAM_TARGET_WILDCARD) {
275107178Snjl		start = 0;
276107178Snjl		end = MAX_INITIATORS - 1;
277107178Snjl	} else {
278107178Snjl		start = end = init_id;
279107178Snjl	}
280107178Snjl
281107178Snjl	for (; start <= end; start++) {
282107178Snjl		istate = tcmd_get_istate(start);
283107178Snjl		if (istate == NULL)
284107178Snjl			break;
285107178Snjl		istate->pending_ua = new_ua;
286107178Snjl	}
287107178Snjl}
288107178Snjl
289107178Snjlstatic int
290107178Snjltcmd_inquiry(struct ccb_accept_tio *atio, struct ccb_scsiio *ctio)
291107178Snjl{
292107178Snjl	struct scsi_inquiry *inq;
293107178Snjl	struct atio_descr *a_descr;
294107178Snjl	struct initiator_state *istate;
295107178Snjl	struct scsi_sense_data *sense;
296107178Snjl
297107178Snjl	a_descr = (struct atio_descr *)atio->ccb_h.targ_descr;
298107178Snjl	inq = (struct scsi_inquiry *)a_descr->cdb;
299107178Snjl
300107178Snjl	if (debug)
301107178Snjl		cdb_debug(a_descr->cdb, "INQUIRY from %u: ", atio->init_id);
302107178Snjl	/*
303107178Snjl	 * Validate the command.  We don't support any VPD pages, so
304107178Snjl	 * complain if EVPD or CMDDT is set.
305107178Snjl	 */
306107178Snjl	istate = tcmd_get_istate(ctio->init_id);
307107178Snjl	sense = &istate->sense_data;
308107178Snjl	if ((inq->byte2 & SI_EVPD) != 0) {
309107178Snjl		tcmd_illegal_req(atio, ctio);
310107178Snjl		sense->sense_key_spec[0] = SSD_SCS_VALID | SSD_FIELDPTR_CMD |
311107178Snjl			SSD_BITPTR_VALID | /*bit value*/1;
312107178Snjl		sense->sense_key_spec[1] = 0;
313107178Snjl		sense->sense_key_spec[2] =
314107178Snjl			offsetof(struct scsi_inquiry, byte2);
315107178Snjl	} else if (inq->page_code != 0) {
316107178Snjl		tcmd_illegal_req(atio, ctio);
317107178Snjl		sense->sense_key_spec[0] = SSD_SCS_VALID | SSD_FIELDPTR_CMD;
318107178Snjl		sense->sense_key_spec[1] = 0;
319107178Snjl		sense->sense_key_spec[2] =
320107178Snjl			offsetof(struct scsi_inquiry, page_code);
321107178Snjl	} else {
322107178Snjl		bcopy(&inq_data, ctio->data_ptr, sizeof(inq_data));
323107178Snjl		ctio->dxfer_len = inq_data.additional_length + 4;
324107178Snjl		ctio->dxfer_len = min(ctio->dxfer_len,
325107178Snjl				      SCSI_CDB6_LEN(inq->length));
326107178Snjl		ctio->ccb_h.flags |= CAM_DIR_IN | CAM_SEND_STATUS;
327107178Snjl		ctio->scsi_status = SCSI_STATUS_OK;
328107178Snjl	}
329107178Snjl	return (0);
330107178Snjl}
331107178Snjl
332107178Snjl/* Initialize the inquiry response structure with the requested flags */
333107178Snjlstatic int
334107178Snjlinit_inquiry(u_int16_t req_flags, u_int16_t sim_flags)
335107178Snjl{
336107178Snjl	struct scsi_inquiry_data *inq;
337107178Snjl
338107178Snjl	inq = &inq_data;
339107178Snjl	bzero(inq, sizeof(*inq));
340107178Snjl	inq->device = T_DIRECT | (SID_QUAL_LU_CONNECTED << 5);
341120428Ssimokawa#ifdef SCSI_REV_SPC
342107178Snjl	inq->version = SCSI_REV_SPC; /* was 2 */
343120428Ssimokawa#else
344120428Ssimokawa	inq->version = SCSI_REV_3; /* was 2 */
345120428Ssimokawa#endif
346107178Snjl
347107178Snjl	/*
348107178Snjl	 * XXX cpi.hba_inquiry doesn't support Addr16 so we give the
349107178Snjl	 * user what they want if they ask for it.
350107178Snjl	 */
351107178Snjl	if ((req_flags & SID_Addr16) != 0) {
352107178Snjl		sim_flags |= SID_Addr16;
353107178Snjl		warnx("Not sure SIM supports Addr16 but enabling it anyway");
354107178Snjl	}
355107178Snjl
356107178Snjl	/* Advertise only what the SIM can actually support */
357107178Snjl	req_flags &= sim_flags;
358107178Snjl	scsi_ulto2b(req_flags, &inq->reserved[1]);
359107178Snjl
360107178Snjl	inq->response_format = 2; /* SCSI2 Inquiry Format */
361107178Snjl	inq->additional_length = SHORT_INQUIRY_LENGTH -
362107178Snjl		offsetof(struct scsi_inquiry_data, additional_length);
363107178Snjl	bcopy("FreeBSD ", inq->vendor, SID_VENDOR_SIZE);
364107178Snjl	bcopy("Emulated Disk   ", inq->product, SID_PRODUCT_SIZE);
365107178Snjl	bcopy("0.1 ", inq->revision, SID_REVISION_SIZE);
366107178Snjl	return (0);
367107178Snjl}
368107178Snjl
369107178Snjlstatic int
370107178Snjltcmd_req_sense(struct ccb_accept_tio *atio, struct ccb_scsiio *ctio)
371107178Snjl{
372107178Snjl	struct scsi_request_sense *rsense;
373107178Snjl	struct scsi_sense_data *sense;
374107178Snjl	struct initiator_state *istate;
375107178Snjl	size_t dlen;
376107178Snjl	struct atio_descr *a_descr;
377107178Snjl
378107178Snjl	a_descr = (struct atio_descr *)atio->ccb_h.targ_descr;
379107178Snjl	rsense = (struct scsi_request_sense *)a_descr->cdb;
380107178Snjl
381107178Snjl	istate = tcmd_get_istate(ctio->init_id);
382107178Snjl	sense = &istate->sense_data;
383107178Snjl
384107178Snjl	if (debug) {
385107178Snjl		cdb_debug(a_descr->cdb, "REQ SENSE from %u: ", atio->init_id);
386107178Snjl		warnx("Sending sense: %#x %#x %#x", sense->flags,
387107178Snjl		      sense->add_sense_code, sense->add_sense_code_qual);
388107178Snjl	}
389107178Snjl
390107178Snjl	if (istate->orig_ca == 0) {
391107178Snjl		tcmd_sense(ctio->init_id, NULL, SSD_KEY_NO_SENSE, 0, 0);
392107178Snjl		warnx("REQUEST SENSE from %u but no pending CA!",
393107178Snjl		      ctio->init_id);
394107178Snjl	}
395107178Snjl
396107178Snjl	bcopy(sense, ctio->data_ptr, sizeof(struct scsi_sense_data));
397107178Snjl	dlen = offsetof(struct scsi_sense_data, extra_len) +
398107178Snjl			sense->extra_len + 1;
399107178Snjl	ctio->dxfer_len = min(dlen, SCSI_CDB6_LEN(rsense->length));
400107178Snjl	ctio->ccb_h.flags |= CAM_DIR_IN | CAM_SEND_STATUS;
401107178Snjl	ctio->scsi_status = SCSI_STATUS_OK;
402107178Snjl	return (0);
403107178Snjl}
404107178Snjl
405107178Snjlstatic int
406107178Snjltcmd_rd_cap(struct ccb_accept_tio *atio, struct ccb_scsiio *ctio)
407107178Snjl{
408107178Snjl	struct scsi_read_capacity_data *srp;
409107178Snjl	struct atio_descr *a_descr;
410121184Ssimokawa	uint32_t vsize;
411107178Snjl
412107178Snjl	a_descr = (struct atio_descr *)atio->ccb_h.targ_descr;
413107178Snjl	srp = (struct scsi_read_capacity_data *)ctio->data_ptr;
414107178Snjl
415121184Ssimokawa	if (volume_size > 0xffffffff)
416121184Ssimokawa		vsize = 0xffffffff;
417121184Ssimokawa	else
418121184Ssimokawa		vsize = (uint32_t)(volume_size - 1);
419121184Ssimokawa
420107178Snjl	if (debug) {
421107178Snjl		cdb_debug(a_descr->cdb, "READ CAP from %u (%u, %u): ",
422121184Ssimokawa			  atio->init_id, vsize, sector_size);
423121184Ssimokawa	}
424121184Ssimokawa
425121184Ssimokawa	bzero(srp, sizeof(*srp));
426121184Ssimokawa	scsi_ulto4b(vsize, srp->addr);
427121184Ssimokawa	scsi_ulto4b(sector_size, srp->length);
428121184Ssimokawa
429121184Ssimokawa	ctio->dxfer_len = sizeof(*srp);
430121184Ssimokawa	ctio->ccb_h.flags |= CAM_DIR_IN | CAM_SEND_STATUS;
431121184Ssimokawa	ctio->scsi_status = SCSI_STATUS_OK;
432121184Ssimokawa	return (0);
433121184Ssimokawa}
434121184Ssimokawa
435121184Ssimokawa#ifdef READ_16
436121184Ssimokawastatic int
437121184Ssimokawatcmd_rd_cap16(struct ccb_accept_tio *atio, struct ccb_scsiio *ctio)
438121184Ssimokawa{
439121184Ssimokawa	struct scsi_read_capacity_16 *scsi_cmd;
440121184Ssimokawa	struct scsi_read_capacity_data_long *srp;
441121184Ssimokawa	struct atio_descr *a_descr;
442121184Ssimokawa
443121184Ssimokawa	a_descr = (struct atio_descr *)atio->ccb_h.targ_descr;
444121184Ssimokawa	scsi_cmd = (struct scsi_read_capacity_16 *)a_descr->cdb;
445121184Ssimokawa	srp = (struct scsi_read_capacity_data_long *)ctio->data_ptr;
446121184Ssimokawa
447121184Ssimokawa	if (scsi_cmd->service_action != SRC16_SERVICE_ACTION) {
448121184Ssimokawa		tcmd_illegal_req(atio, ctio);
449121184Ssimokawa		return (0);
450121184Ssimokawa	}
451121184Ssimokawa
452121184Ssimokawa	if (debug) {
453121184Ssimokawa		cdb_debug(a_descr->cdb, "READ CAP16 from %u (%u, %u): ",
454107178Snjl			  atio->init_id, volume_size - 1, sector_size);
455107178Snjl	}
456107178Snjl
457107178Snjl	bzero(srp, sizeof(*srp));
458121184Ssimokawa	scsi_u64to8b(volume_size - 1, srp->addr);
459107178Snjl	scsi_ulto4b(sector_size, srp->length);
460107178Snjl
461107178Snjl	ctio->dxfer_len = sizeof(*srp);
462107178Snjl	ctio->ccb_h.flags |= CAM_DIR_IN | CAM_SEND_STATUS;
463107178Snjl	ctio->scsi_status = SCSI_STATUS_OK;
464107178Snjl	return (0);
465107178Snjl}
466121184Ssimokawa#endif
467107178Snjl
468107178Snjlstatic int
469107178Snjltcmd_rdwr(struct ccb_accept_tio *atio, struct ccb_scsiio *ctio)
470107178Snjl{
471107178Snjl	struct atio_descr *a_descr;
472107178Snjl	struct ctio_descr *c_descr;
473107178Snjl	int ret;
474107178Snjl
475107178Snjl	a_descr = (struct atio_descr *)atio->ccb_h.targ_descr;
476107178Snjl	c_descr = (struct ctio_descr *)ctio->ccb_h.targ_descr;
477107178Snjl
478107178Snjl	/* Command needs to be decoded */
479107178Snjl	if ((a_descr->flags & CAM_DIR_MASK) == CAM_DIR_RESV) {
480107178Snjl		if (debug)
481107178Snjl			warnx("Calling rdwr_decode");
482107178Snjl		ret = tcmd_rdwr_decode(atio, ctio);
483107178Snjl		if (ret == 0) {
484107178Snjl			send_ccb((union ccb *)ctio, /*priority*/1);
485107178Snjl			return (0);
486107178Snjl		}
487107178Snjl	}
488107178Snjl	ctio->ccb_h.flags |= a_descr->flags;
489107178Snjl
490107178Snjl	/* Call appropriate work function */
491107178Snjl	if ((a_descr->flags & CAM_DIR_IN) != 0) {
492107178Snjl		ret = start_io(atio, ctio, CAM_DIR_IN);
493107178Snjl		if (debug)
494121184Ssimokawa#if __FreeBSD_version >= 500000
495121184Ssimokawa			warnx("Starting DIR_IN @%jd:%u",
496121184Ssimokawa#else
497121184Ssimokawa			warnx("Starting DIR_IN @%lld:%u",
498121184Ssimokawa#endif
499121184Ssimokawa			    c_descr->offset, a_descr->targ_req);
500107178Snjl	} else {
501107178Snjl		ret = start_io(atio, ctio, CAM_DIR_OUT);
502107178Snjl		if (debug)
503121184Ssimokawa#if __FreeBSD_version >= 500000
504121184Ssimokawa			warnx("Starting DIR_OUT @%jd:%u",
505121184Ssimokawa#else
506121184Ssimokawa			warnx("Starting DIR_OUT @%lld:%u",
507121184Ssimokawa#endif
508121184Ssimokawa			    c_descr->offset, a_descr->init_req);
509107178Snjl	}
510107178Snjl
511107178Snjl	return (ret);
512107178Snjl}
513107178Snjl
514107178Snjlstatic int
515107178Snjltcmd_rdwr_decode(struct ccb_accept_tio *atio, struct ccb_scsiio *ctio)
516107178Snjl{
517121184Ssimokawa	uint64_t blkno;
518121184Ssimokawa	uint32_t count;
519107178Snjl	struct atio_descr *a_descr;
520107178Snjl	u_int8_t *cdb;
521107178Snjl
522107178Snjl	a_descr = (struct atio_descr *)atio->ccb_h.targ_descr;
523107178Snjl	cdb = a_descr->cdb;
524107178Snjl	if (debug)
525107178Snjl		cdb_debug(cdb, "R/W from %u: ", atio->init_id);
526107178Snjl
527121184Ssimokawa	switch (cdb[0]) {
528121184Ssimokawa	case READ_6:
529121184Ssimokawa	case WRITE_6:
530121184Ssimokawa	{
531107178Snjl		struct scsi_rw_6 *rw_6 = (struct scsi_rw_6 *)cdb;
532107178Snjl		blkno = scsi_3btoul(rw_6->addr);
533107178Snjl		count = rw_6->length;
534121184Ssimokawa		break;
535121184Ssimokawa	}
536121184Ssimokawa	case READ_10:
537121184Ssimokawa	case WRITE_10:
538121184Ssimokawa	{
539107178Snjl		struct scsi_rw_10 *rw_10 = (struct scsi_rw_10 *)cdb;
540107178Snjl		blkno = scsi_4btoul(rw_10->addr);
541107178Snjl		count = scsi_2btoul(rw_10->length);
542121184Ssimokawa		break;
543107178Snjl	}
544121184Ssimokawa#ifdef READ_16
545121184Ssimokawa	case READ_16:
546121184Ssimokawa	case WRITE_16:
547121184Ssimokawa	{
548121184Ssimokawa		struct scsi_rw_16 *rw_16 = (struct scsi_rw_16 *)cdb;
549121184Ssimokawa		blkno = scsi_8btou64(rw_16->addr);
550121184Ssimokawa		count = scsi_4btoul(rw_16->length);
551121184Ssimokawa		break;
552121184Ssimokawa	}
553121184Ssimokawa#endif
554121184Ssimokawa	default:
555121184Ssimokawa		tcmd_illegal_req(atio, ctio);
556121184Ssimokawa		return (0);
557121184Ssimokawa	}
558107178Snjl	if (blkno + count > volume_size) {
559107178Snjl		warnx("Attempt to access past end of volume");
560107178Snjl		tcmd_sense(ctio->init_id, ctio,
561107178Snjl			   SSD_KEY_ILLEGAL_REQUEST, 0x21, 0);
562107178Snjl		return (0);
563107178Snjl	}
564107178Snjl
565107178Snjl	/* Get an (overall) data length and set direction */
566107178Snjl	a_descr->base_off = ((off_t)blkno) * sector_size;
567107178Snjl	a_descr->total_len = count * sector_size;
568107178Snjl	if (a_descr->total_len == 0) {
569107178Snjl		if (debug)
570121184Ssimokawa#if __FreeBSD_version >= 500000
571121184Ssimokawa			warnx("r/w 0 blocks @ blkno %ju", blkno);
572121184Ssimokawa#else
573121184Ssimokawa			warnx("r/w 0 blocks @ blkno %llu", blkno);
574121184Ssimokawa#endif
575107178Snjl		tcmd_null_ok(atio, ctio);
576107178Snjl		return (0);
577107178Snjl	} else if (cdb[0] == WRITE_6 || cdb[0] == WRITE_10) {
578107178Snjl		a_descr->flags |= CAM_DIR_OUT;
579107178Snjl		if (debug)
580121184Ssimokawa#if __FreeBSD_version >= 500000
581121184Ssimokawa			warnx("write %u blocks @ blkno %ju", count, blkno);
582121184Ssimokawa#else
583121184Ssimokawa			warnx("write %u blocks @ blkno %llu", count, blkno);
584121184Ssimokawa#endif
585107178Snjl	} else {
586107178Snjl		a_descr->flags |= CAM_DIR_IN;
587107178Snjl		if (debug)
588121184Ssimokawa#if __FreeBSD_version >= 500000
589121184Ssimokawa			warnx("read %u blocks @ blkno %ju", count, blkno);
590121184Ssimokawa#else
591121184Ssimokawa			warnx("read %u blocks @ blkno %llu", count, blkno);
592121184Ssimokawa#endif
593107178Snjl	}
594107178Snjl	return (1);
595107178Snjl}
596107178Snjl
597107178Snjlstatic int
598107178Snjlstart_io(struct ccb_accept_tio *atio, struct ccb_scsiio *ctio, int dir)
599107178Snjl{
600107178Snjl	struct atio_descr *a_descr;
601107178Snjl	struct ctio_descr *c_descr;
602107178Snjl	int ret;
603107178Snjl
604107178Snjl	/* Set up common structures */
605107178Snjl	a_descr = (struct atio_descr *)atio->ccb_h.targ_descr;
606107178Snjl	c_descr = (struct ctio_descr *)ctio->ccb_h.targ_descr;
607107178Snjl
608107178Snjl	if (dir == CAM_DIR_IN) {
609107178Snjl		c_descr->offset = a_descr->base_off + a_descr->targ_req;
610107178Snjl		ctio->dxfer_len = a_descr->total_len - a_descr->targ_req;
611107178Snjl	} else {
612107178Snjl		c_descr->offset = a_descr->base_off + a_descr->init_req;
613107178Snjl		ctio->dxfer_len = a_descr->total_len - a_descr->init_req;
614107178Snjl	}
615107178Snjl	ctio->dxfer_len = min(ctio->dxfer_len, buf_size);
616107178Snjl	assert(ctio->dxfer_len >= 0);
617107178Snjl
618107178Snjl	c_descr->aiocb.aio_offset = c_descr->offset;
619107178Snjl	c_descr->aiocb.aio_nbytes = ctio->dxfer_len;
620107178Snjl
621107178Snjl	/* If DIR_IN, start read from target, otherwise begin CTIO xfer. */
622107178Snjl	ret = 1;
623107178Snjl	if (dir == CAM_DIR_IN) {
624107178Snjl		if (aio_read(&c_descr->aiocb) < 0)
625107178Snjl			err(1, "aio_read"); /* XXX */
626107178Snjl		a_descr->targ_req += ctio->dxfer_len;
627107178Snjl		if (a_descr->targ_req == a_descr->total_len) {
628107178Snjl			ctio->ccb_h.flags |= CAM_SEND_STATUS;
629107178Snjl			ctio->scsi_status = SCSI_STATUS_OK;
630107178Snjl			ret = 0;
631107178Snjl		}
632107178Snjl	} else {
633107178Snjl		if (a_descr->targ_ack == a_descr->total_len)
634107178Snjl			tcmd_null_ok(atio, ctio);
635107178Snjl		a_descr->init_req += ctio->dxfer_len;
636107178Snjl		if (a_descr->init_req == a_descr->total_len &&
637107178Snjl		    ctio->dxfer_len > 0) {
638107178Snjl			/*
639107178Snjl			 * If data phase done, remove atio from workq.
640107178Snjl			 * The completion handler will call work_atio to
641107178Snjl			 * send the final status.
642107178Snjl			 */
643107178Snjl			ret = 0;
644107178Snjl		}
645107178Snjl		send_ccb((union ccb *)ctio, /*priority*/1);
646107178Snjl	}
647107178Snjl
648107178Snjl	return (ret);
649107178Snjl}
650107178Snjl
651107178Snjlstatic void
652107178Snjltcmd_rdwr_done(struct ccb_accept_tio *atio, struct ccb_scsiio *ctio,
653107178Snjl	       io_ops event)
654107178Snjl{
655107178Snjl	struct atio_descr *a_descr;
656107178Snjl	struct ctio_descr *c_descr;
657107178Snjl
658107178Snjl	a_descr = (struct atio_descr *)atio->ccb_h.targ_descr;
659107178Snjl	c_descr = (struct ctio_descr *)ctio->ccb_h.targ_descr;
660107178Snjl
661107178Snjl	switch (event) {
662107178Snjl	case AIO_DONE:
663107178Snjl		if (aio_return(&c_descr->aiocb) < 0) {
664107178Snjl			warn("aio_return error");
665107178Snjl			/* XXX */
666107178Snjl			tcmd_sense(ctio->init_id, ctio,
667107178Snjl				   SSD_KEY_MEDIUM_ERROR, 0, 0);
668107178Snjl			send_ccb((union ccb *)ctio, /*priority*/1);
669107178Snjl			break;
670107178Snjl		}
671107178Snjl		a_descr->targ_ack += ctio->dxfer_len;
672107178Snjl		if ((a_descr->flags & CAM_DIR_IN) != 0) {
673107178Snjl			if (debug)
674107178Snjl				warnx("sending CTIO for AIO read");
675107178Snjl			a_descr->init_req += ctio->dxfer_len;
676107178Snjl			send_ccb((union ccb *)ctio, /*priority*/1);
677107178Snjl		} else {
678107178Snjl			/* Use work function to send final status */
679107178Snjl			if (a_descr->init_req == a_descr->total_len)
680107178Snjl				work_atio(atio);
681107178Snjl			if (debug)
682107178Snjl				warnx("AIO done freeing CTIO");
683107178Snjl			free_ccb((union ccb *)ctio);
684107178Snjl		}
685107178Snjl		break;
686107178Snjl	case CTIO_DONE:
687107178Snjl		if (ctio->ccb_h.status != CAM_REQ_CMP) {
688107178Snjl			/* XXX */
689107178Snjl			errx(1, "CTIO failed, status %#x", ctio->ccb_h.status);
690107178Snjl		}
691107178Snjl		a_descr->init_ack += ctio->dxfer_len;
692107178Snjl		if ((a_descr->flags & CAM_DIR_MASK) == CAM_DIR_OUT &&
693107178Snjl		    ctio->dxfer_len > 0) {
694107178Snjl			if (debug)
695107178Snjl				warnx("sending AIO for CTIO write");
696107178Snjl			a_descr->targ_req += ctio->dxfer_len;
697107178Snjl			if (aio_write(&c_descr->aiocb) < 0)
698107178Snjl				err(1, "aio_write"); /* XXX */
699107178Snjl		} else {
700107178Snjl			if (debug)
701107178Snjl				warnx("CTIO done freeing CTIO");
702107178Snjl			free_ccb((union ccb *)ctio);
703107178Snjl		}
704107178Snjl		break;
705107178Snjl	default:
706107178Snjl		warnx("Unknown completion code %d", event);
707107178Snjl		abort();
708107178Snjl		/* NOTREACHED */
709107178Snjl	}
710107178Snjl}
711107178Snjl
712107178Snjl/* Simple ok message used by TUR, SYNC_CACHE, etc. */
713107178Snjlstatic int
714107178Snjltcmd_null_ok(struct ccb_accept_tio *atio, struct ccb_scsiio *ctio)
715107178Snjl{
716107178Snjl	if (debug) {
717107178Snjl		struct atio_descr *a_descr;
718107178Snjl
719107178Snjl		a_descr = (struct atio_descr *)atio->ccb_h.targ_descr;
720107178Snjl		cdb_debug(a_descr->cdb, "Sending null ok to %u : ", atio->init_id);
721107178Snjl	}
722107178Snjl
723107178Snjl	ctio->dxfer_len = 0;
724107178Snjl	ctio->ccb_h.flags &= ~CAM_DIR_MASK;
725107178Snjl	ctio->ccb_h.flags |= CAM_DIR_NONE | CAM_SEND_STATUS;
726107178Snjl	ctio->scsi_status = SCSI_STATUS_OK;
727107178Snjl	return (0);
728107178Snjl}
729107178Snjl
730107178Snjl/* Simple illegal request message used by MODE SENSE, etc. */
731107178Snjlstatic int
732107178Snjltcmd_illegal_req(struct ccb_accept_tio *atio, struct ccb_scsiio *ctio)
733107178Snjl{
734107178Snjl	if (debug) {
735107178Snjl		struct atio_descr *a_descr;
736107178Snjl
737107178Snjl		a_descr = (struct atio_descr *)atio->ccb_h.targ_descr;
738107178Snjl		cdb_debug(a_descr->cdb, "Sending ill req to %u: ", atio->init_id);
739107178Snjl	}
740107178Snjl
741107178Snjl	tcmd_sense(atio->init_id, ctio, SSD_KEY_ILLEGAL_REQUEST,
742107178Snjl		   /*asc*/0x24, /*ascq*/0);
743107178Snjl	return (0);
744107178Snjl}
745107178Snjl
746107178Snjlstatic void
747107178Snjlcdb_debug(u_int8_t *cdb, const char *msg, ...)
748107178Snjl{
749107178Snjl	char msg_buf[512];
750107178Snjl	int len;
751107178Snjl	va_list ap;
752107178Snjl
753107178Snjl	va_start(ap, msg);
754107178Snjl	vsnprintf(msg_buf, sizeof(msg_buf), msg, ap);
755107178Snjl	va_end(ap);
756107178Snjl	len = strlen(msg_buf);
757107178Snjl	scsi_cdb_string(cdb, msg_buf + len, sizeof(msg_buf) - len);
758107178Snjl	warnx("%s", msg_buf);
759107178Snjl}
760