xref: /freebsd/sys/cam/scsi/scsi_pass.c (revision b8b6b5d37acc515d0ab51b54af71c9fffbfb0777)
1898b0535SWarner Losh /*-
23393f8daSKenneth D. Merry  * Copyright (c) 1997, 1998, 2000 Justin T. Gibbs.
32a888f93SKenneth D. Merry  * Copyright (c) 1997, 1998, 1999 Kenneth D. Merry.
476babe50SJustin T. Gibbs  * All rights reserved.
576babe50SJustin T. Gibbs  *
676babe50SJustin T. Gibbs  * Redistribution and use in source and binary forms, with or without
776babe50SJustin T. Gibbs  * modification, are permitted provided that the following conditions
876babe50SJustin T. Gibbs  * are met:
976babe50SJustin T. Gibbs  * 1. Redistributions of source code must retain the above copyright
1076babe50SJustin T. Gibbs  *    notice, this list of conditions, and the following disclaimer,
1176babe50SJustin T. Gibbs  *    without modification, immediately at the beginning of the file.
1276babe50SJustin T. Gibbs  * 2. The name of the author may not be used to endorse or promote products
1376babe50SJustin T. Gibbs  *    derived from this software without specific prior written permission.
1476babe50SJustin T. Gibbs  *
1576babe50SJustin T. Gibbs  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1676babe50SJustin T. Gibbs  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1776babe50SJustin T. Gibbs  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1876babe50SJustin T. Gibbs  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
1976babe50SJustin T. Gibbs  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2076babe50SJustin T. Gibbs  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2176babe50SJustin T. Gibbs  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2276babe50SJustin T. Gibbs  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2376babe50SJustin T. Gibbs  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2476babe50SJustin T. Gibbs  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2576babe50SJustin T. Gibbs  * SUCH DAMAGE.
2676babe50SJustin T. Gibbs  */
2776babe50SJustin T. Gibbs 
28ee709e70SDavid E. O'Brien #include <sys/cdefs.h>
29ee709e70SDavid E. O'Brien __FBSDID("$FreeBSD$");
30ee709e70SDavid E. O'Brien 
3176babe50SJustin T. Gibbs #include <sys/param.h>
3276babe50SJustin T. Gibbs #include <sys/systm.h>
3376babe50SJustin T. Gibbs #include <sys/kernel.h>
3476babe50SJustin T. Gibbs #include <sys/types.h>
359626b608SPoul-Henning Kamp #include <sys/bio.h>
3676babe50SJustin T. Gibbs #include <sys/malloc.h>
3776babe50SJustin T. Gibbs #include <sys/fcntl.h>
3876babe50SJustin T. Gibbs #include <sys/conf.h>
3976babe50SJustin T. Gibbs #include <sys/errno.h>
4076babe50SJustin T. Gibbs #include <sys/devicestat.h>
41f7312ca2SRobert Watson #include <sys/proc.h>
4276babe50SJustin T. Gibbs 
4376babe50SJustin T. Gibbs #include <cam/cam.h>
4476babe50SJustin T. Gibbs #include <cam/cam_ccb.h>
4576babe50SJustin T. Gibbs #include <cam/cam_periph.h>
463393f8daSKenneth D. Merry #include <cam/cam_queue.h>
4776babe50SJustin T. Gibbs #include <cam/cam_xpt_periph.h>
4876babe50SJustin T. Gibbs #include <cam/cam_debug.h>
492b83592fSScott Long #include <cam/cam_sim.h>
5076babe50SJustin T. Gibbs 
5176babe50SJustin T. Gibbs #include <cam/scsi/scsi_all.h>
5276babe50SJustin T. Gibbs #include <cam/scsi/scsi_pass.h>
5376babe50SJustin T. Gibbs 
5476babe50SJustin T. Gibbs typedef enum {
5576babe50SJustin T. Gibbs 	PASS_FLAG_OPEN			= 0x01,
5676babe50SJustin T. Gibbs 	PASS_FLAG_LOCKED		= 0x02,
5776babe50SJustin T. Gibbs 	PASS_FLAG_INVALID		= 0x04
5876babe50SJustin T. Gibbs } pass_flags;
5976babe50SJustin T. Gibbs 
6076babe50SJustin T. Gibbs typedef enum {
6176babe50SJustin T. Gibbs 	PASS_STATE_NORMAL
6276babe50SJustin T. Gibbs } pass_state;
6376babe50SJustin T. Gibbs 
6476babe50SJustin T. Gibbs typedef enum {
6576babe50SJustin T. Gibbs 	PASS_CCB_BUFFER_IO,
6676babe50SJustin T. Gibbs 	PASS_CCB_WAITING
6776babe50SJustin T. Gibbs } pass_ccb_types;
6876babe50SJustin T. Gibbs 
6976babe50SJustin T. Gibbs #define ccb_type	ppriv_field0
7076babe50SJustin T. Gibbs #define ccb_bp		ppriv_ptr1
7176babe50SJustin T. Gibbs 
7276babe50SJustin T. Gibbs struct pass_softc {
7376babe50SJustin T. Gibbs 	pass_state		state;
7476babe50SJustin T. Gibbs 	pass_flags		flags;
7576babe50SJustin T. Gibbs 	u_int8_t		pd_type;
7676babe50SJustin T. Gibbs 	union ccb		saved_ccb;
77a9d2245eSPoul-Henning Kamp 	struct devstat		*device_stats;
7889c9c53dSPoul-Henning Kamp 	struct cdev *dev;
7976babe50SJustin T. Gibbs };
8076babe50SJustin T. Gibbs 
8176babe50SJustin T. Gibbs 
8276babe50SJustin T. Gibbs static	d_open_t	passopen;
8376babe50SJustin T. Gibbs static	d_close_t	passclose;
8476babe50SJustin T. Gibbs static	d_ioctl_t	passioctl;
8576babe50SJustin T. Gibbs 
8676babe50SJustin T. Gibbs static	periph_init_t	passinit;
8776babe50SJustin T. Gibbs static	periph_ctor_t	passregister;
88ee9c90c7SKenneth D. Merry static	periph_oninv_t	passoninvalidate;
8976babe50SJustin T. Gibbs static	periph_dtor_t	passcleanup;
9076babe50SJustin T. Gibbs static	periph_start_t	passstart;
9176babe50SJustin T. Gibbs static	void		passasync(void *callback_arg, u_int32_t code,
9276babe50SJustin T. Gibbs 				  struct cam_path *path, void *arg);
9376babe50SJustin T. Gibbs static	void		passdone(struct cam_periph *periph,
9476babe50SJustin T. Gibbs 				 union ccb *done_ccb);
9576babe50SJustin T. Gibbs static	int		passerror(union ccb *ccb, u_int32_t cam_flags,
9676babe50SJustin T. Gibbs 				  u_int32_t sense_flags);
9776babe50SJustin T. Gibbs static 	int		passsendccb(struct cam_periph *periph, union ccb *ccb,
9876babe50SJustin T. Gibbs 				    union ccb *inccb);
9976babe50SJustin T. Gibbs 
10076babe50SJustin T. Gibbs static struct periph_driver passdriver =
10176babe50SJustin T. Gibbs {
10276babe50SJustin T. Gibbs 	passinit, "pass",
10376babe50SJustin T. Gibbs 	TAILQ_HEAD_INITIALIZER(passdriver.units), /* generation */ 0
10476babe50SJustin T. Gibbs };
10576babe50SJustin T. Gibbs 
1060b7c27b9SPeter Wemm PERIPHDRIVER_DECLARE(pass, passdriver);
10776babe50SJustin T. Gibbs 
1084e2f199eSPoul-Henning Kamp static struct cdevsw pass_cdevsw = {
109dc08ffecSPoul-Henning Kamp 	.d_version =	D_VERSION,
1102b83592fSScott Long 	.d_flags =	0,
1117ac40f5fSPoul-Henning Kamp 	.d_open =	passopen,
1127ac40f5fSPoul-Henning Kamp 	.d_close =	passclose,
1137ac40f5fSPoul-Henning Kamp 	.d_ioctl =	passioctl,
1147ac40f5fSPoul-Henning Kamp 	.d_name =	"pass",
11576babe50SJustin T. Gibbs };
11676babe50SJustin T. Gibbs 
11776babe50SJustin T. Gibbs static void
11876babe50SJustin T. Gibbs passinit(void)
11976babe50SJustin T. Gibbs {
12076babe50SJustin T. Gibbs 	cam_status status;
12176babe50SJustin T. Gibbs 
12276babe50SJustin T. Gibbs 	/*
12376babe50SJustin T. Gibbs 	 * Install a global async callback.  This callback will
12476babe50SJustin T. Gibbs 	 * receive async callbacks like "new device found".
12576babe50SJustin T. Gibbs 	 */
12685d92640SScott Long 	status = xpt_register_async(AC_FOUND_DEVICE, passasync, NULL, NULL);
12776babe50SJustin T. Gibbs 
12876babe50SJustin T. Gibbs 	if (status != CAM_REQ_CMP) {
12976babe50SJustin T. Gibbs 		printf("pass: Failed to attach master async callback "
13076babe50SJustin T. Gibbs 		       "due to status 0x%x!\n", status);
13176babe50SJustin T. Gibbs 	}
13276babe50SJustin T. Gibbs 
13376babe50SJustin T. Gibbs }
13476babe50SJustin T. Gibbs 
13576babe50SJustin T. Gibbs static void
136ee9c90c7SKenneth D. Merry passoninvalidate(struct cam_periph *periph)
137ee9c90c7SKenneth D. Merry {
138ee9c90c7SKenneth D. Merry 	struct pass_softc *softc;
139ee9c90c7SKenneth D. Merry 
140ee9c90c7SKenneth D. Merry 	softc = (struct pass_softc *)periph->softc;
141ee9c90c7SKenneth D. Merry 
142ee9c90c7SKenneth D. Merry 	/*
143ee9c90c7SKenneth D. Merry 	 * De-register any async callbacks.
144ee9c90c7SKenneth D. Merry 	 */
14585d92640SScott Long 	xpt_register_async(0, passasync, periph, periph->path);
146ee9c90c7SKenneth D. Merry 
147ee9c90c7SKenneth D. Merry 	softc->flags |= PASS_FLAG_INVALID;
148ee9c90c7SKenneth D. Merry 
149ee9c90c7SKenneth D. Merry 	/*
1503393f8daSKenneth D. Merry 	 * XXX Return all queued I/O with ENXIO.
151ee9c90c7SKenneth D. Merry 	 * XXX Handle any transactions queued to the card
152ee9c90c7SKenneth D. Merry 	 *     with XPT_ABORT_CCB.
153ee9c90c7SKenneth D. Merry 	 */
154ee9c90c7SKenneth D. Merry 
155ee9c90c7SKenneth D. Merry 	if (bootverbose) {
156f0d9af51SMatt Jacob 		xpt_print(periph->path, "lost device\n");
157ee9c90c7SKenneth D. Merry 	}
158ee9c90c7SKenneth D. Merry 
159ee9c90c7SKenneth D. Merry }
160ee9c90c7SKenneth D. Merry 
161ee9c90c7SKenneth D. Merry static void
16276babe50SJustin T. Gibbs passcleanup(struct cam_periph *periph)
16376babe50SJustin T. Gibbs {
164ee9c90c7SKenneth D. Merry 	struct pass_softc *softc;
165ee9c90c7SKenneth D. Merry 
166ee9c90c7SKenneth D. Merry 	softc = (struct pass_softc *)periph->softc;
167ee9c90c7SKenneth D. Merry 
1685f3fed85SEdward Tomasz Napierala 	if (bootverbose)
169f0d9af51SMatt Jacob 		xpt_print(periph->path, "removing device entry\n");
1705f3fed85SEdward Tomasz Napierala 	devstat_remove_entry(softc->device_stats);
1715f3fed85SEdward Tomasz Napierala 	cam_periph_unlock(periph);
17281059816SMatthew D Fleming 	/*
17381059816SMatthew D Fleming 	 * passcleanup() is indirectly a d_close method via passclose,
17481059816SMatthew D Fleming 	 * so using destroy_dev(9) directly can result in deadlock.
17581059816SMatthew D Fleming 	 */
17681059816SMatthew D Fleming 	destroy_dev_sched(softc->dev);
1775f3fed85SEdward Tomasz Napierala 	cam_periph_lock(periph);
178ee9c90c7SKenneth D. Merry 	free(softc, M_DEVBUF);
17976babe50SJustin T. Gibbs }
18076babe50SJustin T. Gibbs 
18176babe50SJustin T. Gibbs static void
18276babe50SJustin T. Gibbs passasync(void *callback_arg, u_int32_t code,
18376babe50SJustin T. Gibbs 	  struct cam_path *path, void *arg)
18476babe50SJustin T. Gibbs {
18576babe50SJustin T. Gibbs 	struct cam_periph *periph;
18676babe50SJustin T. Gibbs 
18776babe50SJustin T. Gibbs 	periph = (struct cam_periph *)callback_arg;
18876babe50SJustin T. Gibbs 
18976babe50SJustin T. Gibbs 	switch (code) {
19076babe50SJustin T. Gibbs 	case AC_FOUND_DEVICE:
19176babe50SJustin T. Gibbs 	{
19276babe50SJustin T. Gibbs 		struct ccb_getdev *cgd;
19376babe50SJustin T. Gibbs 		cam_status status;
19476babe50SJustin T. Gibbs 
19576babe50SJustin T. Gibbs 		cgd = (struct ccb_getdev *)arg;
196c5ff3b2fSMatt Jacob 		if (cgd == NULL)
197c5ff3b2fSMatt Jacob 			break;
19876babe50SJustin T. Gibbs 
19976babe50SJustin T. Gibbs 		/*
20076babe50SJustin T. Gibbs 		 * Allocate a peripheral instance for
20176babe50SJustin T. Gibbs 		 * this device and start the probe
20276babe50SJustin T. Gibbs 		 * process.
20376babe50SJustin T. Gibbs 		 */
204ee9c90c7SKenneth D. Merry 		status = cam_periph_alloc(passregister, passoninvalidate,
205ee9c90c7SKenneth D. Merry 					  passcleanup, passstart, "pass",
206ee9c90c7SKenneth D. Merry 					  CAM_PERIPH_BIO, cgd->ccb_h.path,
207ee9c90c7SKenneth D. Merry 					  passasync, AC_FOUND_DEVICE, cgd);
20876babe50SJustin T. Gibbs 
20976babe50SJustin T. Gibbs 		if (status != CAM_REQ_CMP
2103393f8daSKenneth D. Merry 		 && status != CAM_REQ_INPROG) {
2113393f8daSKenneth D. Merry 			const struct cam_status_entry *entry;
2123393f8daSKenneth D. Merry 
2133393f8daSKenneth D. Merry 			entry = cam_fetch_status_entry(status);
2143393f8daSKenneth D. Merry 
21576babe50SJustin T. Gibbs 			printf("passasync: Unable to attach new device "
2163393f8daSKenneth D. Merry 			       "due to status %#x: %s\n", status, entry ?
2173393f8daSKenneth D. Merry 			       entry->status_text : "Unknown");
2183393f8daSKenneth D. Merry 		}
21976babe50SJustin T. Gibbs 
22076babe50SJustin T. Gibbs 		break;
22176babe50SJustin T. Gibbs 	}
22276babe50SJustin T. Gibbs 	default:
223516871c6SJustin T. Gibbs 		cam_periph_async(periph, code, path, arg);
22476babe50SJustin T. Gibbs 		break;
22576babe50SJustin T. Gibbs 	}
22676babe50SJustin T. Gibbs }
22776babe50SJustin T. Gibbs 
22876babe50SJustin T. Gibbs static cam_status
22976babe50SJustin T. Gibbs passregister(struct cam_periph *periph, void *arg)
23076babe50SJustin T. Gibbs {
23176babe50SJustin T. Gibbs 	struct pass_softc *softc;
23276babe50SJustin T. Gibbs 	struct ccb_getdev *cgd;
233*b8b6b5d3SAlexander Motin 	struct ccb_pathinq cpi;
2343393f8daSKenneth D. Merry 	int    no_tags;
23576babe50SJustin T. Gibbs 
23676babe50SJustin T. Gibbs 	cgd = (struct ccb_getdev *)arg;
23776babe50SJustin T. Gibbs 	if (periph == NULL) {
23876babe50SJustin T. Gibbs 		printf("passregister: periph was NULL!!\n");
23976babe50SJustin T. Gibbs 		return(CAM_REQ_CMP_ERR);
24076babe50SJustin T. Gibbs 	}
24176babe50SJustin T. Gibbs 
24276babe50SJustin T. Gibbs 	if (cgd == NULL) {
24376babe50SJustin T. Gibbs 		printf("passregister: no getdev CCB, can't register device\n");
24476babe50SJustin T. Gibbs 		return(CAM_REQ_CMP_ERR);
24576babe50SJustin T. Gibbs 	}
24676babe50SJustin T. Gibbs 
24776babe50SJustin T. Gibbs 	softc = (struct pass_softc *)malloc(sizeof(*softc),
24876babe50SJustin T. Gibbs 					    M_DEVBUF, M_NOWAIT);
24976babe50SJustin T. Gibbs 
25076babe50SJustin T. Gibbs 	if (softc == NULL) {
25176babe50SJustin T. Gibbs 		printf("passregister: Unable to probe new device. "
25276babe50SJustin T. Gibbs 		       "Unable to allocate softc\n");
25376babe50SJustin T. Gibbs 		return(CAM_REQ_CMP_ERR);
25476babe50SJustin T. Gibbs 	}
25576babe50SJustin T. Gibbs 
25676babe50SJustin T. Gibbs 	bzero(softc, sizeof(*softc));
25776babe50SJustin T. Gibbs 	softc->state = PASS_STATE_NORMAL;
258*b8b6b5d3SAlexander Motin 	if (cgd->protocol == PROTO_SCSI || cgd->protocol == PROTO_ATAPI)
25910b6172aSMatt Jacob 		softc->pd_type = SID_TYPE(&cgd->inq_data);
260*b8b6b5d3SAlexander Motin 	else if (cgd->protocol == PROTO_SATAPM)
261*b8b6b5d3SAlexander Motin 		softc->pd_type = T_ENCLOSURE;
262*b8b6b5d3SAlexander Motin 	else
263*b8b6b5d3SAlexander Motin 		softc->pd_type = T_DIRECT;
26476babe50SJustin T. Gibbs 
26576babe50SJustin T. Gibbs 	periph->softc = softc;
2663393f8daSKenneth D. Merry 
267*b8b6b5d3SAlexander Motin 	bzero(&cpi, sizeof(cpi));
268*b8b6b5d3SAlexander Motin 	xpt_setup_ccb(&cpi.ccb_h, periph->path, CAM_PRIORITY_NORMAL);
269*b8b6b5d3SAlexander Motin 	cpi.ccb_h.func_code = XPT_PATH_INQ;
270*b8b6b5d3SAlexander Motin 	xpt_action((union ccb *)&cpi);
271*b8b6b5d3SAlexander Motin 
27276babe50SJustin T. Gibbs 	/*
27376babe50SJustin T. Gibbs 	 * We pass in 0 for a blocksize, since we don't
27476babe50SJustin T. Gibbs 	 * know what the blocksize of this device is, if
27576babe50SJustin T. Gibbs 	 * it even has a blocksize.
27676babe50SJustin T. Gibbs 	 */
27785d92640SScott Long 	mtx_unlock(periph->sim->mtx);
2783393f8daSKenneth D. Merry 	no_tags = (cgd->inq_data.flags & SID_CmdQue) == 0;
279c81d2c74SMatt Jacob 	softc->device_stats = devstat_new_entry("pass",
280d3ce8327SEd Schouten 			  periph->unit_number, 0,
2813393f8daSKenneth D. Merry 			  DEVSTAT_NO_BLOCKSIZE
2823393f8daSKenneth D. Merry 			  | (no_tags ? DEVSTAT_NO_ORDERED_TAGS : 0),
28310b6172aSMatt Jacob 			  softc->pd_type |
284*b8b6b5d3SAlexander Motin 			  XPORT_DEVSTAT_TYPE(cpi.transport) |
2852a888f93SKenneth D. Merry 			  DEVSTAT_TYPE_PASS,
2862a888f93SKenneth D. Merry 			  DEVSTAT_PRIORITY_PASS);
28773d26919SKenneth D. Merry 
28873d26919SKenneth D. Merry 	/* Register the device */
289d3ce8327SEd Schouten 	softc->dev = make_dev(&pass_cdevsw, periph->unit_number,
290c81d2c74SMatt Jacob 			      UID_ROOT, GID_OPERATOR, 0600, "%s%d",
291c81d2c74SMatt Jacob 			      periph->periph_name, periph->unit_number);
2922b83592fSScott Long 	mtx_lock(periph->sim->mtx);
293e2a5fdf9SNate Lawson 	softc->dev->si_drv1 = periph;
29473d26919SKenneth D. Merry 
29576babe50SJustin T. Gibbs 	/*
29676babe50SJustin T. Gibbs 	 * Add an async callback so that we get
29776babe50SJustin T. Gibbs 	 * notified if this device goes away.
29876babe50SJustin T. Gibbs 	 */
29985d92640SScott Long 	xpt_register_async(AC_LOST_DEVICE, passasync, periph, periph->path);
30076babe50SJustin T. Gibbs 
30176babe50SJustin T. Gibbs 	if (bootverbose)
30276babe50SJustin T. Gibbs 		xpt_announce_periph(periph, NULL);
30376babe50SJustin T. Gibbs 
30476babe50SJustin T. Gibbs 	return(CAM_REQ_CMP);
30576babe50SJustin T. Gibbs }
30676babe50SJustin T. Gibbs 
30776babe50SJustin T. Gibbs static int
30889c9c53dSPoul-Henning Kamp passopen(struct cdev *dev, int flags, int fmt, struct thread *td)
30976babe50SJustin T. Gibbs {
31076babe50SJustin T. Gibbs 	struct cam_periph *periph;
31176babe50SJustin T. Gibbs 	struct pass_softc *softc;
312e2a5fdf9SNate Lawson 	int error;
31376babe50SJustin T. Gibbs 
314e2a5fdf9SNate Lawson 	periph = (struct cam_periph *)dev->si_drv1;
3152b83592fSScott Long 	if (cam_periph_acquire(periph) != CAM_REQ_CMP)
31676babe50SJustin T. Gibbs 		return (ENXIO);
31776babe50SJustin T. Gibbs 
3182b83592fSScott Long 	cam_periph_lock(periph);
3192b83592fSScott Long 
32076babe50SJustin T. Gibbs 	softc = (struct pass_softc *)periph->softc;
32176babe50SJustin T. Gibbs 
322ee9c90c7SKenneth D. Merry 	if (softc->flags & PASS_FLAG_INVALID) {
3232b83592fSScott Long 		cam_periph_unlock(periph);
3242b83592fSScott Long 		cam_periph_release(periph);
32576babe50SJustin T. Gibbs 		return(ENXIO);
326ee9c90c7SKenneth D. Merry 	}
32722b9c86cSKenneth D. Merry 
32822b9c86cSKenneth D. Merry 	/*
329f5ef42beSRobert Watson 	 * Don't allow access when we're running at a high securelevel.
33022b9c86cSKenneth D. Merry 	 */
331a854ed98SJohn Baldwin 	error = securelevel_gt(td->td_ucred, 1);
332f7312ca2SRobert Watson 	if (error) {
3332b83592fSScott Long 		cam_periph_unlock(periph);
3342b83592fSScott Long 		cam_periph_release(periph);
335f7312ca2SRobert Watson 		return(error);
33622b9c86cSKenneth D. Merry 	}
33776babe50SJustin T. Gibbs 
33876babe50SJustin T. Gibbs 	/*
33966a0780eSKenneth D. Merry 	 * Only allow read-write access.
34066a0780eSKenneth D. Merry 	 */
34122b9c86cSKenneth D. Merry 	if (((flags & FWRITE) == 0) || ((flags & FREAD) == 0)) {
3422b83592fSScott Long 		cam_periph_unlock(periph);
3432b83592fSScott Long 		cam_periph_release(periph);
34466a0780eSKenneth D. Merry 		return(EPERM);
34522b9c86cSKenneth D. Merry 	}
34666a0780eSKenneth D. Merry 
34766a0780eSKenneth D. Merry 	/*
34876babe50SJustin T. Gibbs 	 * We don't allow nonblocking access.
34976babe50SJustin T. Gibbs 	 */
35076babe50SJustin T. Gibbs 	if ((flags & O_NONBLOCK) != 0) {
351f0d9af51SMatt Jacob 		xpt_print(periph->path, "can't do nonblocking access\n");
3522b83592fSScott Long 		cam_periph_unlock(periph);
3532b83592fSScott Long 		cam_periph_release(periph);
35422b9c86cSKenneth D. Merry 		return(EINVAL);
35576babe50SJustin T. Gibbs 	}
35676babe50SJustin T. Gibbs 
35776babe50SJustin T. Gibbs 	if ((softc->flags & PASS_FLAG_OPEN) == 0) {
35876babe50SJustin T. Gibbs 		softc->flags |= PASS_FLAG_OPEN;
359835187bfSScott Long 		cam_periph_unlock(periph);
3602b83592fSScott Long 	} else {
3612b83592fSScott Long 		/* Device closes aren't symmertical, so fix up the refcount */
362835187bfSScott Long 		cam_periph_unlock(periph);
3632b83592fSScott Long 		cam_periph_release(periph);
36476babe50SJustin T. Gibbs 	}
36576babe50SJustin T. Gibbs 
36676babe50SJustin T. Gibbs 	return (error);
36776babe50SJustin T. Gibbs }
36876babe50SJustin T. Gibbs 
36976babe50SJustin T. Gibbs static int
37089c9c53dSPoul-Henning Kamp passclose(struct cdev *dev, int flag, int fmt, struct thread *td)
37176babe50SJustin T. Gibbs {
37276babe50SJustin T. Gibbs 	struct 	cam_periph *periph;
37376babe50SJustin T. Gibbs 	struct	pass_softc *softc;
37476babe50SJustin T. Gibbs 
375e2a5fdf9SNate Lawson 	periph = (struct cam_periph *)dev->si_drv1;
37676babe50SJustin T. Gibbs 	if (periph == NULL)
37776babe50SJustin T. Gibbs 		return (ENXIO);
37876babe50SJustin T. Gibbs 
3792b83592fSScott Long 	cam_periph_lock(periph);
3802b83592fSScott Long 
38176babe50SJustin T. Gibbs 	softc = (struct pass_softc *)periph->softc;
38276babe50SJustin T. Gibbs 	softc->flags &= ~PASS_FLAG_OPEN;
38376babe50SJustin T. Gibbs 
38476babe50SJustin T. Gibbs 	cam_periph_unlock(periph);
38576babe50SJustin T. Gibbs 	cam_periph_release(periph);
38676babe50SJustin T. Gibbs 
38776babe50SJustin T. Gibbs 	return (0);
38876babe50SJustin T. Gibbs }
38976babe50SJustin T. Gibbs 
39076babe50SJustin T. Gibbs static void
39176babe50SJustin T. Gibbs passstart(struct cam_periph *periph, union ccb *start_ccb)
39276babe50SJustin T. Gibbs {
39376babe50SJustin T. Gibbs 	struct pass_softc *softc;
39476babe50SJustin T. Gibbs 
39576babe50SJustin T. Gibbs 	softc = (struct pass_softc *)periph->softc;
39676babe50SJustin T. Gibbs 
39776babe50SJustin T. Gibbs 	switch (softc->state) {
39876babe50SJustin T. Gibbs 	case PASS_STATE_NORMAL:
39976babe50SJustin T. Gibbs 		start_ccb->ccb_h.ccb_type = PASS_CCB_WAITING;
40076babe50SJustin T. Gibbs 		SLIST_INSERT_HEAD(&periph->ccb_list, &start_ccb->ccb_h,
40176babe50SJustin T. Gibbs 				  periph_links.sle);
40276babe50SJustin T. Gibbs 		periph->immediate_priority = CAM_PRIORITY_NONE;
40376babe50SJustin T. Gibbs 		wakeup(&periph->ccb_list);
40476babe50SJustin T. Gibbs 		break;
40576babe50SJustin T. Gibbs 	}
40676babe50SJustin T. Gibbs }
4073393f8daSKenneth D. Merry 
40876babe50SJustin T. Gibbs static void
40976babe50SJustin T. Gibbs passdone(struct cam_periph *periph, union ccb *done_ccb)
41076babe50SJustin T. Gibbs {
41176babe50SJustin T. Gibbs 	struct pass_softc *softc;
41276babe50SJustin T. Gibbs 	struct ccb_scsiio *csio;
41376babe50SJustin T. Gibbs 
41476babe50SJustin T. Gibbs 	softc = (struct pass_softc *)periph->softc;
41576babe50SJustin T. Gibbs 	csio = &done_ccb->csio;
41676babe50SJustin T. Gibbs 	switch (csio->ccb_h.ccb_type) {
41776babe50SJustin T. Gibbs 	case PASS_CCB_WAITING:
41876babe50SJustin T. Gibbs 		/* Caller will release the CCB */
41976babe50SJustin T. Gibbs 		wakeup(&done_ccb->ccb_h.cbfcnp);
42076babe50SJustin T. Gibbs 		return;
42176babe50SJustin T. Gibbs 	}
42276babe50SJustin T. Gibbs 	xpt_release_ccb(done_ccb);
42376babe50SJustin T. Gibbs }
42476babe50SJustin T. Gibbs 
42576babe50SJustin T. Gibbs static int
42689c9c53dSPoul-Henning Kamp passioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, struct thread *td)
42776babe50SJustin T. Gibbs {
42876babe50SJustin T. Gibbs 	struct	cam_periph *periph;
42976babe50SJustin T. Gibbs 	struct	pass_softc *softc;
43076babe50SJustin T. Gibbs 	int	error;
43176babe50SJustin T. Gibbs 
432e2a5fdf9SNate Lawson 	periph = (struct cam_periph *)dev->si_drv1;
43376babe50SJustin T. Gibbs 	if (periph == NULL)
43476babe50SJustin T. Gibbs 		return(ENXIO);
43576babe50SJustin T. Gibbs 
4362b83592fSScott Long 	cam_periph_lock(periph);
43776babe50SJustin T. Gibbs 	softc = (struct pass_softc *)periph->softc;
43876babe50SJustin T. Gibbs 
43976babe50SJustin T. Gibbs 	error = 0;
44076babe50SJustin T. Gibbs 
44176babe50SJustin T. Gibbs 	switch (cmd) {
44276babe50SJustin T. Gibbs 
44376babe50SJustin T. Gibbs 	case CAMIOCOMMAND:
44476babe50SJustin T. Gibbs 	{
44576babe50SJustin T. Gibbs 		union ccb *inccb;
44676babe50SJustin T. Gibbs 		union ccb *ccb;
4479deea857SKenneth D. Merry 		int ccb_malloced;
44876babe50SJustin T. Gibbs 
44976babe50SJustin T. Gibbs 		inccb = (union ccb *)addr;
4509deea857SKenneth D. Merry 
4519deea857SKenneth D. Merry 		/*
4529deea857SKenneth D. Merry 		 * Some CCB types, like scan bus and scan lun can only go
4539deea857SKenneth D. Merry 		 * through the transport layer device.
4549deea857SKenneth D. Merry 		 */
4559deea857SKenneth D. Merry 		if (inccb->ccb_h.func_code & XPT_FC_XPT_ONLY) {
456f0d9af51SMatt Jacob 			xpt_print(periph->path, "CCB function code %#x is "
457f0d9af51SMatt Jacob 			    "restricted to the XPT device\n",
458f0d9af51SMatt Jacob 			    inccb->ccb_h.func_code);
4599deea857SKenneth D. Merry 			error = ENODEV;
4609deea857SKenneth D. Merry 			break;
4619deea857SKenneth D. Merry 		}
4629deea857SKenneth D. Merry 
4639deea857SKenneth D. Merry 		/*
4649deea857SKenneth D. Merry 		 * Non-immediate CCBs need a CCB from the per-device pool
4659deea857SKenneth D. Merry 		 * of CCBs, which is scheduled by the transport layer.
4669deea857SKenneth D. Merry 		 * Immediate CCBs and user-supplied CCBs should just be
4679deea857SKenneth D. Merry 		 * malloced.
4689deea857SKenneth D. Merry 		 */
4699deea857SKenneth D. Merry 		if ((inccb->ccb_h.func_code & XPT_FC_QUEUED)
4709deea857SKenneth D. Merry 		 && ((inccb->ccb_h.func_code & XPT_FC_USER_CCB) == 0)) {
4719deea857SKenneth D. Merry 			ccb = cam_periph_getccb(periph,
4729deea857SKenneth D. Merry 						inccb->ccb_h.pinfo.priority);
4739deea857SKenneth D. Merry 			ccb_malloced = 0;
4749deea857SKenneth D. Merry 		} else {
4758008a935SScott Long 			ccb = xpt_alloc_ccb_nowait();
4769deea857SKenneth D. Merry 
4779deea857SKenneth D. Merry 			if (ccb != NULL)
4789deea857SKenneth D. Merry 				xpt_setup_ccb(&ccb->ccb_h, periph->path,
4799deea857SKenneth D. Merry 					      inccb->ccb_h.pinfo.priority);
4809deea857SKenneth D. Merry 			ccb_malloced = 1;
4819deea857SKenneth D. Merry 		}
4829deea857SKenneth D. Merry 
4839deea857SKenneth D. Merry 		if (ccb == NULL) {
484f0d9af51SMatt Jacob 			xpt_print(periph->path, "unable to allocate CCB\n");
4859deea857SKenneth D. Merry 			error = ENOMEM;
4869deea857SKenneth D. Merry 			break;
4879deea857SKenneth D. Merry 		}
48876babe50SJustin T. Gibbs 
48976babe50SJustin T. Gibbs 		error = passsendccb(periph, ccb, inccb);
49076babe50SJustin T. Gibbs 
4919deea857SKenneth D. Merry 		if (ccb_malloced)
4929deea857SKenneth D. Merry 			xpt_free_ccb(ccb);
4939deea857SKenneth D. Merry 		else
49476babe50SJustin T. Gibbs 			xpt_release_ccb(ccb);
49576babe50SJustin T. Gibbs 
49676babe50SJustin T. Gibbs 		break;
49776babe50SJustin T. Gibbs 	}
49876babe50SJustin T. Gibbs 	default:
49976babe50SJustin T. Gibbs 		error = cam_periph_ioctl(periph, cmd, addr, passerror);
50076babe50SJustin T. Gibbs 		break;
50176babe50SJustin T. Gibbs 	}
50276babe50SJustin T. Gibbs 
5032b83592fSScott Long 	cam_periph_unlock(periph);
50476babe50SJustin T. Gibbs 	return(error);
50576babe50SJustin T. Gibbs }
50676babe50SJustin T. Gibbs 
50776babe50SJustin T. Gibbs /*
50876babe50SJustin T. Gibbs  * Generally, "ccb" should be the CCB supplied by the kernel.  "inccb"
50976babe50SJustin T. Gibbs  * should be the CCB that is copied in from the user.
51076babe50SJustin T. Gibbs  */
51176babe50SJustin T. Gibbs static int
51276babe50SJustin T. Gibbs passsendccb(struct cam_periph *periph, union ccb *ccb, union ccb *inccb)
51376babe50SJustin T. Gibbs {
51476babe50SJustin T. Gibbs 	struct pass_softc *softc;
51576babe50SJustin T. Gibbs 	struct cam_periph_map_info mapinfo;
51676babe50SJustin T. Gibbs 	int error, need_unmap;
51776babe50SJustin T. Gibbs 
51876babe50SJustin T. Gibbs 	softc = (struct pass_softc *)periph->softc;
51976babe50SJustin T. Gibbs 
52076babe50SJustin T. Gibbs 	need_unmap = 0;
52176babe50SJustin T. Gibbs 
52276babe50SJustin T. Gibbs 	/*
52376babe50SJustin T. Gibbs 	 * There are some fields in the CCB header that need to be
52476babe50SJustin T. Gibbs 	 * preserved, the rest we get from the user.
52576babe50SJustin T. Gibbs 	 */
52676babe50SJustin T. Gibbs 	xpt_merge_ccb(ccb, inccb);
52776babe50SJustin T. Gibbs 
52876babe50SJustin T. Gibbs 	/*
52976babe50SJustin T. Gibbs 	 * There's no way for the user to have a completion
53076babe50SJustin T. Gibbs 	 * function, so we put our own completion function in here.
53176babe50SJustin T. Gibbs 	 */
53276babe50SJustin T. Gibbs 	ccb->ccb_h.cbfcnp = passdone;
53376babe50SJustin T. Gibbs 
53476babe50SJustin T. Gibbs 	/*
53576babe50SJustin T. Gibbs 	 * We only attempt to map the user memory into kernel space
53676babe50SJustin T. Gibbs 	 * if they haven't passed in a physical memory pointer,
53776babe50SJustin T. Gibbs 	 * and if there is actually an I/O operation to perform.
53806e79492SKenneth D. Merry 	 * cam_periph_mapmem() supports SCSI, ATA, SMP, ADVINFO and device
5397c103ddeSKenneth D. Merry 	 * match CCBs.  For the SCSI, ATA and ADVINFO CCBs, we only pass the
5407c103ddeSKenneth D. Merry 	 * CCB in if there's actually data to map.  cam_periph_mapmem() will
5417c103ddeSKenneth D. Merry 	 * do the right thing, even if there isn't data to map, but since CCBs
54276babe50SJustin T. Gibbs 	 * without data are a reasonably common occurance (e.g. test unit
54376babe50SJustin T. Gibbs 	 * ready), it will save a few cycles if we check for it here.
54476babe50SJustin T. Gibbs 	 */
54576babe50SJustin T. Gibbs 	if (((ccb->ccb_h.flags & CAM_DATA_PHYS) == 0)
54652c9ce25SScott Long 	 && (((ccb->ccb_h.func_code == XPT_SCSI_IO ||
54752c9ce25SScott Long 	       ccb->ccb_h.func_code == XPT_ATA_IO)
54876babe50SJustin T. Gibbs 	    && ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE))
54906e79492SKenneth D. Merry 	  || (ccb->ccb_h.func_code == XPT_DEV_MATCH)
55006e79492SKenneth D. Merry 	  || (ccb->ccb_h.func_code == XPT_SMP_IO)
5517c103ddeSKenneth D. Merry 	  || ((ccb->ccb_h.func_code == XPT_GDEV_ADVINFO)
5527c103ddeSKenneth D. Merry 	   && (ccb->cgdai.bufsiz > 0)))) {
55376babe50SJustin T. Gibbs 
55476babe50SJustin T. Gibbs 		bzero(&mapinfo, sizeof(mapinfo));
55576babe50SJustin T. Gibbs 
5562b83592fSScott Long 		/*
5572b83592fSScott Long 		 * cam_periph_mapmem calls into proc and vm functions that can
5582b83592fSScott Long 		 * sleep as well as trigger I/O, so we can't hold the lock.
5592b83592fSScott Long 		 * Dropping it here is reasonably safe.
5602b83592fSScott Long 		 */
5612b83592fSScott Long 		cam_periph_unlock(periph);
56276babe50SJustin T. Gibbs 		error = cam_periph_mapmem(ccb, &mapinfo);
5632b83592fSScott Long 		cam_periph_lock(periph);
56476babe50SJustin T. Gibbs 
56576babe50SJustin T. Gibbs 		/*
56676babe50SJustin T. Gibbs 		 * cam_periph_mapmem returned an error, we can't continue.
56776babe50SJustin T. Gibbs 		 * Return the error to the user.
56876babe50SJustin T. Gibbs 		 */
56976babe50SJustin T. Gibbs 		if (error)
57076babe50SJustin T. Gibbs 			return(error);
57176babe50SJustin T. Gibbs 
57276babe50SJustin T. Gibbs 		/*
57376babe50SJustin T. Gibbs 		 * We successfully mapped the memory in, so we need to
57476babe50SJustin T. Gibbs 		 * unmap it when the transaction is done.
57576babe50SJustin T. Gibbs 		 */
57676babe50SJustin T. Gibbs 		need_unmap = 1;
57776babe50SJustin T. Gibbs 	}
57876babe50SJustin T. Gibbs 
57976babe50SJustin T. Gibbs 	/*
58076babe50SJustin T. Gibbs 	 * If the user wants us to perform any error recovery, then honor
58176babe50SJustin T. Gibbs 	 * that request.  Otherwise, it's up to the user to perform any
58276babe50SJustin T. Gibbs 	 * error recovery.
58376babe50SJustin T. Gibbs 	 */
58483c5d981SAlexander Motin 	cam_periph_runccb(ccb,
58583c5d981SAlexander Motin 	    (ccb->ccb_h.flags & CAM_PASS_ERR_RECOVER) ? passerror : NULL,
58683c5d981SAlexander Motin 	    /* cam_flags */ CAM_RETRY_SELTO, /* sense_flags */SF_RETRY_UA,
587a9d2245eSPoul-Henning Kamp 	    softc->device_stats);
58876babe50SJustin T. Gibbs 
58976babe50SJustin T. Gibbs 	if (need_unmap != 0)
59076babe50SJustin T. Gibbs 		cam_periph_unmapmem(ccb, &mapinfo);
59176babe50SJustin T. Gibbs 
59276babe50SJustin T. Gibbs 	ccb->ccb_h.cbfcnp = NULL;
59376babe50SJustin T. Gibbs 	ccb->ccb_h.periph_priv = inccb->ccb_h.periph_priv;
59476babe50SJustin T. Gibbs 	bcopy(ccb, inccb, sizeof(union ccb));
59576babe50SJustin T. Gibbs 
59683c5d981SAlexander Motin 	return(0);
59776babe50SJustin T. Gibbs }
59876babe50SJustin T. Gibbs 
59976babe50SJustin T. Gibbs static int
60076babe50SJustin T. Gibbs passerror(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags)
60176babe50SJustin T. Gibbs {
60276babe50SJustin T. Gibbs 	struct cam_periph *periph;
60376babe50SJustin T. Gibbs 	struct pass_softc *softc;
60476babe50SJustin T. Gibbs 
60576babe50SJustin T. Gibbs 	periph = xpt_path_periph(ccb->ccb_h.path);
60676babe50SJustin T. Gibbs 	softc = (struct pass_softc *)periph->softc;
60776babe50SJustin T. Gibbs 
60876babe50SJustin T. Gibbs 	return(cam_periph_error(ccb, cam_flags, sense_flags,
60976babe50SJustin T. Gibbs 				 &softc->saved_ccb));
61076babe50SJustin T. Gibbs }
611