xref: /dragonfly/sys/bus/cam/scsi/scsi_pt.c (revision fcce2b94)
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: src/sys/cam/scsi/scsi_pt.c,v 1.17 2000/01/17 06:27:37 mjacob Exp $
29  * $DragonFly: src/sys/bus/cam/scsi/scsi_pt.c,v 1.14 2006/04/30 17:22:15 dillon Exp $
30  */
31 
32 #include <sys/param.h>
33 #include <sys/queue.h>
34 #include <sys/systm.h>
35 #include <sys/kernel.h>
36 #include <sys/types.h>
37 #include <sys/buf.h>
38 #include <sys/devicestat.h>
39 #include <sys/malloc.h>
40 #include <sys/conf.h>
41 #include <sys/ptio.h>
42 #include <sys/buf2.h>
43 #include <sys/thread2.h>
44 
45 #include "../cam.h"
46 #include "../cam_ccb.h"
47 #include "../cam_extend.h"
48 #include "../cam_periph.h"
49 #include "../cam_xpt_periph.h"
50 #include "../cam_debug.h"
51 
52 #include "scsi_all.h"
53 #include "scsi_message.h"
54 #include "scsi_pt.h"
55 
56 #include "opt_pt.h"
57 
58 typedef enum {
59 	PT_STATE_PROBE,
60 	PT_STATE_NORMAL
61 } pt_state;
62 
63 typedef enum {
64 	PT_FLAG_NONE		= 0x00,
65 	PT_FLAG_OPEN		= 0x01,
66 	PT_FLAG_DEVICE_INVALID	= 0x02,
67 	PT_FLAG_RETRY_UA	= 0x04
68 } pt_flags;
69 
70 typedef enum {
71 	PT_CCB_BUFFER_IO	= 0x01,
72 	PT_CCB_WAITING		= 0x02,
73 	PT_CCB_RETRY_UA		= 0x04,
74 	PT_CCB_BUFFER_IO_UA	= PT_CCB_BUFFER_IO|PT_CCB_RETRY_UA
75 } pt_ccb_state;
76 
77 /* Offsets into our private area for storing information */
78 #define ccb_state	ppriv_field0
79 #define ccb_bio		ppriv_ptr1
80 
81 struct pt_softc {
82 	struct	 bio_queue_head bio_queue;
83 	struct	 devstat device_stats;
84 	LIST_HEAD(, ccb_hdr) pending_ccbs;
85 	pt_state state;
86 	pt_flags flags;
87 	union	 ccb saved_ccb;
88 	int	 io_timeout;
89 	dev_t	 dev;
90 };
91 
92 static	d_open_t	ptopen;
93 static	d_close_t	ptclose;
94 static	d_strategy_t	ptstrategy;
95 static	periph_init_t	ptinit;
96 static	void		ptasync(void *callback_arg, u_int32_t code,
97 				struct cam_path *path, void *arg);
98 static	periph_ctor_t	ptctor;
99 static	periph_oninv_t	ptoninvalidate;
100 static	periph_dtor_t	ptdtor;
101 static	periph_start_t	ptstart;
102 static	void		ptdone(struct cam_periph *periph,
103 			       union ccb *done_ccb);
104 static	d_ioctl_t	ptioctl;
105 static  int		pterror(union ccb *ccb, u_int32_t cam_flags,
106 				u_int32_t sense_flags);
107 
108 void	scsi_send_receive(struct ccb_scsiio *csio, u_int32_t retries,
109 			  void (*cbfcnp)(struct cam_periph *, union ccb *),
110 			  u_int tag_action, int readop, u_int byte2,
111 			  u_int32_t xfer_len, u_int8_t *data_ptr,
112 			  u_int8_t sense_len, u_int32_t timeout);
113 
114 static struct periph_driver ptdriver =
115 {
116 	ptinit, "pt",
117 	TAILQ_HEAD_INITIALIZER(ptdriver.units), /* generation */ 0
118 };
119 
120 DATA_SET(periphdriver_set, ptdriver);
121 
122 #define PT_CDEV_MAJOR 61
123 
124 static struct cdevsw pt_cdevsw = {
125 	/* name */	"pt",
126 	/* maj */	PT_CDEV_MAJOR,
127 	/* flags */	0,
128 	/* port */	NULL,
129 	/* clone */     NULL,
130 
131 	/* open */	ptopen,
132 	/* close */	ptclose,
133 	/* read */	physread,
134 	/* write */	physwrite,
135 	/* ioctl */	ptioctl,
136 	/* poll */	nopoll,
137 	/* mmap */	nommap,
138 	/* strategy */	ptstrategy,
139 	/* dump */	nodump,
140 	/* psize */	nopsize
141 };
142 
143 static struct extend_array *ptperiphs;
144 
145 #ifndef SCSI_PT_DEFAULT_TIMEOUT
146 #define SCSI_PT_DEFAULT_TIMEOUT		60
147 #endif
148 
149 static int
150 ptopen(dev_t dev, int flags, int fmt, struct thread *td)
151 {
152 	struct cam_periph *periph;
153 	struct pt_softc *softc;
154 	int unit;
155 	int error;
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 	crit_enter();
165 	if (softc->flags & PT_FLAG_DEVICE_INVALID) {
166 		crit_exit();
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, PCATCH)) != 0) {
174 		crit_exit();
175 		return (error); /* error code from tsleep */
176 	}
177 
178 	crit_exit();
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 thread *td)
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, 0)) != 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(dev_t dev, struct bio *bio)
223 {
224 	struct buf *bp = bio->bio_buf;
225 	struct cam_periph *periph;
226 	struct pt_softc *softc;
227 	u_int  unit;
228 
229 	unit = minor(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 	crit_enter();
243 
244 	/*
245 	 * If the device has been made invalid, error out
246 	 */
247 	if ((softc->flags & PT_FLAG_DEVICE_INVALID)) {
248 		crit_exit();
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 	bioq_insert_tail(&softc->bio_queue, bio);
257 
258 	crit_exit();
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(bio);
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 	}
315 }
316 
317 static cam_status
318 ptctor(struct cam_periph *periph, void *arg)
319 {
320 	struct pt_softc *softc;
321 	struct ccb_setasync csa;
322 	struct ccb_getdev *cgd;
323 
324 	cgd = (struct ccb_getdev *)arg;
325 	if (periph == NULL) {
326 		printf("ptregister: periph was NULL!!\n");
327 		return(CAM_REQ_CMP_ERR);
328 	}
329 
330 	if (cgd == NULL) {
331 		printf("ptregister: no getdev CCB, can't register device\n");
332 		return(CAM_REQ_CMP_ERR);
333 	}
334 
335 	softc = malloc(sizeof(*softc), M_DEVBUF, M_INTWAIT | M_ZERO);
336 	LIST_INIT(&softc->pending_ccbs);
337 	softc->state = PT_STATE_NORMAL;
338 	bioq_init(&softc->bio_queue);
339 
340 	softc->io_timeout = SCSI_PT_DEFAULT_TIMEOUT * 1000;
341 
342 	periph->softc = softc;
343 
344 	cam_extend_set(ptperiphs, periph->unit_number, periph);
345 
346 	devstat_add_entry(&softc->device_stats, "pt",
347 			  periph->unit_number, 0,
348 			  DEVSTAT_NO_BLOCKSIZE,
349 			  SID_TYPE(&cgd->inq_data) | DEVSTAT_TYPE_IF_SCSI,
350 			  DEVSTAT_PRIORITY_OTHER);
351 
352 	cdevsw_add(&pt_cdevsw, -1, periph->unit_number);
353 	make_dev(&pt_cdevsw, periph->unit_number, UID_ROOT,
354 		  GID_OPERATOR, 0600, "%s%d", periph->periph_name,
355 		  periph->unit_number);
356 	/*
357 	 * Add async callbacks for bus reset and
358 	 * bus device reset calls.  I don't bother
359 	 * checking if this fails as, in most cases,
360 	 * the system will function just fine without
361 	 * them and the only alternative would be to
362 	 * not attach the device on failure.
363 	 */
364 	xpt_setup_ccb(&csa.ccb_h, periph->path, /*priority*/5);
365 	csa.ccb_h.func_code = XPT_SASYNC_CB;
366 	csa.event_enable = AC_SENT_BDR | AC_BUS_RESET | AC_LOST_DEVICE;
367 	csa.callback = ptasync;
368 	csa.callback_arg = periph;
369 	xpt_action((union ccb *)&csa);
370 
371 	/* Tell the user we've attached to the device */
372 	xpt_announce_periph(periph, NULL);
373 
374 	return(CAM_REQ_CMP);
375 }
376 
377 static void
378 ptoninvalidate(struct cam_periph *periph)
379 {
380 	struct pt_softc *softc;
381 	struct bio *q_bio;
382 	struct buf *q_bp;
383 	struct ccb_setasync csa;
384 
385 	softc = (struct pt_softc *)periph->softc;
386 
387 	/*
388 	 * De-register any async callbacks.
389 	 */
390 	xpt_setup_ccb(&csa.ccb_h, periph->path,
391 		      /* priority */ 5);
392 	csa.ccb_h.func_code = XPT_SASYNC_CB;
393 	csa.event_enable = 0;
394 	csa.callback = ptasync;
395 	csa.callback_arg = periph;
396 	xpt_action((union ccb *)&csa);
397 
398 	softc->flags |= PT_FLAG_DEVICE_INVALID;
399 
400 	/*
401 	 * We need to be in a critical section here to keep the buffer
402 	 * queue from being modified while we traverse it.
403 	 */
404 	crit_enter();
405 
406 	/*
407 	 * Return all queued I/O with ENXIO.
408 	 * XXX Handle any transactions queued to the card
409 	 *     with XPT_ABORT_CCB.
410 	 */
411 	while ((q_bio = bioq_first(&softc->bio_queue)) != NULL){
412 		bioq_remove(&softc->bio_queue, q_bio);
413 		q_bp = q_bio->bio_buf;
414 		q_bp->b_resid = q_bp->b_bcount;
415 		q_bp->b_error = ENXIO;
416 		q_bp->b_flags |= B_ERROR;
417 		biodone(q_bio);
418 	}
419 
420 	crit_exit();
421 
422 	xpt_print_path(periph->path);
423 	printf("lost device\n");
424 }
425 
426 static void
427 ptdtor(struct cam_periph *periph)
428 {
429 	struct pt_softc *softc;
430 
431 	softc = (struct pt_softc *)periph->softc;
432 
433 	devstat_remove_entry(&softc->device_stats);
434 
435 	cam_extend_release(ptperiphs, periph->unit_number);
436 	xpt_print_path(periph->path);
437 	printf("removing device entry\n");
438 	cdevsw_remove(&pt_cdevsw, -1, periph->unit_number);
439 	free(softc, M_DEVBUF);
440 }
441 
442 static void
443 ptasync(void *callback_arg, u_int32_t code, struct cam_path *path, void *arg)
444 {
445 	struct cam_periph *periph;
446 
447 	periph = (struct cam_periph *)callback_arg;
448 	switch (code) {
449 	case AC_FOUND_DEVICE:
450 	{
451 		struct ccb_getdev *cgd;
452 		cam_status status;
453 
454 		cgd = (struct ccb_getdev *)arg;
455 
456 		if (SID_TYPE(&cgd->inq_data) != T_PROCESSOR)
457 			break;
458 
459 		/*
460 		 * Allocate a peripheral instance for
461 		 * this device and start the probe
462 		 * process.
463 		 */
464 		status = cam_periph_alloc(ptctor, ptoninvalidate, ptdtor,
465 					  ptstart, "pt", CAM_PERIPH_BIO,
466 					  cgd->ccb_h.path, ptasync,
467 					  AC_FOUND_DEVICE, cgd);
468 
469 		if (status != CAM_REQ_CMP
470 		 && status != CAM_REQ_INPROG)
471 			printf("ptasync: Unable to attach to new device "
472 				"due to status 0x%x\n", status);
473 		break;
474 	}
475 	case AC_SENT_BDR:
476 	case AC_BUS_RESET:
477 	{
478 		struct pt_softc *softc;
479 		struct ccb_hdr *ccbh;
480 
481 		softc = (struct pt_softc *)periph->softc;
482 		crit_enter();
483 		/*
484 		 * Don't fail on the expected unit attention
485 		 * that will occur.
486 		 */
487 		softc->flags |= PT_FLAG_RETRY_UA;
488 		for (ccbh = LIST_FIRST(&softc->pending_ccbs);
489 		     ccbh != NULL; ccbh = LIST_NEXT(ccbh, periph_links.le))
490 			ccbh->ccb_state |= PT_CCB_RETRY_UA;
491 		crit_exit();
492 		/* FALLTHROUGH */
493 	}
494 	default:
495 		cam_periph_async(periph, code, path, arg);
496 		break;
497 	}
498 }
499 
500 static void
501 ptstart(struct cam_periph *periph, union ccb *start_ccb)
502 {
503 	struct pt_softc *softc;
504 	struct buf *bp;
505 	struct bio *bio;
506 
507 	softc = (struct pt_softc *)periph->softc;
508 
509 	/*
510 	 * See if there is a buf with work for us to do..
511 	 */
512 	crit_enter();
513 	bio = bioq_first(&softc->bio_queue);
514 	if (periph->immediate_priority <= periph->pinfo.priority) {
515 		CAM_DEBUG_PRINT(CAM_DEBUG_SUBTRACE,
516 				("queuing for immediate ccb\n"));
517 		start_ccb->ccb_h.ccb_state = PT_CCB_WAITING;
518 		SLIST_INSERT_HEAD(&periph->ccb_list, &start_ccb->ccb_h,
519 				  periph_links.sle);
520 		periph->immediate_priority = CAM_PRIORITY_NONE;
521 		crit_exit();
522 		wakeup(&periph->ccb_list);
523 	} else if (bio == NULL) {
524 		crit_exit();
525 		xpt_release_ccb(start_ccb);
526 	} else {
527 		bioq_remove(&softc->bio_queue, bio);
528 		bp = bio->bio_buf;
529 
530 		devstat_start_transaction(&softc->device_stats);
531 
532 		scsi_send_receive(&start_ccb->csio,
533 				  /*retries*/4,
534 				  ptdone,
535 				  MSG_SIMPLE_Q_TAG,
536 				  (bp->b_cmd == BUF_CMD_READ),
537 				  /*byte2*/0,
538 				  bp->b_bcount,
539 				  bp->b_data,
540 				  /*sense_len*/SSD_FULL_SIZE,
541 				  /*timeout*/softc->io_timeout);
542 
543 		start_ccb->ccb_h.ccb_state = PT_CCB_BUFFER_IO;
544 
545 		/*
546 		 * Block out any asyncronous callbacks
547 		 * while we touch the pending ccb list.
548 		 */
549 		LIST_INSERT_HEAD(&softc->pending_ccbs, &start_ccb->ccb_h,
550 				 periph_links.le);
551 
552 		start_ccb->ccb_h.ccb_bio = bio;
553 		bio = bioq_first(&softc->bio_queue);
554 		crit_exit();
555 
556 		xpt_action(start_ccb);
557 
558 		if (bio != NULL) {
559 			/* Have more work to do, so ensure we stay scheduled */
560 			xpt_schedule(periph, /* XXX priority */1);
561 		}
562 	}
563 }
564 
565 static void
566 ptdone(struct cam_periph *periph, union ccb *done_ccb)
567 {
568 	struct pt_softc *softc;
569 	struct ccb_scsiio *csio;
570 
571 	softc = (struct pt_softc *)periph->softc;
572 	csio = &done_ccb->csio;
573 	switch (csio->ccb_h.ccb_state) {
574 	case PT_CCB_BUFFER_IO:
575 	case PT_CCB_BUFFER_IO_UA:
576 	{
577 		struct buf *bp;
578 		struct bio *bio;
579 
580 		bio = (struct bio *)done_ccb->ccb_h.ccb_bio;
581 		bp = bio->bio_buf;
582 
583 		if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
584 			int error;
585 			int sf;
586 
587 			if ((csio->ccb_h.ccb_state & PT_CCB_RETRY_UA) != 0)
588 				sf = SF_RETRY_UA;
589 			else
590 				sf = 0;
591 
592 			sf |= SF_RETRY_SELTO;
593 
594 			if ((error = pterror(done_ccb, 0, sf)) == ERESTART) {
595 				/*
596 				 * A retry was scheuled, so
597 				 * just return.
598 				 */
599 				return;
600 			}
601 			if (error != 0) {
602 				struct buf *q_bp;
603 				struct bio *q_bio;
604 
605 				crit_enter();
606 
607 				if (error == ENXIO) {
608 					/*
609 					 * Catastrophic error.  Mark our device
610 					 * as invalid.
611 					 */
612 					xpt_print_path(periph->path);
613 					printf("Invalidating device\n");
614 					softc->flags |= PT_FLAG_DEVICE_INVALID;
615 				}
616 
617 				/*
618 				 * return all queued I/O with EIO, so that
619 				 * the client can retry these I/Os in the
620 				 * proper order should it attempt to recover.
621 				 */
622 				while ((q_bio = bioq_first(&softc->bio_queue))
623 					!= NULL) {
624 					bioq_remove(&softc->bio_queue, q_bio);
625 					q_bp = q_bio->bio_buf;
626 					q_bp->b_resid = q_bp->b_bcount;
627 					q_bp->b_error = EIO;
628 					q_bp->b_flags |= B_ERROR;
629 					biodone(q_bio);
630 				}
631 				crit_exit();
632 				bp->b_error = error;
633 				bp->b_resid = bp->b_bcount;
634 				bp->b_flags |= B_ERROR;
635 			} else {
636 				bp->b_resid = csio->resid;
637 				bp->b_error = 0;
638 				if (bp->b_resid != 0) {
639 					/* Short transfer ??? */
640 					bp->b_flags |= B_ERROR;
641 				}
642 			}
643 			if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0)
644 				cam_release_devq(done_ccb->ccb_h.path,
645 						 /*relsim_flags*/0,
646 						 /*reduction*/0,
647 						 /*timeout*/0,
648 						 /*getcount_only*/0);
649 		} else {
650 			bp->b_resid = csio->resid;
651 			if (bp->b_resid != 0)
652 				bp->b_flags |= B_ERROR;
653 		}
654 
655 		/*
656 		 * Block out any asyncronous callbacks
657 		 * while we touch the pending ccb list.
658 		 */
659 		crit_enter();
660 		LIST_REMOVE(&done_ccb->ccb_h, periph_links.le);
661 		crit_exit();
662 
663 		devstat_end_transaction_buf(&softc->device_stats, bp);
664 		biodone(bio);
665 		break;
666 	}
667 	case PT_CCB_WAITING:
668 		/* Caller will release the CCB */
669 		wakeup(&done_ccb->ccb_h.cbfcnp);
670 		return;
671 	}
672 	xpt_release_ccb(done_ccb);
673 }
674 
675 static int
676 pterror(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags)
677 {
678 	struct pt_softc	  *softc;
679 	struct cam_periph *periph;
680 
681 	periph = xpt_path_periph(ccb->ccb_h.path);
682 	softc = (struct pt_softc *)periph->softc;
683 
684 	return(cam_periph_error(ccb, cam_flags, sense_flags,
685 				&softc->saved_ccb));
686 }
687 
688 static int
689 ptioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct thread *td)
690 {
691 	struct cam_periph *periph;
692 	struct pt_softc *softc;
693 	int unit;
694 	int error;
695 
696 	unit = minor(dev);
697 	periph = cam_extend_get(ptperiphs, unit);
698 
699 	if (periph == NULL)
700 		return(ENXIO);
701 
702 	softc = (struct pt_softc *)periph->softc;
703 
704 	if ((error = cam_periph_lock(periph, PCATCH)) != 0) {
705 		return (error); /* error code from tsleep */
706 	}
707 
708 	switch(cmd) {
709 	case PTIOCGETTIMEOUT:
710 		if (softc->io_timeout >= 1000)
711 			*(int *)addr = softc->io_timeout / 1000;
712 		else
713 			*(int *)addr = 0;
714 		break;
715 	case PTIOCSETTIMEOUT:
716 	{
717 		if (*(int *)addr < 1) {
718 			error = EINVAL;
719 			break;
720 		}
721 
722 		crit_enter();
723 		softc->io_timeout = *(int *)addr * 1000;
724 		crit_exit();
725 
726 		break;
727 	}
728 	default:
729 		error = cam_periph_ioctl(periph, cmd, addr, pterror);
730 		break;
731 	}
732 
733 	cam_periph_unlock(periph);
734 
735 	return(error);
736 }
737 
738 void
739 scsi_send_receive(struct ccb_scsiio *csio, u_int32_t retries,
740 		  void (*cbfcnp)(struct cam_periph *, union ccb *),
741 		  u_int tag_action, int readop, u_int byte2,
742 		  u_int32_t xfer_len, u_int8_t *data_ptr, u_int8_t sense_len,
743 		  u_int32_t timeout)
744 {
745 	struct scsi_send_receive *scsi_cmd;
746 
747 	scsi_cmd = (struct scsi_send_receive *)&csio->cdb_io.cdb_bytes;
748 	scsi_cmd->opcode = readop ? RECEIVE : SEND;
749 	scsi_cmd->byte2 = byte2;
750 	scsi_ulto3b(xfer_len, scsi_cmd->xfer_len);
751 	scsi_cmd->control = 0;
752 
753 	cam_fill_csio(csio,
754 		      retries,
755 		      cbfcnp,
756 		      /*flags*/readop ? CAM_DIR_IN : CAM_DIR_OUT,
757 		      tag_action,
758 		      data_ptr,
759 		      xfer_len,
760 		      sense_len,
761 		      sizeof(*scsi_cmd),
762 		      timeout);
763 }
764