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