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