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