xref: /dragonfly/sys/bus/cam/scsi/scsi_pt.c (revision 2ee85085)
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.12 2005/06/02 20:40:31 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_bp		ppriv_ptr1
80 
81 struct pt_softc {
82 	struct	 buf_queue_head buf_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(struct buf *bp)
223 {
224 	struct cam_periph *periph;
225 	struct pt_softc *softc;
226 	u_int  unit;
227 
228 	unit = minor(bp->b_dev);
229 	periph = cam_extend_get(ptperiphs, unit);
230 	if (periph == NULL) {
231 		bp->b_error = ENXIO;
232 		goto bad;
233 	}
234 	softc = (struct pt_softc *)periph->softc;
235 
236 	/*
237 	 * Mask interrupts so that the pack cannot be invalidated until
238 	 * after we are in the queue.  Otherwise, we might not properly
239 	 * clean up one of the buffers.
240 	 */
241 	crit_enter();
242 
243 	/*
244 	 * If the device has been made invalid, error out
245 	 */
246 	if ((softc->flags & PT_FLAG_DEVICE_INVALID)) {
247 		crit_exit();
248 		bp->b_error = ENXIO;
249 		goto bad;
250 	}
251 
252 	/*
253 	 * Place it in the queue of disk activities for this disk
254 	 */
255 	bufq_insert_tail(&softc->buf_queue, bp);
256 
257 	crit_exit();
258 
259 	/*
260 	 * Schedule ourselves for performing the work.
261 	 */
262 	xpt_schedule(periph, /* XXX priority */1);
263 
264 	return;
265 bad:
266 	bp->b_flags |= B_ERROR;
267 
268 	/*
269 	 * Correctly set the buf to indicate a completed xfer
270 	 */
271 	bp->b_resid = bp->b_bcount;
272 	biodone(bp);
273 }
274 
275 static void
276 ptinit(void)
277 {
278 	cam_status status;
279 	struct cam_path *path;
280 
281 	/*
282 	 * Create our extend array for storing the devices we attach to.
283 	 */
284 	ptperiphs = cam_extend_new();
285 	if (ptperiphs == NULL) {
286 		printf("pt: Failed to alloc extend array!\n");
287 		return;
288 	}
289 
290 	/*
291 	 * Install a global async callback.  This callback will
292 	 * receive async callbacks like "new device found".
293 	 */
294 	status = xpt_create_path(&path, /*periph*/NULL, CAM_XPT_PATH_ID,
295 				 CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD);
296 
297 	if (status == CAM_REQ_CMP) {
298 		struct ccb_setasync csa;
299 
300                 xpt_setup_ccb(&csa.ccb_h, path, /*priority*/5);
301                 csa.ccb_h.func_code = XPT_SASYNC_CB;
302                 csa.event_enable = AC_FOUND_DEVICE;
303                 csa.callback = ptasync;
304                 csa.callback_arg = NULL;
305                 xpt_action((union ccb *)&csa);
306 		status = csa.ccb_h.status;
307                 xpt_free_path(path);
308         }
309 
310 	if (status != CAM_REQ_CMP) {
311 		printf("pt: Failed to attach master async callback "
312 		       "due to status 0x%x!\n", status);
313 	}
314 }
315 
316 static cam_status
317 ptctor(struct cam_periph *periph, void *arg)
318 {
319 	struct pt_softc *softc;
320 	struct ccb_setasync csa;
321 	struct ccb_getdev *cgd;
322 
323 	cgd = (struct ccb_getdev *)arg;
324 	if (periph == NULL) {
325 		printf("ptregister: periph was NULL!!\n");
326 		return(CAM_REQ_CMP_ERR);
327 	}
328 
329 	if (cgd == NULL) {
330 		printf("ptregister: no getdev CCB, can't register device\n");
331 		return(CAM_REQ_CMP_ERR);
332 	}
333 
334 	softc = malloc(sizeof(*softc), M_DEVBUF, M_INTWAIT | M_ZERO);
335 	LIST_INIT(&softc->pending_ccbs);
336 	softc->state = PT_STATE_NORMAL;
337 	bufq_init(&softc->buf_queue);
338 
339 	softc->io_timeout = SCSI_PT_DEFAULT_TIMEOUT * 1000;
340 
341 	periph->softc = softc;
342 
343 	cam_extend_set(ptperiphs, periph->unit_number, periph);
344 
345 	devstat_add_entry(&softc->device_stats, "pt",
346 			  periph->unit_number, 0,
347 			  DEVSTAT_NO_BLOCKSIZE,
348 			  SID_TYPE(&cgd->inq_data) | DEVSTAT_TYPE_IF_SCSI,
349 			  DEVSTAT_PRIORITY_OTHER);
350 
351 	cdevsw_add(&pt_cdevsw, -1, periph->unit_number);
352 	make_dev(&pt_cdevsw, periph->unit_number, UID_ROOT,
353 		  GID_OPERATOR, 0600, "%s%d", periph->periph_name,
354 		  periph->unit_number);
355 	/*
356 	 * Add async callbacks for bus reset and
357 	 * bus device reset calls.  I don't bother
358 	 * checking if this fails as, in most cases,
359 	 * the system will function just fine without
360 	 * them and the only alternative would be to
361 	 * not attach the device on failure.
362 	 */
363 	xpt_setup_ccb(&csa.ccb_h, periph->path, /*priority*/5);
364 	csa.ccb_h.func_code = XPT_SASYNC_CB;
365 	csa.event_enable = AC_SENT_BDR | AC_BUS_RESET | AC_LOST_DEVICE;
366 	csa.callback = ptasync;
367 	csa.callback_arg = periph;
368 	xpt_action((union ccb *)&csa);
369 
370 	/* Tell the user we've attached to the device */
371 	xpt_announce_periph(periph, NULL);
372 
373 	return(CAM_REQ_CMP);
374 }
375 
376 static void
377 ptoninvalidate(struct cam_periph *periph)
378 {
379 	struct pt_softc *softc;
380 	struct buf *q_bp;
381 	struct ccb_setasync csa;
382 
383 	softc = (struct pt_softc *)periph->softc;
384 
385 	/*
386 	 * De-register any async callbacks.
387 	 */
388 	xpt_setup_ccb(&csa.ccb_h, periph->path,
389 		      /* priority */ 5);
390 	csa.ccb_h.func_code = XPT_SASYNC_CB;
391 	csa.event_enable = 0;
392 	csa.callback = ptasync;
393 	csa.callback_arg = periph;
394 	xpt_action((union ccb *)&csa);
395 
396 	softc->flags |= PT_FLAG_DEVICE_INVALID;
397 
398 	/*
399 	 * We need to be in a critical section here to keep the buffer
400 	 * queue from being modified while we traverse it.
401 	 */
402 	crit_enter();
403 
404 	/*
405 	 * Return all queued I/O with ENXIO.
406 	 * XXX Handle any transactions queued to the card
407 	 *     with XPT_ABORT_CCB.
408 	 */
409 	while ((q_bp = bufq_first(&softc->buf_queue)) != NULL){
410 		bufq_remove(&softc->buf_queue, q_bp);
411 		q_bp->b_resid = q_bp->b_bcount;
412 		q_bp->b_error = ENXIO;
413 		q_bp->b_flags |= B_ERROR;
414 		biodone(q_bp);
415 	}
416 
417 	crit_exit();
418 
419 	xpt_print_path(periph->path);
420 	printf("lost device\n");
421 }
422 
423 static void
424 ptdtor(struct cam_periph *periph)
425 {
426 	struct pt_softc *softc;
427 
428 	softc = (struct pt_softc *)periph->softc;
429 
430 	devstat_remove_entry(&softc->device_stats);
431 
432 	cam_extend_release(ptperiphs, periph->unit_number);
433 	xpt_print_path(periph->path);
434 	printf("removing device entry\n");
435 	cdevsw_remove(&pt_cdevsw, -1, periph->unit_number);
436 	free(softc, M_DEVBUF);
437 }
438 
439 static void
440 ptasync(void *callback_arg, u_int32_t code, struct cam_path *path, void *arg)
441 {
442 	struct cam_periph *periph;
443 
444 	periph = (struct cam_periph *)callback_arg;
445 	switch (code) {
446 	case AC_FOUND_DEVICE:
447 	{
448 		struct ccb_getdev *cgd;
449 		cam_status status;
450 
451 		cgd = (struct ccb_getdev *)arg;
452 
453 		if (SID_TYPE(&cgd->inq_data) != T_PROCESSOR)
454 			break;
455 
456 		/*
457 		 * Allocate a peripheral instance for
458 		 * this device and start the probe
459 		 * process.
460 		 */
461 		status = cam_periph_alloc(ptctor, ptoninvalidate, ptdtor,
462 					  ptstart, "pt", CAM_PERIPH_BIO,
463 					  cgd->ccb_h.path, ptasync,
464 					  AC_FOUND_DEVICE, cgd);
465 
466 		if (status != CAM_REQ_CMP
467 		 && status != CAM_REQ_INPROG)
468 			printf("ptasync: Unable to attach to new device "
469 				"due to status 0x%x\n", status);
470 		break;
471 	}
472 	case AC_SENT_BDR:
473 	case AC_BUS_RESET:
474 	{
475 		struct pt_softc *softc;
476 		struct ccb_hdr *ccbh;
477 
478 		softc = (struct pt_softc *)periph->softc;
479 		crit_enter();
480 		/*
481 		 * Don't fail on the expected unit attention
482 		 * that will occur.
483 		 */
484 		softc->flags |= PT_FLAG_RETRY_UA;
485 		for (ccbh = LIST_FIRST(&softc->pending_ccbs);
486 		     ccbh != NULL; ccbh = LIST_NEXT(ccbh, periph_links.le))
487 			ccbh->ccb_state |= PT_CCB_RETRY_UA;
488 		crit_exit();
489 		/* FALLTHROUGH */
490 	}
491 	default:
492 		cam_periph_async(periph, code, path, arg);
493 		break;
494 	}
495 }
496 
497 static void
498 ptstart(struct cam_periph *periph, union ccb *start_ccb)
499 {
500 	struct pt_softc *softc;
501 	struct buf *bp;
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 	bp = bufq_first(&softc->buf_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 (bp == NULL) {
520 		crit_exit();
521 		xpt_release_ccb(start_ccb);
522 	} else {
523 		bufq_remove(&softc->buf_queue, bp);
524 
525 		devstat_start_transaction(&softc->device_stats);
526 
527 		scsi_send_receive(&start_ccb->csio,
528 				  /*retries*/4,
529 				  ptdone,
530 				  MSG_SIMPLE_Q_TAG,
531 				  bp->b_flags & B_READ,
532 				  /*byte2*/0,
533 				  bp->b_bcount,
534 				  bp->b_data,
535 				  /*sense_len*/SSD_FULL_SIZE,
536 				  /*timeout*/softc->io_timeout);
537 
538 		start_ccb->ccb_h.ccb_state = PT_CCB_BUFFER_IO;
539 
540 		/*
541 		 * Block out any asyncronous callbacks
542 		 * while we touch the pending ccb list.
543 		 */
544 		LIST_INSERT_HEAD(&softc->pending_ccbs, &start_ccb->ccb_h,
545 				 periph_links.le);
546 
547 		start_ccb->ccb_h.ccb_bp = bp;
548 		bp = bufq_first(&softc->buf_queue);
549 		crit_exit();
550 
551 		xpt_action(start_ccb);
552 
553 		if (bp != NULL) {
554 			/* Have more work to do, so ensure we stay scheduled */
555 			xpt_schedule(periph, /* XXX priority */1);
556 		}
557 	}
558 }
559 
560 static void
561 ptdone(struct cam_periph *periph, union ccb *done_ccb)
562 {
563 	struct pt_softc *softc;
564 	struct ccb_scsiio *csio;
565 
566 	softc = (struct pt_softc *)periph->softc;
567 	csio = &done_ccb->csio;
568 	switch (csio->ccb_h.ccb_state) {
569 	case PT_CCB_BUFFER_IO:
570 	case PT_CCB_BUFFER_IO_UA:
571 	{
572 		struct buf *bp;
573 
574 		bp = (struct buf *)done_ccb->ccb_h.ccb_bp;
575 		if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
576 			int error;
577 			int sf;
578 
579 			if ((csio->ccb_h.ccb_state & PT_CCB_RETRY_UA) != 0)
580 				sf = SF_RETRY_UA;
581 			else
582 				sf = 0;
583 
584 			sf |= SF_RETRY_SELTO;
585 
586 			if ((error = pterror(done_ccb, 0, sf)) == ERESTART) {
587 				/*
588 				 * A retry was scheuled, so
589 				 * just return.
590 				 */
591 				return;
592 			}
593 			if (error != 0) {
594 				struct buf *q_bp;
595 
596 				crit_enter();
597 
598 				if (error == ENXIO) {
599 					/*
600 					 * Catastrophic error.  Mark our device
601 					 * as invalid.
602 					 */
603 					xpt_print_path(periph->path);
604 					printf("Invalidating device\n");
605 					softc->flags |= PT_FLAG_DEVICE_INVALID;
606 				}
607 
608 				/*
609 				 * return all queued I/O with EIO, so that
610 				 * the client can retry these I/Os in the
611 				 * proper order should it attempt to recover.
612 				 */
613 				while ((q_bp = bufq_first(&softc->buf_queue))
614 					!= NULL) {
615 					bufq_remove(&softc->buf_queue, q_bp);
616 					q_bp->b_resid = q_bp->b_bcount;
617 					q_bp->b_error = EIO;
618 					q_bp->b_flags |= B_ERROR;
619 					biodone(q_bp);
620 				}
621 				crit_exit();
622 				bp->b_error = error;
623 				bp->b_resid = bp->b_bcount;
624 				bp->b_flags |= B_ERROR;
625 			} else {
626 				bp->b_resid = csio->resid;
627 				bp->b_error = 0;
628 				if (bp->b_resid != 0) {
629 					/* Short transfer ??? */
630 					bp->b_flags |= B_ERROR;
631 				}
632 			}
633 			if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0)
634 				cam_release_devq(done_ccb->ccb_h.path,
635 						 /*relsim_flags*/0,
636 						 /*reduction*/0,
637 						 /*timeout*/0,
638 						 /*getcount_only*/0);
639 		} else {
640 			bp->b_resid = csio->resid;
641 			if (bp->b_resid != 0)
642 				bp->b_flags |= B_ERROR;
643 		}
644 
645 		/*
646 		 * Block out any asyncronous callbacks
647 		 * while we touch the pending ccb list.
648 		 */
649 		crit_enter();
650 		LIST_REMOVE(&done_ccb->ccb_h, periph_links.le);
651 		crit_exit();
652 
653 		devstat_end_transaction_buf(&softc->device_stats, bp);
654 		biodone(bp);
655 		break;
656 	}
657 	case PT_CCB_WAITING:
658 		/* Caller will release the CCB */
659 		wakeup(&done_ccb->ccb_h.cbfcnp);
660 		return;
661 	}
662 	xpt_release_ccb(done_ccb);
663 }
664 
665 static int
666 pterror(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags)
667 {
668 	struct pt_softc	  *softc;
669 	struct cam_periph *periph;
670 
671 	periph = xpt_path_periph(ccb->ccb_h.path);
672 	softc = (struct pt_softc *)periph->softc;
673 
674 	return(cam_periph_error(ccb, cam_flags, sense_flags,
675 				&softc->saved_ccb));
676 }
677 
678 static int
679 ptioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct thread *td)
680 {
681 	struct cam_periph *periph;
682 	struct pt_softc *softc;
683 	int unit;
684 	int error;
685 
686 	unit = minor(dev);
687 	periph = cam_extend_get(ptperiphs, unit);
688 
689 	if (periph == NULL)
690 		return(ENXIO);
691 
692 	softc = (struct pt_softc *)periph->softc;
693 
694 	if ((error = cam_periph_lock(periph, PCATCH)) != 0) {
695 		return (error); /* error code from tsleep */
696 	}
697 
698 	switch(cmd) {
699 	case PTIOCGETTIMEOUT:
700 		if (softc->io_timeout >= 1000)
701 			*(int *)addr = softc->io_timeout / 1000;
702 		else
703 			*(int *)addr = 0;
704 		break;
705 	case PTIOCSETTIMEOUT:
706 	{
707 		if (*(int *)addr < 1) {
708 			error = EINVAL;
709 			break;
710 		}
711 
712 		crit_enter();
713 		softc->io_timeout = *(int *)addr * 1000;
714 		crit_exit();
715 
716 		break;
717 	}
718 	default:
719 		error = cam_periph_ioctl(periph, cmd, addr, pterror);
720 		break;
721 	}
722 
723 	cam_periph_unlock(periph);
724 
725 	return(error);
726 }
727 
728 void
729 scsi_send_receive(struct ccb_scsiio *csio, u_int32_t retries,
730 		  void (*cbfcnp)(struct cam_periph *, union ccb *),
731 		  u_int tag_action, int readop, u_int byte2,
732 		  u_int32_t xfer_len, u_int8_t *data_ptr, u_int8_t sense_len,
733 		  u_int32_t timeout)
734 {
735 	struct scsi_send_receive *scsi_cmd;
736 
737 	scsi_cmd = (struct scsi_send_receive *)&csio->cdb_io.cdb_bytes;
738 	scsi_cmd->opcode = readop ? RECEIVE : SEND;
739 	scsi_cmd->byte2 = byte2;
740 	scsi_ulto3b(xfer_len, scsi_cmd->xfer_len);
741 	scsi_cmd->control = 0;
742 
743 	cam_fill_csio(csio,
744 		      retries,
745 		      cbfcnp,
746 		      /*flags*/readop ? CAM_DIR_IN : CAM_DIR_OUT,
747 		      tag_action,
748 		      data_ptr,
749 		      xfer_len,
750 		      sense_len,
751 		      sizeof(*scsi_cmd),
752 		      timeout);
753 }
754