scsi_sg.c revision 268255
1145132Sanholt/*-
2145132Sanholt * Copyright (c) 2007 Scott Long
3145132Sanholt * All rights reserved.
4145132Sanholt *
5145132Sanholt * Redistribution and use in source and binary forms, with or without
6145132Sanholt * modification, are permitted provided that the following conditions
7145132Sanholt * are met:
8145132Sanholt * 1. Redistributions of source code must retain the above copyright
9145132Sanholt *    notice, this list of conditions, and the following disclaimer,
10145132Sanholt *    without modification, immediately at the beginning of the file.
11145132Sanholt * 2. The name of the author may not be used to endorse or promote products
12145132Sanholt *    derived from this software without specific prior written permission.
13145132Sanholt *
14145132Sanholt * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15145132Sanholt * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16145132Sanholt * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17145132Sanholt * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
18145132Sanholt * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19145132Sanholt * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20145132Sanholt * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21145132Sanholt * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22145132Sanholt * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23145132Sanholt * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24145132Sanholt * SUCH DAMAGE.
25145132Sanholt */
26145132Sanholt
27145132Sanholt/*
28145132Sanholt * scsi_sg peripheral driver.  This driver is meant to implement the Linux
29145132Sanholt * SG passthrough interface for SCSI.
30145132Sanholt */
31152909Sanholt
32152909Sanholt#include <sys/cdefs.h>
33152909Sanholt__FBSDID("$FreeBSD: stable/10/sys/cam/scsi/scsi_sg.c 268255 2014-07-04 15:09:56Z mav $");
34182080Srnoland
35182080Srnoland#include <sys/param.h>
36182080Srnoland#include <sys/systm.h>
37182080Srnoland#include <sys/kernel.h>
38182080Srnoland#include <sys/types.h>
39182080Srnoland#include <sys/bio.h>
40182080Srnoland#include <sys/malloc.h>
41145132Sanholt#include <sys/fcntl.h>
42145132Sanholt#include <sys/ioccom.h>
43152909Sanholt#include <sys/conf.h>
44145132Sanholt#include <sys/errno.h>
45153013Sanholt#include <sys/devicestat.h>
46153013Sanholt#include <sys/proc.h>
47153013Sanholt#include <sys/uio.h>
48145132Sanholt
49153013Sanholt#include <cam/cam.h>
50145132Sanholt#include <cam/cam_ccb.h>
51182080Srnoland#include <cam/cam_periph.h>
52182080Srnoland#include <cam/cam_queue.h>
53152909Sanholt#include <cam/cam_xpt_periph.h>
54152909Sanholt#include <cam/cam_debug.h>
55145132Sanholt#include <cam/cam_sim.h>
56145132Sanholt
57145132Sanholt#include <cam/scsi/scsi_all.h>
58145132Sanholt#include <cam/scsi/scsi_message.h>
59145132Sanholt#include <cam/scsi/scsi_sg.h>
60145132Sanholt
61145132Sanholt#include <compat/linux/linux_ioctl.h>
62182080Srnoland
63182080Srnolandtypedef enum {
64182080Srnoland	SG_FLAG_LOCKED		= 0x01,
65182080Srnoland	SG_FLAG_INVALID		= 0x02
66182080Srnoland} sg_flags;
67182080Srnoland
68182080Srnolandtypedef enum {
69182080Srnoland	SG_STATE_NORMAL
70145132Sanholt} sg_state;
71182080Srnoland
72182080Srnolandtypedef enum {
73182080Srnoland	SG_RDWR_FREE,
74182080Srnoland	SG_RDWR_INPROG,
75145132Sanholt	SG_RDWR_DONE
76182080Srnoland} sg_rdwr_state;
77182080Srnoland
78145132Sanholttypedef enum {
79182080Srnoland	SG_CCB_RDWR_IO
80182080Srnoland} sg_ccb_types;
81145132Sanholt
82189099Srnoland#define ccb_type	ppriv_field0
83182080Srnoland#define ccb_rdwr	ppriv_ptr1
84182080Srnoland
85182080Srnolandstruct sg_rdwr {
86182080Srnoland	TAILQ_ENTRY(sg_rdwr)	rdwr_link;
87182080Srnoland	int			tag;
88182080Srnoland	int			state;
89145132Sanholt	int			buf_len;
90182080Srnoland	char			*buf;
91182080Srnoland	union ccb		*ccb;
92145132Sanholt	union {
93182080Srnoland		struct sg_header hdr;
94182080Srnoland		struct sg_io_hdr io_hdr;
95189099Srnoland	} hdr;
96182080Srnoland};
97145132Sanholt
98189099Srnolandstruct sg_softc {
99189099Srnoland	sg_state		state;
100182080Srnoland	sg_flags		flags;
101182080Srnoland	int			open_count;
102182080Srnoland	struct devstat		*device_stats;
103182080Srnoland	TAILQ_HEAD(, sg_rdwr)	rdwr_done;
104145132Sanholt	struct cdev		*dev;
105182080Srnoland	int			sg_timeout;
106145132Sanholt	int			sg_user_timeout;
107182080Srnoland	uint8_t			pd_type;
108182080Srnoland	union ccb		saved_ccb;
109182080Srnoland};
110182080Srnoland
111182080Srnolandstatic d_open_t		sgopen;
112182080Srnolandstatic d_close_t	sgclose;
113182080Srnolandstatic d_ioctl_t	sgioctl;
114182080Srnolandstatic d_write_t	sgwrite;
115145132Sanholtstatic d_read_t		sgread;
116182080Srnoland
117182080Srnolandstatic periph_init_t	sginit;
118182080Srnolandstatic periph_ctor_t	sgregister;
119182080Srnolandstatic periph_oninv_t	sgoninvalidate;
120182080Srnolandstatic periph_dtor_t	sgcleanup;
121145132Sanholtstatic void		sgasync(void *callback_arg, uint32_t code,
122145132Sanholt				struct cam_path *path, void *arg);
123145132Sanholtstatic void		sgdone(struct cam_periph *periph, union ccb *done_ccb);
124145132Sanholtstatic int		sgsendccb(struct cam_periph *periph, union ccb *ccb);
125145132Sanholtstatic int		sgsendrdwr(struct cam_periph *periph, union ccb *ccb);
126145132Sanholtstatic int		sgerror(union ccb *ccb, uint32_t cam_flags,
127145132Sanholt				uint32_t sense_flags);
128145132Sanholtstatic void		sg_scsiio_status(struct ccb_scsiio *csio,
129145132Sanholt					 u_short *hoststat, u_short *drvstat);
130145132Sanholt
131189053Srnolandstatic int		scsi_group_len(u_char cmd);
132145132Sanholt
133145132Sanholtstatic struct periph_driver sgdriver =
134191274Srnoland{
135189128Srnoland	sginit, "sg",
136196466Srnoland	TAILQ_HEAD_INITIALIZER(sgdriver.units), /* gen */ 0
137196466Srnoland};
138196466SrnolandPERIPHDRIVER_DECLARE(sg, sgdriver);
139189128Srnoland
140189052Srnolandstatic struct cdevsw sg_cdevsw = {
141189052Srnoland	.d_version =	D_VERSION,
142189052Srnoland	.d_flags =	D_NEEDGIANT | D_TRACKCLOSE,
143189052Srnoland	.d_open =	sgopen,
144189052Srnoland	.d_close =	sgclose,
145189052Srnoland	.d_ioctl =	sgioctl,
146189052Srnoland	.d_write =	sgwrite,
147189052Srnoland	.d_read =	sgread,
148189052Srnoland	.d_name =	"sg",
149189052Srnoland};
150189052Srnoland
151189052Srnolandstatic int sg_version = 30125;
152189052Srnoland
153189052Srnolandstatic void
154189052Srnolandsginit(void)
155189052Srnoland{
156189052Srnoland	cam_status status;
157189052Srnoland
158189052Srnoland	/*
159189052Srnoland	 * Install a global async callback.  This callback will receive aync
160189052Srnoland	 * callbacks like "new device found".
161189563Srnoland	 */
162145132Sanholt	status = xpt_register_async(AC_FOUND_DEVICE, sgasync, NULL, NULL);
163152909Sanholt
164145132Sanholt	if (status != CAM_REQ_CMP) {
165153579Sjhb		printf("sg: Failed to attach master async callbac "
166153033Sanholt			"due to status 0x%x!\n", status);
167145132Sanholt	}
168189563Srnoland}
169189563Srnoland
170153033Sanholtstatic void
171189563Srnolandsgdevgonecb(void *arg)
172153033Sanholt{
173153033Sanholt	struct cam_periph *periph;
174153579Sjhb	struct sg_softc *softc;
175189563Srnoland	struct mtx *mtx;
176189563Srnoland	int i;
177153579Sjhb
178145132Sanholt	periph = (struct cam_periph *)arg;
179189563Srnoland	mtx = cam_periph_mtx(periph);
180189563Srnoland	mtx_lock(mtx);
181186299Srnoland
182186299Srnoland	softc = (struct sg_softc *)periph->softc;
183152909Sanholt	KASSERT(softc->open_count >= 0, ("Negative open count %d",
184152909Sanholt		softc->open_count));
185189915Srnoland
186189915Srnoland	/*
187189915Srnoland	 * When we get this callback, we will get no more close calls from
188189915Srnoland	 * devfs.  So if we have any dangling opens, we need to release the
189145132Sanholt	 * reference held for that particular context.
190145132Sanholt	 */
191145132Sanholt	for (i = 0; i < softc->open_count; i++)
192145132Sanholt		cam_periph_release_locked(periph);
193145132Sanholt
194145132Sanholt	softc->open_count = 0;
195189563Srnoland
196145132Sanholt	/*
197182080Srnoland	 * Release the reference held for the device node, it is gone now.
198152909Sanholt	 */
199189052Srnoland	cam_periph_release_locked(periph);
200152909Sanholt
201189563Srnoland	/*
202189563Srnoland	 * We reference the lock directly here, instead of using
203152909Sanholt	 * cam_periph_unlock().  The reason is that the final call to
204153579Sjhb	 * cam_periph_release_locked() above could result in the periph
205189563Srnoland	 * getting freed.  If that is the case, dereferencing the periph
206189563Srnoland	 * with a cam_periph_unlock() call would cause a page fault.
207152909Sanholt	 */
208189563Srnoland	mtx_unlock(mtx);
209153579Sjhb}
210189563Srnoland
211153579Sjhb
212152909Sanholtstatic void
213196465Srnolandsgoninvalidate(struct cam_periph *periph)
214152909Sanholt{
215152909Sanholt	struct sg_softc *softc;
216152909Sanholt
217152909Sanholt	softc = (struct sg_softc *)periph->softc;
218196465Srnoland
219183573Srnoland	/*
220189052Srnoland	 * Deregister any async callbacks.
221189052Srnoland	 */
222189052Srnoland	xpt_register_async(0, sgasync, periph, periph->path);
223189052Srnoland
224189052Srnoland	softc->flags |= SG_FLAG_INVALID;
225189052Srnoland
226189052Srnoland	/*
227189052Srnoland	 * Tell devfs this device has gone away, and ask for a callback
228189052Srnoland	 * when it has cleaned up its state.
229189052Srnoland	 */
230189052Srnoland	destroy_dev_sched_cb(softc->dev, sgdevgonecb, periph);
231189052Srnoland
232191274Srnoland	/*
233191274Srnoland	 * XXX Return all queued I/O with ENXIO.
234191274Srnoland	 * XXX Handle any transactions queued to the card
235191274Srnoland	 *     with XPT_ABORT_CCB.
236191274Srnoland	 */
237191274Srnoland
238191274Srnoland}
239189052Srnoland
240191274Srnolandstatic void
241191274Srnolandsgcleanup(struct cam_periph *periph)
242191274Srnoland{
243191274Srnoland	struct sg_softc *softc;
244191274Srnoland
245191274Srnoland	softc = (struct sg_softc *)periph->softc;
246189052Srnoland
247189052Srnoland	devstat_remove_entry(softc->device_stats);
248191274Srnoland
249191274Srnoland	free(softc, M_DEVBUF);
250191274Srnoland}
251191274Srnoland
252191274Srnolandstatic void
253191274Srnolandsgasync(void *callback_arg, uint32_t code, struct cam_path *path, void *arg)
254191274Srnoland{
255189052Srnoland	struct cam_periph *periph;
256189052Srnoland
257182080Srnoland	periph = (struct cam_periph *)callback_arg;
258182080Srnoland
259182080Srnoland	switch (code) {
260182080Srnoland	case AC_FOUND_DEVICE:
261152909Sanholt	{
262189052Srnoland		struct ccb_getdev *cgd;
263189052Srnoland		cam_status status;
264152909Sanholt
265152909Sanholt		cgd = (struct ccb_getdev *)arg;
266152909Sanholt		if (cgd == NULL)
267145132Sanholt			break;
268145132Sanholt
269189563Srnoland		if (cgd->protocol != PROTO_SCSI)
270145132Sanholt			break;
271189052Srnoland
272189052Srnoland		/*
273189563Srnoland		 * Allocate a peripheral instance for this device and
274189052Srnoland		 * start the probe process.
275189052Srnoland		 */
276189052Srnoland		status = cam_periph_alloc(sgregister, sgoninvalidate,
277194749Srnoland					  sgcleanup, NULL, "sg",
278194749Srnoland					  CAM_PERIPH_BIO, path,
279194749Srnoland					  sgasync, AC_FOUND_DEVICE, cgd);
280189052Srnoland		if ((status != CAM_REQ_CMP) && (status != CAM_REQ_INPROG)) {
281194749Srnoland			const struct cam_status_entry *entry;
282194749Srnoland
283194749Srnoland			entry = cam_fetch_status_entry(status);
284194749Srnoland			printf("sgasync: Unable to attach new device "
285189052Srnoland				"due to status %#x: %s\n", status, entry ?
286189052Srnoland				entry->status_text : "Unknown");
287145132Sanholt		}
288145132Sanholt		break;
289145132Sanholt	}
290145132Sanholt	default:
291145132Sanholt		cam_periph_async(periph, code, path, arg);
292145132Sanholt		break;
293145132Sanholt	}
294145132Sanholt}
295145132Sanholt
296152909Sanholtstatic cam_status
297152909Sanholtsgregister(struct cam_periph *periph, void *arg)
298152909Sanholt{
299145132Sanholt	struct sg_softc *softc;
300145132Sanholt	struct ccb_getdev *cgd;
301145132Sanholt	struct ccb_pathinq cpi;
302145132Sanholt	int no_tags;
303189915Srnoland
304189915Srnoland	cgd = (struct ccb_getdev *)arg;
305152909Sanholt	if (cgd == NULL) {
306145132Sanholt		printf("sgregister: no getdev CCB, can't register device\n");
307145132Sanholt		return (CAM_REQ_CMP_ERR);
308145132Sanholt	}
309145132Sanholt
310145132Sanholt	softc = malloc(sizeof(*softc), M_DEVBUF, M_ZERO | M_NOWAIT);
311182080Srnoland	if (softc == NULL) {
312145132Sanholt		printf("sgregister: Unable to allocate softc\n");
313152909Sanholt		return (CAM_REQ_CMP_ERR);
314145132Sanholt	}
315145132Sanholt
316145132Sanholt	softc->state = SG_STATE_NORMAL;
317145132Sanholt	softc->pd_type = SID_TYPE(&cgd->inq_data);
318152909Sanholt	softc->sg_timeout = SG_DEFAULT_TIMEOUT / SG_DEFAULT_HZ * hz;
319152909Sanholt	softc->sg_user_timeout = SG_DEFAULT_TIMEOUT;
320183573Srnoland	TAILQ_INIT(&softc->rdwr_done);
321152909Sanholt	periph->softc = softc;
322152909Sanholt
323145132Sanholt	bzero(&cpi, sizeof(cpi));
324183573Srnoland	xpt_setup_ccb(&cpi.ccb_h, periph->path, CAM_PRIORITY_NORMAL);
325183573Srnoland	cpi.ccb_h.func_code = XPT_PATH_INQ;
326152909Sanholt	xpt_action((union ccb *)&cpi);
327145132Sanholt
328145132Sanholt	/*
329183573Srnoland	 * We pass in 0 for all blocksize, since we don't know what the
330145132Sanholt	 * blocksize of the device is, if it even has a blocksize.
331145132Sanholt	 */
332145132Sanholt	cam_periph_unlock(periph);
333145132Sanholt	no_tags = (cgd->inq_data.flags & SID_CmdQue) == 0;
334145132Sanholt	softc->device_stats = devstat_new_entry("sg",
335183573Srnoland			periph->unit_number, 0,
336145132Sanholt			DEVSTAT_NO_BLOCKSIZE
337145132Sanholt			| (no_tags ? DEVSTAT_NO_ORDERED_TAGS : 0),
338145132Sanholt			softc->pd_type |
339145132Sanholt			XPORT_DEVSTAT_TYPE(cpi.transport) |
340145132Sanholt			DEVSTAT_TYPE_PASS,
341145132Sanholt			DEVSTAT_PRIORITY_PASS);
342145132Sanholt
343145132Sanholt	/*
344145132Sanholt	 * Acquire a reference to the periph before we create the devfs
345145132Sanholt	 * instance for it.  We'll release this reference once the devfs
346145132Sanholt	 * instance has been freed.
347145132Sanholt	 */
348183573Srnoland	if (cam_periph_acquire(periph) != CAM_REQ_CMP) {
349145132Sanholt		xpt_print(periph->path, "%s: lost periph during "
350145132Sanholt			  "registration!\n", __func__);
351145132Sanholt		cam_periph_lock(periph);
352145132Sanholt		return (CAM_REQ_CMP_ERR);
353182080Srnoland	}
354145132Sanholt
355145132Sanholt	/* Register the device */
356145132Sanholt	softc->dev = make_dev(&sg_cdevsw, periph->unit_number,
357145132Sanholt			      UID_ROOT, GID_OPERATOR, 0600, "%s%d",
358145132Sanholt			      periph->periph_name, periph->unit_number);
359145132Sanholt	if (periph->unit_number < 26) {
360145132Sanholt		(void)make_dev_alias(softc->dev, "sg%c",
361183573Srnoland		    periph->unit_number + 'a');
362145132Sanholt	} else {
363183573Srnoland		(void)make_dev_alias(softc->dev, "sg%c%c",
364183573Srnoland		    ((periph->unit_number / 26) - 1) + 'a',
365145132Sanholt		    (periph->unit_number % 26) + 'a');
366145132Sanholt	}
367145132Sanholt	cam_periph_lock(periph);
368145132Sanholt	softc->dev->si_drv1 = periph;
369183573Srnoland
370183833Srnoland	/*
371145132Sanholt	 * Add as async callback so that we get
372145132Sanholt	 * notified if this device goes away.
373145132Sanholt	 */
374183573Srnoland	xpt_register_async(AC_LOST_DEVICE, sgasync, periph, periph->path);
375183573Srnoland
376183573Srnoland	if (bootverbose)
377145132Sanholt		xpt_announce_periph(periph, NULL);
378183833Srnoland
379145132Sanholt	return (CAM_REQ_CMP);
380145132Sanholt}
381145132Sanholt
382145132Sanholtstatic void
383182467Srnolandsgdone(struct cam_periph *periph, union ccb *done_ccb)
384182080Srnoland{
385182467Srnoland	struct sg_softc *softc;
386182080Srnoland	struct ccb_scsiio *csio;
387183573Srnoland
388183573Srnoland	softc = (struct sg_softc *)periph->softc;
389145132Sanholt	csio = &done_ccb->csio;
390145132Sanholt	switch (csio->ccb_h.ccb_type) {
391145132Sanholt	case SG_CCB_RDWR_IO:
392152909Sanholt	{
393152909Sanholt		struct sg_rdwr *rdwr;
394152909Sanholt		int state;
395183573Srnoland
396145132Sanholt		devstat_end_transaction(softc->device_stats,
397183573Srnoland					csio->dxfer_len,
398145132Sanholt					csio->tag_action & 0xf,
399145132Sanholt					((csio->ccb_h.flags & CAM_DIR_MASK) ==
400183833Srnoland					CAM_DIR_NONE) ? DEVSTAT_NO_DATA :
401145132Sanholt					(csio->ccb_h.flags & CAM_DIR_OUT) ?
402145132Sanholt					DEVSTAT_WRITE : DEVSTAT_READ,
403145132Sanholt					NULL, NULL);
404145132Sanholt
405152909Sanholt		rdwr = done_ccb->ccb_h.ccb_rdwr;
406145132Sanholt		state = rdwr->state;
407145132Sanholt		rdwr->state = SG_RDWR_DONE;
408145132Sanholt		wakeup(rdwr);
409145132Sanholt		break;
410145132Sanholt	}
411145132Sanholt	default:
412145132Sanholt		panic("unknown sg CCB type");
413145132Sanholt	}
414145132Sanholt}
415145132Sanholt
416182080Srnolandstatic int
417182080Srnolandsgopen(struct cdev *dev, int flags, int fmt, struct thread *td)
418145132Sanholt{
419145132Sanholt	struct cam_periph *periph;
420145132Sanholt	struct sg_softc *softc;
421183573Srnoland	int error = 0;
422145132Sanholt
423182080Srnoland	periph = (struct cam_periph *)dev->si_drv1;
424145132Sanholt	if (periph == NULL)
425145132Sanholt		return (ENXIO);
426145132Sanholt
427145132Sanholt	if (cam_periph_acquire(periph) != CAM_REQ_CMP)
428145132Sanholt		return (ENXIO);
429145132Sanholt
430182080Srnoland	/*
431145132Sanholt	 * Don't allow access when we're running at a high securelevel.
432182080Srnoland	 */
433152909Sanholt	error = securelevel_gt(td->td_ucred, 1);
434183573Srnoland	if (error) {
435145132Sanholt		cam_periph_release(periph);
436145132Sanholt		return (error);
437207066Srnoland	}
438207066Srnoland
439207066Srnoland	cam_periph_lock(periph);
440207066Srnoland
441207066Srnoland	softc = (struct sg_softc *)periph->softc;
442145132Sanholt	if (softc->flags & SG_FLAG_INVALID) {
443207066Srnoland		cam_periph_release_locked(periph);
444145132Sanholt		cam_periph_unlock(periph);
445145132Sanholt		return (ENXIO);
446145132Sanholt	}
447145132Sanholt
448182080Srnoland	softc->open_count++;
449182080Srnoland
450182080Srnoland	cam_periph_unlock(periph);
451182080Srnoland
452182080Srnoland	return (error);
453182080Srnoland}
454182080Srnoland
455182080Srnolandstatic int
456183573Srnolandsgclose(struct cdev *dev, int flag, int fmt, struct thread *td)
457183573Srnoland{
458182080Srnoland	struct cam_periph *periph;
459183573Srnoland	struct sg_softc   *softc;
460152909Sanholt	struct mtx *mtx;
461182080Srnoland
462183573Srnoland	periph = (struct cam_periph *)dev->si_drv1;
463182080Srnoland	if (periph == NULL)
464182080Srnoland		return (ENXIO);
465182080Srnoland	mtx = cam_periph_mtx(periph);
466152909Sanholt	mtx_lock(mtx);
467145132Sanholt
468145132Sanholt	softc = periph->softc;
469145132Sanholt	softc->open_count--;
470145132Sanholt
471183573Srnoland	cam_periph_release_locked(periph);
472145132Sanholt
473145132Sanholt	/*
474183573Srnoland	 * We reference the lock directly here, instead of using
475183573Srnoland	 * cam_periph_unlock().  The reason is that the call to
476145132Sanholt	 * cam_periph_release_locked() above could result in the periph
477145132Sanholt	 * getting freed.  If that is the case, dereferencing the periph
478182080Srnoland	 * with a cam_periph_unlock() call would cause a page fault.
479145132Sanholt	 *
480145132Sanholt	 * cam_periph_release() avoids this problem using the same method,
481215367Snwhitehorn	 * but we're manually acquiring and dropping the lock here to
482145132Sanholt	 * protect the open count and avoid another lock acquisition and
483145132Sanholt	 * release.
484145132Sanholt	 */
485145132Sanholt	mtx_unlock(mtx);
486145132Sanholt
487145132Sanholt	return (0);
488145132Sanholt}
489145132Sanholt
490145132Sanholtstatic int
491145132Sanholtsgioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct thread *td)
492145132Sanholt{
493182080Srnoland	union ccb *ccb;
494182080Srnoland	struct ccb_scsiio *csio;
495182080Srnoland	struct cam_periph *periph;
496182080Srnoland	struct sg_softc *softc;
497182080Srnoland	struct sg_io_hdr *req;
498182080Srnoland	int dir, error;
499182080Srnoland
500152909Sanholt	periph = (struct cam_periph *)dev->si_drv1;
501183573Srnoland	if (periph == NULL)
502183573Srnoland		return (ENXIO);
503183573Srnoland
504183573Srnoland	cam_periph_lock(periph);
505183573Srnoland
506145132Sanholt	softc = (struct sg_softc *)periph->softc;
507145132Sanholt	error = 0;
508145132Sanholt
509145132Sanholt	switch (cmd) {
510145132Sanholt	case SG_GET_VERSION_NUM:
511145132Sanholt	{
512152909Sanholt		int *version = (int *)arg;
513145132Sanholt
514145132Sanholt		*version = sg_version;
515183573Srnoland		break;
516182080Srnoland	}
517182080Srnoland	case SG_SET_TIMEOUT:
518182080Srnoland	{
519145132Sanholt		u_int user_timeout = *(u_int *)arg;
520183573Srnoland
521145132Sanholt		softc->sg_user_timeout = user_timeout;
522145132Sanholt		softc->sg_timeout = user_timeout / SG_DEFAULT_HZ * hz;
523145132Sanholt		break;
524182080Srnoland	}
525145132Sanholt	case SG_GET_TIMEOUT:
526152909Sanholt		/*
527145132Sanholt		 * The value is returned directly to the syscall.
528183573Srnoland		 */
529145132Sanholt		td->td_retval[0] = softc->sg_user_timeout;
530145132Sanholt		error = 0;
531145132Sanholt		break;
532145132Sanholt	case SG_IO:
533145132Sanholt		req = (struct sg_io_hdr *)arg;
534145132Sanholt
535145132Sanholt		if (req->cmd_len > IOCDBLEN) {
536145132Sanholt			error = EINVAL;
537145132Sanholt			break;
538152909Sanholt		}
539145132Sanholt
540145132Sanholt		if (req->iovec_count != 0) {
541145132Sanholt			error = EOPNOTSUPP;
542145132Sanholt			break;
543189130Srnoland		}
544189130Srnoland
545145132Sanholt		ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL);
546152909Sanholt		csio = &ccb->csio;
547145132Sanholt
548145132Sanholt		error = copyin(req->cmdp, &csio->cdb_io.cdb_bytes,
549152909Sanholt		    req->cmd_len);
550152909Sanholt		if (error) {
551152909Sanholt			xpt_release_ccb(ccb);
552152909Sanholt			break;
553152909Sanholt		}
554152909Sanholt
555152909Sanholt		switch(req->dxfer_direction) {
556152909Sanholt		case SG_DXFER_TO_DEV:
557152909Sanholt			dir = CAM_DIR_OUT;
558152909Sanholt			break;
559152909Sanholt		case SG_DXFER_FROM_DEV:
560145132Sanholt			dir = CAM_DIR_IN;
561145132Sanholt			break;
562183573Srnoland		case SG_DXFER_TO_FROM_DEV:
563183833Srnoland			dir = CAM_DIR_IN | CAM_DIR_OUT;
564145132Sanholt			break;
565145132Sanholt		case SG_DXFER_NONE:
566145132Sanholt		default:
567183573Srnoland			dir = CAM_DIR_NONE;
568182468Srnoland			break;
569183573Srnoland		}
570182468Srnoland
571182468Srnoland		cam_fill_csio(csio,
572145132Sanholt			      /*retries*/1,
573182080Srnoland			      sgdone,
574207066Srnoland			      dir|CAM_DEV_QFRZDIS,
575182080Srnoland			      MSG_SIMPLE_Q_TAG,
576145132Sanholt			      req->dxferp,
577182080Srnoland			      req->dxfer_len,
578182080Srnoland			      req->mx_sb_len,
579182080Srnoland			      req->cmd_len,
580182080Srnoland			      req->timeout);
581182080Srnoland
582182080Srnoland		error = sgsendccb(periph, ccb);
583182080Srnoland		if (error) {
584145132Sanholt			req->host_status = DID_ERROR;
585145132Sanholt			req->driver_status = DRIVER_INVALID;
586145132Sanholt			xpt_release_ccb(ccb);
587182080Srnoland			break;
588145132Sanholt		}
589183573Srnoland
590145132Sanholt		req->status = csio->scsi_status;
591145132Sanholt		req->masked_status = (csio->scsi_status >> 1) & 0x7f;
592145132Sanholt		sg_scsiio_status(csio, &req->host_status, &req->driver_status);
593145132Sanholt		req->resid = csio->resid;
594145132Sanholt		req->duration = csio->ccb_h.timeout;
595145132Sanholt		req->info = 0;
596145132Sanholt
597145132Sanholt		if ((csio->ccb_h.status & CAM_AUTOSNS_VALID)
598182080Srnoland		    && (req->sbp != NULL)) {
599145132Sanholt			req->sb_len_wr = req->mx_sb_len - csio->sense_resid;
600145132Sanholt			error = copyout(&csio->sense_data, req->sbp,
601183573Srnoland					req->sb_len_wr);
602183573Srnoland		}
603183573Srnoland
604145132Sanholt		xpt_release_ccb(ccb);
605183573Srnoland		break;
606183573Srnoland
607183573Srnoland	case SG_GET_RESERVED_SIZE:
608145132Sanholt	{
609145132Sanholt		int *size = (int *)arg;
610145132Sanholt		*size = DFLTPHYS;
611145132Sanholt		break;
612145132Sanholt	}
613145132Sanholt
614182080Srnoland	case SG_GET_SCSI_ID:
615145132Sanholt	{
616145132Sanholt		struct sg_scsi_id *id = (struct sg_scsi_id *)arg;
617196465Srnoland
618145132Sanholt		id->host_no = cam_sim_path(xpt_path_sim(periph->path));
619183573Srnoland		id->channel = xpt_path_path_id(periph->path);
620145132Sanholt		id->scsi_id = xpt_path_target_id(periph->path);
621145132Sanholt		id->lun = xpt_path_lun_id(periph->path);
622145132Sanholt		id->scsi_type = softc->pd_type;
623183573Srnoland		id->h_cmd_per_lun = 1;
624183573Srnoland		id->d_queue_depth = 1;
625145132Sanholt		id->unused[0] = 0;
626145132Sanholt		id->unused[1] = 0;
627183573Srnoland		break;
628152909Sanholt	}
629145132Sanholt
630145132Sanholt	case SG_GET_SG_TABLESIZE:
631145132Sanholt	{
632145132Sanholt		int *size = (int *)arg;
633145132Sanholt		*size = 0;
634145132Sanholt		break;
635183573Srnoland	}
636145132Sanholt
637183573Srnoland	case SG_EMULATED_HOST:
638183573Srnoland	case SG_SET_TRANSFORM:
639145132Sanholt	case SG_GET_TRANSFORM:
640182080Srnoland	case SG_GET_NUM_WAITING:
641183573Srnoland	case SG_SCSI_RESET:
642145132Sanholt	case SG_GET_REQUEST_TABLE:
643145132Sanholt	case SG_SET_KEEP_ORPHAN:
644145132Sanholt	case SG_GET_KEEP_ORPHAN:
645183573Srnoland	case SG_GET_ACCESS_COUNT:
646183573Srnoland	case SG_SET_FORCE_LOW_DMA:
647145132Sanholt	case SG_GET_LOW_DMA:
648145132Sanholt	case SG_SET_FORCE_PACK_ID:
649145132Sanholt	case SG_GET_PACK_ID:
650145132Sanholt	case SG_SET_RESERVED_SIZE:
651145132Sanholt	case SG_GET_COMMAND_Q:
652183573Srnoland	case SG_SET_COMMAND_Q:
653183573Srnoland	case SG_SET_DEBUG:
654145132Sanholt	case SG_NEXT_CMD_LEN:
655145132Sanholt	default:
656182080Srnoland#ifdef CAMDEBUG
657145132Sanholt		printf("sgioctl: rejecting cmd 0x%lx\n", cmd);
658145132Sanholt#endif
659145132Sanholt		error = ENODEV;
660183573Srnoland		break;
661183573Srnoland	}
662145132Sanholt
663183573Srnoland	cam_periph_unlock(periph);
664145132Sanholt	return (error);
665145132Sanholt}
666145132Sanholt
667145132Sanholtstatic int
668145132Sanholtsgwrite(struct cdev *dev, struct uio *uio, int ioflag)
669145132Sanholt{
670183573Srnoland	union ccb *ccb;
671152909Sanholt	struct cam_periph *periph;
672145132Sanholt	struct ccb_scsiio *csio;
673145132Sanholt	struct sg_softc *sc;
674183573Srnoland	struct sg_header *hdr;
675145132Sanholt	struct sg_rdwr *rdwr;
676182080Srnoland	u_char cdb_cmd;
677145132Sanholt	char *buf;
678145132Sanholt	int error = 0, cdb_len, buf_len, dir;
679183573Srnoland
680182080Srnoland	periph = dev->si_drv1;
681145132Sanholt	rdwr = malloc(sizeof(*rdwr), M_DEVBUF, M_WAITOK | M_ZERO);
682183573Srnoland	hdr = &rdwr->hdr.hdr;
683145132Sanholt
684145132Sanholt	/* Copy in the header block and sanity check it */
685183573Srnoland	if (uio->uio_resid < sizeof(*hdr)) {
686182080Srnoland		error = EINVAL;
687189869Srnoland		goto out_hdr;
688145132Sanholt	}
689145132Sanholt	error = uiomove(hdr, sizeof(*hdr), uio);
690145132Sanholt	if (error)
691145132Sanholt		goto out_hdr;
692183573Srnoland
693183573Srnoland	/* XXX: We don't support SG 3.x read/write API. */
694145132Sanholt	if (hdr->reply_len < 0) {
695145132Sanholt		error = ENODEV;
696145132Sanholt		goto out_hdr;
697183573Srnoland	}
698183573Srnoland
699182080Srnoland	ccb = xpt_alloc_ccb();
700145132Sanholt	if (ccb == NULL) {
701145132Sanholt		error = ENOMEM;
702145132Sanholt		goto out_hdr;
703183573Srnoland	}
704183573Srnoland	csio = &ccb->csio;
705182080Srnoland
706183833Srnoland	/*
707145132Sanholt	 * Copy in the CDB block.  The designers of the interface didn't
708145132Sanholt	 * bother to provide a size for this in the header, so we have to
709145132Sanholt	 * figure it out ourselves.
710145132Sanholt	 */
711145132Sanholt	if (uio->uio_resid < 1)
712183573Srnoland		goto out_ccb;
713145132Sanholt	error = uiomove(&cdb_cmd, 1, uio);
714145132Sanholt	if (error)
715152909Sanholt		goto out_ccb;
716145132Sanholt	if (hdr->twelve_byte)
717145132Sanholt		cdb_len = 12;
718145132Sanholt	else
719145132Sanholt		cdb_len = scsi_group_len(cdb_cmd);
720145132Sanholt	/*
721145132Sanholt	 * We've already read the first byte of the CDB and advanced the uio
722145132Sanholt	 * pointer.  Just read the rest.
723145132Sanholt	 */
724145132Sanholt	csio->cdb_io.cdb_bytes[0] = cdb_cmd;
725145132Sanholt	error = uiomove(&csio->cdb_io.cdb_bytes[1], cdb_len - 1, uio);
726182080Srnoland	if (error)
727145132Sanholt		goto out_ccb;
728145132Sanholt
729182080Srnoland	/*
730145132Sanholt	 * Now set up the data block.  Again, the designers didn't bother
731145132Sanholt	 * to make this reliable.
732183573Srnoland	 */
733145132Sanholt	buf_len = uio->uio_resid;
734183573Srnoland	if (buf_len != 0) {
735183573Srnoland		buf = malloc(buf_len, M_DEVBUF, M_WAITOK | M_ZERO);
736145132Sanholt		error = uiomove(buf, buf_len, uio);
737145132Sanholt		if (error)
738145132Sanholt			goto out_buf;
739145132Sanholt		dir = CAM_DIR_OUT;
740183573Srnoland	} else if (hdr->reply_len != 0) {
741182080Srnoland		buf = malloc(hdr->reply_len, M_DEVBUF, M_WAITOK | M_ZERO);
742145132Sanholt		buf_len = hdr->reply_len;
743183573Srnoland		dir = CAM_DIR_IN;
744182080Srnoland	} else {
745183573Srnoland		buf = NULL;
746145132Sanholt		buf_len = 0;
747145132Sanholt		dir = CAM_DIR_NONE;
748145132Sanholt	}
749145132Sanholt
750145132Sanholt	cam_periph_lock(periph);
751145132Sanholt	sc = periph->softc;
752145132Sanholt	xpt_setup_ccb(&ccb->ccb_h, periph->path, CAM_PRIORITY_NORMAL);
753145132Sanholt	cam_fill_csio(csio,
754145132Sanholt		      /*retries*/1,
755145132Sanholt		      sgdone,
756145132Sanholt		      dir|CAM_DEV_QFRZDIS,
757145132Sanholt		      MSG_SIMPLE_Q_TAG,
758145132Sanholt		      buf,
759145132Sanholt		      buf_len,
760145132Sanholt		      SG_MAX_SENSE,
761145132Sanholt		      cdb_len,
762145132Sanholt		      sc->sg_timeout);
763145132Sanholt
764145132Sanholt	/*
765145132Sanholt	 * Send off the command and hope that it works. This path does not
766145132Sanholt	 * go through sgstart because the I/O is supposed to be asynchronous.
767145132Sanholt	 */
768145132Sanholt	rdwr->buf = buf;
769145132Sanholt	rdwr->buf_len = buf_len;
770183573Srnoland	rdwr->tag = hdr->pack_id;
771145132Sanholt	rdwr->ccb = ccb;
772183573Srnoland	rdwr->state = SG_RDWR_INPROG;
773145132Sanholt	ccb->ccb_h.ccb_rdwr = rdwr;
774145132Sanholt	ccb->ccb_h.ccb_type = SG_CCB_RDWR_IO;
775183573Srnoland	TAILQ_INSERT_TAIL(&sc->rdwr_done, rdwr, rdwr_link);
776145132Sanholt	error = sgsendrdwr(periph, ccb);
777145132Sanholt	cam_periph_unlock(periph);
778145132Sanholt	return (error);
779145132Sanholt
780145132Sanholtout_buf:
781183573Srnoland	free(buf, M_DEVBUF);
782145132Sanholtout_ccb:
783145132Sanholt	xpt_free_ccb(ccb);
784182080Srnolandout_hdr:
785152909Sanholt	free(rdwr, M_DEVBUF);
786182080Srnoland	return (error);
787182080Srnoland}
788145132Sanholt
789145132Sanholtstatic int
790182080Srnolandsgread(struct cdev *dev, struct uio *uio, int ioflag)
791145132Sanholt{
792182080Srnoland	struct ccb_scsiio *csio;
793182080Srnoland	struct cam_periph *periph;
794145132Sanholt	struct sg_softc *sc;
795182080Srnoland	struct sg_header *hdr;
796182080Srnoland	struct sg_rdwr *rdwr;
797182080Srnoland	u_short hstat, dstat;
798145132Sanholt	int error, pack_len, reply_len, pack_id;
799145132Sanholt
800145132Sanholt	periph = dev->si_drv1;
801145132Sanholt
802182080Srnoland	/* XXX The pack len field needs to be updated and written out instead
803145132Sanholt	 * of discarded.  Not sure how to do that.
804145132Sanholt	 */
805182080Srnoland	uio->uio_rw = UIO_WRITE;
806182080Srnoland	if ((error = uiomove(&pack_len, 4, uio)) != 0)
807182080Srnoland		return (error);
808145132Sanholt	if ((error = uiomove(&reply_len, 4, uio)) != 0)
809182080Srnoland		return (error);
810182080Srnoland	if ((error = uiomove(&pack_id, 4, uio)) != 0)
811182080Srnoland		return (error);
812182080Srnoland	uio->uio_rw = UIO_READ;
813182080Srnoland
814182080Srnoland	cam_periph_lock(periph);
815182080Srnoland	sc = periph->softc;
816182080Srnolandsearch:
817182080Srnoland	TAILQ_FOREACH(rdwr, &sc->rdwr_done, rdwr_link) {
818145132Sanholt		if (rdwr->tag == pack_id)
819145132Sanholt			break;
820145132Sanholt	}
821145132Sanholt	if ((rdwr == NULL) || (rdwr->state != SG_RDWR_DONE)) {
822145132Sanholt		if (cam_periph_sleep(periph, rdwr, PCATCH, "sgread", 0) == ERESTART)
823145132Sanholt			return (EAGAIN);
824145132Sanholt		goto search;
825145132Sanholt	}
826145132Sanholt	TAILQ_REMOVE(&sc->rdwr_done, rdwr, rdwr_link);
827145132Sanholt	cam_periph_unlock(periph);
828145132Sanholt
829145132Sanholt	hdr = &rdwr->hdr.hdr;
830145132Sanholt	csio = &rdwr->ccb->csio;
831145132Sanholt	sg_scsiio_status(csio, &hstat, &dstat);
832145132Sanholt	hdr->host_status = hstat;
833145132Sanholt	hdr->driver_status = dstat;
834145132Sanholt	hdr->target_status = csio->scsi_status >> 1;
835145132Sanholt
836145132Sanholt	switch (hstat) {
837145132Sanholt	case DID_OK:
838145132Sanholt	case DID_PASSTHROUGH:
839145132Sanholt	case DID_SOFT_ERROR:
840145132Sanholt		hdr->result = 0;
841145132Sanholt		break;
842145132Sanholt	case DID_NO_CONNECT:
843145132Sanholt	case DID_BUS_BUSY:
844145132Sanholt	case DID_TIME_OUT:
845145132Sanholt		hdr->result = EBUSY;
846145132Sanholt		break;
847145132Sanholt	case DID_BAD_TARGET:
848145132Sanholt	case DID_ABORT:
849145132Sanholt	case DID_PARITY:
850145132Sanholt	case DID_RESET:
851145132Sanholt	case DID_BAD_INTR:
852145132Sanholt	case DID_ERROR:
853145132Sanholt	default:
854145132Sanholt		hdr->result = EIO;
855145132Sanholt		break;
856145132Sanholt	}
857
858	if (dstat == DRIVER_SENSE) {
859		bcopy(&csio->sense_data, hdr->sense_buffer,
860		      min(csio->sense_len, SG_MAX_SENSE));
861#ifdef CAMDEBUG
862		scsi_sense_print(csio);
863#endif
864	}
865
866	error = uiomove(&hdr->result, sizeof(*hdr) -
867			offsetof(struct sg_header, result), uio);
868	if ((error == 0) && (hdr->result == 0))
869		error = uiomove(rdwr->buf, rdwr->buf_len, uio);
870
871	cam_periph_lock(periph);
872	xpt_free_ccb(rdwr->ccb);
873	cam_periph_unlock(periph);
874	free(rdwr->buf, M_DEVBUF);
875	free(rdwr, M_DEVBUF);
876	return (error);
877}
878
879static int
880sgsendccb(struct cam_periph *periph, union ccb *ccb)
881{
882	struct sg_softc *softc;
883	struct cam_periph_map_info mapinfo;
884	int error;
885
886	softc = periph->softc;
887	bzero(&mapinfo, sizeof(mapinfo));
888
889	/*
890	 * cam_periph_mapmem calls into proc and vm functions that can
891	 * sleep as well as trigger I/O, so we can't hold the lock.
892	 * Dropping it here is reasonably safe.
893	 * The only CCB opcode that is possible here is XPT_SCSI_IO, no
894	 * need for additional checks.
895	 */
896	cam_periph_unlock(periph);
897	error = cam_periph_mapmem(ccb, &mapinfo);
898	cam_periph_lock(periph);
899	if (error)
900		return (error);
901
902	error = cam_periph_runccb(ccb,
903				  sgerror,
904				  CAM_RETRY_SELTO,
905				  SF_RETRY_UA,
906				  softc->device_stats);
907
908	cam_periph_unmapmem(ccb, &mapinfo);
909
910	return (error);
911}
912
913static int
914sgsendrdwr(struct cam_periph *periph, union ccb *ccb)
915{
916	struct sg_softc *softc;
917
918	softc = periph->softc;
919	devstat_start_transaction(softc->device_stats, NULL);
920	xpt_action(ccb);
921	return (0);
922}
923
924static int
925sgerror(union ccb *ccb, uint32_t cam_flags, uint32_t sense_flags)
926{
927	struct cam_periph *periph;
928	struct sg_softc *softc;
929
930	periph = xpt_path_periph(ccb->ccb_h.path);
931	softc = (struct sg_softc *)periph->softc;
932
933	return (cam_periph_error(ccb, cam_flags, sense_flags,
934				 &softc->saved_ccb));
935}
936
937static void
938sg_scsiio_status(struct ccb_scsiio *csio, u_short *hoststat, u_short *drvstat)
939{
940	int status;
941
942	status = csio->ccb_h.status;
943
944	switch (status & CAM_STATUS_MASK) {
945	case CAM_REQ_CMP:
946		*hoststat = DID_OK;
947		*drvstat = 0;
948		break;
949	case CAM_REQ_CMP_ERR:
950		*hoststat = DID_ERROR;
951		*drvstat = 0;
952		break;
953	case CAM_REQ_ABORTED:
954		*hoststat = DID_ABORT;
955		*drvstat = 0;
956		break;
957	case CAM_REQ_INVALID:
958		*hoststat = DID_ERROR;
959		*drvstat = DRIVER_INVALID;
960		break;
961	case CAM_DEV_NOT_THERE:
962		*hoststat = DID_BAD_TARGET;
963		*drvstat = 0;
964		break;
965	case CAM_SEL_TIMEOUT:
966		*hoststat = DID_NO_CONNECT;
967		*drvstat = 0;
968		break;
969	case CAM_CMD_TIMEOUT:
970		*hoststat = DID_TIME_OUT;
971		*drvstat = 0;
972		break;
973	case CAM_SCSI_STATUS_ERROR:
974		*hoststat = DID_ERROR;
975		*drvstat = 0;
976		break;
977	case CAM_SCSI_BUS_RESET:
978		*hoststat = DID_RESET;
979		*drvstat = 0;
980		break;
981	case CAM_UNCOR_PARITY:
982		*hoststat = DID_PARITY;
983		*drvstat = 0;
984		break;
985	case CAM_SCSI_BUSY:
986		*hoststat = DID_BUS_BUSY;
987		*drvstat = 0;
988		break;
989	default:
990		*hoststat = DID_ERROR;
991		*drvstat = DRIVER_ERROR;
992	}
993
994	if (status & CAM_AUTOSNS_VALID)
995		*drvstat = DRIVER_SENSE;
996}
997
998static int
999scsi_group_len(u_char cmd)
1000{
1001	int len[] = {6, 10, 10, 12, 12, 12, 10, 10};
1002	int group;
1003
1004	group = (cmd >> 5) & 0x7;
1005	return (len[group]);
1006}
1007
1008