scsi_pt.c revision 50073
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 *      $Id: scsi_pt.c,v 1.11 1999/08/17 20:25:47 billf Exp $
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/buf.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
55typedef enum {
56	PT_STATE_PROBE,
57	PT_STATE_NORMAL
58} pt_state;
59
60typedef 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
67typedef 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
78struct pt_softc {
79	struct	 buf_queue_head buf_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};
87
88static	d_open_t	ptopen;
89static	d_close_t	ptclose;
90static	d_strategy_t	ptstrategy;
91static	periph_init_t	ptinit;
92static	void		ptasync(void *callback_arg, u_int32_t code,
93				struct cam_path *path, void *arg);
94static	periph_ctor_t	ptctor;
95static	periph_oninv_t	ptoninvalidate;
96static	periph_dtor_t	ptdtor;
97static	periph_start_t	ptstart;
98static	void		ptdone(struct cam_periph *periph,
99			       union ccb *done_ccb);
100static	d_ioctl_t	ptioctl;
101static  int		pterror(union ccb *ccb, u_int32_t cam_flags,
102				u_int32_t sense_flags);
103
104void	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
110static struct periph_driver ptdriver =
111{
112	ptinit, "pt",
113	TAILQ_HEAD_INITIALIZER(ptdriver.units), /* generation */ 0
114};
115
116DATA_SET(periphdriver_set, ptdriver);
117
118#define PT_CDEV_MAJOR 61
119
120static struct cdevsw pt_cdevsw = {
121	/* open */	ptopen,
122	/* close */	ptclose,
123	/* read */	physread,
124	/* write */	physwrite,
125	/* ioctl */	ptioctl,
126	/* stop */	nostop,
127	/* reset */	noreset,
128	/* devtotty */	nodevtotty,
129	/* poll */	nopoll,
130	/* mmap */	nommap,
131	/* strategy */	ptstrategy,
132	/* name */	"pt",
133	/* parms */	noparms,
134	/* maj */	PT_CDEV_MAJOR,
135	/* dump */	nodump,
136	/* psize */	nopsize,
137	/* flags */	0,
138	/* maxio */	0,
139	/* bmaj */	-1
140};
141
142static struct extend_array *ptperiphs;
143
144#ifndef SCSI_PT_DEFAULT_TIMEOUT
145#define SCSI_PT_DEFAULT_TIMEOUT		60
146#endif
147
148static int
149ptopen(dev_t dev, int flags, int fmt, struct proc *p)
150{
151	struct cam_periph *periph;
152	struct pt_softc *softc;
153	int unit;
154	int error;
155	int s;
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	s = splsoftcam();
165	if (softc->flags & PT_FLAG_DEVICE_INVALID) {
166		splx(s);
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, PRIBIO|PCATCH)) != 0) {
174		splx(s);
175		return (error); /* error code from tsleep */
176	}
177
178	splx(s);
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
192static int
193ptclose(dev_t dev, int flag, int fmt, struct proc *p)
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, PRIBIO)) != 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 */
221static void
222ptstrategy(struct buf *bp)
223{
224	struct cam_periph *periph;
225	struct pt_softc *softc;
226	u_int  unit;
227	int    s;
228
229	unit = minor(bp->b_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	s = splbio();
243
244	/*
245	 * If the device has been made invalid, error out
246	 */
247	if ((softc->flags & PT_FLAG_DEVICE_INVALID)) {
248		splx(s);
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	bufq_insert_tail(&softc->buf_queue, bp);
257
258	splx(s);
259
260	/*
261	 * Schedule ourselves for performing the work.
262	 */
263	xpt_schedule(periph, /* XXX priority */1);
264
265	return;
266bad:
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(bp);
274}
275
276static void
277ptinit(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	} else {
315		/* If we were successfull, register our devsw */
316		cdevsw_add(&pt_cdevsw);
317	}
318}
319
320static cam_status
321ptctor(struct cam_periph *periph, void *arg)
322{
323	struct pt_softc *softc;
324	struct ccb_setasync csa;
325	struct ccb_getdev *cgd;
326
327	cgd = (struct ccb_getdev *)arg;
328	if (periph == NULL) {
329		printf("ptregister: periph was NULL!!\n");
330		return(CAM_REQ_CMP_ERR);
331	}
332
333	if (cgd == NULL) {
334		printf("ptregister: no getdev CCB, can't register device\n");
335		return(CAM_REQ_CMP_ERR);
336	}
337
338	softc = (struct pt_softc *)malloc(sizeof(*softc),M_DEVBUF,M_NOWAIT);
339
340	if (softc == NULL) {
341		printf("daregister: Unable to probe new device. "
342		       "Unable to allocate softc\n");
343		return(CAM_REQ_CMP_ERR);
344	}
345
346	bzero(softc, sizeof(*softc));
347	LIST_INIT(&softc->pending_ccbs);
348	softc->state = PT_STATE_NORMAL;
349	bufq_init(&softc->buf_queue);
350
351	softc->io_timeout = SCSI_PT_DEFAULT_TIMEOUT * 1000;
352
353	periph->softc = softc;
354
355	cam_extend_set(ptperiphs, periph->unit_number, periph);
356
357	/*
358	 * The DA driver supports a blocksize, but
359	 * we don't know the blocksize until we do
360	 * a read capacity.  So, set a flag to
361	 * indicate that the blocksize is
362	 * unavailable right now.  We'll clear the
363	 * flag as soon as we've done a read capacity.
364	 */
365	devstat_add_entry(&softc->device_stats, "pt",
366			  periph->unit_number, 0,
367			  DEVSTAT_NO_BLOCKSIZE,
368			  cgd->pd_type | DEVSTAT_TYPE_IF_SCSI,
369			  DEVSTAT_PRIORITY_OTHER);
370
371	/*
372	 * Add async callbacks for bus reset and
373	 * bus device reset calls.  I don't bother
374	 * checking if this fails as, in most cases,
375	 * the system will function just fine without
376	 * them and the only alternative would be to
377	 * not attach the device on failure.
378	 */
379	xpt_setup_ccb(&csa.ccb_h, periph->path, /*priority*/5);
380	csa.ccb_h.func_code = XPT_SASYNC_CB;
381	csa.event_enable = AC_SENT_BDR | AC_BUS_RESET | AC_LOST_DEVICE;
382	csa.callback = ptasync;
383	csa.callback_arg = periph;
384	xpt_action((union ccb *)&csa);
385
386	/* Tell the user we've attached to the device */
387	xpt_announce_periph(periph, NULL);
388
389	return(CAM_REQ_CMP);
390}
391
392static void
393ptoninvalidate(struct cam_periph *periph)
394{
395	int s;
396	struct pt_softc *softc;
397	struct buf *q_bp;
398	struct ccb_setasync csa;
399
400	softc = (struct pt_softc *)periph->softc;
401
402	/*
403	 * De-register any async callbacks.
404	 */
405	xpt_setup_ccb(&csa.ccb_h, periph->path,
406		      /* priority */ 5);
407	csa.ccb_h.func_code = XPT_SASYNC_CB;
408	csa.event_enable = 0;
409	csa.callback = ptasync;
410	csa.callback_arg = periph;
411	xpt_action((union ccb *)&csa);
412
413	softc->flags |= PT_FLAG_DEVICE_INVALID;
414
415	/*
416	 * Although the oninvalidate() routines are always called at
417	 * splsoftcam, we need to be at splbio() here to keep the buffer
418	 * queue from being modified while we traverse it.
419	 */
420	s = splbio();
421
422	/*
423	 * Return all queued I/O with ENXIO.
424	 * XXX Handle any transactions queued to the card
425	 *     with XPT_ABORT_CCB.
426	 */
427	while ((q_bp = bufq_first(&softc->buf_queue)) != NULL){
428		bufq_remove(&softc->buf_queue, q_bp);
429		q_bp->b_resid = q_bp->b_bcount;
430		q_bp->b_error = ENXIO;
431		q_bp->b_flags |= B_ERROR;
432		biodone(q_bp);
433	}
434
435	splx(s);
436
437	xpt_print_path(periph->path);
438	printf("lost device\n");
439}
440
441static void
442ptdtor(struct cam_periph *periph)
443{
444	struct pt_softc *softc;
445
446	softc = (struct pt_softc *)periph->softc;
447
448	devstat_remove_entry(&softc->device_stats);
449
450	cam_extend_release(ptperiphs, periph->unit_number);
451	xpt_print_path(periph->path);
452	printf("removing device entry\n");
453	free(softc, M_DEVBUF);
454}
455
456static void
457ptasync(void *callback_arg, u_int32_t code, struct cam_path *path, void *arg)
458{
459	struct cam_periph *periph;
460
461	periph = (struct cam_periph *)callback_arg;
462	switch (code) {
463	case AC_FOUND_DEVICE:
464	{
465		struct ccb_getdev *cgd;
466		cam_status status;
467
468		cgd = (struct ccb_getdev *)arg;
469
470		if (cgd->pd_type != T_PROCESSOR)
471			break;
472
473		/*
474		 * Allocate a peripheral instance for
475		 * this device and start the probe
476		 * process.
477		 */
478		status = cam_periph_alloc(ptctor, ptoninvalidate, ptdtor,
479					  ptstart, "pt", CAM_PERIPH_BIO,
480					  cgd->ccb_h.path, ptasync,
481					  AC_FOUND_DEVICE, cgd);
482
483		if (status != CAM_REQ_CMP
484		 && status != CAM_REQ_INPROG)
485			printf("ptasync: Unable to attach to new device "
486				"due to status 0x%x\n", status);
487		break;
488	}
489	case AC_SENT_BDR:
490	case AC_BUS_RESET:
491	{
492		struct pt_softc *softc;
493		struct ccb_hdr *ccbh;
494		int s;
495
496		softc = (struct pt_softc *)periph->softc;
497		s = splsoftcam();
498		/*
499		 * Don't fail on the expected unit attention
500		 * that will occur.
501		 */
502		softc->flags |= PT_FLAG_RETRY_UA;
503		for (ccbh = LIST_FIRST(&softc->pending_ccbs);
504		     ccbh != NULL; ccbh = LIST_NEXT(ccbh, periph_links.le))
505			ccbh->ccb_state |= PT_CCB_RETRY_UA;
506		splx(s);
507		/* FALLTHROUGH */
508	}
509	default:
510		cam_periph_async(periph, code, path, arg);
511		break;
512	}
513}
514
515static void
516ptstart(struct cam_periph *periph, union ccb *start_ccb)
517{
518	struct pt_softc *softc;
519	struct buf *bp;
520	int s;
521
522	softc = (struct pt_softc *)periph->softc;
523
524	/*
525	 * See if there is a buf with work for us to do..
526	 */
527	s = splbio();
528	bp = bufq_first(&softc->buf_queue);
529	if (periph->immediate_priority <= periph->pinfo.priority) {
530		CAM_DEBUG_PRINT(CAM_DEBUG_SUBTRACE,
531				("queuing for immediate ccb\n"));
532		start_ccb->ccb_h.ccb_state = PT_CCB_WAITING;
533		SLIST_INSERT_HEAD(&periph->ccb_list, &start_ccb->ccb_h,
534				  periph_links.sle);
535		periph->immediate_priority = CAM_PRIORITY_NONE;
536		splx(s);
537		wakeup(&periph->ccb_list);
538	} else if (bp == NULL) {
539		splx(s);
540		xpt_release_ccb(start_ccb);
541	} else {
542		int oldspl;
543
544		bufq_remove(&softc->buf_queue, bp);
545
546		devstat_start_transaction(&softc->device_stats);
547
548		scsi_send_receive(&start_ccb->csio,
549				  /*retries*/4,
550				  ptdone,
551				  MSG_SIMPLE_Q_TAG,
552				  bp->b_flags & B_READ,
553				  /*byte2*/0,
554				  bp->b_bcount,
555				  bp->b_data,
556				  /*sense_len*/SSD_FULL_SIZE,
557				  /*timeout*/softc->io_timeout);
558
559		start_ccb->ccb_h.ccb_state = PT_CCB_BUFFER_IO;
560
561		/*
562		 * Block out any asyncronous callbacks
563		 * while we touch the pending ccb list.
564		 */
565		oldspl = splcam();
566		LIST_INSERT_HEAD(&softc->pending_ccbs, &start_ccb->ccb_h,
567				 periph_links.le);
568		splx(oldspl);
569
570		start_ccb->ccb_h.ccb_bp = bp;
571		bp = bufq_first(&softc->buf_queue);
572		splx(s);
573
574		xpt_action(start_ccb);
575
576		if (bp != NULL) {
577			/* Have more work to do, so ensure we stay scheduled */
578			xpt_schedule(periph, /* XXX priority */1);
579		}
580	}
581}
582
583static void
584ptdone(struct cam_periph *periph, union ccb *done_ccb)
585{
586	struct pt_softc *softc;
587	struct ccb_scsiio *csio;
588
589	softc = (struct pt_softc *)periph->softc;
590	csio = &done_ccb->csio;
591	switch (csio->ccb_h.ccb_state) {
592	case PT_CCB_BUFFER_IO:
593	case PT_CCB_BUFFER_IO_UA:
594	{
595		struct buf *bp;
596		int    oldspl;
597
598		bp = (struct buf *)done_ccb->ccb_h.ccb_bp;
599		if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
600			int error;
601			int s;
602			int sf;
603
604			if ((csio->ccb_h.ccb_state & PT_CCB_RETRY_UA) != 0)
605				sf = SF_RETRY_UA;
606			else
607				sf = 0;
608
609			sf |= SF_RETRY_SELTO;
610
611			if ((error = pterror(done_ccb, 0, sf)) == ERESTART) {
612				/*
613				 * A retry was scheuled, so
614				 * just return.
615				 */
616				return;
617			}
618			if (error != 0) {
619				struct buf *q_bp;
620
621				s = splbio();
622
623				if (error == ENXIO) {
624					/*
625					 * Catastrophic error.  Mark our device
626					 * as invalid.
627					 */
628					xpt_print_path(periph->path);
629					printf("Invalidating device\n");
630					softc->flags |= PT_FLAG_DEVICE_INVALID;
631				}
632
633				/*
634				 * return all queued I/O with EIO, so that
635				 * the client can retry these I/Os in the
636				 * proper order should it attempt to recover.
637				 */
638				while ((q_bp = bufq_first(&softc->buf_queue))
639					!= NULL) {
640					bufq_remove(&softc->buf_queue, q_bp);
641					q_bp->b_resid = q_bp->b_bcount;
642					q_bp->b_error = EIO;
643					q_bp->b_flags |= B_ERROR;
644					biodone(q_bp);
645				}
646				splx(s);
647				bp->b_error = error;
648				bp->b_resid = bp->b_bcount;
649				bp->b_flags |= B_ERROR;
650			} else {
651				bp->b_resid = csio->resid;
652				bp->b_error = 0;
653				if (bp->b_resid != 0) {
654					/* Short transfer ??? */
655					bp->b_flags |= B_ERROR;
656				}
657			}
658			if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0)
659				cam_release_devq(done_ccb->ccb_h.path,
660						 /*relsim_flags*/0,
661						 /*reduction*/0,
662						 /*timeout*/0,
663						 /*getcount_only*/0);
664		} else {
665			bp->b_resid = csio->resid;
666			if (bp->b_resid != 0)
667				bp->b_flags |= B_ERROR;
668		}
669
670		/*
671		 * Block out any asyncronous callbacks
672		 * while we touch the pending ccb list.
673		 */
674		oldspl = splcam();
675		LIST_REMOVE(&done_ccb->ccb_h, periph_links.le);
676		splx(oldspl);
677
678		devstat_end_transaction(&softc->device_stats,
679					bp->b_bcount - bp->b_resid,
680					done_ccb->csio.tag_action & 0xf,
681					(bp->b_flags & B_READ) ? DEVSTAT_READ
682							       : DEVSTAT_WRITE);
683
684		biodone(bp);
685		break;
686	}
687	case PT_CCB_WAITING:
688		/* Caller will release the CCB */
689		wakeup(&done_ccb->ccb_h.cbfcnp);
690		return;
691	}
692	xpt_release_ccb(done_ccb);
693}
694
695static int
696pterror(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags)
697{
698	struct pt_softc	  *softc;
699	struct cam_periph *periph;
700
701	periph = xpt_path_periph(ccb->ccb_h.path);
702	softc = (struct pt_softc *)periph->softc;
703
704	return(cam_periph_error(ccb, cam_flags, sense_flags,
705				&softc->saved_ccb));
706}
707
708static int
709ptioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
710{
711	struct cam_periph *periph;
712	struct pt_softc *softc;
713	int unit;
714	int error;
715
716	unit = minor(dev);
717	periph = cam_extend_get(ptperiphs, unit);
718
719	if (periph == NULL)
720		return(ENXIO);
721
722	softc = (struct pt_softc *)periph->softc;
723
724	if ((error = cam_periph_lock(periph, PRIBIO|PCATCH)) != 0) {
725		return (error); /* error code from tsleep */
726	}
727
728	switch(cmd) {
729	case PTIOCGETTIMEOUT:
730		if (softc->io_timeout >= 1000)
731			*(int *)addr = softc->io_timeout / 1000;
732		else
733			*(int *)addr = 0;
734		break;
735	case PTIOCSETTIMEOUT:
736	{
737		int s;
738
739		if (*(int *)addr < 1) {
740			error = EINVAL;
741			break;
742		}
743
744		s = splsoftcam();
745		softc->io_timeout = *(int *)addr * 1000;
746		splx(s);
747
748		break;
749	}
750	default:
751		error = cam_periph_ioctl(periph, cmd, addr, pterror);
752		break;
753	}
754
755	cam_periph_unlock(periph);
756
757	return(error);
758}
759
760void
761scsi_send_receive(struct ccb_scsiio *csio, u_int32_t retries,
762		  void (*cbfcnp)(struct cam_periph *, union ccb *),
763		  u_int tag_action, int readop, u_int byte2,
764		  u_int32_t xfer_len, u_int8_t *data_ptr, u_int8_t sense_len,
765		  u_int32_t timeout)
766{
767	struct scsi_send_receive *scsi_cmd;
768
769	scsi_cmd = (struct scsi_send_receive *)&csio->cdb_io.cdb_bytes;
770	scsi_cmd->opcode = readop ? RECEIVE : SEND;
771	scsi_cmd->byte2 = byte2;
772	scsi_ulto3b(xfer_len, scsi_cmd->xfer_len);
773	scsi_cmd->control = 0;
774
775	cam_fill_csio(csio,
776		      retries,
777		      cbfcnp,
778		      /*flags*/readop ? CAM_DIR_IN : CAM_DIR_OUT,
779		      tag_action,
780		      data_ptr,
781		      xfer_len,
782		      sense_len,
783		      sizeof(*scsi_cmd),
784		      timeout);
785}
786