xref: /dragonfly/sys/bus/cam/scsi/scsi_pt.c (revision 0bb9290e)
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.15 2006/07/28 02:17:32 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 dev_ops pt_ops = {
125 	{ "pt", PT_CDEV_MAJOR, 0 },
126 	.d_open =	ptopen,
127 	.d_close =	ptclose,
128 	.d_read =	physread,
129 	.d_write =	physwrite,
130 	.d_ioctl =	ptioctl,
131 	.d_strategy =	ptstrategy,
132 };
133 
134 static struct extend_array *ptperiphs;
135 
136 #ifndef SCSI_PT_DEFAULT_TIMEOUT
137 #define SCSI_PT_DEFAULT_TIMEOUT		60
138 #endif
139 
140 static int
141 ptopen(struct dev_open_args *ap)
142 {
143 	dev_t dev = ap->a_head.a_dev;
144 	struct cam_periph *periph;
145 	struct pt_softc *softc;
146 	int unit;
147 	int error;
148 
149 	unit = minor(dev);
150 	periph = cam_extend_get(ptperiphs, unit);
151 	if (periph == NULL)
152 		return (ENXIO);
153 
154 	softc = (struct pt_softc *)periph->softc;
155 
156 	crit_enter();
157 	if (softc->flags & PT_FLAG_DEVICE_INVALID) {
158 		crit_exit();
159 		return(ENXIO);
160 	}
161 
162 	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE,
163 	    ("ptopen: dev=%s (unit %d)\n", devtoname(dev), unit));
164 
165 	if ((error = cam_periph_lock(periph, PCATCH)) != 0) {
166 		crit_exit();
167 		return (error); /* error code from tsleep */
168 	}
169 
170 	crit_exit();
171 
172 	if ((softc->flags & PT_FLAG_OPEN) == 0) {
173 		if (cam_periph_acquire(periph) != CAM_REQ_CMP)
174 			error = ENXIO;
175 		else
176 			softc->flags |= PT_FLAG_OPEN;
177 	} else
178 		error = EBUSY;
179 
180 	cam_periph_unlock(periph);
181 	return (error);
182 }
183 
184 static int
185 ptclose(struct dev_close_args *ap)
186 {
187 	dev_t dev = ap->a_head.a_dev;
188 	struct	cam_periph *periph;
189 	struct	pt_softc *softc;
190 	int	unit;
191 	int	error;
192 
193 	unit = minor(dev);
194 	periph = cam_extend_get(ptperiphs, unit);
195 	if (periph == NULL)
196 		return (ENXIO);
197 
198 	softc = (struct pt_softc *)periph->softc;
199 
200 	if ((error = cam_periph_lock(periph, 0)) != 0)
201 		return (error); /* error code from tsleep */
202 
203 	softc->flags &= ~PT_FLAG_OPEN;
204 	cam_periph_unlock(periph);
205 	cam_periph_release(periph);
206 	return (0);
207 }
208 
209 /*
210  * Actually translate the requested transfer into one the physical driver
211  * can understand.  The transfer is described by a buf and will include
212  * only one physical transfer.
213  */
214 static int
215 ptstrategy(struct dev_strategy_args *ap)
216 {
217 	dev_t dev = ap->a_head.a_dev;
218 	struct bio *bio = ap->a_bio;
219 	struct buf *bp = bio->bio_buf;
220 	struct cam_periph *periph;
221 	struct pt_softc *softc;
222 	u_int  unit;
223 
224 	unit = minor(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 	crit_enter();
238 
239 	/*
240 	 * If the device has been made invalid, error out
241 	 */
242 	if ((softc->flags & PT_FLAG_DEVICE_INVALID)) {
243 		crit_exit();
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 	bioq_insert_tail(&softc->bio_queue, bio);
252 
253 	crit_exit();
254 
255 	/*
256 	 * Schedule ourselves for performing the work.
257 	 */
258 	xpt_schedule(periph, /* XXX priority */1);
259 
260 	return(0);
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(bio);
269 	return(0);
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 = malloc(sizeof(*softc), M_DEVBUF, M_INTWAIT | M_ZERO);
332 	LIST_INIT(&softc->pending_ccbs);
333 	softc->state = PT_STATE_NORMAL;
334 	bioq_init(&softc->bio_queue);
335 
336 	softc->io_timeout = SCSI_PT_DEFAULT_TIMEOUT * 1000;
337 
338 	periph->softc = softc;
339 
340 	cam_extend_set(ptperiphs, periph->unit_number, periph);
341 
342 	devstat_add_entry(&softc->device_stats, "pt",
343 			  periph->unit_number, 0,
344 			  DEVSTAT_NO_BLOCKSIZE,
345 			  SID_TYPE(&cgd->inq_data) | DEVSTAT_TYPE_IF_SCSI,
346 			  DEVSTAT_PRIORITY_OTHER);
347 
348 	dev_ops_add(&pt_ops, -1, periph->unit_number);
349 	make_dev(&pt_ops, periph->unit_number, UID_ROOT,
350 		  GID_OPERATOR, 0600, "%s%d", periph->periph_name,
351 		  periph->unit_number);
352 	/*
353 	 * Add async callbacks for bus reset and
354 	 * bus device reset calls.  I don't bother
355 	 * checking if this fails as, in most cases,
356 	 * the system will function just fine without
357 	 * them and the only alternative would be to
358 	 * not attach the device on failure.
359 	 */
360 	xpt_setup_ccb(&csa.ccb_h, periph->path, /*priority*/5);
361 	csa.ccb_h.func_code = XPT_SASYNC_CB;
362 	csa.event_enable = AC_SENT_BDR | AC_BUS_RESET | AC_LOST_DEVICE;
363 	csa.callback = ptasync;
364 	csa.callback_arg = periph;
365 	xpt_action((union ccb *)&csa);
366 
367 	/* Tell the user we've attached to the device */
368 	xpt_announce_periph(periph, NULL);
369 
370 	return(CAM_REQ_CMP);
371 }
372 
373 static void
374 ptoninvalidate(struct cam_periph *periph)
375 {
376 	struct pt_softc *softc;
377 	struct bio *q_bio;
378 	struct buf *q_bp;
379 	struct ccb_setasync csa;
380 
381 	softc = (struct pt_softc *)periph->softc;
382 
383 	/*
384 	 * De-register any async callbacks.
385 	 */
386 	xpt_setup_ccb(&csa.ccb_h, periph->path,
387 		      /* priority */ 5);
388 	csa.ccb_h.func_code = XPT_SASYNC_CB;
389 	csa.event_enable = 0;
390 	csa.callback = ptasync;
391 	csa.callback_arg = periph;
392 	xpt_action((union ccb *)&csa);
393 
394 	softc->flags |= PT_FLAG_DEVICE_INVALID;
395 
396 	/*
397 	 * We need to be in a critical section here to keep the buffer
398 	 * queue from being modified while we traverse it.
399 	 */
400 	crit_enter();
401 
402 	/*
403 	 * Return all queued I/O with ENXIO.
404 	 * XXX Handle any transactions queued to the card
405 	 *     with XPT_ABORT_CCB.
406 	 */
407 	while ((q_bio = bioq_first(&softc->bio_queue)) != NULL){
408 		bioq_remove(&softc->bio_queue, q_bio);
409 		q_bp = q_bio->bio_buf;
410 		q_bp->b_resid = q_bp->b_bcount;
411 		q_bp->b_error = ENXIO;
412 		q_bp->b_flags |= B_ERROR;
413 		biodone(q_bio);
414 	}
415 
416 	crit_exit();
417 
418 	xpt_print_path(periph->path);
419 	printf("lost device\n");
420 }
421 
422 static void
423 ptdtor(struct cam_periph *periph)
424 {
425 	struct pt_softc *softc;
426 
427 	softc = (struct pt_softc *)periph->softc;
428 
429 	devstat_remove_entry(&softc->device_stats);
430 
431 	cam_extend_release(ptperiphs, periph->unit_number);
432 	xpt_print_path(periph->path);
433 	printf("removing device entry\n");
434 	dev_ops_remove(&pt_ops, -1, periph->unit_number);
435 	free(softc, M_DEVBUF);
436 }
437 
438 static void
439 ptasync(void *callback_arg, u_int32_t code, struct cam_path *path, void *arg)
440 {
441 	struct cam_periph *periph;
442 
443 	periph = (struct cam_periph *)callback_arg;
444 	switch (code) {
445 	case AC_FOUND_DEVICE:
446 	{
447 		struct ccb_getdev *cgd;
448 		cam_status status;
449 
450 		cgd = (struct ccb_getdev *)arg;
451 
452 		if (SID_TYPE(&cgd->inq_data) != T_PROCESSOR)
453 			break;
454 
455 		/*
456 		 * Allocate a peripheral instance for
457 		 * this device and start the probe
458 		 * process.
459 		 */
460 		status = cam_periph_alloc(ptctor, ptoninvalidate, ptdtor,
461 					  ptstart, "pt", CAM_PERIPH_BIO,
462 					  cgd->ccb_h.path, ptasync,
463 					  AC_FOUND_DEVICE, cgd);
464 
465 		if (status != CAM_REQ_CMP
466 		 && status != CAM_REQ_INPROG)
467 			printf("ptasync: Unable to attach to new device "
468 				"due to status 0x%x\n", status);
469 		break;
470 	}
471 	case AC_SENT_BDR:
472 	case AC_BUS_RESET:
473 	{
474 		struct pt_softc *softc;
475 		struct ccb_hdr *ccbh;
476 
477 		softc = (struct pt_softc *)periph->softc;
478 		crit_enter();
479 		/*
480 		 * Don't fail on the expected unit attention
481 		 * that will occur.
482 		 */
483 		softc->flags |= PT_FLAG_RETRY_UA;
484 		for (ccbh = LIST_FIRST(&softc->pending_ccbs);
485 		     ccbh != NULL; ccbh = LIST_NEXT(ccbh, periph_links.le))
486 			ccbh->ccb_state |= PT_CCB_RETRY_UA;
487 		crit_exit();
488 		/* FALLTHROUGH */
489 	}
490 	default:
491 		cam_periph_async(periph, code, path, arg);
492 		break;
493 	}
494 }
495 
496 static void
497 ptstart(struct cam_periph *periph, union ccb *start_ccb)
498 {
499 	struct pt_softc *softc;
500 	struct buf *bp;
501 	struct bio *bio;
502 
503 	softc = (struct pt_softc *)periph->softc;
504 
505 	/*
506 	 * See if there is a buf with work for us to do..
507 	 */
508 	crit_enter();
509 	bio = bioq_first(&softc->bio_queue);
510 	if (periph->immediate_priority <= periph->pinfo.priority) {
511 		CAM_DEBUG_PRINT(CAM_DEBUG_SUBTRACE,
512 				("queuing for immediate ccb\n"));
513 		start_ccb->ccb_h.ccb_state = PT_CCB_WAITING;
514 		SLIST_INSERT_HEAD(&periph->ccb_list, &start_ccb->ccb_h,
515 				  periph_links.sle);
516 		periph->immediate_priority = CAM_PRIORITY_NONE;
517 		crit_exit();
518 		wakeup(&periph->ccb_list);
519 	} else if (bio == NULL) {
520 		crit_exit();
521 		xpt_release_ccb(start_ccb);
522 	} else {
523 		bioq_remove(&softc->bio_queue, bio);
524 		bp = bio->bio_buf;
525 
526 		devstat_start_transaction(&softc->device_stats);
527 
528 		scsi_send_receive(&start_ccb->csio,
529 				  /*retries*/4,
530 				  ptdone,
531 				  MSG_SIMPLE_Q_TAG,
532 				  (bp->b_cmd == BUF_CMD_READ),
533 				  /*byte2*/0,
534 				  bp->b_bcount,
535 				  bp->b_data,
536 				  /*sense_len*/SSD_FULL_SIZE,
537 				  /*timeout*/softc->io_timeout);
538 
539 		start_ccb->ccb_h.ccb_state = PT_CCB_BUFFER_IO;
540 
541 		/*
542 		 * Block out any asyncronous callbacks
543 		 * while we touch the pending ccb list.
544 		 */
545 		LIST_INSERT_HEAD(&softc->pending_ccbs, &start_ccb->ccb_h,
546 				 periph_links.le);
547 
548 		start_ccb->ccb_h.ccb_bio = bio;
549 		bio = bioq_first(&softc->bio_queue);
550 		crit_exit();
551 
552 		xpt_action(start_ccb);
553 
554 		if (bio != NULL) {
555 			/* Have more work to do, so ensure we stay scheduled */
556 			xpt_schedule(periph, /* XXX priority */1);
557 		}
558 	}
559 }
560 
561 static void
562 ptdone(struct cam_periph *periph, union ccb *done_ccb)
563 {
564 	struct pt_softc *softc;
565 	struct ccb_scsiio *csio;
566 
567 	softc = (struct pt_softc *)periph->softc;
568 	csio = &done_ccb->csio;
569 	switch (csio->ccb_h.ccb_state) {
570 	case PT_CCB_BUFFER_IO:
571 	case PT_CCB_BUFFER_IO_UA:
572 	{
573 		struct buf *bp;
574 		struct bio *bio;
575 
576 		bio = (struct bio *)done_ccb->ccb_h.ccb_bio;
577 		bp = bio->bio_buf;
578 
579 		if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
580 			int error;
581 			int sf;
582 
583 			if ((csio->ccb_h.ccb_state & PT_CCB_RETRY_UA) != 0)
584 				sf = SF_RETRY_UA;
585 			else
586 				sf = 0;
587 
588 			sf |= SF_RETRY_SELTO;
589 
590 			if ((error = pterror(done_ccb, 0, sf)) == ERESTART) {
591 				/*
592 				 * A retry was scheuled, so
593 				 * just return.
594 				 */
595 				return;
596 			}
597 			if (error != 0) {
598 				struct buf *q_bp;
599 				struct bio *q_bio;
600 
601 				crit_enter();
602 
603 				if (error == ENXIO) {
604 					/*
605 					 * Catastrophic error.  Mark our device
606 					 * as invalid.
607 					 */
608 					xpt_print_path(periph->path);
609 					printf("Invalidating device\n");
610 					softc->flags |= PT_FLAG_DEVICE_INVALID;
611 				}
612 
613 				/*
614 				 * return all queued I/O with EIO, so that
615 				 * the client can retry these I/Os in the
616 				 * proper order should it attempt to recover.
617 				 */
618 				while ((q_bio = bioq_first(&softc->bio_queue))
619 					!= NULL) {
620 					bioq_remove(&softc->bio_queue, q_bio);
621 					q_bp = q_bio->bio_buf;
622 					q_bp->b_resid = q_bp->b_bcount;
623 					q_bp->b_error = EIO;
624 					q_bp->b_flags |= B_ERROR;
625 					biodone(q_bio);
626 				}
627 				crit_exit();
628 				bp->b_error = error;
629 				bp->b_resid = bp->b_bcount;
630 				bp->b_flags |= B_ERROR;
631 			} else {
632 				bp->b_resid = csio->resid;
633 				bp->b_error = 0;
634 				if (bp->b_resid != 0) {
635 					/* Short transfer ??? */
636 					bp->b_flags |= B_ERROR;
637 				}
638 			}
639 			if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0)
640 				cam_release_devq(done_ccb->ccb_h.path,
641 						 /*relsim_flags*/0,
642 						 /*reduction*/0,
643 						 /*timeout*/0,
644 						 /*getcount_only*/0);
645 		} else {
646 			bp->b_resid = csio->resid;
647 			if (bp->b_resid != 0)
648 				bp->b_flags |= B_ERROR;
649 		}
650 
651 		/*
652 		 * Block out any asyncronous callbacks
653 		 * while we touch the pending ccb list.
654 		 */
655 		crit_enter();
656 		LIST_REMOVE(&done_ccb->ccb_h, periph_links.le);
657 		crit_exit();
658 
659 		devstat_end_transaction_buf(&softc->device_stats, bp);
660 		biodone(bio);
661 		break;
662 	}
663 	case PT_CCB_WAITING:
664 		/* Caller will release the CCB */
665 		wakeup(&done_ccb->ccb_h.cbfcnp);
666 		return;
667 	}
668 	xpt_release_ccb(done_ccb);
669 }
670 
671 static int
672 pterror(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags)
673 {
674 	struct pt_softc	  *softc;
675 	struct cam_periph *periph;
676 
677 	periph = xpt_path_periph(ccb->ccb_h.path);
678 	softc = (struct pt_softc *)periph->softc;
679 
680 	return(cam_periph_error(ccb, cam_flags, sense_flags,
681 				&softc->saved_ccb));
682 }
683 
684 static int
685 ptioctl(struct dev_ioctl_args *ap)
686 {
687 	dev_t dev = ap->a_head.a_dev;
688 	caddr_t addr = ap->a_data;
689 	struct cam_periph *periph;
690 	struct pt_softc *softc;
691 	int unit;
692 	int error;
693 
694 	unit = minor(dev);
695 	periph = cam_extend_get(ptperiphs, unit);
696 
697 	if (periph == NULL)
698 		return(ENXIO);
699 
700 	softc = (struct pt_softc *)periph->softc;
701 
702 	if ((error = cam_periph_lock(periph, PCATCH)) != 0) {
703 		return (error); /* error code from tsleep */
704 	}
705 
706 	switch(ap->a_cmd) {
707 	case PTIOCGETTIMEOUT:
708 		if (softc->io_timeout >= 1000)
709 			*(int *)addr = softc->io_timeout / 1000;
710 		else
711 			*(int *)addr = 0;
712 		break;
713 	case PTIOCSETTIMEOUT:
714 	{
715 		if (*(int *)addr < 1) {
716 			error = EINVAL;
717 			break;
718 		}
719 
720 		crit_enter();
721 		softc->io_timeout = *(int *)addr * 1000;
722 		crit_exit();
723 
724 		break;
725 	}
726 	default:
727 		error = cam_periph_ioctl(periph, ap->a_cmd, addr, pterror);
728 		break;
729 	}
730 
731 	cam_periph_unlock(periph);
732 
733 	return(error);
734 }
735 
736 void
737 scsi_send_receive(struct ccb_scsiio *csio, u_int32_t retries,
738 		  void (*cbfcnp)(struct cam_periph *, union ccb *),
739 		  u_int tag_action, int readop, u_int byte2,
740 		  u_int32_t xfer_len, u_int8_t *data_ptr, u_int8_t sense_len,
741 		  u_int32_t timeout)
742 {
743 	struct scsi_send_receive *scsi_cmd;
744 
745 	scsi_cmd = (struct scsi_send_receive *)&csio->cdb_io.cdb_bytes;
746 	scsi_cmd->opcode = readop ? RECEIVE : SEND;
747 	scsi_cmd->byte2 = byte2;
748 	scsi_ulto3b(xfer_len, scsi_cmd->xfer_len);
749 	scsi_cmd->control = 0;
750 
751 	cam_fill_csio(csio,
752 		      retries,
753 		      cbfcnp,
754 		      /*flags*/readop ? CAM_DIR_IN : CAM_DIR_OUT,
755 		      tag_action,
756 		      data_ptr,
757 		      xfer_len,
758 		      sense_len,
759 		      sizeof(*scsi_cmd),
760 		      timeout);
761 }
762