xref: /dragonfly/sys/bus/cam/scsi/scsi_pt.c (revision 857fcb57d0e5e7f20e3d0ef58163c718377e363e)
1 /*
2  * Implementation of SCSI Processor Target Peripheral driver for CAM.
3  *
4  * Copyright (c) 1998 Justin T. Gibbs.
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: src/sys/cam/scsi/scsi_pt.c,v 1.17 2000/01/17 06:27:37 mjacob Exp $
29  */
30 
31 #include <sys/param.h>
32 #include <sys/queue.h>
33 #include <sys/systm.h>
34 #include <sys/kernel.h>
35 #include <sys/types.h>
36 #include <sys/buf.h>
37 #include <sys/devicestat.h>
38 #include <sys/malloc.h>
39 #include <sys/caps.h>
40 #include <sys/conf.h>
41 #include <sys/ptio.h>
42 #include <sys/buf2.h>
43 
44 #include "../cam.h"
45 #include "../cam_ccb.h"
46 #include "../cam_extend.h"
47 #include "../cam_periph.h"
48 #include "../cam_xpt_periph.h"
49 #include "../cam_debug.h"
50 
51 #include "scsi_all.h"
52 #include "scsi_message.h"
53 #include "scsi_pt.h"
54 
55 #include "opt_pt.h"
56 
57 typedef enum {
58           PT_STATE_PROBE,
59           PT_STATE_NORMAL
60 } pt_state;
61 
62 typedef enum {
63           PT_FLAG_NONE                  = 0x00,
64           PT_FLAG_OPEN                  = 0x01,
65           PT_FLAG_DEVICE_INVALID        = 0x02,
66           PT_FLAG_RETRY_UA    = 0x04
67 } pt_flags;
68 
69 typedef enum {
70           PT_CCB_BUFFER_IO    = 0x01,
71           PT_CCB_WAITING                = 0x02,
72           PT_CCB_RETRY_UA               = 0x04,
73           PT_CCB_BUFFER_IO_UA = PT_CCB_BUFFER_IO|PT_CCB_RETRY_UA
74 } pt_ccb_state;
75 
76 /* Offsets into our private area for storing information */
77 #define ccb_state   ppriv_field0
78 #define ccb_bio               ppriv_ptr1
79 
80 struct pt_softc {
81           struct     bio_queue_head bio_queue;
82           struct     devstat device_stats;
83           LIST_HEAD(, ccb_hdr) pending_ccbs;
84           pt_state state;
85           pt_flags flags;
86           union      ccb saved_ccb;
87           int        io_timeout;
88           cdev_t     dev;
89 };
90 
91 static    d_open_t  ptopen;
92 static    d_close_t ptclose;
93 static    d_strategy_t        ptstrategy;
94 static    periph_init_t       ptinit;
95 static    void                ptasync(void *callback_arg, u_int32_t code,
96                                         struct cam_path *path, void *arg);
97 static    periph_ctor_t       ptctor;
98 static    periph_oninv_t      ptoninvalidate;
99 static    periph_dtor_t       ptdtor;
100 static    periph_start_t      ptstart;
101 static    void                ptdone(struct cam_periph *periph,
102                                      union ccb *done_ccb);
103 static    d_ioctl_t ptioctl;
104 static  int                   pterror(union ccb *ccb, u_int32_t cam_flags,
105                                         u_int32_t sense_flags);
106 
107 void      scsi_send_receive(struct ccb_scsiio *csio, u_int32_t retries,
108                                 void (*cbfcnp)(struct cam_periph *, union ccb *),
109                                 u_int tag_action, int readop, u_int byte2,
110                                 u_int32_t xfer_len, u_int8_t *data_ptr,
111                                 u_int8_t sense_len, u_int32_t timeout);
112 
113 static struct periph_driver ptdriver =
114 {
115           ptinit, "pt",
116           TAILQ_HEAD_INITIALIZER(ptdriver.units), /* generation */ 0
117 };
118 
119 PERIPHDRIVER_DECLARE(pt, ptdriver);
120 
121 static struct dev_ops pt_ops = {
122           { "pt", 0, 0 },
123           .d_open = ptopen,
124           .d_close =          ptclose,
125           .d_read = physread,
126           .d_write =          physwrite,
127           .d_ioctl =          ptioctl,
128           .d_strategy =       ptstrategy,
129 };
130 
131 static struct extend_array *ptperiphs;
132 
133 #ifndef SCSI_PT_DEFAULT_TIMEOUT
134 #define SCSI_PT_DEFAULT_TIMEOUT                   60
135 #endif
136 
137 static int
ptopen(struct dev_open_args * ap)138 ptopen(struct dev_open_args *ap)
139 {
140           cdev_t dev = ap->a_head.a_dev;
141           struct cam_periph *periph;
142           struct pt_softc *softc;
143           int unit;
144           int error = 0;
145 
146           /*
147            * Disallow CAM access if RESTRICTEDROOT
148            */
149           if (caps_priv_check_self(SYSCAP_RESTRICTEDROOT))
150                     return (EPERM);
151 
152           unit = minor(dev);
153           periph = cam_extend_get(ptperiphs, unit);
154           if (cam_periph_acquire(periph) != CAM_REQ_CMP)
155                     return (ENXIO);
156 
157           softc = (struct pt_softc *)periph->softc;
158 
159           cam_periph_lock(periph);
160           if (softc->flags & PT_FLAG_DEVICE_INVALID) {
161                     cam_periph_unlock(periph);
162                     cam_periph_release(periph);
163                     return(ENXIO);
164           }
165 
166           if ((softc->flags & PT_FLAG_OPEN) == 0)
167                     softc->flags |= PT_FLAG_OPEN;
168           else {
169                     error = EBUSY;
170                     cam_periph_release(periph);
171           }
172 
173           CAM_DEBUG(periph->path, CAM_DEBUG_TRACE,
174               ("ptopen: dev=%s\n", devtoname(dev)));
175 
176           cam_periph_unlock(periph);
177           return (error);
178 }
179 
180 static int
ptclose(struct dev_close_args * ap)181 ptclose(struct dev_close_args *ap)
182 {
183           cdev_t dev = ap->a_head.a_dev;
184           struct    cam_periph *periph;
185           struct    pt_softc *softc;
186           int       unit;
187 
188           unit = minor(dev);
189           periph = cam_extend_get(ptperiphs, unit);
190           if (periph == NULL)
191                     return (ENXIO);
192 
193           softc = (struct pt_softc *)periph->softc;
194 
195           cam_periph_lock(periph);
196 
197           softc->flags &= ~PT_FLAG_OPEN;
198           cam_periph_unlock(periph);
199           cam_periph_release(periph);
200           return (0);
201 }
202 
203 /*
204  * Actually translate the requested transfer into one the physical driver
205  * can understand.  The transfer is described by a buf and will include
206  * only one physical transfer.
207  */
208 static int
ptstrategy(struct dev_strategy_args * ap)209 ptstrategy(struct dev_strategy_args *ap)
210 {
211           cdev_t dev = ap->a_head.a_dev;
212           struct bio *bio = ap->a_bio;
213           struct buf *bp = bio->bio_buf;
214           struct cam_periph *periph;
215           struct pt_softc *softc;
216           u_int  unit;
217 
218           unit = minor(dev);
219           periph = cam_extend_get(ptperiphs, unit);
220           if (periph == NULL) {
221                     bp->b_error = ENXIO;
222                     goto bad;
223           }
224           cam_periph_lock(periph);
225           softc = (struct pt_softc *)periph->softc;
226 
227           /*
228            * If the device has been made invalid, error out
229            */
230           if ((softc->flags & PT_FLAG_DEVICE_INVALID)) {
231                     cam_periph_unlock(periph);
232                     bp->b_error = ENXIO;
233                     goto bad;
234           }
235 
236           /*
237            * Place it in the queue of disk activities for this disk
238            */
239           bioq_insert_tail(&softc->bio_queue, bio);
240 
241           /*
242            * Schedule ourselves for performing the work.
243            */
244           xpt_schedule(periph, /* XXX priority */1);
245           cam_periph_unlock(periph);
246 
247           return(0);
248 bad:
249           bp->b_flags |= B_ERROR;
250 
251           /*
252            * Correctly set the buf to indicate a completed xfer
253            */
254           bp->b_resid = bp->b_bcount;
255           biodone(bio);
256           return(0);
257 }
258 
259 static void
ptinit(void)260 ptinit(void)
261 {
262           cam_status status;
263 
264           /*
265            * Create our extend array for storing the devices we attach to.
266            */
267           ptperiphs = cam_extend_new();
268           if (ptperiphs == NULL) {
269                     kprintf("pt: Failed to alloc extend array!\n");
270                     return;
271           }
272 
273           /*
274            * Install a global async callback.  This callback will
275            * receive async callbacks like "new device found".
276            */
277           status = xpt_register_async(AC_FOUND_DEVICE, ptasync, NULL, NULL);
278 
279           if (status != CAM_REQ_CMP) {
280                     kprintf("pt: Failed to attach master async callback "
281                            "due to status 0x%x!\n", status);
282           }
283 }
284 
285 static cam_status
ptctor(struct cam_periph * periph,void * arg)286 ptctor(struct cam_periph *periph, void *arg)
287 {
288           struct pt_softc *softc;
289           struct ccb_getdev *cgd;
290 
291           cgd = (struct ccb_getdev *)arg;
292           if (periph == NULL) {
293                     kprintf("ptregister: periph was NULL!!\n");
294                     return(CAM_REQ_CMP_ERR);
295           }
296 
297           if (cgd == NULL) {
298                     kprintf("ptregister: no getdev CCB, can't register device\n");
299                     return(CAM_REQ_CMP_ERR);
300           }
301 
302           softc = kmalloc(sizeof(*softc), M_DEVBUF, M_INTWAIT | M_ZERO);
303           LIST_INIT(&softc->pending_ccbs);
304           softc->state = PT_STATE_NORMAL;
305           bioq_init(&softc->bio_queue);
306 
307           softc->io_timeout = SCSI_PT_DEFAULT_TIMEOUT * 1000;
308 
309           periph->softc = softc;
310 
311           cam_periph_unlock(periph);
312           cam_extend_set(ptperiphs, periph->unit_number, periph);
313 
314           devstat_add_entry(&softc->device_stats, "pt",
315                                 periph->unit_number, 0,
316                                 DEVSTAT_NO_BLOCKSIZE,
317                                 SID_TYPE(&cgd->inq_data) | DEVSTAT_TYPE_IF_SCSI,
318                                 DEVSTAT_PRIORITY_OTHER);
319 
320           make_dev(&pt_ops, periph->unit_number, UID_ROOT,
321                       GID_OPERATOR, 0600, "%s%d", periph->periph_name,
322                       periph->unit_number);
323           cam_periph_lock(periph);
324           /*
325            * Add async callbacks for bus reset and
326            * bus device reset calls.  I don't bother
327            * checking if this fails as, in most cases,
328            * the system will function just fine without
329            * them and the only alternative would be to
330            * not attach the device on failure.
331            */
332           xpt_register_async(AC_SENT_BDR | AC_BUS_RESET | AC_LOST_DEVICE,
333                                  ptasync, periph, periph->path);
334 
335           /* Tell the user we've attached to the device */
336           xpt_announce_periph(periph, NULL);
337 
338           return(CAM_REQ_CMP);
339 }
340 
341 static void
ptoninvalidate(struct cam_periph * periph)342 ptoninvalidate(struct cam_periph *periph)
343 {
344           struct pt_softc *softc;
345           struct bio *q_bio;
346           struct buf *q_bp;
347 
348           softc = (struct pt_softc *)periph->softc;
349 
350           /*
351            * De-register any async callbacks.
352            */
353           xpt_register_async(0, ptasync, periph, periph->path);
354 
355           softc->flags |= PT_FLAG_DEVICE_INVALID;
356 
357           /*
358            * Return all queued I/O with ENXIO.
359            * XXX Handle any transactions queued to the card
360            *     with XPT_ABORT_CCB.
361            */
362           while ((q_bio = bioq_takefirst(&softc->bio_queue)) != NULL) {
363                     q_bp = q_bio->bio_buf;
364                     q_bp->b_resid = q_bp->b_bcount;
365                     q_bp->b_error = ENXIO;
366                     q_bp->b_flags |= B_ERROR;
367                     biodone(q_bio);
368           }
369 
370           xpt_print(periph->path, "lost device\n");
371 }
372 
373 static void
ptdtor(struct cam_periph * periph)374 ptdtor(struct cam_periph *periph)
375 {
376           struct pt_softc *softc;
377 
378           softc = (struct pt_softc *)periph->softc;
379 
380           devstat_remove_entry(&softc->device_stats);
381 
382           cam_extend_release(ptperiphs, periph->unit_number);
383           xpt_print(periph->path, "removing device entry\n");
384           dev_ops_remove_minor(&pt_ops, periph->unit_number);
385           kfree(softc, M_DEVBUF);
386 }
387 
388 static void
ptasync(void * callback_arg,u_int32_t code,struct cam_path * path,void * arg)389 ptasync(void *callback_arg, u_int32_t code, struct cam_path *path, void *arg)
390 {
391           struct cam_periph *periph;
392 
393           periph = (struct cam_periph *)callback_arg;
394           switch (code) {
395           case AC_FOUND_DEVICE:
396           {
397                     struct ccb_getdev *cgd;
398                     cam_status status;
399 
400                     cgd = (struct ccb_getdev *)arg;
401                     if (cgd == NULL)
402                               break;
403 
404                     if (SID_TYPE(&cgd->inq_data) != T_PROCESSOR)
405                               break;
406 
407                     /*
408                      * Allocate a peripheral instance for
409                      * this device and start the probe
410                      * process.
411                      */
412                     status = cam_periph_alloc(ptctor, ptoninvalidate, ptdtor,
413                                                     ptstart, "pt", CAM_PERIPH_BIO,
414                                                     cgd->ccb_h.path, ptasync,
415                                                     AC_FOUND_DEVICE, cgd);
416 
417                     if (status != CAM_REQ_CMP
418                      && status != CAM_REQ_INPROG)
419                               kprintf("ptasync: Unable to attach to new device "
420                                         "due to status 0x%x\n", status);
421                     break;
422           }
423           case AC_SENT_BDR:
424           case AC_BUS_RESET:
425           {
426                     struct pt_softc *softc;
427                     struct ccb_hdr *ccbh;
428 
429                     softc = (struct pt_softc *)periph->softc;
430                     /*
431                      * Don't fail on the expected unit attention
432                      * that will occur.
433                      */
434                     softc->flags |= PT_FLAG_RETRY_UA;
435                     LIST_FOREACH(ccbh, &softc->pending_ccbs, periph_links.le)
436                               ccbh->ccb_state |= PT_CCB_RETRY_UA;
437                     /* FALLTHROUGH */
438           }
439           default:
440                     cam_periph_async(periph, code, path, arg);
441                     break;
442           }
443 }
444 
445 static void
ptstart(struct cam_periph * periph,union ccb * start_ccb)446 ptstart(struct cam_periph *periph, union ccb *start_ccb)
447 {
448           struct pt_softc *softc;
449           struct buf *bp;
450           struct bio *bio;
451 
452           softc = (struct pt_softc *)periph->softc;
453 
454           /*
455            * See if there is a buf with work for us to do..
456            */
457           bio = bioq_first(&softc->bio_queue);
458           if (periph->immediate_priority <= periph->pinfo.priority) {
459                     CAM_DEBUG_PRINT(CAM_DEBUG_SUBTRACE,
460                                         ("queuing for immediate ccb\n"));
461                     start_ccb->ccb_h.ccb_state = PT_CCB_WAITING;
462                     SLIST_INSERT_HEAD(&periph->ccb_list, &start_ccb->ccb_h,
463                                           periph_links.sle);
464                     periph->immediate_priority = CAM_PRIORITY_NONE;
465                     wakeup(&periph->ccb_list);
466           } else if (bio == NULL) {
467                     xpt_release_ccb(start_ccb);
468           } else {
469                     bioq_remove(&softc->bio_queue, bio);
470                     bp = bio->bio_buf;
471 
472                     devstat_start_transaction(&softc->device_stats);
473 
474                     scsi_send_receive(&start_ccb->csio,
475                                           /*retries*/4,
476                                           ptdone,
477                                           MSG_SIMPLE_Q_TAG,
478                                           (bp->b_cmd == BUF_CMD_READ),
479                                           /*byte2*/0,
480                                           bp->b_bcount,
481                                           bp->b_data,
482                                           /*sense_len*/SSD_FULL_SIZE,
483                                           /*timeout*/softc->io_timeout);
484 
485                     start_ccb->ccb_h.ccb_state = PT_CCB_BUFFER_IO_UA;
486 
487                     /*
488                      * Block out any asyncronous callbacks
489                      * while we touch the pending ccb list.
490                      */
491                     LIST_INSERT_HEAD(&softc->pending_ccbs, &start_ccb->ccb_h,
492                                          periph_links.le);
493 
494                     start_ccb->ccb_h.ccb_bio = bio;
495                     bio = bioq_first(&softc->bio_queue);
496 
497                     xpt_action(start_ccb);
498 
499                     if (bio != NULL) {
500                               /* Have more work to do, so ensure we stay scheduled */
501                               xpt_schedule(periph, /* XXX priority */1);
502                     }
503           }
504 }
505 
506 static void
ptdone(struct cam_periph * periph,union ccb * done_ccb)507 ptdone(struct cam_periph *periph, union ccb *done_ccb)
508 {
509           struct pt_softc *softc;
510           struct ccb_scsiio *csio;
511 
512           softc = (struct pt_softc *)periph->softc;
513           csio = &done_ccb->csio;
514           switch (csio->ccb_h.ccb_state) {
515           case PT_CCB_BUFFER_IO:
516           case PT_CCB_BUFFER_IO_UA:
517           {
518                     struct buf *bp;
519                     struct bio *bio;
520 
521                     bio = (struct bio *)done_ccb->ccb_h.ccb_bio;
522                     bp = bio->bio_buf;
523 
524                     if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
525                               int error;
526                               int sf;
527 
528                               if ((csio->ccb_h.ccb_state & PT_CCB_RETRY_UA) != 0)
529                                         sf = SF_RETRY_UA;
530                               else
531                                         sf = 0;
532 
533                               error = pterror(done_ccb, CAM_RETRY_SELTO, sf);
534                               if (error == ERESTART) {
535                                         /*
536                                          * A retry was scheuled, so
537                                          * just return.
538                                          */
539                                         return;
540                               }
541                               if (error != 0) {
542                                         struct buf *q_bp;
543                                         struct bio *q_bio;
544 
545                                         if (error == ENXIO) {
546                                                   /*
547                                                    * Catastrophic error.  Mark our device
548                                                    * as invalid.
549                                                    */
550                                                   xpt_print(periph->path,
551                                                       "Invalidating device\n");
552                                                   softc->flags |= PT_FLAG_DEVICE_INVALID;
553                                         }
554 
555                                         /*
556                                          * return all queued I/O with EIO, so that
557                                          * the client can retry these I/Os in the
558                                          * proper order should it attempt to recover.
559                                          */
560                                         while ((q_bio = bioq_takefirst(&softc->bio_queue)) != NULL) {
561                                                   q_bp = q_bio->bio_buf;
562                                                   q_bp->b_resid = q_bp->b_bcount;
563                                                   q_bp->b_error = EIO;
564                                                   q_bp->b_flags |= B_ERROR;
565                                                   biodone(q_bio);
566                                         }
567                                         bp->b_error = error;
568                                         bp->b_resid = bp->b_bcount;
569                                         bp->b_flags |= B_ERROR;
570                               } else {
571                                         bp->b_resid = csio->resid;
572                                         bp->b_error = 0;
573                                         if (bp->b_resid != 0) {
574                                                   /* Short transfer ??? */
575                                                   bp->b_flags |= B_ERROR;
576                                         }
577                               }
578                               if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0)
579                                         cam_release_devq(done_ccb->ccb_h.path,
580                                                              /*relsim_flags*/0,
581                                                              /*reduction*/0,
582                                                              /*timeout*/0,
583                                                              /*getcount_only*/0);
584                     } else {
585                               bp->b_resid = csio->resid;
586                               if (bp->b_resid != 0)
587                                         bp->b_flags |= B_ERROR;
588                     }
589 
590                     /*
591                      * Block out any asyncronous callbacks
592                      * while we touch the pending ccb list.
593                      */
594                     LIST_REMOVE(&done_ccb->ccb_h, periph_links.le);
595 
596                     devstat_end_transaction_buf(&softc->device_stats, bp);
597                     biodone(bio);
598                     break;
599           }
600           case PT_CCB_WAITING:
601                     /* Caller will release the CCB */
602                     wakeup(&done_ccb->ccb_h.cbfcnp);
603                     return;
604           }
605           xpt_release_ccb(done_ccb);
606 }
607 
608 static int
pterror(union ccb * ccb,u_int32_t cam_flags,u_int32_t sense_flags)609 pterror(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags)
610 {
611           struct pt_softc       *softc;
612           struct cam_periph *periph;
613 
614           periph = xpt_path_periph(ccb->ccb_h.path);
615           softc = (struct pt_softc *)periph->softc;
616 
617           return(cam_periph_error(ccb, cam_flags, sense_flags,
618                                         &softc->saved_ccb));
619 }
620 
621 static int
ptioctl(struct dev_ioctl_args * ap)622 ptioctl(struct dev_ioctl_args *ap)
623 {
624           cdev_t dev = ap->a_head.a_dev;
625           caddr_t addr = ap->a_data;
626           struct cam_periph *periph;
627           struct pt_softc *softc;
628           int unit;
629           int error = 0;
630 
631           unit = minor(dev);
632           periph = cam_extend_get(ptperiphs, unit);
633 
634           if (periph == NULL)
635                     return(ENXIO);
636 
637           softc = (struct pt_softc *)periph->softc;
638 
639           cam_periph_lock(periph);
640 
641           switch(ap->a_cmd) {
642           case PTIOCGETTIMEOUT:
643                     if (softc->io_timeout >= 1000)
644                               *(int *)addr = softc->io_timeout / 1000;
645                     else
646                               *(int *)addr = 0;
647                     break;
648           case PTIOCSETTIMEOUT:
649                     if (*(int *)addr < 1) {
650                               error = EINVAL;
651                               break;
652                     }
653 
654                     softc->io_timeout = *(int *)addr * 1000;
655 
656                     break;
657           default:
658                     error = cam_periph_ioctl(periph, ap->a_cmd, addr, pterror);
659                     break;
660           }
661 
662           cam_periph_unlock(periph);
663 
664           return(error);
665 }
666 
667 void
scsi_send_receive(struct ccb_scsiio * csio,u_int32_t retries,void (* cbfcnp)(struct cam_periph *,union ccb *),u_int tag_action,int readop,u_int byte2,u_int32_t xfer_len,u_int8_t * data_ptr,u_int8_t sense_len,u_int32_t timeout)668 scsi_send_receive(struct ccb_scsiio *csio, u_int32_t retries,
669                       void (*cbfcnp)(struct cam_periph *, union ccb *),
670                       u_int tag_action, int readop, u_int byte2,
671                       u_int32_t xfer_len, u_int8_t *data_ptr, u_int8_t sense_len,
672                       u_int32_t timeout)
673 {
674           struct scsi_send_receive *scsi_cmd;
675 
676           scsi_cmd = (struct scsi_send_receive *)&csio->cdb_io.cdb_bytes;
677           scsi_cmd->opcode = readop ? RECEIVE : SEND;
678           scsi_cmd->byte2 = byte2;
679           scsi_ulto3b(xfer_len, scsi_cmd->xfer_len);
680           scsi_cmd->control = 0;
681 
682           cam_fill_csio(csio,
683                           retries,
684                           cbfcnp,
685                           /*flags*/readop ? CAM_DIR_IN : CAM_DIR_OUT,
686                           tag_action,
687                           data_ptr,
688                           xfer_len,
689                           sense_len,
690                           sizeof(*scsi_cmd),
691                           timeout);
692 }
693