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