xref: /freebsd/sys/cam/scsi/scsi_sg.c (revision 86d45c7f3b33e9d0c1a8ec5deaaddf22829d785f)
11eba4c79SScott Long /*-
21eba4c79SScott Long  * Copyright (c) 2007 Scott Long
31eba4c79SScott Long  * All rights reserved.
41eba4c79SScott Long  *
51eba4c79SScott Long  * Redistribution and use in source and binary forms, with or without
61eba4c79SScott Long  * modification, are permitted provided that the following conditions
71eba4c79SScott Long  * are met:
81eba4c79SScott Long  * 1. Redistributions of source code must retain the above copyright
91eba4c79SScott Long  *    notice, this list of conditions, and the following disclaimer,
101eba4c79SScott Long  *    without modification, immediately at the beginning of the file.
111eba4c79SScott Long  * 2. The name of the author may not be used to endorse or promote products
121eba4c79SScott Long  *    derived from this software without specific prior written permission.
131eba4c79SScott Long  *
141eba4c79SScott Long  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
151eba4c79SScott Long  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
161eba4c79SScott Long  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
171eba4c79SScott Long  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
181eba4c79SScott Long  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
191eba4c79SScott Long  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
201eba4c79SScott Long  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
211eba4c79SScott Long  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
221eba4c79SScott Long  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
231eba4c79SScott Long  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
241eba4c79SScott Long  * SUCH DAMAGE.
251eba4c79SScott Long  */
261eba4c79SScott Long 
271eba4c79SScott Long /*
281eba4c79SScott Long  * scsi_sg peripheral driver.  This driver is meant to implement the Linux
291eba4c79SScott Long  * SG passthrough interface for SCSI.
301eba4c79SScott Long  */
311eba4c79SScott Long 
321eba4c79SScott Long #include <sys/cdefs.h>
331eba4c79SScott Long __FBSDID("$FreeBSD$");
341eba4c79SScott Long 
351eba4c79SScott Long #include <sys/param.h>
361eba4c79SScott Long #include <sys/systm.h>
371eba4c79SScott Long #include <sys/kernel.h>
381eba4c79SScott Long #include <sys/types.h>
391eba4c79SScott Long #include <sys/bio.h>
401eba4c79SScott Long #include <sys/malloc.h>
411eba4c79SScott Long #include <sys/fcntl.h>
421eba4c79SScott Long #include <sys/ioccom.h>
431eba4c79SScott Long #include <sys/conf.h>
441eba4c79SScott Long #include <sys/errno.h>
451eba4c79SScott Long #include <sys/devicestat.h>
461eba4c79SScott Long #include <sys/proc.h>
471eba4c79SScott Long #include <sys/uio.h>
481eba4c79SScott Long 
491eba4c79SScott Long #include <cam/cam.h>
501eba4c79SScott Long #include <cam/cam_ccb.h>
511eba4c79SScott Long #include <cam/cam_periph.h>
521eba4c79SScott Long #include <cam/cam_queue.h>
531eba4c79SScott Long #include <cam/cam_xpt_periph.h>
541eba4c79SScott Long #include <cam/cam_debug.h>
551eba4c79SScott Long #include <cam/cam_sim.h>
561eba4c79SScott Long 
571eba4c79SScott Long #include <cam/scsi/scsi_all.h>
581eba4c79SScott Long #include <cam/scsi/scsi_message.h>
591eba4c79SScott Long #include <cam/scsi/scsi_sg.h>
601eba4c79SScott Long 
611eba4c79SScott Long #include <compat/linux/linux_ioctl.h>
621eba4c79SScott Long 
631eba4c79SScott Long typedef enum {
64c552ebe1SKenneth D. Merry 	SG_FLAG_LOCKED		= 0x01,
65c552ebe1SKenneth D. Merry 	SG_FLAG_INVALID		= 0x02
661eba4c79SScott Long } sg_flags;
671eba4c79SScott Long 
681eba4c79SScott Long typedef enum {
691eba4c79SScott Long 	SG_STATE_NORMAL
701eba4c79SScott Long } sg_state;
711eba4c79SScott Long 
721eba4c79SScott Long typedef enum {
73472cdbefSScott Long 	SG_RDWR_FREE,
741eba4c79SScott Long 	SG_RDWR_INPROG,
751eba4c79SScott Long 	SG_RDWR_DONE
761eba4c79SScott Long } sg_rdwr_state;
771eba4c79SScott Long 
781eba4c79SScott Long typedef enum {
791eba4c79SScott Long 	SG_CCB_RDWR_IO,
801eba4c79SScott Long 	SG_CCB_WAITING
811eba4c79SScott Long } sg_ccb_types;
821eba4c79SScott Long 
831eba4c79SScott Long #define ccb_type	ppriv_field0
841eba4c79SScott Long #define ccb_rdwr	ppriv_ptr1
851eba4c79SScott Long 
861eba4c79SScott Long struct sg_rdwr {
871eba4c79SScott Long 	TAILQ_ENTRY(sg_rdwr)	rdwr_link;
881eba4c79SScott Long 	int			tag;
891eba4c79SScott Long 	int			state;
901eba4c79SScott Long 	int			buf_len;
911eba4c79SScott Long 	char			*buf;
921eba4c79SScott Long 	union ccb		*ccb;
931eba4c79SScott Long 	union {
941eba4c79SScott Long 		struct sg_header hdr;
951eba4c79SScott Long 		struct sg_io_hdr io_hdr;
961eba4c79SScott Long 	} hdr;
971eba4c79SScott Long };
981eba4c79SScott Long 
991eba4c79SScott Long struct sg_softc {
1001eba4c79SScott Long 	sg_state		state;
1011eba4c79SScott Long 	sg_flags		flags;
102*86d45c7fSKenneth D. Merry 	int			open_count;
1031eba4c79SScott Long 	struct devstat		*device_stats;
1041eba4c79SScott Long 	TAILQ_HEAD(, sg_rdwr)	rdwr_done;
1051eba4c79SScott Long 	struct cdev		*dev;
106715ab212SScott Long 	int			sg_timeout;
107715ab212SScott Long 	int			sg_user_timeout;
108715ab212SScott Long 	uint8_t			pd_type;
1091eba4c79SScott Long 	union ccb		saved_ccb;
1101eba4c79SScott Long };
1111eba4c79SScott Long 
1121eba4c79SScott Long static d_open_t		sgopen;
1131eba4c79SScott Long static d_close_t	sgclose;
1141eba4c79SScott Long static d_ioctl_t	sgioctl;
1151eba4c79SScott Long static d_write_t	sgwrite;
1161eba4c79SScott Long static d_read_t		sgread;
1171eba4c79SScott Long 
1181eba4c79SScott Long static periph_init_t	sginit;
1191eba4c79SScott Long static periph_ctor_t	sgregister;
1201eba4c79SScott Long static periph_oninv_t	sgoninvalidate;
1211eba4c79SScott Long static periph_dtor_t	sgcleanup;
1221eba4c79SScott Long static periph_start_t	sgstart;
1231eba4c79SScott Long static void		sgasync(void *callback_arg, uint32_t code,
1241eba4c79SScott Long 				struct cam_path *path, void *arg);
1251eba4c79SScott Long static void		sgdone(struct cam_periph *periph, union ccb *done_ccb);
1261eba4c79SScott Long static int		sgsendccb(struct cam_periph *periph, union ccb *ccb);
1271eba4c79SScott Long static int		sgsendrdwr(struct cam_periph *periph, union ccb *ccb);
1281eba4c79SScott Long static int		sgerror(union ccb *ccb, uint32_t cam_flags,
1291eba4c79SScott Long 				uint32_t sense_flags);
1301eba4c79SScott Long static void		sg_scsiio_status(struct ccb_scsiio *csio,
1311eba4c79SScott Long 					 u_short *hoststat, u_short *drvstat);
1321eba4c79SScott Long 
1331eba4c79SScott Long static int		scsi_group_len(u_char cmd);
1341eba4c79SScott Long 
1351eba4c79SScott Long static struct periph_driver sgdriver =
1361eba4c79SScott Long {
1371eba4c79SScott Long 	sginit, "sg",
1381eba4c79SScott Long 	TAILQ_HEAD_INITIALIZER(sgdriver.units), /* gen */ 0
1391eba4c79SScott Long };
1401eba4c79SScott Long PERIPHDRIVER_DECLARE(sg, sgdriver);
1411eba4c79SScott Long 
1421eba4c79SScott Long static struct cdevsw sg_cdevsw = {
1431eba4c79SScott Long 	.d_version =	D_VERSION,
144c552ebe1SKenneth D. Merry 	.d_flags =	D_NEEDGIANT | D_TRACKCLOSE,
1451eba4c79SScott Long 	.d_open =	sgopen,
1461eba4c79SScott Long 	.d_close =	sgclose,
1471eba4c79SScott Long 	.d_ioctl =	sgioctl,
1481eba4c79SScott Long 	.d_write =	sgwrite,
1491eba4c79SScott Long 	.d_read =	sgread,
1501eba4c79SScott Long 	.d_name =	"sg",
1511eba4c79SScott Long };
1521eba4c79SScott Long 
1531eba4c79SScott Long static int sg_version = 30125;
1541eba4c79SScott Long 
1551eba4c79SScott Long static void
1561eba4c79SScott Long sginit(void)
1571eba4c79SScott Long {
1581eba4c79SScott Long 	cam_status status;
1591eba4c79SScott Long 
1601eba4c79SScott Long 	/*
1611eba4c79SScott Long 	 * Install a global async callback.  This callback will receive aync
1621eba4c79SScott Long 	 * callbacks like "new device found".
1631eba4c79SScott Long 	 */
16485d92640SScott Long 	status = xpt_register_async(AC_FOUND_DEVICE, sgasync, NULL, NULL);
1651eba4c79SScott Long 
1661eba4c79SScott Long 	if (status != CAM_REQ_CMP) {
1671eba4c79SScott Long 		printf("sg: Failed to attach master async callbac "
1681eba4c79SScott Long 			"due to status 0x%x!\n", status);
1691eba4c79SScott Long 	}
1701eba4c79SScott Long }
1711eba4c79SScott Long 
1721eba4c79SScott Long static void
173*86d45c7fSKenneth D. Merry sgdevgonecb(void *arg)
174*86d45c7fSKenneth D. Merry {
175*86d45c7fSKenneth D. Merry 	struct cam_sim    *sim;
176*86d45c7fSKenneth D. Merry 	struct cam_periph *periph;
177*86d45c7fSKenneth D. Merry 	struct sg_softc *softc;
178*86d45c7fSKenneth D. Merry 	int i;
179*86d45c7fSKenneth D. Merry 
180*86d45c7fSKenneth D. Merry 	periph = (struct cam_periph *)arg;
181*86d45c7fSKenneth D. Merry 	sim = periph->sim;
182*86d45c7fSKenneth D. Merry 	softc = (struct sg_softc *)periph->softc;
183*86d45c7fSKenneth D. Merry 
184*86d45c7fSKenneth D. Merry 	KASSERT(softc->open_count >= 0, ("Negative open count %d",
185*86d45c7fSKenneth D. Merry 		softc->open_count));
186*86d45c7fSKenneth D. Merry 
187*86d45c7fSKenneth D. Merry 	mtx_lock(sim->mtx);
188*86d45c7fSKenneth D. Merry 
189*86d45c7fSKenneth D. Merry 	/*
190*86d45c7fSKenneth D. Merry 	 * When we get this callback, we will get no more close calls from
191*86d45c7fSKenneth D. Merry 	 * devfs.  So if we have any dangling opens, we need to release the
192*86d45c7fSKenneth D. Merry 	 * reference held for that particular context.
193*86d45c7fSKenneth D. Merry 	 */
194*86d45c7fSKenneth D. Merry 	for (i = 0; i < softc->open_count; i++)
195*86d45c7fSKenneth D. Merry 		cam_periph_release_locked(periph);
196*86d45c7fSKenneth D. Merry 
197*86d45c7fSKenneth D. Merry 	softc->open_count = 0;
198*86d45c7fSKenneth D. Merry 
199*86d45c7fSKenneth D. Merry 	/*
200*86d45c7fSKenneth D. Merry 	 * Release the reference held for the device node, it is gone now.
201*86d45c7fSKenneth D. Merry 	 */
202*86d45c7fSKenneth D. Merry 	cam_periph_release_locked(periph);
203*86d45c7fSKenneth D. Merry 
204*86d45c7fSKenneth D. Merry 	/*
205*86d45c7fSKenneth D. Merry 	 * We reference the SIM lock directly here, instead of using
206*86d45c7fSKenneth D. Merry 	 * cam_periph_unlock().  The reason is that the final call to
207*86d45c7fSKenneth D. Merry 	 * cam_periph_release_locked() above could result in the periph
208*86d45c7fSKenneth D. Merry 	 * getting freed.  If that is the case, dereferencing the periph
209*86d45c7fSKenneth D. Merry 	 * with a cam_periph_unlock() call would cause a page fault.
210*86d45c7fSKenneth D. Merry 	 */
211*86d45c7fSKenneth D. Merry 	mtx_unlock(sim->mtx);
212*86d45c7fSKenneth D. Merry }
213*86d45c7fSKenneth D. Merry 
214*86d45c7fSKenneth D. Merry 
215*86d45c7fSKenneth D. Merry static void
2161eba4c79SScott Long sgoninvalidate(struct cam_periph *periph)
2171eba4c79SScott Long {
2181eba4c79SScott Long 	struct sg_softc *softc;
2191eba4c79SScott Long 
2201eba4c79SScott Long 	softc = (struct sg_softc *)periph->softc;
2211eba4c79SScott Long 
2221eba4c79SScott Long 	/*
2231eba4c79SScott Long 	 * Deregister any async callbacks.
2241eba4c79SScott Long 	 */
22585d92640SScott Long 	xpt_register_async(0, sgasync, periph, periph->path);
2261eba4c79SScott Long 
2271eba4c79SScott Long 	softc->flags |= SG_FLAG_INVALID;
2281eba4c79SScott Long 
2291eba4c79SScott Long 	/*
230*86d45c7fSKenneth D. Merry 	 * Tell devfs this device has gone away, and ask for a callback
231*86d45c7fSKenneth D. Merry 	 * when it has cleaned up its state.
232*86d45c7fSKenneth D. Merry 	 */
233*86d45c7fSKenneth D. Merry 	destroy_dev_sched_cb(softc->dev, sgdevgonecb, periph);
234*86d45c7fSKenneth D. Merry 
235*86d45c7fSKenneth D. Merry 	/*
2361eba4c79SScott Long 	 * XXX Return all queued I/O with ENXIO.
2371eba4c79SScott Long 	 * XXX Handle any transactions queued to the card
2381eba4c79SScott Long 	 *     with XPT_ABORT_CCB.
2391eba4c79SScott Long 	 */
2401eba4c79SScott Long 
2411eba4c79SScott Long 	if (bootverbose) {
2421eba4c79SScott Long 		xpt_print(periph->path, "lost device\n");
2431eba4c79SScott Long 	}
2441eba4c79SScott Long }
2451eba4c79SScott Long 
2461eba4c79SScott Long static void
2471eba4c79SScott Long sgcleanup(struct cam_periph *periph)
2481eba4c79SScott Long {
2491eba4c79SScott Long 	struct sg_softc *softc;
2501eba4c79SScott Long 
2511eba4c79SScott Long 	softc = (struct sg_softc *)periph->softc;
2525f3fed85SEdward Tomasz Napierala 	if (bootverbose)
2531eba4c79SScott Long 		xpt_print(periph->path, "removing device entry\n");
254*86d45c7fSKenneth D. Merry 
2555f3fed85SEdward Tomasz Napierala 	devstat_remove_entry(softc->device_stats);
256*86d45c7fSKenneth D. Merry 
2571eba4c79SScott Long 	free(softc, M_DEVBUF);
2581eba4c79SScott Long }
2591eba4c79SScott Long 
2601eba4c79SScott Long static void
2611eba4c79SScott Long sgasync(void *callback_arg, uint32_t code, struct cam_path *path, void *arg)
2621eba4c79SScott Long {
2631eba4c79SScott Long 	struct cam_periph *periph;
2641eba4c79SScott Long 
2651eba4c79SScott Long 	periph = (struct cam_periph *)callback_arg;
2661eba4c79SScott Long 
2671eba4c79SScott Long 	switch (code) {
2681eba4c79SScott Long 	case AC_FOUND_DEVICE:
2691eba4c79SScott Long 	{
2701eba4c79SScott Long 		struct ccb_getdev *cgd;
2711eba4c79SScott Long 		cam_status status;
2721eba4c79SScott Long 
2731eba4c79SScott Long 		cgd = (struct ccb_getdev *)arg;
2741eba4c79SScott Long 		if (cgd == NULL)
2751eba4c79SScott Long 			break;
2761eba4c79SScott Long 
27752c9ce25SScott Long 		if (cgd->protocol != PROTO_SCSI)
27852c9ce25SScott Long 			break;
27952c9ce25SScott Long 
2801eba4c79SScott Long 		/*
2811eba4c79SScott Long 		 * Allocate a peripheral instance for this device and
2821eba4c79SScott Long 		 * start the probe process.
2831eba4c79SScott Long 		 */
2841eba4c79SScott Long 		status = cam_periph_alloc(sgregister, sgoninvalidate,
2851eba4c79SScott Long 					  sgcleanup, sgstart, "sg",
2861eba4c79SScott Long 					  CAM_PERIPH_BIO, cgd->ccb_h.path,
2871eba4c79SScott Long 					  sgasync, AC_FOUND_DEVICE, cgd);
2881eba4c79SScott Long 		if ((status != CAM_REQ_CMP) && (status != CAM_REQ_INPROG)) {
2891eba4c79SScott Long 			const struct cam_status_entry *entry;
2901eba4c79SScott Long 
2911eba4c79SScott Long 			entry = cam_fetch_status_entry(status);
2921eba4c79SScott Long 			printf("sgasync: Unable to attach new device "
2931eba4c79SScott Long 				"due to status %#x: %s\n", status, entry ?
2941eba4c79SScott Long 				entry->status_text : "Unknown");
2951eba4c79SScott Long 		}
2961eba4c79SScott Long 		break;
2971eba4c79SScott Long 	}
2981eba4c79SScott Long 	default:
2991eba4c79SScott Long 		cam_periph_async(periph, code, path, arg);
3001eba4c79SScott Long 		break;
3011eba4c79SScott Long 	}
3021eba4c79SScott Long }
3031eba4c79SScott Long 
3041eba4c79SScott Long static cam_status
3051eba4c79SScott Long sgregister(struct cam_periph *periph, void *arg)
3061eba4c79SScott Long {
3071eba4c79SScott Long 	struct sg_softc *softc;
3081eba4c79SScott Long 	struct ccb_getdev *cgd;
309b8b6b5d3SAlexander Motin 	struct ccb_pathinq cpi;
3101eba4c79SScott Long 	int no_tags;
3111eba4c79SScott Long 
3121eba4c79SScott Long 	cgd = (struct ccb_getdev *)arg;
3131eba4c79SScott Long 	if (cgd == NULL) {
3141eba4c79SScott Long 		printf("sgregister: no getdev CCB, can't register device\n");
3151eba4c79SScott Long 		return (CAM_REQ_CMP_ERR);
3161eba4c79SScott Long 	}
3171eba4c79SScott Long 
3184400b36dSScott Long 	softc = malloc(sizeof(*softc), M_DEVBUF, M_ZERO | M_NOWAIT);
3191eba4c79SScott Long 	if (softc == NULL) {
3201eba4c79SScott Long 		printf("sgregister: Unable to allocate softc\n");
3211eba4c79SScott Long 		return (CAM_REQ_CMP_ERR);
3221eba4c79SScott Long 	}
3231eba4c79SScott Long 
3241eba4c79SScott Long 	softc->state = SG_STATE_NORMAL;
3251eba4c79SScott Long 	softc->pd_type = SID_TYPE(&cgd->inq_data);
326715ab212SScott Long 	softc->sg_timeout = SG_DEFAULT_TIMEOUT / SG_DEFAULT_HZ * hz;
327715ab212SScott Long 	softc->sg_user_timeout = SG_DEFAULT_TIMEOUT;
3281eba4c79SScott Long 	TAILQ_INIT(&softc->rdwr_done);
3291eba4c79SScott Long 	periph->softc = softc;
3301eba4c79SScott Long 
331b8b6b5d3SAlexander Motin 	bzero(&cpi, sizeof(cpi));
332b8b6b5d3SAlexander Motin 	xpt_setup_ccb(&cpi.ccb_h, periph->path, CAM_PRIORITY_NORMAL);
333b8b6b5d3SAlexander Motin 	cpi.ccb_h.func_code = XPT_PATH_INQ;
334b8b6b5d3SAlexander Motin 	xpt_action((union ccb *)&cpi);
335b8b6b5d3SAlexander Motin 
3361eba4c79SScott Long 	/*
3371eba4c79SScott Long 	 * We pass in 0 for all blocksize, since we don't know what the
3381eba4c79SScott Long 	 * blocksize of the device is, if it even has a blocksize.
3391eba4c79SScott Long 	 */
34085d92640SScott Long 	cam_periph_unlock(periph);
3411eba4c79SScott Long 	no_tags = (cgd->inq_data.flags & SID_CmdQue) == 0;
3421eba4c79SScott Long 	softc->device_stats = devstat_new_entry("sg",
343d3ce8327SEd Schouten 			periph->unit_number, 0,
3441eba4c79SScott Long 			DEVSTAT_NO_BLOCKSIZE
3451eba4c79SScott Long 			| (no_tags ? DEVSTAT_NO_ORDERED_TAGS : 0),
3461eba4c79SScott Long 			softc->pd_type |
347b8b6b5d3SAlexander Motin 			XPORT_DEVSTAT_TYPE(cpi.transport) |
3481eba4c79SScott Long 			DEVSTAT_TYPE_PASS,
3491eba4c79SScott Long 			DEVSTAT_PRIORITY_PASS);
3501eba4c79SScott Long 
351*86d45c7fSKenneth D. Merry 	/*
352*86d45c7fSKenneth D. Merry 	 * Acquire a reference to the periph before we create the devfs
353*86d45c7fSKenneth D. Merry 	 * instance for it.  We'll release this reference once the devfs
354*86d45c7fSKenneth D. Merry 	 * instance has been freed.
355*86d45c7fSKenneth D. Merry 	 */
356*86d45c7fSKenneth D. Merry 	if (cam_periph_acquire(periph) != CAM_REQ_CMP) {
357*86d45c7fSKenneth D. Merry 		xpt_print(periph->path, "%s: lost periph during "
358*86d45c7fSKenneth D. Merry 			  "registration!\n", __func__);
359*86d45c7fSKenneth D. Merry 		cam_periph_lock(periph);
360*86d45c7fSKenneth D. Merry 		return (CAM_REQ_CMP_ERR);
361*86d45c7fSKenneth D. Merry 	}
362*86d45c7fSKenneth D. Merry 
3631eba4c79SScott Long 	/* Register the device */
364d3ce8327SEd Schouten 	softc->dev = make_dev(&sg_cdevsw, periph->unit_number,
3651eba4c79SScott Long 			      UID_ROOT, GID_OPERATOR, 0600, "%s%d",
3661eba4c79SScott Long 			      periph->periph_name, periph->unit_number);
367cf454e30SMatt Jacob 	if (periph->unit_number < 26) {
368c59b4dcdSMatt Jacob 		(void)make_dev_alias(softc->dev, "sg%c",
369c59b4dcdSMatt Jacob 		    periph->unit_number + 'a');
370cf454e30SMatt Jacob 	} else {
371cf454e30SMatt Jacob 		(void)make_dev_alias(softc->dev, "sg%c%c",
372c59b4dcdSMatt Jacob 		    ((periph->unit_number / 26) - 1) + 'a',
373c59b4dcdSMatt Jacob 		    (periph->unit_number % 26) + 'a');
374cf454e30SMatt Jacob 	}
3752b83592fSScott Long 	cam_periph_lock(periph);
3761eba4c79SScott Long 	softc->dev->si_drv1 = periph;
3771eba4c79SScott Long 
3781eba4c79SScott Long 	/*
3791eba4c79SScott Long 	 * Add as async callback so that we get
3801eba4c79SScott Long 	 * notified if this device goes away.
3811eba4c79SScott Long 	 */
38285d92640SScott Long 	xpt_register_async(AC_LOST_DEVICE, sgasync, periph, periph->path);
3831eba4c79SScott Long 
3841eba4c79SScott Long 	if (bootverbose)
3851eba4c79SScott Long 		xpt_announce_periph(periph, NULL);
3861eba4c79SScott Long 
3871eba4c79SScott Long 	return (CAM_REQ_CMP);
3881eba4c79SScott Long }
3891eba4c79SScott Long 
3901eba4c79SScott Long static void
3911eba4c79SScott Long sgstart(struct cam_periph *periph, union ccb *start_ccb)
3921eba4c79SScott Long {
3931eba4c79SScott Long 	struct sg_softc *softc;
3941eba4c79SScott Long 
3951eba4c79SScott Long 	softc = (struct sg_softc *)periph->softc;
3961eba4c79SScott Long 
3971eba4c79SScott Long 	switch (softc->state) {
3981eba4c79SScott Long 	case SG_STATE_NORMAL:
3991eba4c79SScott Long 		start_ccb->ccb_h.ccb_type = SG_CCB_WAITING;
4001eba4c79SScott Long 		SLIST_INSERT_HEAD(&periph->ccb_list, &start_ccb->ccb_h,
4011eba4c79SScott Long 				  periph_links.sle);
4021eba4c79SScott Long 		periph->immediate_priority = CAM_PRIORITY_NONE;
4031eba4c79SScott Long 		wakeup(&periph->ccb_list);
4041eba4c79SScott Long 		break;
4051eba4c79SScott Long 	}
4061eba4c79SScott Long }
4071eba4c79SScott Long 
4081eba4c79SScott Long static void
4091eba4c79SScott Long sgdone(struct cam_periph *periph, union ccb *done_ccb)
4101eba4c79SScott Long {
4111eba4c79SScott Long 	struct sg_softc *softc;
4121eba4c79SScott Long 	struct ccb_scsiio *csio;
4131eba4c79SScott Long 
4141eba4c79SScott Long 	softc = (struct sg_softc *)periph->softc;
4151eba4c79SScott Long 	csio = &done_ccb->csio;
4161eba4c79SScott Long 	switch (csio->ccb_h.ccb_type) {
4171eba4c79SScott Long 	case SG_CCB_WAITING:
4181eba4c79SScott Long 		/* Caller will release the CCB */
4191eba4c79SScott Long 		wakeup(&done_ccb->ccb_h.cbfcnp);
4201eba4c79SScott Long 		return;
4211eba4c79SScott Long 	case SG_CCB_RDWR_IO:
4221eba4c79SScott Long 	{
4231eba4c79SScott Long 		struct sg_rdwr *rdwr;
4241eba4c79SScott Long 		int state;
4251eba4c79SScott Long 
4261eba4c79SScott Long 		devstat_end_transaction(softc->device_stats,
4271eba4c79SScott Long 					csio->dxfer_len,
4281eba4c79SScott Long 					csio->tag_action & 0xf,
4291eba4c79SScott Long 					((csio->ccb_h.flags & CAM_DIR_MASK) ==
4301eba4c79SScott Long 					CAM_DIR_NONE) ? DEVSTAT_NO_DATA :
4311eba4c79SScott Long 					(csio->ccb_h.flags & CAM_DIR_OUT) ?
4321eba4c79SScott Long 					DEVSTAT_WRITE : DEVSTAT_READ,
4331eba4c79SScott Long 					NULL, NULL);
4341eba4c79SScott Long 
4351eba4c79SScott Long 		rdwr = done_ccb->ccb_h.ccb_rdwr;
4361eba4c79SScott Long 		state = rdwr->state;
4371eba4c79SScott Long 		rdwr->state = SG_RDWR_DONE;
4381eba4c79SScott Long 		wakeup(rdwr);
4391eba4c79SScott Long 		break;
4401eba4c79SScott Long 	}
4411eba4c79SScott Long 	default:
4421eba4c79SScott Long 		panic("unknown sg CCB type");
4431eba4c79SScott Long 	}
4441eba4c79SScott Long }
4451eba4c79SScott Long 
4461eba4c79SScott Long static int
4471eba4c79SScott Long sgopen(struct cdev *dev, int flags, int fmt, struct thread *td)
4481eba4c79SScott Long {
4491eba4c79SScott Long 	struct cam_periph *periph;
4501eba4c79SScott Long 	struct sg_softc *softc;
4511eba4c79SScott Long 	int error = 0;
4521eba4c79SScott Long 
4531eba4c79SScott Long 	periph = (struct cam_periph *)dev->si_drv1;
4541eba4c79SScott Long 	if (periph == NULL)
4551eba4c79SScott Long 		return (ENXIO);
4561eba4c79SScott Long 
4578900f4b8SKenneth D. Merry 	if (cam_periph_acquire(periph) != CAM_REQ_CMP)
4588900f4b8SKenneth D. Merry 		return (ENXIO);
4598900f4b8SKenneth D. Merry 
4601eba4c79SScott Long 	/*
4611eba4c79SScott Long 	 * Don't allow access when we're running at a high securelevel.
4621eba4c79SScott Long 	 */
4631eba4c79SScott Long 	error = securelevel_gt(td->td_ucred, 1);
4648900f4b8SKenneth D. Merry 	if (error) {
4658900f4b8SKenneth D. Merry 		cam_periph_release(periph);
4661eba4c79SScott Long 		return (error);
4678900f4b8SKenneth D. Merry 	}
4681eba4c79SScott Long 
4692b83592fSScott Long 	cam_periph_lock(periph);
4702b83592fSScott Long 
4712b83592fSScott Long 	softc = (struct sg_softc *)periph->softc;
4722b83592fSScott Long 	if (softc->flags & SG_FLAG_INVALID) {
473c552ebe1SKenneth D. Merry 		cam_periph_release_locked(periph);
4742b83592fSScott Long 		cam_periph_unlock(periph);
4752b83592fSScott Long 		return (ENXIO);
4762b83592fSScott Long 	}
4771eba4c79SScott Long 
478*86d45c7fSKenneth D. Merry 	softc->open_count++;
479*86d45c7fSKenneth D. Merry 
480835187bfSScott Long 	cam_periph_unlock(periph);
4811eba4c79SScott Long 
4821eba4c79SScott Long 	return (error);
4831eba4c79SScott Long }
4841eba4c79SScott Long 
4851eba4c79SScott Long static int
4861eba4c79SScott Long sgclose(struct cdev *dev, int flag, int fmt, struct thread *td)
4871eba4c79SScott Long {
488*86d45c7fSKenneth D. Merry 	struct cam_sim    *sim;
4891eba4c79SScott Long 	struct cam_periph *periph;
490*86d45c7fSKenneth D. Merry 	struct sg_softc   *softc;
4911eba4c79SScott Long 
4921eba4c79SScott Long 	periph = (struct cam_periph *)dev->si_drv1;
4931eba4c79SScott Long 	if (periph == NULL)
4941eba4c79SScott Long 		return (ENXIO);
4951eba4c79SScott Long 
496*86d45c7fSKenneth D. Merry 	sim = periph->sim;
497*86d45c7fSKenneth D. Merry 	softc = periph->softc;
498*86d45c7fSKenneth D. Merry 
499*86d45c7fSKenneth D. Merry 	mtx_lock(sim->mtx);
500*86d45c7fSKenneth D. Merry 
501*86d45c7fSKenneth D. Merry 	softc->open_count--;
502*86d45c7fSKenneth D. Merry 
503*86d45c7fSKenneth D. Merry 	cam_periph_release_locked(periph);
504*86d45c7fSKenneth D. Merry 
505*86d45c7fSKenneth D. Merry 	/*
506*86d45c7fSKenneth D. Merry 	 * We reference the SIM lock directly here, instead of using
507*86d45c7fSKenneth D. Merry 	 * cam_periph_unlock().  The reason is that the call to
508*86d45c7fSKenneth D. Merry 	 * cam_periph_release_locked() above could result in the periph
509*86d45c7fSKenneth D. Merry 	 * getting freed.  If that is the case, dereferencing the periph
510*86d45c7fSKenneth D. Merry 	 * with a cam_periph_unlock() call would cause a page fault.
511*86d45c7fSKenneth D. Merry 	 *
512*86d45c7fSKenneth D. Merry 	 * cam_periph_release() avoids this problem using the same method,
513*86d45c7fSKenneth D. Merry 	 * but we're manually acquiring and dropping the lock here to
514*86d45c7fSKenneth D. Merry 	 * protect the open count and avoid another lock acquisition and
515*86d45c7fSKenneth D. Merry 	 * release.
516*86d45c7fSKenneth D. Merry 	 */
517*86d45c7fSKenneth D. Merry 	mtx_unlock(sim->mtx);
5181eba4c79SScott Long 
5191eba4c79SScott Long 	return (0);
5201eba4c79SScott Long }
5211eba4c79SScott Long 
5221eba4c79SScott Long static int
5231eba4c79SScott Long sgioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct thread *td)
5241eba4c79SScott Long {
5251eba4c79SScott Long 	union ccb *ccb;
5261eba4c79SScott Long 	struct ccb_scsiio *csio;
5271eba4c79SScott Long 	struct cam_periph *periph;
5281eba4c79SScott Long 	struct sg_softc *softc;
5291eba4c79SScott Long 	struct sg_io_hdr req;
5301eba4c79SScott Long 	int dir, error;
5311eba4c79SScott Long 
5321eba4c79SScott Long 	periph = (struct cam_periph *)dev->si_drv1;
5331eba4c79SScott Long 	if (periph == NULL)
5341eba4c79SScott Long 		return (ENXIO);
5351eba4c79SScott Long 
5362b83592fSScott Long 	cam_periph_lock(periph);
5372b83592fSScott Long 
5381eba4c79SScott Long 	softc = (struct sg_softc *)periph->softc;
5391eba4c79SScott Long 	error = 0;
5401eba4c79SScott Long 
5411eba4c79SScott Long 	switch (cmd) {
5421eba4c79SScott Long 	case LINUX_SCSI_GET_BUS_NUMBER: {
5431eba4c79SScott Long 		int busno;
5441eba4c79SScott Long 
5451eba4c79SScott Long 		busno = xpt_path_path_id(periph->path);
5461eba4c79SScott Long 		error = copyout(&busno, arg, sizeof(busno));
5471eba4c79SScott Long 		break;
5481eba4c79SScott Long 	}
5491eba4c79SScott Long 	case LINUX_SCSI_GET_IDLUN: {
5501eba4c79SScott Long 		struct scsi_idlun idlun;
5511eba4c79SScott Long 		struct cam_sim *sim;
5521eba4c79SScott Long 
5531eba4c79SScott Long 		idlun.dev_id = xpt_path_target_id(periph->path);
5541eba4c79SScott Long 		sim = xpt_path_sim(periph->path);
5551eba4c79SScott Long 		idlun.host_unique_id = sim->unit_number;
5561eba4c79SScott Long 		error = copyout(&idlun, arg, sizeof(idlun));
5571eba4c79SScott Long 		break;
5581eba4c79SScott Long 	}
5591eba4c79SScott Long 	case SG_GET_VERSION_NUM:
5601eba4c79SScott Long 	case LINUX_SG_GET_VERSION_NUM:
5611eba4c79SScott Long 		error = copyout(&sg_version, arg, sizeof(sg_version));
5621eba4c79SScott Long 		break;
5631eba4c79SScott Long 	case SG_SET_TIMEOUT:
564715ab212SScott Long 	case LINUX_SG_SET_TIMEOUT: {
565715ab212SScott Long 		u_int user_timeout;
566715ab212SScott Long 
567715ab212SScott Long 		error = copyin(arg, &user_timeout, sizeof(u_int));
568715ab212SScott Long 		if (error == 0) {
569715ab212SScott Long 			softc->sg_user_timeout = user_timeout;
570715ab212SScott Long 			softc->sg_timeout = user_timeout / SG_DEFAULT_HZ * hz;
571715ab212SScott Long 		}
5721eba4c79SScott Long 		break;
573715ab212SScott Long 	}
5741eba4c79SScott Long 	case SG_GET_TIMEOUT:
5751eba4c79SScott Long 	case LINUX_SG_GET_TIMEOUT:
5761eba4c79SScott Long 		/*
577715ab212SScott Long 		 * The value is returned directly to the syscall.
5781eba4c79SScott Long 		 */
579715ab212SScott Long 		td->td_retval[0] = softc->sg_user_timeout;
5801eba4c79SScott Long 		error = 0;
5811eba4c79SScott Long 		break;
5821eba4c79SScott Long 	case SG_IO:
5831eba4c79SScott Long 	case LINUX_SG_IO:
5841eba4c79SScott Long 		error = copyin(arg, &req, sizeof(req));
5851eba4c79SScott Long 		if (error)
5861eba4c79SScott Long 			break;
5871eba4c79SScott Long 
5881eba4c79SScott Long 		if (req.cmd_len > IOCDBLEN) {
5891eba4c79SScott Long 			error = EINVAL;
5901eba4c79SScott Long 			break;
5911eba4c79SScott Long 		}
5921eba4c79SScott Long 
5931eba4c79SScott Long 		if (req.iovec_count != 0) {
5941eba4c79SScott Long 			error = EOPNOTSUPP;
5951eba4c79SScott Long 			break;
5961eba4c79SScott Long 		}
5971eba4c79SScott Long 
5981e637ba6SAlexander Motin 		ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL);
5991eba4c79SScott Long 		csio = &ccb->csio;
6001eba4c79SScott Long 
6011eba4c79SScott Long 		error = copyin(req.cmdp, &csio->cdb_io.cdb_bytes,
6021eba4c79SScott Long 		    req.cmd_len);
6031eba4c79SScott Long 		if (error) {
6041eba4c79SScott Long 			xpt_release_ccb(ccb);
6051eba4c79SScott Long 			break;
6061eba4c79SScott Long 		}
6071eba4c79SScott Long 
6081eba4c79SScott Long 		switch(req.dxfer_direction) {
6091eba4c79SScott Long 		case SG_DXFER_TO_DEV:
6101eba4c79SScott Long 			dir = CAM_DIR_OUT;
6111eba4c79SScott Long 			break;
6121eba4c79SScott Long 		case SG_DXFER_FROM_DEV:
6131eba4c79SScott Long 			dir = CAM_DIR_IN;
6141eba4c79SScott Long 			break;
6151eba4c79SScott Long 		case SG_DXFER_TO_FROM_DEV:
6161eba4c79SScott Long 			dir = CAM_DIR_IN | CAM_DIR_OUT;
6171eba4c79SScott Long 			break;
6181eba4c79SScott Long 		case SG_DXFER_NONE:
6191eba4c79SScott Long 		default:
6201eba4c79SScott Long 			dir = CAM_DIR_NONE;
6211eba4c79SScott Long 			break;
6221eba4c79SScott Long 		}
6231eba4c79SScott Long 
6241eba4c79SScott Long 		cam_fill_csio(csio,
6251eba4c79SScott Long 			      /*retries*/1,
6261eba4c79SScott Long 			      sgdone,
6271eba4c79SScott Long 			      dir|CAM_DEV_QFRZDIS,
6281eba4c79SScott Long 			      MSG_SIMPLE_Q_TAG,
6291eba4c79SScott Long 			      req.dxferp,
6301eba4c79SScott Long 			      req.dxfer_len,
6311eba4c79SScott Long 			      req.mx_sb_len,
6321eba4c79SScott Long 			      req.cmd_len,
6331eba4c79SScott Long 			      req.timeout);
6341eba4c79SScott Long 
6351eba4c79SScott Long 		error = sgsendccb(periph, ccb);
6361eba4c79SScott Long 		if (error) {
6371eba4c79SScott Long 			req.host_status = DID_ERROR;
6381eba4c79SScott Long 			req.driver_status = DRIVER_INVALID;
6391eba4c79SScott Long 			xpt_release_ccb(ccb);
6401eba4c79SScott Long 			break;
6411eba4c79SScott Long 		}
6421eba4c79SScott Long 
6431eba4c79SScott Long 		req.status = csio->scsi_status;
6441eba4c79SScott Long 		req.masked_status = (csio->scsi_status >> 1) & 0x7f;
6451eba4c79SScott Long 		sg_scsiio_status(csio, &req.host_status, &req.driver_status);
6461eba4c79SScott Long 		req.resid = csio->resid;
6471eba4c79SScott Long 		req.duration = csio->ccb_h.timeout;
6481eba4c79SScott Long 		req.info = 0;
6491eba4c79SScott Long 
6501eba4c79SScott Long 		error = copyout(&req, arg, sizeof(req));
6511eba4c79SScott Long 		if ((error == 0) && (csio->ccb_h.status & CAM_AUTOSNS_VALID)
6521eba4c79SScott Long 		    && (req.sbp != NULL)) {
6531eba4c79SScott Long 			req.sb_len_wr = req.mx_sb_len - csio->sense_resid;
6541eba4c79SScott Long 			error = copyout(&csio->sense_data, req.sbp,
6551eba4c79SScott Long 					req.sb_len_wr);
6561eba4c79SScott Long 		}
6571eba4c79SScott Long 
6581eba4c79SScott Long 		xpt_release_ccb(ccb);
6591eba4c79SScott Long 		break;
6601eba4c79SScott Long 
6611eba4c79SScott Long 	case SG_GET_RESERVED_SIZE:
6621eba4c79SScott Long 	case LINUX_SG_GET_RESERVED_SIZE: {
6631eba4c79SScott Long 		int size = 32768;
6641eba4c79SScott Long 
6651eba4c79SScott Long 		error = copyout(&size, arg, sizeof(size));
6661eba4c79SScott Long 		break;
6671eba4c79SScott Long 	}
6681eba4c79SScott Long 
6691eba4c79SScott Long 	case SG_GET_SCSI_ID:
6701eba4c79SScott Long 	case LINUX_SG_GET_SCSI_ID:
6711eba4c79SScott Long 	{
6721eba4c79SScott Long 		struct sg_scsi_id id;
6731eba4c79SScott Long 
67475b06c87SMatt Jacob 		id.host_no = cam_sim_path(xpt_path_sim(periph->path));
6751eba4c79SScott Long 		id.channel = xpt_path_path_id(periph->path);
6761eba4c79SScott Long 		id.scsi_id = xpt_path_target_id(periph->path);
6771eba4c79SScott Long 		id.lun = xpt_path_lun_id(periph->path);
6781eba4c79SScott Long 		id.scsi_type = softc->pd_type;
6791eba4c79SScott Long 		id.h_cmd_per_lun = 1;
6801eba4c79SScott Long 		id.d_queue_depth = 1;
6811eba4c79SScott Long 		id.unused[0] = 0;
6821eba4c79SScott Long 		id.unused[1] = 0;
6831eba4c79SScott Long 
6841eba4c79SScott Long 		error = copyout(&id, arg, sizeof(id));
6851eba4c79SScott Long 		break;
6861eba4c79SScott Long 	}
6871eba4c79SScott Long 
6881eba4c79SScott Long 	case SG_EMULATED_HOST:
6891eba4c79SScott Long 	case SG_SET_TRANSFORM:
6901eba4c79SScott Long 	case SG_GET_TRANSFORM:
6911eba4c79SScott Long 	case SG_GET_NUM_WAITING:
6921eba4c79SScott Long 	case SG_SCSI_RESET:
6931eba4c79SScott Long 	case SG_GET_REQUEST_TABLE:
6941eba4c79SScott Long 	case SG_SET_KEEP_ORPHAN:
6951eba4c79SScott Long 	case SG_GET_KEEP_ORPHAN:
6961eba4c79SScott Long 	case SG_GET_ACCESS_COUNT:
6971eba4c79SScott Long 	case SG_SET_FORCE_LOW_DMA:
6981eba4c79SScott Long 	case SG_GET_LOW_DMA:
6991eba4c79SScott Long 	case SG_GET_SG_TABLESIZE:
7001eba4c79SScott Long 	case SG_SET_FORCE_PACK_ID:
7011eba4c79SScott Long 	case SG_GET_PACK_ID:
7021eba4c79SScott Long 	case SG_SET_RESERVED_SIZE:
7031eba4c79SScott Long 	case SG_GET_COMMAND_Q:
7041eba4c79SScott Long 	case SG_SET_COMMAND_Q:
7051eba4c79SScott Long 	case SG_SET_DEBUG:
7061eba4c79SScott Long 	case SG_NEXT_CMD_LEN:
7071eba4c79SScott Long 	case LINUX_SG_EMULATED_HOST:
7081eba4c79SScott Long 	case LINUX_SG_SET_TRANSFORM:
7091eba4c79SScott Long 	case LINUX_SG_GET_TRANSFORM:
7101eba4c79SScott Long 	case LINUX_SG_GET_NUM_WAITING:
7111eba4c79SScott Long 	case LINUX_SG_SCSI_RESET:
7121eba4c79SScott Long 	case LINUX_SG_GET_REQUEST_TABLE:
7131eba4c79SScott Long 	case LINUX_SG_SET_KEEP_ORPHAN:
7141eba4c79SScott Long 	case LINUX_SG_GET_KEEP_ORPHAN:
7151eba4c79SScott Long 	case LINUX_SG_GET_ACCESS_COUNT:
7161eba4c79SScott Long 	case LINUX_SG_SET_FORCE_LOW_DMA:
7171eba4c79SScott Long 	case LINUX_SG_GET_LOW_DMA:
7181eba4c79SScott Long 	case LINUX_SG_GET_SG_TABLESIZE:
7191eba4c79SScott Long 	case LINUX_SG_SET_FORCE_PACK_ID:
7201eba4c79SScott Long 	case LINUX_SG_GET_PACK_ID:
7211eba4c79SScott Long 	case LINUX_SG_SET_RESERVED_SIZE:
7221eba4c79SScott Long 	case LINUX_SG_GET_COMMAND_Q:
7231eba4c79SScott Long 	case LINUX_SG_SET_COMMAND_Q:
7241eba4c79SScott Long 	case LINUX_SG_SET_DEBUG:
7251eba4c79SScott Long 	case LINUX_SG_NEXT_CMD_LEN:
7261eba4c79SScott Long 	default:
7271eba4c79SScott Long #ifdef CAMDEBUG
7281eba4c79SScott Long 		printf("sgioctl: rejecting cmd 0x%lx\n", cmd);
7291eba4c79SScott Long #endif
7301eba4c79SScott Long 		error = ENODEV;
7311eba4c79SScott Long 		break;
7321eba4c79SScott Long 	}
7331eba4c79SScott Long 
7342b83592fSScott Long 	cam_periph_unlock(periph);
7351eba4c79SScott Long 	return (error);
7361eba4c79SScott Long }
7371eba4c79SScott Long 
7381eba4c79SScott Long static int
7391eba4c79SScott Long sgwrite(struct cdev *dev, struct uio *uio, int ioflag)
7401eba4c79SScott Long {
7411eba4c79SScott Long 	union ccb *ccb;
7421eba4c79SScott Long 	struct cam_periph *periph;
7431eba4c79SScott Long 	struct ccb_scsiio *csio;
7441eba4c79SScott Long 	struct sg_softc *sc;
7451eba4c79SScott Long 	struct sg_header *hdr;
7461eba4c79SScott Long 	struct sg_rdwr *rdwr;
7471eba4c79SScott Long 	u_char cdb_cmd;
7481eba4c79SScott Long 	char *buf;
7491eba4c79SScott Long 	int error = 0, cdb_len, buf_len, dir;
7501eba4c79SScott Long 
7511eba4c79SScott Long 	periph = dev->si_drv1;
7524400b36dSScott Long 	rdwr = malloc(sizeof(*rdwr), M_DEVBUF, M_WAITOK | M_ZERO);
7531eba4c79SScott Long 	hdr = &rdwr->hdr.hdr;
7541eba4c79SScott Long 
7551eba4c79SScott Long 	/* Copy in the header block and sanity check it */
7561eba4c79SScott Long 	if (uio->uio_resid < sizeof(*hdr)) {
7571eba4c79SScott Long 		error = EINVAL;
7581eba4c79SScott Long 		goto out_hdr;
7591eba4c79SScott Long 	}
7601eba4c79SScott Long 	error = uiomove(hdr, sizeof(*hdr), uio);
7611eba4c79SScott Long 	if (error)
7621eba4c79SScott Long 		goto out_hdr;
7631eba4c79SScott Long 
7648008a935SScott Long 	ccb = xpt_alloc_ccb();
7651eba4c79SScott Long 	if (ccb == NULL) {
7661eba4c79SScott Long 		error = ENOMEM;
7671eba4c79SScott Long 		goto out_hdr;
7681eba4c79SScott Long 	}
7691eba4c79SScott Long 	csio = &ccb->csio;
7701eba4c79SScott Long 
7711eba4c79SScott Long 	/*
7721eba4c79SScott Long 	 * Copy in the CDB block.  The designers of the interface didn't
7731eba4c79SScott Long 	 * bother to provide a size for this in the header, so we have to
7741eba4c79SScott Long 	 * figure it out ourselves.
7751eba4c79SScott Long 	 */
7761eba4c79SScott Long 	if (uio->uio_resid < 1)
7771eba4c79SScott Long 		goto out_ccb;
7781eba4c79SScott Long 	error = uiomove(&cdb_cmd, 1, uio);
7791eba4c79SScott Long 	if (error)
7801eba4c79SScott Long 		goto out_ccb;
7811eba4c79SScott Long 	if (hdr->twelve_byte)
7821eba4c79SScott Long 		cdb_len = 12;
7831eba4c79SScott Long 	else
7841eba4c79SScott Long 		cdb_len = scsi_group_len(cdb_cmd);
7851eba4c79SScott Long 	/*
7861eba4c79SScott Long 	 * We've already read the first byte of the CDB and advanced the uio
7871eba4c79SScott Long 	 * pointer.  Just read the rest.
7881eba4c79SScott Long 	 */
7891eba4c79SScott Long 	csio->cdb_io.cdb_bytes[0] = cdb_cmd;
7901eba4c79SScott Long 	error = uiomove(&csio->cdb_io.cdb_bytes[1], cdb_len - 1, uio);
7911eba4c79SScott Long 	if (error)
7921eba4c79SScott Long 		goto out_ccb;
7931eba4c79SScott Long 
7941eba4c79SScott Long 	/*
7951eba4c79SScott Long 	 * Now set up the data block.  Again, the designers didn't bother
7961eba4c79SScott Long 	 * to make this reliable.
7971eba4c79SScott Long 	 */
7981eba4c79SScott Long 	buf_len = uio->uio_resid;
7991eba4c79SScott Long 	if (buf_len != 0) {
8004400b36dSScott Long 		buf = malloc(buf_len, M_DEVBUF, M_WAITOK | M_ZERO);
8011eba4c79SScott Long 		error = uiomove(buf, buf_len, uio);
8021eba4c79SScott Long 		if (error)
8031eba4c79SScott Long 			goto out_buf;
8041eba4c79SScott Long 		dir = CAM_DIR_OUT;
8051eba4c79SScott Long 	} else if (hdr->reply_len != 0) {
8064400b36dSScott Long 		buf = malloc(hdr->reply_len, M_DEVBUF, M_WAITOK | M_ZERO);
8071eba4c79SScott Long 		buf_len = hdr->reply_len;
8081eba4c79SScott Long 		dir = CAM_DIR_IN;
8091eba4c79SScott Long 	} else {
8101eba4c79SScott Long 		buf = NULL;
8111eba4c79SScott Long 		buf_len = 0;
8121eba4c79SScott Long 		dir = CAM_DIR_NONE;
8131eba4c79SScott Long 	}
8141eba4c79SScott Long 
8152b83592fSScott Long 	cam_periph_lock(periph);
8162b83592fSScott Long 	sc = periph->softc;
8171e637ba6SAlexander Motin 	xpt_setup_ccb(&ccb->ccb_h, periph->path, CAM_PRIORITY_NORMAL);
8181eba4c79SScott Long 	cam_fill_csio(csio,
8191eba4c79SScott Long 		      /*retries*/1,
8201eba4c79SScott Long 		      sgdone,
8211eba4c79SScott Long 		      dir|CAM_DEV_QFRZDIS,
8221eba4c79SScott Long 		      MSG_SIMPLE_Q_TAG,
8231eba4c79SScott Long 		      buf,
8241eba4c79SScott Long 		      buf_len,
8251eba4c79SScott Long 		      SG_MAX_SENSE,
8261eba4c79SScott Long 		      cdb_len,
827715ab212SScott Long 		      sc->sg_timeout);
8281eba4c79SScott Long 
8291eba4c79SScott Long 	/*
8301eba4c79SScott Long 	 * Send off the command and hope that it works. This path does not
8311eba4c79SScott Long 	 * go through sgstart because the I/O is supposed to be asynchronous.
8321eba4c79SScott Long 	 */
8331eba4c79SScott Long 	rdwr->buf = buf;
8341eba4c79SScott Long 	rdwr->buf_len = buf_len;
8351eba4c79SScott Long 	rdwr->tag = hdr->pack_id;
8361eba4c79SScott Long 	rdwr->ccb = ccb;
8371eba4c79SScott Long 	rdwr->state = SG_RDWR_INPROG;
8381eba4c79SScott Long 	ccb->ccb_h.ccb_rdwr = rdwr;
8391eba4c79SScott Long 	ccb->ccb_h.ccb_type = SG_CCB_RDWR_IO;
8401eba4c79SScott Long 	TAILQ_INSERT_TAIL(&sc->rdwr_done, rdwr, rdwr_link);
8412b83592fSScott Long 	error = sgsendrdwr(periph, ccb);
8422b83592fSScott Long 	cam_periph_unlock(periph);
8432b83592fSScott Long 	return (error);
8441eba4c79SScott Long 
8451eba4c79SScott Long out_buf:
8461eba4c79SScott Long 	free(buf, M_DEVBUF);
8471eba4c79SScott Long out_ccb:
8481eba4c79SScott Long 	xpt_free_ccb(ccb);
8491eba4c79SScott Long out_hdr:
8501eba4c79SScott Long 	free(rdwr, M_DEVBUF);
8511eba4c79SScott Long 	return (error);
8521eba4c79SScott Long }
8531eba4c79SScott Long 
8541eba4c79SScott Long static int
8551eba4c79SScott Long sgread(struct cdev *dev, struct uio *uio, int ioflag)
8561eba4c79SScott Long {
8571eba4c79SScott Long 	struct ccb_scsiio *csio;
8581eba4c79SScott Long 	struct cam_periph *periph;
8591eba4c79SScott Long 	struct sg_softc *sc;
8601eba4c79SScott Long 	struct sg_header *hdr;
8611eba4c79SScott Long 	struct sg_rdwr *rdwr;
8621eba4c79SScott Long 	u_short hstat, dstat;
8631eba4c79SScott Long 	int error, pack_len, reply_len, pack_id;
8641eba4c79SScott Long 
8651eba4c79SScott Long 	periph = dev->si_drv1;
8661eba4c79SScott Long 
8671eba4c79SScott Long 	/* XXX The pack len field needs to be updated and written out instead
8681eba4c79SScott Long 	 * of discarded.  Not sure how to do that.
8691eba4c79SScott Long 	 */
8701eba4c79SScott Long 	uio->uio_rw = UIO_WRITE;
8711eba4c79SScott Long 	if ((error = uiomove(&pack_len, 4, uio)) != 0)
8721eba4c79SScott Long 		return (error);
8731eba4c79SScott Long 	if ((error = uiomove(&reply_len, 4, uio)) != 0)
8741eba4c79SScott Long 		return (error);
8751eba4c79SScott Long 	if ((error = uiomove(&pack_id, 4, uio)) != 0)
8761eba4c79SScott Long 		return (error);
8771eba4c79SScott Long 	uio->uio_rw = UIO_READ;
8781eba4c79SScott Long 
8792b83592fSScott Long 	cam_periph_lock(periph);
8802b83592fSScott Long 	sc = periph->softc;
8811eba4c79SScott Long search:
8821eba4c79SScott Long 	TAILQ_FOREACH(rdwr, &sc->rdwr_done, rdwr_link) {
8831eba4c79SScott Long 		if (rdwr->tag == pack_id)
8841eba4c79SScott Long 			break;
8851eba4c79SScott Long 	}
8861eba4c79SScott Long 	if ((rdwr == NULL) || (rdwr->state != SG_RDWR_DONE)) {
8872b83592fSScott Long 		if (msleep(rdwr, periph->sim->mtx, PCATCH, "sgread", 0) == ERESTART)
8881eba4c79SScott Long 			return (EAGAIN);
8891eba4c79SScott Long 		goto search;
8901eba4c79SScott Long 	}
8911eba4c79SScott Long 	TAILQ_REMOVE(&sc->rdwr_done, rdwr, rdwr_link);
8922b83592fSScott Long 	cam_periph_unlock(periph);
8931eba4c79SScott Long 
8941eba4c79SScott Long 	hdr = &rdwr->hdr.hdr;
8951eba4c79SScott Long 	csio = &rdwr->ccb->csio;
8961eba4c79SScott Long 	sg_scsiio_status(csio, &hstat, &dstat);
8971eba4c79SScott Long 	hdr->host_status = hstat;
8981eba4c79SScott Long 	hdr->driver_status = dstat;
8991eba4c79SScott Long 	hdr->target_status = csio->scsi_status >> 1;
9001eba4c79SScott Long 
9011eba4c79SScott Long 	switch (hstat) {
9021eba4c79SScott Long 	case DID_OK:
9031eba4c79SScott Long 	case DID_PASSTHROUGH:
9041eba4c79SScott Long 	case DID_SOFT_ERROR:
9051eba4c79SScott Long 		hdr->result = 0;
9061eba4c79SScott Long 		break;
9071eba4c79SScott Long 	case DID_NO_CONNECT:
9081eba4c79SScott Long 	case DID_BUS_BUSY:
9091eba4c79SScott Long 	case DID_TIME_OUT:
9101eba4c79SScott Long 		hdr->result = EBUSY;
9111eba4c79SScott Long 		break;
9121eba4c79SScott Long 	case DID_BAD_TARGET:
9131eba4c79SScott Long 	case DID_ABORT:
9141eba4c79SScott Long 	case DID_PARITY:
9151eba4c79SScott Long 	case DID_RESET:
9161eba4c79SScott Long 	case DID_BAD_INTR:
9171eba4c79SScott Long 	case DID_ERROR:
9181eba4c79SScott Long 	default:
9191eba4c79SScott Long 		hdr->result = EIO;
9201eba4c79SScott Long 		break;
9211eba4c79SScott Long 	}
9221eba4c79SScott Long 
9231eba4c79SScott Long 	if (dstat == DRIVER_SENSE) {
9241eba4c79SScott Long 		bcopy(&csio->sense_data, hdr->sense_buffer,
9251eba4c79SScott Long 		      min(csio->sense_len, SG_MAX_SENSE));
9261eba4c79SScott Long #ifdef CAMDEBUG
9271eba4c79SScott Long 		scsi_sense_print(csio);
9281eba4c79SScott Long #endif
9291eba4c79SScott Long 	}
9301eba4c79SScott Long 
9311eba4c79SScott Long 	error = uiomove(&hdr->result, sizeof(*hdr) -
9321eba4c79SScott Long 			offsetof(struct sg_header, result), uio);
9331eba4c79SScott Long 	if ((error == 0) && (hdr->result == 0))
9341eba4c79SScott Long 		error = uiomove(rdwr->buf, rdwr->buf_len, uio);
9351eba4c79SScott Long 
9362b83592fSScott Long 	cam_periph_lock(periph);
9371eba4c79SScott Long 	xpt_free_ccb(rdwr->ccb);
9382b83592fSScott Long 	cam_periph_unlock(periph);
9391eba4c79SScott Long 	free(rdwr->buf, M_DEVBUF);
9401eba4c79SScott Long 	free(rdwr, M_DEVBUF);
9411eba4c79SScott Long 	return (error);
9421eba4c79SScott Long }
9431eba4c79SScott Long 
9441eba4c79SScott Long static int
9451eba4c79SScott Long sgsendccb(struct cam_periph *periph, union ccb *ccb)
9461eba4c79SScott Long {
9471eba4c79SScott Long 	struct sg_softc *softc;
9481eba4c79SScott Long 	struct cam_periph_map_info mapinfo;
9491eba4c79SScott Long 	int error, need_unmap = 0;
9501eba4c79SScott Long 
9511eba4c79SScott Long 	softc = periph->softc;
9521eba4c79SScott Long 	if (((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE)
9531eba4c79SScott Long 	    && (ccb->csio.data_ptr != NULL)) {
9541eba4c79SScott Long 		bzero(&mapinfo, sizeof(mapinfo));
9552b83592fSScott Long 
9562b83592fSScott Long 		/*
9572b83592fSScott Long 		 * cam_periph_mapmem calls into proc and vm functions that can
9582b83592fSScott Long 		 * sleep as well as trigger I/O, so we can't hold the lock.
9592b83592fSScott Long 		 * Dropping it here is reasonably safe.
9602b83592fSScott Long 		 */
9612b83592fSScott Long 		cam_periph_unlock(periph);
9621eba4c79SScott Long 		error = cam_periph_mapmem(ccb, &mapinfo);
9632b83592fSScott Long 		cam_periph_lock(periph);
9641eba4c79SScott Long 		if (error)
9651eba4c79SScott Long 			return (error);
9661eba4c79SScott Long 		need_unmap = 1;
9671eba4c79SScott Long 	}
9681eba4c79SScott Long 
9691eba4c79SScott Long 	error = cam_periph_runccb(ccb,
9701eba4c79SScott Long 				  sgerror,
9711eba4c79SScott Long 				  CAM_RETRY_SELTO,
9721eba4c79SScott Long 				  SF_RETRY_UA,
9731eba4c79SScott Long 				  softc->device_stats);
9741eba4c79SScott Long 
9751eba4c79SScott Long 	if (need_unmap)
9761eba4c79SScott Long 		cam_periph_unmapmem(ccb, &mapinfo);
9771eba4c79SScott Long 
9781eba4c79SScott Long 	return (error);
9791eba4c79SScott Long }
9801eba4c79SScott Long 
9811eba4c79SScott Long static int
9821eba4c79SScott Long sgsendrdwr(struct cam_periph *periph, union ccb *ccb)
9831eba4c79SScott Long {
9841eba4c79SScott Long 	struct sg_softc *softc;
9851eba4c79SScott Long 
9861eba4c79SScott Long 	softc = periph->softc;
9871eba4c79SScott Long 	devstat_start_transaction(softc->device_stats, NULL);
9881eba4c79SScott Long 	xpt_action(ccb);
9891eba4c79SScott Long 	return (0);
9901eba4c79SScott Long }
9911eba4c79SScott Long 
9921eba4c79SScott Long static int
9931eba4c79SScott Long sgerror(union ccb *ccb, uint32_t cam_flags, uint32_t sense_flags)
9941eba4c79SScott Long {
9951eba4c79SScott Long 	struct cam_periph *periph;
9961eba4c79SScott Long 	struct sg_softc *softc;
9971eba4c79SScott Long 
9981eba4c79SScott Long 	periph = xpt_path_periph(ccb->ccb_h.path);
9991eba4c79SScott Long 	softc = (struct sg_softc *)periph->softc;
10001eba4c79SScott Long 
10011eba4c79SScott Long 	return (cam_periph_error(ccb, cam_flags, sense_flags,
10021eba4c79SScott Long 				 &softc->saved_ccb));
10031eba4c79SScott Long }
10041eba4c79SScott Long 
10051eba4c79SScott Long static void
10061eba4c79SScott Long sg_scsiio_status(struct ccb_scsiio *csio, u_short *hoststat, u_short *drvstat)
10071eba4c79SScott Long {
10081eba4c79SScott Long 	int status;
10091eba4c79SScott Long 
10101eba4c79SScott Long 	status = csio->ccb_h.status;
10111eba4c79SScott Long 
10121eba4c79SScott Long 	switch (status & CAM_STATUS_MASK) {
10131eba4c79SScott Long 	case CAM_REQ_CMP:
10141eba4c79SScott Long 		*hoststat = DID_OK;
10151eba4c79SScott Long 		*drvstat = 0;
10161eba4c79SScott Long 		break;
10171eba4c79SScott Long 	case CAM_REQ_CMP_ERR:
10181eba4c79SScott Long 		*hoststat = DID_ERROR;
10191eba4c79SScott Long 		*drvstat = 0;
10201eba4c79SScott Long 		break;
10211eba4c79SScott Long 	case CAM_REQ_ABORTED:
10221eba4c79SScott Long 		*hoststat = DID_ABORT;
10231eba4c79SScott Long 		*drvstat = 0;
10241eba4c79SScott Long 		break;
10251eba4c79SScott Long 	case CAM_REQ_INVALID:
10261eba4c79SScott Long 		*hoststat = DID_ERROR;
10271eba4c79SScott Long 		*drvstat = DRIVER_INVALID;
10281eba4c79SScott Long 		break;
10291eba4c79SScott Long 	case CAM_DEV_NOT_THERE:
10301eba4c79SScott Long 		*hoststat = DID_BAD_TARGET;
10311eba4c79SScott Long 		*drvstat = 0;
10324fee613eSEdward Tomasz Napierala 		break;
10331eba4c79SScott Long 	case CAM_SEL_TIMEOUT:
10341eba4c79SScott Long 		*hoststat = DID_NO_CONNECT;
10351eba4c79SScott Long 		*drvstat = 0;
10361eba4c79SScott Long 		break;
10371eba4c79SScott Long 	case CAM_CMD_TIMEOUT:
10381eba4c79SScott Long 		*hoststat = DID_TIME_OUT;
10391eba4c79SScott Long 		*drvstat = 0;
10401eba4c79SScott Long 		break;
10411eba4c79SScott Long 	case CAM_SCSI_STATUS_ERROR:
10421eba4c79SScott Long 		*hoststat = DID_ERROR;
10431eba4c79SScott Long 		*drvstat = 0;
10440c70e307SEdward Tomasz Napierala 		break;
10451eba4c79SScott Long 	case CAM_SCSI_BUS_RESET:
10461eba4c79SScott Long 		*hoststat = DID_RESET;
10471eba4c79SScott Long 		*drvstat = 0;
10481eba4c79SScott Long 		break;
10491eba4c79SScott Long 	case CAM_UNCOR_PARITY:
10501eba4c79SScott Long 		*hoststat = DID_PARITY;
10511eba4c79SScott Long 		*drvstat = 0;
10521eba4c79SScott Long 		break;
10531eba4c79SScott Long 	case CAM_SCSI_BUSY:
10541eba4c79SScott Long 		*hoststat = DID_BUS_BUSY;
10551eba4c79SScott Long 		*drvstat = 0;
10560c70e307SEdward Tomasz Napierala 		break;
10571eba4c79SScott Long 	default:
10581eba4c79SScott Long 		*hoststat = DID_ERROR;
10591eba4c79SScott Long 		*drvstat = DRIVER_ERROR;
10601eba4c79SScott Long 	}
10611eba4c79SScott Long 
10621eba4c79SScott Long 	if (status & CAM_AUTOSNS_VALID)
10631eba4c79SScott Long 		*drvstat = DRIVER_SENSE;
10641eba4c79SScott Long }
10651eba4c79SScott Long 
10661eba4c79SScott Long static int
10671eba4c79SScott Long scsi_group_len(u_char cmd)
10681eba4c79SScott Long {
10691eba4c79SScott Long 	int len[] = {6, 10, 10, 12, 12, 12, 10, 10};
10701eba4c79SScott Long 	int group;
10711eba4c79SScott Long 
10721eba4c79SScott Long 	group = (cmd >> 5) & 0x7;
10731eba4c79SScott Long 	return (len[group]);
10741eba4c79SScott Long }
10751eba4c79SScott Long 
1076