xref: /dragonfly/sys/bus/cam/scsi/scsi_pass.c (revision 6e285212)
1 /*
2  * Copyright (c) 1997, 1998 Justin T. Gibbs.
3  * Copyright (c) 1997, 1998, 1999 Kenneth D. Merry.
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions, and the following disclaimer,
11  *    without modification, immediately at the beginning of the file.
12  * 2. The name of the author may not be used to endorse or promote products
13  *    derived from this software without specific prior written permission.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
19  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  * $FreeBSD: src/sys/cam/scsi/scsi_pass.c,v 1.19 2000/01/17 06:27:37 mjacob Exp $
28  * $DragonFly: src/sys/bus/cam/scsi/scsi_pass.c,v 1.2 2003/06/17 04:28:19 dillon Exp $
29  */
30 
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/kernel.h>
34 #include <sys/types.h>
35 #include <sys/buf.h>
36 #include <sys/malloc.h>
37 #include <sys/fcntl.h>
38 #include <sys/stat.h>
39 #include <sys/conf.h>
40 #include <sys/buf.h>
41 #include <sys/proc.h>
42 #include <sys/errno.h>
43 #include <sys/devicestat.h>
44 
45 #include <cam/cam.h>
46 #include <cam/cam_ccb.h>
47 #include <cam/cam_extend.h>
48 #include <cam/cam_periph.h>
49 #include <cam/cam_xpt_periph.h>
50 #include <cam/cam_debug.h>
51 
52 #include <cam/scsi/scsi_all.h>
53 #include <cam/scsi/scsi_message.h>
54 #include <cam/scsi/scsi_da.h>
55 #include <cam/scsi/scsi_pass.h>
56 
57 typedef enum {
58 	PASS_FLAG_OPEN			= 0x01,
59 	PASS_FLAG_LOCKED		= 0x02,
60 	PASS_FLAG_INVALID		= 0x04
61 } pass_flags;
62 
63 typedef enum {
64 	PASS_STATE_NORMAL
65 } pass_state;
66 
67 typedef enum {
68 	PASS_CCB_BUFFER_IO,
69 	PASS_CCB_WAITING
70 } pass_ccb_types;
71 
72 #define ccb_type	ppriv_field0
73 #define ccb_bp		ppriv_ptr1
74 
75 struct pass_softc {
76 	pass_state	state;
77 	pass_flags	flags;
78 	u_int8_t	pd_type;
79 	struct		buf_queue_head buf_queue;
80 	union ccb	saved_ccb;
81 	struct devstat	device_stats;
82 	dev_t		dev;
83 };
84 
85 #ifndef MIN
86 #define MIN(x,y) ((x<y) ? x : y)
87 #endif
88 
89 #define PASS_CDEV_MAJOR 31
90 
91 static	d_open_t	passopen;
92 static	d_close_t	passclose;
93 static	d_ioctl_t	passioctl;
94 static	d_strategy_t	passstrategy;
95 
96 static	periph_init_t	passinit;
97 static	periph_ctor_t	passregister;
98 static	periph_oninv_t	passoninvalidate;
99 static	periph_dtor_t	passcleanup;
100 static	periph_start_t	passstart;
101 static	void		passasync(void *callback_arg, u_int32_t code,
102 				  struct cam_path *path, void *arg);
103 static	void		passdone(struct cam_periph *periph,
104 				 union ccb *done_ccb);
105 static	int		passerror(union ccb *ccb, u_int32_t cam_flags,
106 				  u_int32_t sense_flags);
107 static 	int		passsendccb(struct cam_periph *periph, union ccb *ccb,
108 				    union ccb *inccb);
109 
110 static struct periph_driver passdriver =
111 {
112 	passinit, "pass",
113 	TAILQ_HEAD_INITIALIZER(passdriver.units), /* generation */ 0
114 };
115 
116 DATA_SET(periphdriver_set, passdriver);
117 
118 static struct cdevsw pass_cdevsw = {
119 	/* open */	passopen,
120 	/* close */	passclose,
121 	/* read */	physread,
122 	/* write */	physwrite,
123 	/* ioctl */	passioctl,
124 	/* poll */	nopoll,
125 	/* mmap */	nommap,
126 	/* strategy */	passstrategy,
127 	/* name */	"pass",
128 	/* maj */	PASS_CDEV_MAJOR,
129 	/* dump */	nodump,
130 	/* psize */	nopsize,
131 	/* flags */	0,
132 	/* bmaj */	-1
133 };
134 
135 static struct extend_array *passperiphs;
136 
137 static void
138 passinit(void)
139 {
140 	cam_status status;
141 	struct cam_path *path;
142 
143 	/*
144 	 * Create our extend array for storing the devices we attach to.
145 	 */
146 	passperiphs = cam_extend_new();
147 	if (passperiphs == NULL) {
148 		printf("passm: Failed to alloc extend array!\n");
149 		return;
150 	}
151 
152 	/*
153 	 * Install a global async callback.  This callback will
154 	 * receive async callbacks like "new device found".
155 	 */
156 	status = xpt_create_path(&path, /*periph*/NULL, CAM_XPT_PATH_ID,
157 				 CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD);
158 
159 	if (status == CAM_REQ_CMP) {
160 		struct ccb_setasync csa;
161 
162                 xpt_setup_ccb(&csa.ccb_h, path, /*priority*/5);
163                 csa.ccb_h.func_code = XPT_SASYNC_CB;
164                 csa.event_enable = AC_FOUND_DEVICE;
165                 csa.callback = passasync;
166                 csa.callback_arg = NULL;
167                 xpt_action((union ccb *)&csa);
168 		status = csa.ccb_h.status;
169                 xpt_free_path(path);
170         }
171 
172 	if (status != CAM_REQ_CMP) {
173 		printf("pass: Failed to attach master async callback "
174 		       "due to status 0x%x!\n", status);
175 	}
176 
177 }
178 
179 static void
180 passoninvalidate(struct cam_periph *periph)
181 {
182 	int s;
183 	struct pass_softc *softc;
184 	struct buf *q_bp;
185 	struct ccb_setasync csa;
186 
187 	softc = (struct pass_softc *)periph->softc;
188 
189 	/*
190 	 * De-register any async callbacks.
191 	 */
192 	xpt_setup_ccb(&csa.ccb_h, periph->path,
193 		      /* priority */ 5);
194 	csa.ccb_h.func_code = XPT_SASYNC_CB;
195 	csa.event_enable = 0;
196 	csa.callback = passasync;
197 	csa.callback_arg = periph;
198 	xpt_action((union ccb *)&csa);
199 
200 	softc->flags |= PASS_FLAG_INVALID;
201 
202 	/*
203 	 * Although the oninvalidate() routines are always called at
204 	 * splsoftcam, we need to be at splbio() here to keep the buffer
205 	 * queue from being modified while we traverse it.
206 	 */
207 	s = splbio();
208 
209 	/*
210 	 * Return all queued I/O with ENXIO.
211 	 * XXX Handle any transactions queued to the card
212 	 *     with XPT_ABORT_CCB.
213 	 */
214 	while ((q_bp = bufq_first(&softc->buf_queue)) != NULL){
215 		bufq_remove(&softc->buf_queue, q_bp);
216 		q_bp->b_resid = q_bp->b_bcount;
217 		q_bp->b_error = ENXIO;
218 		q_bp->b_flags |= B_ERROR;
219 		biodone(q_bp);
220 	}
221 	splx(s);
222 
223 	if (bootverbose) {
224 		xpt_print_path(periph->path);
225 		printf("lost device\n");
226 	}
227 
228 }
229 
230 static void
231 passcleanup(struct cam_periph *periph)
232 {
233 	struct pass_softc *softc;
234 
235 	softc = (struct pass_softc *)periph->softc;
236 
237 	devstat_remove_entry(&softc->device_stats);
238 
239 	destroy_dev(softc->dev);
240 
241 	cam_extend_release(passperiphs, periph->unit_number);
242 
243 	if (bootverbose) {
244 		xpt_print_path(periph->path);
245 		printf("removing device entry\n");
246 	}
247 	free(softc, M_DEVBUF);
248 }
249 
250 static void
251 passasync(void *callback_arg, u_int32_t code,
252 	  struct cam_path *path, void *arg)
253 {
254 	struct cam_periph *periph;
255 
256 	periph = (struct cam_periph *)callback_arg;
257 
258 	switch (code) {
259 	case AC_FOUND_DEVICE:
260 	{
261 		struct ccb_getdev *cgd;
262 		cam_status status;
263 
264 		cgd = (struct ccb_getdev *)arg;
265 
266 		/*
267 		 * Allocate a peripheral instance for
268 		 * this device and start the probe
269 		 * process.
270 		 */
271 		status = cam_periph_alloc(passregister, passoninvalidate,
272 					  passcleanup, passstart, "pass",
273 					  CAM_PERIPH_BIO, cgd->ccb_h.path,
274 					  passasync, AC_FOUND_DEVICE, cgd);
275 
276 		if (status != CAM_REQ_CMP
277 		 && status != CAM_REQ_INPROG)
278 			printf("passasync: Unable to attach new device "
279 				"due to status 0x%x\n", status);
280 
281 		break;
282 	}
283 	default:
284 		cam_periph_async(periph, code, path, arg);
285 		break;
286 	}
287 }
288 
289 static cam_status
290 passregister(struct cam_periph *periph, void *arg)
291 {
292 	struct pass_softc *softc;
293 	struct ccb_setasync csa;
294 	struct ccb_getdev *cgd;
295 
296 	cgd = (struct ccb_getdev *)arg;
297 	if (periph == NULL) {
298 		printf("passregister: periph was NULL!!\n");
299 		return(CAM_REQ_CMP_ERR);
300 	}
301 
302 	if (cgd == NULL) {
303 		printf("passregister: no getdev CCB, can't register device\n");
304 		return(CAM_REQ_CMP_ERR);
305 	}
306 
307 	softc = (struct pass_softc *)malloc(sizeof(*softc),
308 					    M_DEVBUF, M_NOWAIT);
309 
310 	if (softc == NULL) {
311 		printf("passregister: Unable to probe new device. "
312 		       "Unable to allocate softc\n");
313 		return(CAM_REQ_CMP_ERR);
314 	}
315 
316 	bzero(softc, sizeof(*softc));
317 	softc->state = PASS_STATE_NORMAL;
318 	softc->pd_type = SID_TYPE(&cgd->inq_data);
319 	bufq_init(&softc->buf_queue);
320 
321 	periph->softc = softc;
322 
323 	cam_extend_set(passperiphs, periph->unit_number, periph);
324 	/*
325 	 * We pass in 0 for a blocksize, since we don't
326 	 * know what the blocksize of this device is, if
327 	 * it even has a blocksize.
328 	 */
329 	devstat_add_entry(&softc->device_stats, "pass", periph->unit_number,
330 			  0, DEVSTAT_NO_BLOCKSIZE | DEVSTAT_NO_ORDERED_TAGS,
331 			  softc->pd_type |
332 			  DEVSTAT_TYPE_IF_SCSI |
333 			  DEVSTAT_TYPE_PASS,
334 			  DEVSTAT_PRIORITY_PASS);
335 
336 	/* Register the device */
337 	softc->dev = make_dev(&pass_cdevsw, periph->unit_number, UID_ROOT,
338 			      GID_OPERATOR, 0600, "%s%d", periph->periph_name,
339 			      periph->unit_number);
340 
341 	/*
342 	 * Add an async callback so that we get
343 	 * notified if this device goes away.
344 	 */
345 	xpt_setup_ccb(&csa.ccb_h, periph->path, /* priority */ 5);
346 	csa.ccb_h.func_code = XPT_SASYNC_CB;
347 	csa.event_enable = AC_LOST_DEVICE;
348 	csa.callback = passasync;
349 	csa.callback_arg = periph;
350 	xpt_action((union ccb *)&csa);
351 
352 	if (bootverbose)
353 		xpt_announce_periph(periph, NULL);
354 
355 	return(CAM_REQ_CMP);
356 }
357 
358 static int
359 passopen(dev_t dev, int flags, int fmt, struct proc *p)
360 {
361 	struct cam_periph *periph;
362 	struct pass_softc *softc;
363 	int unit, error;
364 	int s;
365 
366 	error = 0; /* default to no error */
367 
368 	/* unit = dkunit(dev); */
369 	/* XXX KDM fix this */
370 	unit = minor(dev) & 0xff;
371 
372 	periph = cam_extend_get(passperiphs, unit);
373 
374 	if (periph == NULL)
375 		return (ENXIO);
376 
377 	softc = (struct pass_softc *)periph->softc;
378 
379 	s = splsoftcam();
380 	if (softc->flags & PASS_FLAG_INVALID) {
381 		splx(s);
382 		return(ENXIO);
383 	}
384 
385 	/*
386 	 * Don't allow access when we're running at a high securelvel.
387 	 */
388 	if (securelevel > 1) {
389 		splx(s);
390 		return(EPERM);
391 	}
392 
393 	/*
394 	 * Only allow read-write access.
395 	 */
396 	if (((flags & FWRITE) == 0) || ((flags & FREAD) == 0)) {
397 		splx(s);
398 		return(EPERM);
399 	}
400 
401 	/*
402 	 * We don't allow nonblocking access.
403 	 */
404 	if ((flags & O_NONBLOCK) != 0) {
405 		xpt_print_path(periph->path);
406 		printf("can't do nonblocking accesss\n");
407 		splx(s);
408 		return(EINVAL);
409 	}
410 
411 	if ((error = cam_periph_lock(periph, PRIBIO | PCATCH)) != 0) {
412 		splx(s);
413 		return (error);
414 	}
415 
416 	splx(s);
417 
418 	if ((softc->flags & PASS_FLAG_OPEN) == 0) {
419 		if (cam_periph_acquire(periph) != CAM_REQ_CMP)
420 			return(ENXIO);
421 		softc->flags |= PASS_FLAG_OPEN;
422 	}
423 
424 	cam_periph_unlock(periph);
425 
426 	return (error);
427 }
428 
429 static int
430 passclose(dev_t dev, int flag, int fmt, struct proc *p)
431 {
432 	struct 	cam_periph *periph;
433 	struct	pass_softc *softc;
434 	int	unit, error;
435 
436 	/* unit = dkunit(dev); */
437 	/* XXX KDM fix this */
438 	unit = minor(dev) & 0xff;
439 
440 	periph = cam_extend_get(passperiphs, unit);
441 	if (periph == NULL)
442 		return (ENXIO);
443 
444 	softc = (struct pass_softc *)periph->softc;
445 
446 	if ((error = cam_periph_lock(periph, PRIBIO)) != 0)
447 		return (error);
448 
449 	softc->flags &= ~PASS_FLAG_OPEN;
450 
451 	cam_periph_unlock(periph);
452 	cam_periph_release(periph);
453 
454 	return (0);
455 }
456 
457 /*
458  * Actually translate the requested transfer into one the physical driver
459  * can understand.  The transfer is described by a buf and will include
460  * only one physical transfer.
461  */
462 static void
463 passstrategy(struct buf *bp)
464 {
465 	struct cam_periph *periph;
466 	struct pass_softc *softc;
467 	u_int  unit;
468 	int    s;
469 
470 	/*
471 	 * The read/write interface for the passthrough driver doesn't
472 	 * really work right now.  So, we just pass back EINVAL to tell the
473 	 * user to go away.
474 	 */
475 	bp->b_error = EINVAL;
476 	goto bad;
477 
478 	/* unit = dkunit(bp->b_dev); */
479 	/* XXX KDM fix this */
480 	unit = minor(bp->b_dev) & 0xff;
481 
482 	periph = cam_extend_get(passperiphs, unit);
483 	if (periph == NULL) {
484 		bp->b_error = ENXIO;
485 		goto bad;
486 	}
487 	softc = (struct pass_softc *)periph->softc;
488 
489 	/*
490 	 * Odd number of bytes or negative offset
491 	 */
492 	/* valid request?  */
493 	if (bp->b_blkno < 0) {
494 		bp->b_error = EINVAL;
495 		goto bad;
496         }
497 
498 	/*
499 	 * Mask interrupts so that the pack cannot be invalidated until
500 	 * after we are in the queue.  Otherwise, we might not properly
501 	 * clean up one of the buffers.
502 	 */
503 	s = splbio();
504 
505 	bufq_insert_tail(&softc->buf_queue, bp);
506 
507 	splx(s);
508 
509 	/*
510 	 * Schedule ourselves for performing the work.
511 	 */
512 	xpt_schedule(periph, /* XXX priority */1);
513 
514 	return;
515 bad:
516 	bp->b_flags |= B_ERROR;
517 
518 	/*
519 	 * Correctly set the buf to indicate a completed xfer
520 	 */
521 	bp->b_resid = bp->b_bcount;
522 	biodone(bp);
523 	return;
524 }
525 
526 static void
527 passstart(struct cam_periph *periph, union ccb *start_ccb)
528 {
529 	struct pass_softc *softc;
530 	int s;
531 
532 	softc = (struct pass_softc *)periph->softc;
533 
534 	switch (softc->state) {
535 	case PASS_STATE_NORMAL:
536 	{
537 		struct buf *bp;
538 
539 		s = splbio();
540 		bp = bufq_first(&softc->buf_queue);
541 		if (periph->immediate_priority <= periph->pinfo.priority) {
542 			start_ccb->ccb_h.ccb_type = PASS_CCB_WAITING;
543 			SLIST_INSERT_HEAD(&periph->ccb_list, &start_ccb->ccb_h,
544 					  periph_links.sle);
545 			periph->immediate_priority = CAM_PRIORITY_NONE;
546 			splx(s);
547 			wakeup(&periph->ccb_list);
548 		} else if (bp == NULL) {
549 			splx(s);
550 			xpt_release_ccb(start_ccb);
551 		} else {
552 
553 			bufq_remove(&softc->buf_queue, bp);
554 
555 			devstat_start_transaction(&softc->device_stats);
556 
557 			/*
558 			 * XXX JGibbs -
559 			 * Interpret the contents of the bp as a CCB
560 			 * and pass it to a routine shared by our ioctl
561 			 * code and passtart.
562 			 * For now, just biodone it with EIO so we don't
563 			 * hang.
564 			 */
565 			bp->b_error = EIO;
566 			bp->b_flags |= B_ERROR;
567 			bp->b_resid = bp->b_bcount;
568 			biodone(bp);
569 			bp = bufq_first(&softc->buf_queue);
570 			splx(s);
571 
572 			xpt_action(start_ccb);
573 
574 		}
575 		if (bp != NULL) {
576 			/* Have more work to do, so ensure we stay scheduled */
577 			xpt_schedule(periph, /* XXX priority */1);
578 		}
579 		break;
580 	}
581 	}
582 }
583 static void
584 passdone(struct cam_periph *periph, union ccb *done_ccb)
585 {
586 	struct pass_softc *softc;
587 	struct ccb_scsiio *csio;
588 
589 	softc = (struct pass_softc *)periph->softc;
590 	csio = &done_ccb->csio;
591 	switch (csio->ccb_h.ccb_type) {
592 	case PASS_CCB_BUFFER_IO:
593 	{
594 		struct buf		*bp;
595 		cam_status		status;
596 		u_int8_t		scsi_status;
597 		devstat_trans_flags	ds_flags;
598 
599 		status = done_ccb->ccb_h.status;
600 		scsi_status = done_ccb->csio.scsi_status;
601 		bp = (struct buf *)done_ccb->ccb_h.ccb_bp;
602 		/* XXX handle errors */
603 		if (!(((status & CAM_STATUS_MASK) == CAM_REQ_CMP)
604 		  && (scsi_status == SCSI_STATUS_OK))) {
605 			int error;
606 
607 			if ((error = passerror(done_ccb, 0, 0)) == ERESTART) {
608 				/*
609 				 * A retry was scheuled, so
610 				 * just return.
611 				 */
612 				return;
613 			}
614 
615 			/*
616 			 * XXX unfreeze the queue after we complete
617 			 * the abort process
618 			 */
619 			bp->b_error = error;
620 			bp->b_flags |= B_ERROR;
621 		}
622 
623 		if ((done_ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN)
624 			ds_flags = DEVSTAT_READ;
625 		else if ((done_ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_OUT)
626 			ds_flags = DEVSTAT_WRITE;
627 		else
628 			ds_flags = DEVSTAT_NO_DATA;
629 
630 		devstat_end_transaction_buf(&softc->device_stats, bp);
631 		biodone(bp);
632 		break;
633 	}
634 	case PASS_CCB_WAITING:
635 	{
636 		/* Caller will release the CCB */
637 		wakeup(&done_ccb->ccb_h.cbfcnp);
638 		return;
639 	}
640 	}
641 	xpt_release_ccb(done_ccb);
642 }
643 
644 static int
645 passioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
646 {
647 	struct 	cam_periph *periph;
648 	struct	pass_softc *softc;
649 	u_int8_t unit;
650 	int      error;
651 
652 
653 	/* unit = dkunit(dev); */
654 	/* XXX KDM fix this */
655 	unit = minor(dev) & 0xff;
656 
657 	periph = cam_extend_get(passperiphs, unit);
658 
659 	if (periph == NULL)
660 		return(ENXIO);
661 
662 	softc = (struct pass_softc *)periph->softc;
663 
664 	error = 0;
665 
666 	switch (cmd) {
667 
668 	case CAMIOCOMMAND:
669 	{
670 		union ccb *inccb;
671 		union ccb *ccb;
672 		int ccb_malloced;
673 
674 		inccb = (union ccb *)addr;
675 
676 		/*
677 		 * Some CCB types, like scan bus and scan lun can only go
678 		 * through the transport layer device.
679 		 */
680 		if (inccb->ccb_h.func_code & XPT_FC_XPT_ONLY) {
681 			xpt_print_path(periph->path);
682 			printf("CCB function code %#x is restricted to the "
683 			       "XPT device\n", inccb->ccb_h.func_code);
684 			error = ENODEV;
685 			break;
686 		}
687 
688 		/*
689 		 * Non-immediate CCBs need a CCB from the per-device pool
690 		 * of CCBs, which is scheduled by the transport layer.
691 		 * Immediate CCBs and user-supplied CCBs should just be
692 		 * malloced.
693 		 */
694 		if ((inccb->ccb_h.func_code & XPT_FC_QUEUED)
695 		 && ((inccb->ccb_h.func_code & XPT_FC_USER_CCB) == 0)) {
696 			ccb = cam_periph_getccb(periph,
697 						inccb->ccb_h.pinfo.priority);
698 			ccb_malloced = 0;
699 		} else {
700 			ccb = xpt_alloc_ccb();
701 
702 			if (ccb != NULL)
703 				xpt_setup_ccb(&ccb->ccb_h, periph->path,
704 					      inccb->ccb_h.pinfo.priority);
705 			ccb_malloced = 1;
706 		}
707 
708 		if (ccb == NULL) {
709 			xpt_print_path(periph->path);
710 			printf("unable to allocate CCB\n");
711 			error = ENOMEM;
712 			break;
713 		}
714 
715 		error = passsendccb(periph, ccb, inccb);
716 
717 		if (ccb_malloced)
718 			xpt_free_ccb(ccb);
719 		else
720 			xpt_release_ccb(ccb);
721 
722 		break;
723 	}
724 	default:
725 		error = cam_periph_ioctl(periph, cmd, addr, passerror);
726 		break;
727 	}
728 
729 	return(error);
730 }
731 
732 /*
733  * Generally, "ccb" should be the CCB supplied by the kernel.  "inccb"
734  * should be the CCB that is copied in from the user.
735  */
736 static int
737 passsendccb(struct cam_periph *periph, union ccb *ccb, union ccb *inccb)
738 {
739 	struct pass_softc *softc;
740 	struct cam_periph_map_info mapinfo;
741 	int error, need_unmap;
742 
743 	softc = (struct pass_softc *)periph->softc;
744 
745 	need_unmap = 0;
746 
747 	/*
748 	 * There are some fields in the CCB header that need to be
749 	 * preserved, the rest we get from the user.
750 	 */
751 	xpt_merge_ccb(ccb, inccb);
752 
753 	/*
754 	 * There's no way for the user to have a completion
755 	 * function, so we put our own completion function in here.
756 	 */
757 	ccb->ccb_h.cbfcnp = passdone;
758 
759 	/*
760 	 * We only attempt to map the user memory into kernel space
761 	 * if they haven't passed in a physical memory pointer,
762 	 * and if there is actually an I/O operation to perform.
763 	 * Right now cam_periph_mapmem() only supports SCSI and device
764 	 * match CCBs.  For the SCSI CCBs, we only pass the CCB in if
765 	 * there's actually data to map.  cam_periph_mapmem() will do the
766 	 * right thing, even if there isn't data to map, but since CCBs
767 	 * without data are a reasonably common occurance (e.g. test unit
768 	 * ready), it will save a few cycles if we check for it here.
769 	 */
770 	if (((ccb->ccb_h.flags & CAM_DATA_PHYS) == 0)
771 	 && (((ccb->ccb_h.func_code == XPT_SCSI_IO)
772 	    && ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE))
773 	  || (ccb->ccb_h.func_code == XPT_DEV_MATCH))) {
774 
775 		bzero(&mapinfo, sizeof(mapinfo));
776 
777 		error = cam_periph_mapmem(ccb, &mapinfo);
778 
779 		/*
780 		 * cam_periph_mapmem returned an error, we can't continue.
781 		 * Return the error to the user.
782 		 */
783 		if (error)
784 			return(error);
785 
786 		/*
787 		 * We successfully mapped the memory in, so we need to
788 		 * unmap it when the transaction is done.
789 		 */
790 		need_unmap = 1;
791 	}
792 
793 	/*
794 	 * If the user wants us to perform any error recovery, then honor
795 	 * that request.  Otherwise, it's up to the user to perform any
796 	 * error recovery.
797 	 */
798 	error = cam_periph_runccb(ccb,
799 				  (ccb->ccb_h.flags & CAM_PASS_ERR_RECOVER) ?
800 				  passerror : NULL,
801 				  /* cam_flags */ 0,
802 				  /* sense_flags */SF_RETRY_UA | SF_RETRY_SELTO,
803 				  &softc->device_stats);
804 
805 	if (need_unmap != 0)
806 		cam_periph_unmapmem(ccb, &mapinfo);
807 
808 	ccb->ccb_h.cbfcnp = NULL;
809 	ccb->ccb_h.periph_priv = inccb->ccb_h.periph_priv;
810 	bcopy(ccb, inccb, sizeof(union ccb));
811 
812 	return(error);
813 }
814 
815 static int
816 passerror(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags)
817 {
818 	struct cam_periph *periph;
819 	struct pass_softc *softc;
820 
821 	periph = xpt_path_periph(ccb->ccb_h.path);
822 	softc = (struct pass_softc *)periph->softc;
823 
824 	return(cam_periph_error(ccb, cam_flags, sense_flags,
825 				 &softc->saved_ccb));
826 }
827