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