1139749Simp/*-
2123474Swpaul * SPDX-License-Identifier: BSD-2-Clause
3123474Swpaul *
4123474Swpaul * Implementation of SCSI Processor Target Peripheral driver for CAM.
5123474Swpaul *
6123474Swpaul * Copyright (c) 1998 Justin T. Gibbs.
7123474Swpaul * All rights reserved.
8123474Swpaul *
9123474Swpaul * Redistribution and use in source and binary forms, with or without
10123474Swpaul * modification, are permitted provided that the following conditions
11123474Swpaul * are met:
12123474Swpaul * 1. Redistributions of source code must retain the above copyright
13123474Swpaul *    notice, this list of conditions, and the following disclaimer,
14123474Swpaul *    without modification, immediately at the beginning of the file.
15123474Swpaul * 2. The name of the author may not be used to endorse or promote products
16123474Swpaul *    derived from this software without specific prior written permission.
17123474Swpaul *
18123474Swpaul * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19123474Swpaul * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20123474Swpaul * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21123474Swpaul * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
22123474Swpaul * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23123474Swpaul * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24123474Swpaul * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25123474Swpaul * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26123474Swpaul * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27123474Swpaul * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28123474Swpaul * SUCH DAMAGE.
29123474Swpaul */
30123474Swpaul
31123474Swpaul#include <sys/param.h>
32123474Swpaul#include <sys/queue.h>
33123474Swpaul#include <sys/systm.h>
34123474Swpaul#include <sys/kernel.h>
35128229Swpaul#include <sys/types.h>
36128229Swpaul#include <sys/bio.h>
37128229Swpaul#include <sys/devicestat.h>
38151207Swpaul#include <sys/malloc.h>
39151207Swpaul#include <sys/conf.h>
40151207Swpaul#include <sys/ptio.h>
41151207Swpaul
42151207Swpaul#include <cam/cam.h>
43151207Swpaul#include <cam/cam_ccb.h>
44151207Swpaul#include <cam/cam_periph.h>
45151207Swpaul#include <cam/cam_xpt_periph.h>
46151207Swpaul#include <cam/cam_debug.h>
47151207Swpaul
48126706Swpaul#include <cam/scsi/scsi_all.h>
49123474Swpaul#include <cam/scsi/scsi_message.h>
50123474Swpaul#include <cam/scsi/scsi_pt.h>
51123620Swpaul
52123474Swpaul#include "opt_pt.h"
53123474Swpaul
54123474Swpaultypedef enum {
55126706Swpaul	PT_STATE_PROBE,
56126706Swpaul	PT_STATE_NORMAL
57126706Swpaul} pt_state;
58126706Swpaul
59126706Swpaultypedef enum {
60126706Swpaul	PT_FLAG_NONE		= 0x00,
61186507Sweongyo	PT_FLAG_OPEN		= 0x01,
62186507Sweongyo	PT_FLAG_DEVICE_INVALID	= 0x02,
63186507Sweongyo	PT_FLAG_RETRY_UA	= 0x04
64186507Sweongyo} pt_flags;
65186507Sweongyo
66186507Sweongyotypedef enum {
67123474Swpaul	PT_CCB_BUFFER_IO	= 0x01,
68151207Swpaul	PT_CCB_RETRY_UA		= 0x04,
69123474Swpaul	PT_CCB_BUFFER_IO_UA	= PT_CCB_BUFFER_IO|PT_CCB_RETRY_UA
70123474Swpaul} pt_ccb_state;
71123474Swpaul
72145895Swpaul/* Offsets into our private area for storing information */
73123474Swpaul#define ccb_state	ppriv_field0
74123474Swpaul#define ccb_bp		ppriv_ptr1
75123474Swpaul
76123474Swpaulstruct pt_softc {
77151207Swpaul	struct	 bio_queue_head bio_queue;
78123474Swpaul	struct	 devstat *device_stats;
79123474Swpaul	LIST_HEAD(, ccb_hdr) pending_ccbs;
80123474Swpaul	pt_state state;
81151207Swpaul	pt_flags flags;
82151207Swpaul	int	 io_timeout;
83151207Swpaul	struct cdev *dev;
84151207Swpaul};
85151207Swpaul
86151207Swpaulstatic	d_open_t	ptopen;
87151207Swpaulstatic	d_close_t	ptclose;
88151207Swpaulstatic	d_strategy_t	ptstrategy;
89151207Swpaulstatic	periph_init_t	ptinit;
90123474Swpaulstatic	void		ptasync(void *callback_arg, uint32_t code,
91123474Swpaul				struct cam_path *path, void *arg);
92151207Swpaulstatic	periph_ctor_t	ptctor;
93131750Swpaulstatic	periph_oninv_t	ptoninvalidate;
94151451Swpaulstatic	periph_dtor_t	ptdtor;
95123474Swpaulstatic	periph_start_t	ptstart;
96183587Sweongyostatic	void		ptdone(struct cam_periph *periph,
97123474Swpaul			       union ccb *done_ccb);
98151207Swpaulstatic	d_ioctl_t	ptioctl;
99151207Swpaulstatic  int		pterror(union ccb *ccb, uint32_t cam_flags,
100151207Swpaul				uint32_t sense_flags);
101151207Swpaul
102151207Swpaulvoid	scsi_send_receive(struct ccb_scsiio *csio, uint32_t retries,
103151207Swpaul			  void (*cbfcnp)(struct cam_periph *, union ccb *),
104151207Swpaul			  u_int tag_action, int readop, u_int byte2,
105151207Swpaul			  uint32_t xfer_len, uint8_t *data_ptr,
106151207Swpaul			  uint8_t sense_len, uint32_t timeout);
107151207Swpaul
108178354Ssamstatic struct periph_driver ptdriver =
109178354Ssam{
110178354Ssam	ptinit, "pt",
111178354Ssam	TAILQ_HEAD_INITIALIZER(ptdriver.units), /* generation */ 0
112178354Ssam};
113178354Ssam
114178354SsamPERIPHDRIVER_DECLARE(pt, ptdriver);
115178354Ssam
116189488Sweongyostatic struct cdevsw pt_cdevsw = {
117186507Sweongyo	.d_version =	D_VERSION,
118189488Sweongyo	.d_flags =	0,
119189488Sweongyo	.d_open =	ptopen,
120186507Sweongyo	.d_close =	ptclose,
121186507Sweongyo	.d_read =	physread,
122189488Sweongyo	.d_write =	physwrite,
123189488Sweongyo	.d_ioctl =	ptioctl,
124192984Sthompsa	.d_strategy =	ptstrategy,
125189488Sweongyo	.d_name =	"pt",
126189488Sweongyo};
127189488Sweongyo
128189488Sweongyo#ifndef SCSI_PT_DEFAULT_TIMEOUT
129189488Sweongyo#define SCSI_PT_DEFAULT_TIMEOUT		60
130186507Sweongyo#endif
131189488Sweongyo
132189488Sweongyostatic int
133189488Sweongyoptopen(struct cdev *dev, int flags, int fmt, struct thread *td)
134189488Sweongyo{
135189488Sweongyo	struct cam_periph *periph;
136189488Sweongyo	struct pt_softc *softc;
137189488Sweongyo	int error = 0;
138186507Sweongyo
139189488Sweongyo	periph = (struct cam_periph *)dev->si_drv1;
140189488Sweongyo	if (cam_periph_acquire(periph) != 0)
141193045Sthompsa		return (ENXIO);
142189488Sweongyo
143189488Sweongyo	softc = (struct pt_softc *)periph->softc;
144186507Sweongyo
145189719Sweongyo	cam_periph_lock(periph);
146189719Sweongyo	if (softc->flags & PT_FLAG_DEVICE_INVALID) {
147189719Sweongyo		cam_periph_release_locked(periph);
148189719Sweongyo		cam_periph_unlock(periph);
149189950Sweongyo		return(ENXIO);
150189719Sweongyo	}
151189719Sweongyo
152189719Sweongyo	if ((softc->flags & PT_FLAG_OPEN) == 0)
153189719Sweongyo		softc->flags |= PT_FLAG_OPEN;
154123474Swpaul	else {
155147256Sbrooks		error = EBUSY;
156123474Swpaul		cam_periph_release(periph);
157124821Swpaul	}
158124821Swpaul
159124821Swpaul	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE,
160123474Swpaul	    ("ptopen: dev=%s\n", devtoname(dev)));
161123474Swpaul
162123474Swpaul	cam_periph_unlock(periph);
163123474Swpaul	return (error);
164123474Swpaul}
165123474Swpaul
166123474Swpaulstatic int
167123474Swpaulptclose(struct cdev *dev, int flag, int fmt, struct thread *td)
168123474Swpaul{
169123474Swpaul	struct	cam_periph *periph;
170123474Swpaul	struct	pt_softc *softc;
171123474Swpaul
172131953Swpaul	periph = (struct cam_periph *)dev->si_drv1;
173123474Swpaul	softc = (struct pt_softc *)periph->softc;
174131953Swpaul
175123474Swpaul	cam_periph_lock(periph);
176179723Scokane
177151207Swpaul	softc->flags &= ~PT_FLAG_OPEN;
178123474Swpaul	cam_periph_release_locked(periph);
179123474Swpaul	cam_periph_unlock(periph);
180141524Swpaul	return (0);
181141524Swpaul}
182123474Swpaul
183191746Sthompsa/*
184178286Scokane * Actually translate the requested transfer into one the physical driver
185123474Swpaul * can understand.  The transfer is described by a buf and will include
186123474Swpaul * only one physical transfer.
187123474Swpaul */
188123474Swpaulstatic void
189123474Swpaulptstrategy(struct bio *bp)
190123474Swpaul{
191141963Swpaul	struct cam_periph *periph;
192123474Swpaul	struct pt_softc *softc;
193123474Swpaul
194123474Swpaul	periph = (struct cam_periph *)bp->bio_dev->si_drv1;
195123695Swpaul	bp->bio_resid = bp->bio_bcount;
196123695Swpaul	if (periph == NULL) {
197151207Swpaul		biofinish(bp, NULL, ENXIO);
198123695Swpaul		return;
199123695Swpaul	}
200125076Swpaul	cam_periph_lock(periph);
201123474Swpaul	softc = (struct pt_softc *)periph->softc;
202123620Swpaul
203123474Swpaul	/*
204145485Swpaul	 * If the device has been made invalid, error out
205151207Swpaul	 */
206151207Swpaul	if ((softc->flags & PT_FLAG_DEVICE_INVALID)) {
207151207Swpaul		cam_periph_unlock(periph);
208151451Swpaul		biofinish(bp, NULL, ENXIO);
209146230Swpaul		return;
210123474Swpaul	}
211151207Swpaul
212123474Swpaul	/*
213123474Swpaul	 * Place it in the queue of disk activities for this disk
214123474Swpaul	 */
215123474Swpaul	bioq_insert_tail(&softc->bio_queue, bp);
216123474Swpaul
217151207Swpaul	/*
218151207Swpaul	 * Schedule ourselves for performing the work.
219151207Swpaul	 */
220151451Swpaul	xpt_schedule(periph, CAM_PRIORITY_NORMAL);
221151451Swpaul	cam_periph_unlock(periph);
222171390Sthompsa
223171390Sthompsa	return;
224171390Sthompsa}
225179498Scokane
226179498Scokanestatic void
227186507Sweongyoptinit(void)
228192984Sthompsa{
229189719Sweongyo	cam_status status;
230189950Sweongyo
231189950Sweongyo	/*
232189488Sweongyo	 * Install a global async callback.  This callback will
233189488Sweongyo	 * receive async callbacks like "new device found".
234189488Sweongyo	 */
235189488Sweongyo	status = xpt_register_async(AC_FOUND_DEVICE, ptasync, NULL, NULL);
236189488Sweongyo
237189488Sweongyo	if (status != CAM_REQ_CMP) {
238189488Sweongyo		printf("pt: Failed to attach master async callback "
239189719Sweongyo		       "due to status 0x%x!\n", status);
240189719Sweongyo	}
241189719Sweongyo}
242186507Sweongyo
243186507Sweongyostatic cam_status
244189950Sweongyoptctor(struct cam_periph *periph, void *arg)
245123474Swpaul{
246123474Swpaul	struct pt_softc *softc;
247189488Sweongyo	struct ccb_getdev *cgd;
248189488Sweongyo	struct ccb_pathinq cpi;
249189488Sweongyo	struct make_dev_args args;
250189719Sweongyo	int error;
251189719Sweongyo
252189719Sweongyo	cgd = (struct ccb_getdev *)arg;
253189719Sweongyo	if (cgd == NULL) {
254		printf("ptregister: no getdev CCB, can't register device\n");
255		return(CAM_REQ_CMP_ERR);
256	}
257
258	softc = (struct pt_softc *)malloc(sizeof(*softc),M_DEVBUF,M_NOWAIT);
259
260	if (softc == NULL) {
261		printf("daregister: Unable to probe new device. "
262		       "Unable to allocate softc\n");
263		return(CAM_REQ_CMP_ERR);
264	}
265
266	bzero(softc, sizeof(*softc));
267	LIST_INIT(&softc->pending_ccbs);
268	softc->state = PT_STATE_NORMAL;
269	bioq_init(&softc->bio_queue);
270
271	softc->io_timeout = SCSI_PT_DEFAULT_TIMEOUT * 1000;
272
273	periph->softc = softc;
274
275	xpt_path_inq(&cpi, periph->path);
276
277	cam_periph_unlock(periph);
278
279	make_dev_args_init(&args);
280	args.mda_devsw = &pt_cdevsw;
281	args.mda_unit = periph->unit_number;
282	args.mda_uid = UID_ROOT;
283	args.mda_gid = GID_OPERATOR;
284	args.mda_mode = 0600;
285	args.mda_si_drv1 = periph;
286	error = make_dev_s(&args, &softc->dev, "%s%d", periph->periph_name,
287	    periph->unit_number);
288	if (error != 0) {
289		cam_periph_lock(periph);
290		return (CAM_REQ_CMP_ERR);
291	}
292
293	softc->device_stats = devstat_new_entry("pt",
294			  periph->unit_number, 0,
295			  DEVSTAT_NO_BLOCKSIZE,
296			  SID_TYPE(&cgd->inq_data) |
297			  XPORT_DEVSTAT_TYPE(cpi.transport),
298			  DEVSTAT_PRIORITY_OTHER);
299
300	cam_periph_lock(periph);
301
302	/*
303	 * Add async callbacks for bus reset and
304	 * bus device reset calls.  I don't bother
305	 * checking if this fails as, in most cases,
306	 * the system will function just fine without
307	 * them and the only alternative would be to
308	 * not attach the device on failure.
309	 */
310	xpt_register_async(AC_SENT_BDR | AC_BUS_RESET | AC_LOST_DEVICE,
311			   ptasync, periph, periph->path);
312
313	/* Tell the user we've attached to the device */
314	xpt_announce_periph(periph, NULL);
315
316	return(CAM_REQ_CMP);
317}
318
319static void
320ptoninvalidate(struct cam_periph *periph)
321{
322	struct pt_softc *softc;
323
324	softc = (struct pt_softc *)periph->softc;
325
326	/*
327	 * De-register any async callbacks.
328	 */
329	xpt_register_async(0, ptasync, periph, periph->path);
330
331	softc->flags |= PT_FLAG_DEVICE_INVALID;
332
333	/*
334	 * Return all queued I/O with ENXIO.
335	 * XXX Handle any transactions queued to the card
336	 *     with XPT_ABORT_CCB.
337	 */
338	bioq_flush(&softc->bio_queue, NULL, ENXIO);
339}
340
341static void
342ptdtor(struct cam_periph *periph)
343{
344	struct pt_softc *softc;
345
346	softc = (struct pt_softc *)periph->softc;
347
348	devstat_remove_entry(softc->device_stats);
349	cam_periph_unlock(periph);
350	destroy_dev(softc->dev);
351	cam_periph_lock(periph);
352	free(softc, M_DEVBUF);
353}
354
355static void
356ptasync(void *callback_arg, uint32_t code, struct cam_path *path, void *arg)
357{
358	struct cam_periph *periph;
359
360	periph = (struct cam_periph *)callback_arg;
361	switch (code) {
362	case AC_FOUND_DEVICE:
363	{
364		struct ccb_getdev *cgd;
365		cam_status status;
366
367		cgd = (struct ccb_getdev *)arg;
368		if (cgd == NULL)
369			break;
370
371		if (cgd->protocol != PROTO_SCSI)
372			break;
373		if (SID_QUAL(&cgd->inq_data) != SID_QUAL_LU_CONNECTED)
374			break;
375		if (SID_TYPE(&cgd->inq_data) != T_PROCESSOR)
376			break;
377
378		/*
379		 * Allocate a peripheral instance for
380		 * this device and start the probe
381		 * process.
382		 */
383		status = cam_periph_alloc(ptctor, ptoninvalidate, ptdtor,
384					  ptstart, "pt", CAM_PERIPH_BIO,
385					  path, ptasync,
386					  AC_FOUND_DEVICE, cgd);
387
388		if (status != CAM_REQ_CMP
389		 && status != CAM_REQ_INPROG)
390			printf("ptasync: Unable to attach to new device "
391				"due to status 0x%x\n", status);
392		break;
393	}
394	case AC_SENT_BDR:
395	case AC_BUS_RESET:
396	{
397		struct pt_softc *softc;
398		struct ccb_hdr *ccbh;
399
400		softc = (struct pt_softc *)periph->softc;
401		/*
402		 * Don't fail on the expected unit attention
403		 * that will occur.
404		 */
405		softc->flags |= PT_FLAG_RETRY_UA;
406		LIST_FOREACH(ccbh, &softc->pending_ccbs, periph_links.le)
407			ccbh->ccb_state |= PT_CCB_RETRY_UA;
408	}
409	/* FALLTHROUGH */
410	default:
411		cam_periph_async(periph, code, path, arg);
412		break;
413	}
414}
415
416static void
417ptstart(struct cam_periph *periph, union ccb *start_ccb)
418{
419	struct pt_softc *softc;
420	struct bio *bp;
421
422	softc = (struct pt_softc *)periph->softc;
423
424	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("ptstart\n"));
425
426	/*
427	 * See if there is a buf with work for us to do..
428	 */
429	bp = bioq_first(&softc->bio_queue);
430	if (bp == NULL) {
431		xpt_release_ccb(start_ccb);
432	} else {
433		bioq_remove(&softc->bio_queue, bp);
434
435		devstat_start_transaction_bio(softc->device_stats, bp);
436
437		scsi_send_receive(&start_ccb->csio,
438				  /*retries*/4,
439				  ptdone,
440				  MSG_SIMPLE_Q_TAG,
441				  bp->bio_cmd == BIO_READ,
442				  /*byte2*/0,
443				  bp->bio_bcount,
444				  bp->bio_data,
445				  /*sense_len*/SSD_FULL_SIZE,
446				  /*timeout*/softc->io_timeout);
447
448		start_ccb->ccb_h.ccb_state = PT_CCB_BUFFER_IO_UA;
449
450		/*
451		 * Block out any asynchronous callbacks
452		 * while we touch the pending ccb list.
453		 */
454		LIST_INSERT_HEAD(&softc->pending_ccbs, &start_ccb->ccb_h,
455				 periph_links.le);
456
457		start_ccb->ccb_h.ccb_bp = bp;
458		bp = bioq_first(&softc->bio_queue);
459
460		xpt_action(start_ccb);
461
462		if (bp != NULL) {
463			/* Have more work to do, so ensure we stay scheduled */
464			xpt_schedule(periph, CAM_PRIORITY_NORMAL);
465		}
466	}
467}
468
469static void
470ptdone(struct cam_periph *periph, union ccb *done_ccb)
471{
472	struct pt_softc *softc;
473	struct ccb_scsiio *csio;
474
475	softc = (struct pt_softc *)periph->softc;
476
477	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("ptdone\n"));
478
479	csio = &done_ccb->csio;
480	switch (csio->ccb_h.ccb_state) {
481	case PT_CCB_BUFFER_IO:
482	case PT_CCB_BUFFER_IO_UA:
483	{
484		struct bio *bp;
485
486		bp = (struct bio *)done_ccb->ccb_h.ccb_bp;
487		if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
488			int error;
489			int sf;
490
491			if ((csio->ccb_h.ccb_state & PT_CCB_RETRY_UA) != 0)
492				sf = SF_RETRY_UA;
493			else
494				sf = 0;
495
496			error = pterror(done_ccb, CAM_RETRY_SELTO, sf);
497			if (error == ERESTART) {
498				/*
499				 * A retry was scheuled, so
500				 * just return.
501				 */
502				return;
503			}
504			if (error != 0) {
505				if (error == ENXIO) {
506					/*
507					 * Catastrophic error.  Mark our device
508					 * as invalid.
509					 */
510					xpt_print(periph->path,
511					    "Invalidating device\n");
512					softc->flags |= PT_FLAG_DEVICE_INVALID;
513				}
514
515				/*
516				 * return all queued I/O with EIO, so that
517				 * the client can retry these I/Os in the
518				 * proper order should it attempt to recover.
519				 */
520				bioq_flush(&softc->bio_queue, NULL, EIO);
521				bp->bio_error = error;
522				bp->bio_resid = bp->bio_bcount;
523				bp->bio_flags |= BIO_ERROR;
524			} else {
525				bp->bio_resid = csio->resid;
526				bp->bio_error = 0;
527				if (bp->bio_resid != 0) {
528					/* Short transfer ??? */
529					bp->bio_flags |= BIO_ERROR;
530				}
531			}
532			if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0)
533				cam_release_devq(done_ccb->ccb_h.path,
534						 /*relsim_flags*/0,
535						 /*reduction*/0,
536						 /*timeout*/0,
537						 /*getcount_only*/0);
538		} else {
539			bp->bio_resid = csio->resid;
540			if (bp->bio_resid != 0)
541				bp->bio_flags |= BIO_ERROR;
542		}
543
544		/*
545		 * Block out any asynchronous callbacks
546		 * while we touch the pending ccb list.
547		 */
548		LIST_REMOVE(&done_ccb->ccb_h, periph_links.le);
549
550		biofinish(bp, softc->device_stats, 0);
551		break;
552	}
553	}
554	xpt_release_ccb(done_ccb);
555}
556
557static int
558pterror(union ccb *ccb, uint32_t cam_flags, uint32_t sense_flags)
559{
560
561	return(cam_periph_error(ccb, cam_flags, sense_flags));
562}
563
564static int
565ptioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, struct thread *td)
566{
567	struct cam_periph *periph;
568	struct pt_softc *softc;
569	int error = 0;
570
571	periph = (struct cam_periph *)dev->si_drv1;
572	softc = (struct pt_softc *)periph->softc;
573
574	cam_periph_lock(periph);
575
576	switch(cmd) {
577	case PTIOCGETTIMEOUT:
578		if (softc->io_timeout >= 1000)
579			*(int *)addr = softc->io_timeout / 1000;
580		else
581			*(int *)addr = 0;
582		break;
583	case PTIOCSETTIMEOUT:
584		if (*(int *)addr < 1) {
585			error = EINVAL;
586			break;
587		}
588
589		softc->io_timeout = *(int *)addr * 1000;
590
591		break;
592	default:
593		error = cam_periph_ioctl(periph, cmd, addr, pterror);
594		break;
595	}
596
597	cam_periph_unlock(periph);
598
599	return(error);
600}
601
602void
603scsi_send_receive(struct ccb_scsiio *csio, uint32_t retries,
604		  void (*cbfcnp)(struct cam_periph *, union ccb *),
605		  u_int tag_action, int readop, u_int byte2,
606		  uint32_t xfer_len, uint8_t *data_ptr, uint8_t sense_len,
607		  uint32_t timeout)
608{
609	struct scsi_send_receive *scsi_cmd;
610
611	scsi_cmd = (struct scsi_send_receive *)&csio->cdb_io.cdb_bytes;
612	scsi_cmd->opcode = readop ? RECEIVE : SEND;
613	scsi_cmd->byte2 = byte2;
614	scsi_ulto3b(xfer_len, scsi_cmd->xfer_len);
615	scsi_cmd->control = 0;
616
617	cam_fill_csio(csio,
618		      retries,
619		      cbfcnp,
620		      /*flags*/readop ? CAM_DIR_IN : CAM_DIR_OUT,
621		      tag_action,
622		      data_ptr,
623		      xfer_len,
624		      sense_len,
625		      sizeof(*scsi_cmd),
626		      timeout);
627}
628