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