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