]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - share/examples/scsi_target/scsi_cmds.c
Import tzdata 2020c
[FreeBSD/FreeBSD.git] / share / examples / scsi_target / scsi_cmds.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * SCSI Disk Emulator
5  *
6  * Copyright (c) 2002 Nate Lawson.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions, and the following disclaimer,
14  *    without modification, immediately at the beginning of the file.
15  * 2. The name of the author may not be used to endorse or promote products
16  *    derived from this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
22  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  *
30  * $FreeBSD$
31  */
32
33 #include <stdio.h>
34 #include <stddef.h>
35 #include <stdarg.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <err.h>
39 #include <aio.h>
40 #include <unistd.h>
41 #include <assert.h>
42 #include <sys/param.h>
43 #include <sys/types.h>
44
45 #include <cam/cam.h>
46 #include <cam/cam_ccb.h>
47 #include <cam/scsi/scsi_all.h>
48 #include <cam/scsi/scsi_targetio.h>
49 #include "scsi_target.h"
50
51 typedef int targ_start_func(struct ccb_accept_tio *, struct ccb_scsiio *);
52 typedef void targ_done_func(struct ccb_accept_tio *, struct ccb_scsiio *,
53                               io_ops);
54 #ifndef REPORT_LUNS
55 #define REPORT_LUNS     0xa0
56 #endif
57
58 struct targ_cdb_handlers {
59         u_int8_t          cmd;
60         targ_start_func  *start;
61         targ_done_func   *done;
62 #define ILLEGAL_CDB       0xFF
63 };
64
65 static targ_start_func          tcmd_inquiry;
66 static targ_start_func          tcmd_req_sense;
67 static targ_start_func          tcmd_rd_cap;
68 #ifdef READ_16
69 static targ_start_func          tcmd_rd_cap16;
70 #endif
71 static targ_start_func          tcmd_rdwr;
72 static targ_start_func          tcmd_rdwr_decode;
73 static targ_done_func           tcmd_rdwr_done;
74 static targ_start_func          tcmd_null_ok;
75 static targ_start_func          tcmd_illegal_req;
76 static int                      start_io(struct ccb_accept_tio *atio,
77                                          struct ccb_scsiio *ctio, int dir);
78 static int init_inquiry(u_int16_t req_flags, u_int16_t sim_flags);
79 static struct initiator_state *
80                         tcmd_get_istate(u_int init_id);
81 static void cdb_debug(u_int8_t *cdb, const char *msg, ...);
82
83 static struct targ_cdb_handlers cdb_handlers[] = { 
84         { READ_10,              tcmd_rdwr,              tcmd_rdwr_done },
85         { WRITE_10,             tcmd_rdwr,              tcmd_rdwr_done },
86         { READ_6,               tcmd_rdwr,              tcmd_rdwr_done },
87         { WRITE_6,              tcmd_rdwr,              tcmd_rdwr_done },
88         { INQUIRY,              tcmd_inquiry,           NULL },
89         { REQUEST_SENSE,        tcmd_req_sense,         NULL },
90         { READ_CAPACITY,        tcmd_rd_cap,            NULL },
91         { TEST_UNIT_READY,      tcmd_null_ok,           NULL },
92         { START_STOP_UNIT,      tcmd_null_ok,           NULL },
93         { SYNCHRONIZE_CACHE,    tcmd_null_ok,           NULL },
94         { MODE_SENSE_6,         tcmd_illegal_req,       NULL },
95         { MODE_SELECT_6,        tcmd_illegal_req,       NULL },
96         { REPORT_LUNS,          tcmd_illegal_req,       NULL },
97 #ifdef READ_16
98         { READ_16,              tcmd_rdwr,              tcmd_rdwr_done },
99         { WRITE_16,             tcmd_rdwr,              tcmd_rdwr_done },
100         { SERVICE_ACTION_IN,    tcmd_rd_cap16,          NULL },
101 #endif
102         { ILLEGAL_CDB,          NULL,                   NULL }
103 };
104
105 static struct scsi_inquiry_data inq_data;
106 static struct initiator_state istates[MAX_INITIATORS];
107 extern int              debug;
108 extern off_t            volume_size;
109 extern u_int            sector_size;
110 extern size_t           buf_size;
111
112 cam_status
113 tcmd_init(u_int16_t req_inq_flags, u_int16_t sim_inq_flags)
114 {
115         struct initiator_state *istate;
116         int i, ret;
117
118         /* Initialize our inquiry data */
119         ret = init_inquiry(req_inq_flags, sim_inq_flags);
120         if (ret != 0)
121                 return (ret);
122
123         /* We start out life with a UA to indicate power-on/reset. */
124         for (i = 0; i < MAX_INITIATORS; i++) {
125                 istate = tcmd_get_istate(i);
126                 bzero(istate, sizeof(*istate));
127                 istate->pending_ua = UA_POWER_ON;
128         }
129
130         return (0);
131 }
132
133 /* Caller allocates CTIO, sets its init_id
134 return 0 if done, 1 if more processing needed 
135 on 0, caller sets SEND_STATUS */
136 int
137 tcmd_handle(struct ccb_accept_tio *atio, struct ccb_scsiio *ctio, io_ops event)
138 {
139         static struct targ_cdb_handlers *last_cmd; 
140         struct initiator_state *istate;
141         struct atio_descr *a_descr;
142         int ret;
143
144         if (debug) {
145                 warnx("tcmd_handle atio %p ctio %p atioflags %#x", atio, ctio,
146                       atio->ccb_h.flags);
147         }
148         ret = 0;
149         a_descr = (struct atio_descr *)atio->ccb_h.targ_descr;
150
151         /* Do a full lookup if one-behind cache failed */
152         if (last_cmd == NULL || last_cmd->cmd != a_descr->cdb[0]) {
153                 struct targ_cdb_handlers *h; 
154
155                 for (h = cdb_handlers; h->cmd != ILLEGAL_CDB; h++) {
156                         if (a_descr->cdb[0] == h->cmd)
157                                 break;
158                 }
159                 last_cmd = h;
160         }
161
162         /* call completion and exit */
163         if (event != ATIO_WORK) {
164                 if (last_cmd->done != NULL)
165                         last_cmd->done(atio, ctio, event);
166                 else
167                         free_ccb((union ccb *)ctio);
168                 return (1);
169         }
170
171         if (last_cmd->cmd == ILLEGAL_CDB) {
172                 if (event != ATIO_WORK) {
173                         warnx("no done func for %#x???", a_descr->cdb[0]);
174                         abort();
175                 }
176                 /* Not found, return illegal request */
177                 warnx("cdb %#x not handled", a_descr->cdb[0]);
178                 tcmd_illegal_req(atio, ctio);
179                 send_ccb((union ccb *)ctio, /*priority*/1);
180                 return (0);
181         }
182
183         istate = tcmd_get_istate(ctio->init_id);
184         if (istate == NULL) {
185                 tcmd_illegal_req(atio, ctio);
186                 send_ccb((union ccb *)ctio, /*priority*/1);
187                 return (0);
188         }
189
190         if (istate->pending_ca == 0 && istate->pending_ua != 0 &&
191             a_descr->cdb[0] != INQUIRY) {
192                 tcmd_sense(ctio->init_id, ctio, SSD_KEY_UNIT_ATTENTION,
193                            0x29, istate->pending_ua == UA_POWER_ON ? 1 : 2);
194                 istate->pending_ca = CA_UNIT_ATTN;
195                 if (debug) {
196                         cdb_debug(a_descr->cdb, "UA active for %u: ",
197                                   atio->init_id);
198                 }
199                 send_ccb((union ccb *)ctio, /*priority*/1);
200                 return (0);
201         } 
202
203         /* Store current CA and UA for later */
204         istate->orig_ua = istate->pending_ua;
205         istate->orig_ca = istate->pending_ca;
206
207         /*
208          * As per SAM2, any command that occurs
209          * after a CA is reported, clears the CA.  We must
210          * also clear the UA condition, if any, that caused
211          * the CA to occur assuming the UA is not for a
212          * persistent condition.
213          */
214         istate->pending_ca = CA_NONE;
215         if (istate->orig_ca == CA_UNIT_ATTN)
216                 istate->pending_ua = UA_NONE;
217
218         /* If we have a valid handler, call start or completion function */
219         if (last_cmd->cmd != ILLEGAL_CDB) {
220                 ret = last_cmd->start(atio, ctio);
221                 /* XXX hack */
222                 if (last_cmd->start != tcmd_rdwr) {
223                         a_descr->init_req += ctio->dxfer_len;
224                         send_ccb((union ccb *)ctio, /*priority*/1);
225                 }
226         }
227
228         return (ret);
229 }
230
231 static struct initiator_state *
232 tcmd_get_istate(u_int init_id)
233 {
234         if (init_id >= MAX_INITIATORS) {
235                 warnx("illegal init_id %d, max %d", init_id, MAX_INITIATORS - 1);
236                 return (NULL);
237         } else {
238                 return (&istates[init_id]);
239         }
240 }
241
242 void
243 tcmd_sense(u_int init_id, struct ccb_scsiio *ctio, u_int8_t flags,
244                u_int8_t asc, u_int8_t ascq)
245 {
246         struct initiator_state *istate;
247         struct scsi_sense_data_fixed *sense;
248
249         /* Set our initiator's istate */
250         istate = tcmd_get_istate(init_id);
251         if (istate == NULL)
252                 return;
253         istate->pending_ca |= CA_CMD_SENSE; /* XXX set instead of or? */
254         sense = (struct scsi_sense_data_fixed *)&istate->sense_data;
255         bzero(sense, sizeof(*sense));
256         sense->error_code = SSD_CURRENT_ERROR;
257         sense->flags = flags;
258         sense->add_sense_code = asc;
259         sense->add_sense_code_qual = ascq;
260         sense->extra_len =
261                 offsetof(struct scsi_sense_data_fixed, sense_key_spec[2]) -
262                 offsetof(struct scsi_sense_data_fixed, extra_len);
263
264         /* Fill out the supplied CTIO */
265         if (ctio != NULL) {
266                 bcopy(sense, &ctio->sense_data, sizeof(*sense));
267                 ctio->sense_len = sizeof(*sense);  /* XXX */
268                 ctio->ccb_h.flags &= ~CAM_DIR_MASK;
269                 ctio->ccb_h.flags |= CAM_DIR_NONE | CAM_SEND_SENSE |
270                                      CAM_SEND_STATUS;
271                 ctio->dxfer_len = 0;
272                 ctio->scsi_status = SCSI_STATUS_CHECK_COND;
273         }
274 }
275
276 void
277 tcmd_ua(u_int init_id, ua_types new_ua)
278 {
279         struct initiator_state *istate;
280         u_int start, end;
281
282         if (init_id == CAM_TARGET_WILDCARD) {
283                 start = 0;
284                 end = MAX_INITIATORS - 1;
285         } else {
286                 start = end = init_id;
287         }
288
289         for (; start <= end; start++) {
290                 istate = tcmd_get_istate(start);
291                 if (istate == NULL)
292                         break;
293                 istate->pending_ua = new_ua;
294         }
295 }
296
297 static int
298 tcmd_inquiry(struct ccb_accept_tio *atio, struct ccb_scsiio *ctio)
299 {
300         struct scsi_inquiry *inq;
301         struct atio_descr *a_descr;
302         struct initiator_state *istate;
303         struct scsi_sense_data_fixed *sense;
304
305         a_descr = (struct atio_descr *)atio->ccb_h.targ_descr;
306         inq = (struct scsi_inquiry *)a_descr->cdb;
307
308         if (debug)
309                 cdb_debug(a_descr->cdb, "INQUIRY from %u: ", atio->init_id);
310         /*
311          * Validate the command.  We don't support any VPD pages, so
312          * complain if EVPD or CMDDT is set.
313          */
314         istate = tcmd_get_istate(ctio->init_id);
315         sense = (struct scsi_sense_data_fixed *)&istate->sense_data;
316         if ((inq->byte2 & SI_EVPD) != 0) {
317                 tcmd_illegal_req(atio, ctio);
318                 sense->sense_key_spec[0] = SSD_SCS_VALID | SSD_FIELDPTR_CMD |
319                         SSD_BITPTR_VALID | /*bit value*/1;
320                 sense->sense_key_spec[1] = 0;
321                 sense->sense_key_spec[2] =
322                         offsetof(struct scsi_inquiry, byte2);
323         } else if (inq->page_code != 0) {
324                 tcmd_illegal_req(atio, ctio);
325                 sense->sense_key_spec[0] = SSD_SCS_VALID | SSD_FIELDPTR_CMD;
326                 sense->sense_key_spec[1] = 0;
327                 sense->sense_key_spec[2] =
328                         offsetof(struct scsi_inquiry, page_code);
329         } else {
330                 bcopy(&inq_data, ctio->data_ptr, sizeof(inq_data));
331                 ctio->dxfer_len = inq_data.additional_length + 4;
332                 ctio->dxfer_len = min(ctio->dxfer_len,
333                                       scsi_2btoul(inq->length));
334                 ctio->ccb_h.flags |= CAM_DIR_IN | CAM_SEND_STATUS;
335                 ctio->scsi_status = SCSI_STATUS_OK;
336         }
337         return (0);
338 }
339
340 /* Initialize the inquiry response structure with the requested flags */
341 static int
342 init_inquiry(u_int16_t req_flags, u_int16_t sim_flags)
343 {
344         struct scsi_inquiry_data *inq;
345
346         inq = &inq_data;
347         bzero(inq, sizeof(*inq));
348         inq->device = T_DIRECT | (SID_QUAL_LU_CONNECTED << 5);
349 #ifdef SCSI_REV_SPC
350         inq->version = SCSI_REV_SPC; /* was 2 */
351 #else
352         inq->version = SCSI_REV_3; /* was 2 */
353 #endif
354
355         /*
356          * XXX cpi.hba_inquiry doesn't support Addr16 so we give the
357          * user what they want if they ask for it.
358          */
359         if ((req_flags & SID_Addr16) != 0) {
360                 sim_flags |= SID_Addr16;
361                 warnx("Not sure SIM supports Addr16 but enabling it anyway");
362         }
363
364         /* Advertise only what the SIM can actually support */
365         req_flags &= sim_flags;
366         scsi_ulto2b(req_flags, &inq->spc2_flags);
367
368         inq->response_format = 2; /* SCSI2 Inquiry Format */
369         inq->additional_length = SHORT_INQUIRY_LENGTH -
370                 offsetof(struct scsi_inquiry_data, additional_length);
371         bcopy("FreeBSD ", inq->vendor, SID_VENDOR_SIZE);
372         bcopy("Emulated Disk   ", inq->product, SID_PRODUCT_SIZE);
373         bcopy("0.1 ", inq->revision, SID_REVISION_SIZE);
374         return (0);
375 }
376
377 static int
378 tcmd_req_sense(struct ccb_accept_tio *atio, struct ccb_scsiio *ctio)
379 {
380         struct scsi_request_sense *rsense;
381         struct scsi_sense_data_fixed *sense;
382         struct initiator_state *istate;
383         size_t dlen;
384         struct atio_descr *a_descr;
385
386         a_descr = (struct atio_descr *)atio->ccb_h.targ_descr;
387         rsense = (struct scsi_request_sense *)a_descr->cdb;
388         
389         istate = tcmd_get_istate(ctio->init_id);
390         sense = (struct scsi_sense_data_fixed *)&istate->sense_data;
391
392         if (debug) {
393                 cdb_debug(a_descr->cdb, "REQ SENSE from %u: ", atio->init_id);
394                 warnx("Sending sense: %#x %#x %#x", sense->flags,
395                       sense->add_sense_code, sense->add_sense_code_qual);
396         }
397
398         if (istate->orig_ca == 0) {
399                 tcmd_sense(ctio->init_id, NULL, SSD_KEY_NO_SENSE, 0, 0);
400                 warnx("REQUEST SENSE from %u but no pending CA!",
401                       ctio->init_id);
402         }
403
404         bcopy(sense, ctio->data_ptr, sizeof(struct scsi_sense_data));
405         dlen = offsetof(struct scsi_sense_data_fixed, extra_len) +
406                         sense->extra_len + 1;
407         ctio->dxfer_len = min(dlen, SCSI_CDB6_LEN(rsense->length));
408         ctio->ccb_h.flags |= CAM_DIR_IN | CAM_SEND_STATUS;
409         ctio->scsi_status = SCSI_STATUS_OK;
410         return (0);
411 }
412
413 static int
414 tcmd_rd_cap(struct ccb_accept_tio *atio, struct ccb_scsiio *ctio)
415 {
416         struct scsi_read_capacity_data *srp;
417         struct atio_descr *a_descr;
418         uint32_t vsize;
419
420         a_descr = (struct atio_descr *)atio->ccb_h.targ_descr;
421         srp = (struct scsi_read_capacity_data *)ctio->data_ptr;
422
423         if (volume_size > 0xffffffff)
424                 vsize = 0xffffffff;
425         else
426                 vsize = (uint32_t)(volume_size - 1);
427
428         if (debug) {
429                 cdb_debug(a_descr->cdb, "READ CAP from %u (%u, %u): ",
430                           atio->init_id, vsize, sector_size);
431         }
432
433         bzero(srp, sizeof(*srp));
434         scsi_ulto4b(vsize, srp->addr);
435         scsi_ulto4b(sector_size, srp->length);
436
437         ctio->dxfer_len = sizeof(*srp);
438         ctio->ccb_h.flags |= CAM_DIR_IN | CAM_SEND_STATUS;
439         ctio->scsi_status = SCSI_STATUS_OK;
440         return (0);
441 }
442
443 #ifdef READ_16
444 static int
445 tcmd_rd_cap16(struct ccb_accept_tio *atio, struct ccb_scsiio *ctio)
446 {
447         struct scsi_read_capacity_16 *scsi_cmd;
448         struct scsi_read_capacity_data_long *srp;
449         struct atio_descr *a_descr;
450
451         a_descr = (struct atio_descr *)atio->ccb_h.targ_descr;
452         scsi_cmd = (struct scsi_read_capacity_16 *)a_descr->cdb;
453         srp = (struct scsi_read_capacity_data_long *)ctio->data_ptr;
454
455         if (scsi_cmd->service_action != SRC16_SERVICE_ACTION) {
456                 tcmd_illegal_req(atio, ctio);
457                 return (0);
458         }
459
460         if (debug) {
461                 cdb_debug(a_descr->cdb, "READ CAP16 from %u (%u, %u): ",
462                           atio->init_id, volume_size - 1, sector_size);
463         }
464
465         bzero(srp, sizeof(*srp));
466         scsi_u64to8b(volume_size - 1, srp->addr);
467         scsi_ulto4b(sector_size, srp->length);
468
469         ctio->dxfer_len = sizeof(*srp);
470         ctio->ccb_h.flags |= CAM_DIR_IN | CAM_SEND_STATUS;
471         ctio->scsi_status = SCSI_STATUS_OK;
472         return (0);
473 }
474 #endif
475
476 static int
477 tcmd_rdwr(struct ccb_accept_tio *atio, struct ccb_scsiio *ctio)
478 {
479         struct atio_descr *a_descr;
480         struct ctio_descr *c_descr;
481         int ret;
482
483         a_descr = (struct atio_descr *)atio->ccb_h.targ_descr;
484         c_descr = (struct ctio_descr *)ctio->ccb_h.targ_descr;
485
486         /* Command needs to be decoded */
487         if ((a_descr->flags & CAM_DIR_MASK) == CAM_DIR_BOTH) {
488                 if (debug)
489                         warnx("Calling rdwr_decode");
490                 ret = tcmd_rdwr_decode(atio, ctio);
491                 if (ret == 0) {
492                         send_ccb((union ccb *)ctio, /*priority*/1);
493                         return (0);
494                 }
495         }
496         ctio->ccb_h.flags |= a_descr->flags;
497
498         /* Call appropriate work function */
499         if ((a_descr->flags & CAM_DIR_IN) != 0) {
500                 ret = start_io(atio, ctio, CAM_DIR_IN);
501                 if (debug)
502                         warnx("Starting %p DIR_IN @" OFF_FMT ":%u",
503                             a_descr, c_descr->offset, a_descr->targ_req);
504         } else {
505                 ret = start_io(atio, ctio, CAM_DIR_OUT);
506                 if (debug)
507                         warnx("Starting %p DIR_OUT @" OFF_FMT ":%u",
508                             a_descr, c_descr->offset, a_descr->init_req);
509         }
510
511         return (ret);
512 }
513
514 static int
515 tcmd_rdwr_decode(struct ccb_accept_tio *atio, struct ccb_scsiio *ctio)
516 {
517         uint64_t blkno;
518         uint32_t count;
519         struct atio_descr *a_descr;
520         u_int8_t *cdb;
521
522         a_descr = (struct atio_descr *)atio->ccb_h.targ_descr;
523         cdb = a_descr->cdb;
524         if (debug)
525                 cdb_debug(cdb, "R/W from %u: ", atio->init_id);
526
527         switch (cdb[0]) {
528         case READ_6:
529         case WRITE_6:
530         {
531                 struct scsi_rw_6 *rw_6 = (struct scsi_rw_6 *)cdb;
532                 blkno = scsi_3btoul(rw_6->addr);
533                 count = rw_6->length;
534                 break;
535         }
536         case READ_10:
537         case WRITE_10:
538         {
539                 struct scsi_rw_10 *rw_10 = (struct scsi_rw_10 *)cdb;
540                 blkno = scsi_4btoul(rw_10->addr);
541                 count = scsi_2btoul(rw_10->length);
542                 break;
543         }
544 #ifdef READ_16
545         case READ_16:
546         case WRITE_16:
547         {
548                 struct scsi_rw_16 *rw_16 = (struct scsi_rw_16 *)cdb;
549                 blkno = scsi_8btou64(rw_16->addr);
550                 count = scsi_4btoul(rw_16->length);
551                 break;
552         }
553 #endif
554         default:
555                 tcmd_illegal_req(atio, ctio);
556                 return (0);
557         }
558         if (blkno + count > volume_size) {
559                 warnx("Attempt to access past end of volume");
560                 tcmd_sense(ctio->init_id, ctio,
561                            SSD_KEY_ILLEGAL_REQUEST, 0x21, 0);
562                 return (0);
563         }
564
565         /* Get an (overall) data length and set direction */
566         a_descr->base_off = ((off_t)blkno) * sector_size;
567         a_descr->total_len = count * sector_size;
568         if (a_descr->total_len == 0) {
569                 if (debug)
570                         warnx("r/w 0 blocks @ blkno " OFF_FMT, blkno);
571                 tcmd_null_ok(atio, ctio);
572                 return (0);
573         } else if (cdb[0] == WRITE_6 || cdb[0] == WRITE_10) {
574                 a_descr->flags |= CAM_DIR_OUT;
575                 if (debug)
576                         warnx("write %u blocks @ blkno " OFF_FMT, count, blkno);
577         } else {
578                 a_descr->flags |= CAM_DIR_IN;
579                 if (debug)
580                         warnx("read %u blocks @ blkno " OFF_FMT,  count, blkno);
581         }
582         return (1);
583 }
584
585 static int
586 start_io(struct ccb_accept_tio *atio, struct ccb_scsiio *ctio, int dir)
587 {
588         struct atio_descr *a_descr;
589         struct ctio_descr *c_descr;
590         int ret;
591
592         /* Set up common structures */
593         a_descr = (struct atio_descr *)atio->ccb_h.targ_descr;
594         c_descr = (struct ctio_descr *)ctio->ccb_h.targ_descr;
595
596         if (dir == CAM_DIR_IN) {
597                 c_descr->offset = a_descr->base_off + a_descr->targ_req;
598                 ctio->dxfer_len = a_descr->total_len - a_descr->targ_req;
599         } else {
600                 c_descr->offset = a_descr->base_off + a_descr->init_req;
601                 ctio->dxfer_len = a_descr->total_len - a_descr->init_req;
602         }
603         ctio->dxfer_len = min(ctio->dxfer_len, buf_size);
604         assert(ctio->dxfer_len >= 0);
605
606         c_descr->aiocb.aio_offset = c_descr->offset;
607         c_descr->aiocb.aio_nbytes = ctio->dxfer_len;
608
609         /* If DIR_IN, start read from target, otherwise begin CTIO xfer. */
610         ret = 1;
611         if (dir == CAM_DIR_IN) {
612                 if (notaio) {
613                         if (debug)
614                                 warnx("read sync %lu @ block " OFF_FMT,
615                                     (unsigned long)
616                                     (ctio->dxfer_len / sector_size),
617                                     c_descr->offset / sector_size);
618                         if (lseek(c_descr->aiocb.aio_fildes,
619                             c_descr->aiocb.aio_offset, SEEK_SET) < 0) {
620                                 perror("lseek");
621                                 err(1, "lseek");
622                         }
623                         if (read(c_descr->aiocb.aio_fildes,
624                             (void *)c_descr->aiocb.aio_buf,
625                             ctio->dxfer_len) != ctio->dxfer_len) {
626                                 err(1, "read");
627                         }
628                 } else {
629                         if (debug)
630                                 warnx("read async %lu @ block " OFF_FMT,
631                                     (unsigned long)
632                                     (ctio->dxfer_len / sector_size),
633                                     c_descr->offset / sector_size);
634                         if (aio_read(&c_descr->aiocb) < 0) {
635                                 err(1, "aio_read"); /* XXX */
636                         }
637                 }
638                 a_descr->targ_req += ctio->dxfer_len;
639                 /* if we're done, we can mark the CCB as to send status */
640                 if (a_descr->targ_req == a_descr->total_len) {
641                         ctio->ccb_h.flags |= CAM_SEND_STATUS;
642                         ctio->scsi_status = SCSI_STATUS_OK;
643                         ret = 0;
644                 }
645                 if (notaio)
646                         tcmd_rdwr_done(atio, ctio, AIO_DONE);
647         } else {
648                 if (a_descr->targ_ack == a_descr->total_len)
649                         tcmd_null_ok(atio, ctio);
650                 a_descr->init_req += ctio->dxfer_len;
651                 if (a_descr->init_req == a_descr->total_len &&
652                     ctio->dxfer_len > 0) {
653                         /*
654                          * If data phase done, remove atio from workq.
655                          * The completion handler will call work_atio to
656                          * send the final status.
657                          */
658                         ret = 0;
659                 }
660                 send_ccb((union ccb *)ctio, /*priority*/1);
661         }
662
663         return (ret);
664 }
665
666 static void
667 tcmd_rdwr_done(struct ccb_accept_tio *atio, struct ccb_scsiio *ctio,
668                io_ops event)
669 {
670         struct atio_descr *a_descr;
671         struct ctio_descr *c_descr;
672
673         a_descr = (struct atio_descr *)atio->ccb_h.targ_descr;
674         c_descr = (struct ctio_descr *)ctio->ccb_h.targ_descr;
675
676         switch (event) {
677         case AIO_DONE:
678                 if (!notaio && aio_return(&c_descr->aiocb) < 0) {
679                         warn("aio_return error");
680                         /* XXX */
681                         tcmd_sense(ctio->init_id, ctio,
682                                    SSD_KEY_MEDIUM_ERROR, 0, 0);
683                         send_ccb((union ccb *)ctio, /*priority*/1);
684                         break;
685                 }
686                 a_descr->targ_ack += ctio->dxfer_len;
687                 if ((a_descr->flags & CAM_DIR_IN) != 0) {
688                         if (debug) {
689                                 if (notaio)
690                                         warnx("sending CTIO for AIO read");
691                                 else
692                                         warnx("sending CTIO for sync read");
693                         }
694                         a_descr->init_req += ctio->dxfer_len;
695                         send_ccb((union ccb *)ctio, /*priority*/1);
696                 } else {
697                         /* Use work function to send final status */
698                         if (a_descr->init_req == a_descr->total_len)
699                                 work_atio(atio);
700                         if (debug)
701                                 warnx("AIO done freeing CTIO");
702                         free_ccb((union ccb *)ctio);
703                 }
704                 break;
705         case CTIO_DONE:
706                 switch (ctio->ccb_h.status & CAM_STATUS_MASK) {
707                 case CAM_REQ_CMP:
708                         break;
709                 case CAM_REQUEUE_REQ:
710                         warnx("requeueing request");
711                         if ((a_descr->flags & CAM_DIR_MASK) == CAM_DIR_OUT) {
712                                 if (aio_write(&c_descr->aiocb) < 0) {
713                                         err(1, "aio_write"); /* XXX */
714                                 }
715                         } else {
716                                 if (aio_read(&c_descr->aiocb) < 0) {
717                                         err(1, "aio_read"); /* XXX */
718                                 }
719                         }
720                         return;
721                 default:
722                         errx(1, "CTIO failed, status %#x", ctio->ccb_h.status);
723                 }
724                 a_descr->init_ack += ctio->dxfer_len;
725                 if ((a_descr->flags & CAM_DIR_MASK) == CAM_DIR_OUT &&
726                     ctio->dxfer_len > 0) {
727                         a_descr->targ_req += ctio->dxfer_len;
728                         if (notaio) {
729                                 if (debug)
730                                         warnx("write sync %lu @ block "
731                                             OFF_FMT, (unsigned long)
732                                             (ctio->dxfer_len / sector_size),
733                                             c_descr->offset / sector_size);
734                                 if (lseek(c_descr->aiocb.aio_fildes,
735                                     c_descr->aiocb.aio_offset, SEEK_SET) < 0) {
736                                         perror("lseek");
737                                         err(1, "lseek");
738                                 }
739                                 if (write(c_descr->aiocb.aio_fildes,
740                                     (void *) c_descr->aiocb.aio_buf,
741                                     ctio->dxfer_len) != ctio->dxfer_len) {
742                                         err(1, "write");
743                                 }
744                                 tcmd_rdwr_done(atio, ctio, AIO_DONE);
745                         } else {
746                                 if (debug)
747                                         warnx("write async %lu @ block "
748                                             OFF_FMT, (unsigned long)
749                                             (ctio->dxfer_len / sector_size),
750                                             c_descr->offset / sector_size);
751                                 if (aio_write(&c_descr->aiocb) < 0) {
752                                         err(1, "aio_write"); /* XXX */
753                                 }
754                         }
755                 } else {
756                         if (debug)
757                                 warnx("CTIO done freeing CTIO");
758                         free_ccb((union ccb *)ctio);
759                 }
760                 break;
761         default:
762                 warnx("Unknown completion code %d", event);
763                 abort();
764                 /* NOTREACHED */
765         }
766 }
767
768 /* Simple ok message used by TUR, SYNC_CACHE, etc. */
769 static int
770 tcmd_null_ok(struct ccb_accept_tio *atio, struct ccb_scsiio *ctio)
771 {
772         if (debug) {
773                 struct atio_descr *a_descr;
774
775                 a_descr = (struct atio_descr *)atio->ccb_h.targ_descr;
776                 cdb_debug(a_descr->cdb, "Sending null ok to %u : ", atio->init_id);
777         }
778
779         ctio->dxfer_len = 0;
780         ctio->ccb_h.flags &= ~CAM_DIR_MASK;
781         ctio->ccb_h.flags |= CAM_DIR_NONE | CAM_SEND_STATUS;
782         ctio->scsi_status = SCSI_STATUS_OK;
783         return (0);
784 }
785
786 /* Simple illegal request message used by MODE SENSE, etc. */
787 static int
788 tcmd_illegal_req(struct ccb_accept_tio *atio, struct ccb_scsiio *ctio)
789 {
790         if (debug) {
791                 struct atio_descr *a_descr;
792
793                 a_descr = (struct atio_descr *)atio->ccb_h.targ_descr;
794                 cdb_debug(a_descr->cdb, "Sending ill req to %u: ", atio->init_id);
795         }
796         
797         tcmd_sense(atio->init_id, ctio, SSD_KEY_ILLEGAL_REQUEST,
798                    /*asc*/0x24, /*ascq*/0);
799         return (0);
800 }
801
802 static void
803 cdb_debug(u_int8_t *cdb, const char *msg, ...)
804 {
805         char msg_buf[512];
806         int len;
807         va_list ap;
808
809         va_start(ap, msg);
810         vsnprintf(msg_buf, sizeof(msg_buf), msg, ap);
811         va_end(ap);
812         len = strlen(msg_buf);
813         scsi_cdb_string(cdb, msg_buf + len, sizeof(msg_buf) - len);
814         warnx("%s", msg_buf);
815 }