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