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