scsi_sg.c revision 169605
1/*-
2 * Copyright (c) 2007 Scott Long
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions, and the following disclaimer,
10 *    without modification, immediately at the beginning of the file.
11 * 2. The name of the author may not be used to endorse or promote products
12 *    derived from this software without specific prior written permission.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
18 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27/*
28 * scsi_sg peripheral driver.  This driver is meant to implement the Linux
29 * SG passthrough interface for SCSI.
30 */
31
32#include <sys/cdefs.h>
33__FBSDID("$FreeBSD: head/sys/cam/scsi/scsi_sg.c 169605 2007-05-16 16:54:23Z scottl $");
34
35#include <sys/param.h>
36#include <sys/systm.h>
37#include <sys/kernel.h>
38#include <sys/types.h>
39#include <sys/bio.h>
40#include <sys/malloc.h>
41#include <sys/fcntl.h>
42#include <sys/ioccom.h>
43#include <sys/conf.h>
44#include <sys/errno.h>
45#include <sys/devicestat.h>
46#include <sys/proc.h>
47#include <sys/uio.h>
48
49#include <cam/cam.h>
50#include <cam/cam_ccb.h>
51#include <cam/cam_periph.h>
52#include <cam/cam_queue.h>
53#include <cam/cam_xpt_periph.h>
54#include <cam/cam_debug.h>
55#include <cam/cam_sim.h>
56
57#include <cam/scsi/scsi_all.h>
58#include <cam/scsi/scsi_message.h>
59#include <cam/scsi/scsi_sg.h>
60
61#include <compat/linux/linux_ioctl.h>
62
63typedef enum {
64	SG_FLAG_OPEN		= 0x01,
65	SG_FLAG_LOCKED		= 0x02,
66	SG_FLAG_INVALID		= 0x04
67} sg_flags;
68
69typedef enum {
70	SG_STATE_NORMAL
71} sg_state;
72
73typedef enum {
74	SG_RDWR_FREE,
75	SG_RDWR_INPROG,
76	SG_RDWR_DONE
77} sg_rdwr_state;
78
79typedef enum {
80	SG_CCB_RDWR_IO,
81	SG_CCB_WAITING
82} sg_ccb_types;
83
84#define ccb_type	ppriv_field0
85#define ccb_rdwr	ppriv_ptr1
86
87struct sg_rdwr {
88	TAILQ_ENTRY(sg_rdwr)	rdwr_link;
89	int			tag;
90	int			state;
91	int			buf_len;
92	char			*buf;
93	union ccb		*ccb;
94	union {
95		struct sg_header hdr;
96		struct sg_io_hdr io_hdr;
97	} hdr;
98};
99
100struct sg_softc {
101	sg_state		state;
102	sg_flags		flags;
103	struct devstat		*device_stats;
104	TAILQ_HEAD(, sg_rdwr)	rdwr_done;
105	struct cdev		*dev;
106	int			sg_timeout;
107	int			sg_user_timeout;
108	uint8_t			pd_type;
109	union ccb		saved_ccb;
110};
111
112static d_open_t		sgopen;
113static d_close_t	sgclose;
114static d_ioctl_t	sgioctl;
115static d_write_t	sgwrite;
116static d_read_t		sgread;
117
118static periph_init_t	sginit;
119static periph_ctor_t	sgregister;
120static periph_oninv_t	sgoninvalidate;
121static periph_dtor_t	sgcleanup;
122static periph_start_t	sgstart;
123static void		sgasync(void *callback_arg, uint32_t code,
124				struct cam_path *path, void *arg);
125static void		sgdone(struct cam_periph *periph, union ccb *done_ccb);
126static int		sgsendccb(struct cam_periph *periph, union ccb *ccb);
127static int		sgsendrdwr(struct cam_periph *periph, union ccb *ccb);
128static int		sgerror(union ccb *ccb, uint32_t cam_flags,
129				uint32_t sense_flags);
130static void		sg_scsiio_status(struct ccb_scsiio *csio,
131					 u_short *hoststat, u_short *drvstat);
132
133static int		scsi_group_len(u_char cmd);
134
135static struct periph_driver sgdriver =
136{
137	sginit, "sg",
138	TAILQ_HEAD_INITIALIZER(sgdriver.units), /* gen */ 0
139};
140PERIPHDRIVER_DECLARE(sg, sgdriver);
141
142static struct cdevsw sg_cdevsw = {
143	.d_version =	D_VERSION,
144	.d_flags =	D_NEEDGIANT,
145	.d_open =	sgopen,
146	.d_close =	sgclose,
147	.d_ioctl =	sgioctl,
148	.d_write =	sgwrite,
149	.d_read =	sgread,
150	.d_name =	"sg",
151};
152
153static int sg_version = 30125;
154
155static void
156sginit(void)
157{
158	cam_status status;
159
160	/*
161	 * Install a global async callback.  This callback will receive aync
162	 * callbacks like "new device found".
163	 */
164	status = xpt_register_async(AC_FOUND_DEVICE, sgasync, NULL, NULL);
165
166	if (status != CAM_REQ_CMP) {
167		printf("sg: Failed to attach master async callbac "
168			"due to status 0x%x!\n", status);
169	}
170}
171
172static void
173sgoninvalidate(struct cam_periph *periph)
174{
175	struct sg_softc *softc;
176
177	softc = (struct sg_softc *)periph->softc;
178
179	/*
180	 * Deregister any async callbacks.
181	 */
182	xpt_register_async(0, sgasync, periph, periph->path);
183
184	softc->flags |= SG_FLAG_INVALID;
185
186	/*
187	 * XXX Return all queued I/O with ENXIO.
188	 * XXX Handle any transactions queued to the card
189	 *     with XPT_ABORT_CCB.
190	 */
191
192	if (bootverbose) {
193		xpt_print(periph->path, "lost device\n");
194	}
195}
196
197static void
198sgcleanup(struct cam_periph *periph)
199{
200	struct sg_softc *softc;
201
202	softc = (struct sg_softc *)periph->softc;
203	devstat_remove_entry(softc->device_stats);
204	destroy_dev(softc->dev);
205	if (bootverbose) {
206		xpt_print(periph->path, "removing device entry\n");
207	}
208	free(softc, M_DEVBUF);
209}
210
211static void
212sgasync(void *callback_arg, uint32_t code, struct cam_path *path, void *arg)
213{
214	struct cam_periph *periph;
215
216	periph = (struct cam_periph *)callback_arg;
217
218	switch (code) {
219	case AC_FOUND_DEVICE:
220	{
221		struct ccb_getdev *cgd;
222		cam_status status;
223
224		cgd = (struct ccb_getdev *)arg;
225		if (cgd == NULL)
226			break;
227
228		/*
229		 * Allocate a peripheral instance for this device and
230		 * start the probe process.
231		 */
232		status = cam_periph_alloc(sgregister, sgoninvalidate,
233					  sgcleanup, sgstart, "sg",
234					  CAM_PERIPH_BIO, cgd->ccb_h.path,
235					  sgasync, AC_FOUND_DEVICE, cgd);
236		if ((status != CAM_REQ_CMP) && (status != CAM_REQ_INPROG)) {
237			const struct cam_status_entry *entry;
238
239			entry = cam_fetch_status_entry(status);
240			printf("sgasync: Unable to attach new device "
241				"due to status %#x: %s\n", status, entry ?
242				entry->status_text : "Unknown");
243		}
244		break;
245	}
246	default:
247		cam_periph_async(periph, code, path, arg);
248		break;
249	}
250}
251
252static cam_status
253sgregister(struct cam_periph *periph, void *arg)
254{
255	struct sg_softc *softc;
256	struct ccb_getdev *cgd;
257	int no_tags;
258
259	cgd = (struct ccb_getdev *)arg;
260	if (periph == NULL) {
261		printf("sgregister: periph was NULL!!\n");
262		return (CAM_REQ_CMP_ERR);
263	}
264
265	if (cgd == NULL) {
266		printf("sgregister: no getdev CCB, can't register device\n");
267		return (CAM_REQ_CMP_ERR);
268	}
269
270	softc = malloc(sizeof(*softc), M_DEVBUF, M_ZERO | M_NOWAIT);
271	if (softc == NULL) {
272		printf("sgregister: Unable to allocate softc\n");
273		return (CAM_REQ_CMP_ERR);
274	}
275
276	softc->state = SG_STATE_NORMAL;
277	softc->pd_type = SID_TYPE(&cgd->inq_data);
278	softc->sg_timeout = SG_DEFAULT_TIMEOUT / SG_DEFAULT_HZ * hz;
279	softc->sg_user_timeout = SG_DEFAULT_TIMEOUT;
280	TAILQ_INIT(&softc->rdwr_done);
281	periph->softc = softc;
282
283	/*
284	 * We pass in 0 for all blocksize, since we don't know what the
285	 * blocksize of the device is, if it even has a blocksize.
286	 */
287	cam_periph_unlock(periph);
288	no_tags = (cgd->inq_data.flags & SID_CmdQue) == 0;
289	softc->device_stats = devstat_new_entry("sg",
290			unit2minor(periph->unit_number), 0,
291			DEVSTAT_NO_BLOCKSIZE
292			| (no_tags ? DEVSTAT_NO_ORDERED_TAGS : 0),
293			softc->pd_type |
294			DEVSTAT_TYPE_IF_SCSI |
295			DEVSTAT_TYPE_PASS,
296			DEVSTAT_PRIORITY_PASS);
297
298	/* Register the device */
299	softc->dev = make_dev(&sg_cdevsw, unit2minor(periph->unit_number),
300			      UID_ROOT, GID_OPERATOR, 0600, "%s%d",
301			      periph->periph_name, periph->unit_number);
302	(void)make_dev_alias(softc->dev, "sg%c", 'a' + periph->unit_number);
303	cam_periph_lock(periph);
304	softc->dev->si_drv1 = periph;
305
306	/*
307	 * Add as async callback so that we get
308	 * notified if this device goes away.
309	 */
310	xpt_register_async(AC_LOST_DEVICE, sgasync, periph, periph->path);
311
312	if (bootverbose)
313		xpt_announce_periph(periph, NULL);
314
315	return (CAM_REQ_CMP);
316}
317
318static void
319sgstart(struct cam_periph *periph, union ccb *start_ccb)
320{
321	struct sg_softc *softc;
322
323	softc = (struct sg_softc *)periph->softc;
324
325	switch (softc->state) {
326	case SG_STATE_NORMAL:
327		start_ccb->ccb_h.ccb_type = SG_CCB_WAITING;
328		SLIST_INSERT_HEAD(&periph->ccb_list, &start_ccb->ccb_h,
329				  periph_links.sle);
330		periph->immediate_priority = CAM_PRIORITY_NONE;
331		wakeup(&periph->ccb_list);
332		break;
333	}
334}
335
336static void
337sgdone(struct cam_periph *periph, union ccb *done_ccb)
338{
339	struct sg_softc *softc;
340	struct ccb_scsiio *csio;
341
342	softc = (struct sg_softc *)periph->softc;
343	csio = &done_ccb->csio;
344	switch (csio->ccb_h.ccb_type) {
345	case SG_CCB_WAITING:
346		/* Caller will release the CCB */
347		wakeup(&done_ccb->ccb_h.cbfcnp);
348		return;
349	case SG_CCB_RDWR_IO:
350	{
351		struct sg_rdwr *rdwr;
352		int state;
353
354		devstat_end_transaction(softc->device_stats,
355					csio->dxfer_len,
356					csio->tag_action & 0xf,
357					((csio->ccb_h.flags & CAM_DIR_MASK) ==
358					CAM_DIR_NONE) ? DEVSTAT_NO_DATA :
359					(csio->ccb_h.flags & CAM_DIR_OUT) ?
360					DEVSTAT_WRITE : DEVSTAT_READ,
361					NULL, NULL);
362
363		rdwr = done_ccb->ccb_h.ccb_rdwr;
364		state = rdwr->state;
365		rdwr->state = SG_RDWR_DONE;
366		wakeup(rdwr);
367		break;
368	}
369	default:
370		panic("unknown sg CCB type");
371	}
372}
373
374static int
375sgopen(struct cdev *dev, int flags, int fmt, struct thread *td)
376{
377	struct cam_periph *periph;
378	struct sg_softc *softc;
379	int error = 0;
380
381	periph = (struct cam_periph *)dev->si_drv1;
382	if (periph == NULL)
383		return (ENXIO);
384
385	/*
386	 * Don't allow access when we're running at a high securelevel.
387	 */
388	error = securelevel_gt(td->td_ucred, 1);
389	if (error)
390		return (error);
391
392	cam_periph_lock(periph);
393
394	softc = (struct sg_softc *)periph->softc;
395	if (softc->flags & SG_FLAG_INVALID) {
396		cam_periph_unlock(periph);
397		return (ENXIO);
398	}
399
400	if ((softc->flags & SG_FLAG_OPEN) == 0) {
401		softc->flags |= SG_FLAG_OPEN;
402	} else {
403		/* Device closes aren't symmetrical, fix up the refcount. */
404		cam_periph_release(periph);
405	}
406
407	cam_periph_unlock(periph);
408
409	return (error);
410}
411
412static int
413sgclose(struct cdev *dev, int flag, int fmt, struct thread *td)
414{
415	struct cam_periph *periph;
416	struct sg_softc *softc;
417
418	periph = (struct cam_periph *)dev->si_drv1;
419	if (periph == NULL)
420		return (ENXIO);
421
422	cam_periph_lock(periph);
423
424	softc = (struct sg_softc *)periph->softc;
425	softc->flags &= ~SG_FLAG_OPEN;
426
427	cam_periph_unlock(periph);
428	cam_periph_release(periph);
429
430	return (0);
431}
432
433static int
434sgioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct thread *td)
435{
436	union ccb *ccb;
437	struct ccb_scsiio *csio;
438	struct cam_periph *periph;
439	struct sg_softc *softc;
440	struct sg_io_hdr req;
441	int dir, error;
442
443	periph = (struct cam_periph *)dev->si_drv1;
444	if (periph == NULL)
445		return (ENXIO);
446
447	cam_periph_lock(periph);
448
449	softc = (struct sg_softc *)periph->softc;
450	error = 0;
451
452	switch (cmd) {
453	case LINUX_SCSI_GET_BUS_NUMBER: {
454		int busno;
455
456		busno = xpt_path_path_id(periph->path);
457		error = copyout(&busno, arg, sizeof(busno));
458		break;
459	}
460	case LINUX_SCSI_GET_IDLUN: {
461		struct scsi_idlun idlun;
462		struct cam_sim *sim;
463
464		idlun.dev_id = xpt_path_target_id(periph->path);
465		sim = xpt_path_sim(periph->path);
466		idlun.host_unique_id = sim->unit_number;
467		error = copyout(&idlun, arg, sizeof(idlun));
468		break;
469	}
470	case SG_GET_VERSION_NUM:
471	case LINUX_SG_GET_VERSION_NUM:
472		error = copyout(&sg_version, arg, sizeof(sg_version));
473		break;
474	case SG_SET_TIMEOUT:
475	case LINUX_SG_SET_TIMEOUT: {
476		u_int user_timeout;
477
478		error = copyin(arg, &user_timeout, sizeof(u_int));
479		if (error == 0) {
480			softc->sg_user_timeout = user_timeout;
481			softc->sg_timeout = user_timeout / SG_DEFAULT_HZ * hz;
482		}
483		break;
484	}
485	case SG_GET_TIMEOUT:
486	case LINUX_SG_GET_TIMEOUT:
487		/*
488		 * The value is returned directly to the syscall.
489		 */
490		td->td_retval[0] = softc->sg_user_timeout;
491		error = 0;
492		break;
493	case SG_IO:
494	case LINUX_SG_IO:
495		error = copyin(arg, &req, sizeof(req));
496		if (error)
497			break;
498
499		if (req.cmd_len > IOCDBLEN) {
500			error = EINVAL;
501			break;
502		}
503
504		if (req.iovec_count != 0) {
505			error = EOPNOTSUPP;
506			break;
507		}
508
509		ccb = cam_periph_getccb(periph, /*priority*/5);
510		csio = &ccb->csio;
511
512		error = copyin(req.cmdp, &csio->cdb_io.cdb_bytes,
513		    req.cmd_len);
514		if (error) {
515			xpt_release_ccb(ccb);
516			break;
517		}
518
519		switch(req.dxfer_direction) {
520		case SG_DXFER_TO_DEV:
521			dir = CAM_DIR_OUT;
522			break;
523		case SG_DXFER_FROM_DEV:
524			dir = CAM_DIR_IN;
525			break;
526		case SG_DXFER_TO_FROM_DEV:
527			dir = CAM_DIR_IN | CAM_DIR_OUT;
528			break;
529		case SG_DXFER_NONE:
530		default:
531			dir = CAM_DIR_NONE;
532			break;
533		}
534
535		cam_fill_csio(csio,
536			      /*retries*/1,
537			      sgdone,
538			      dir|CAM_DEV_QFRZDIS,
539			      MSG_SIMPLE_Q_TAG,
540			      req.dxferp,
541			      req.dxfer_len,
542			      req.mx_sb_len,
543			      req.cmd_len,
544			      req.timeout);
545
546		error = sgsendccb(periph, ccb);
547		if (error) {
548			req.host_status = DID_ERROR;
549			req.driver_status = DRIVER_INVALID;
550			xpt_release_ccb(ccb);
551			break;
552		}
553
554		req.status = csio->scsi_status;
555		req.masked_status = (csio->scsi_status >> 1) & 0x7f;
556		sg_scsiio_status(csio, &req.host_status, &req.driver_status);
557		req.resid = csio->resid;
558		req.duration = csio->ccb_h.timeout;
559		req.info = 0;
560
561		error = copyout(&req, arg, sizeof(req));
562		if ((error == 0) && (csio->ccb_h.status & CAM_AUTOSNS_VALID)
563		    && (req.sbp != NULL)) {
564			req.sb_len_wr = req.mx_sb_len - csio->sense_resid;
565			error = copyout(&csio->sense_data, req.sbp,
566					req.sb_len_wr);
567		}
568
569		xpt_release_ccb(ccb);
570		break;
571
572	case SG_GET_RESERVED_SIZE:
573	case LINUX_SG_GET_RESERVED_SIZE: {
574		int size = 32768;
575
576		error = copyout(&size, arg, sizeof(size));
577		break;
578	}
579
580	case SG_GET_SCSI_ID:
581	case LINUX_SG_GET_SCSI_ID:
582	{
583		struct sg_scsi_id id;
584
585		id.host_no = 0; /* XXX */
586		id.channel = xpt_path_path_id(periph->path);
587		id.scsi_id = xpt_path_target_id(periph->path);
588		id.lun = xpt_path_lun_id(periph->path);
589		id.scsi_type = softc->pd_type;
590		id.h_cmd_per_lun = 1;
591		id.d_queue_depth = 1;
592		id.unused[0] = 0;
593		id.unused[1] = 0;
594
595		error = copyout(&id, arg, sizeof(id));
596		break;
597	}
598
599	case SG_EMULATED_HOST:
600	case SG_SET_TRANSFORM:
601	case SG_GET_TRANSFORM:
602	case SG_GET_NUM_WAITING:
603	case SG_SCSI_RESET:
604	case SG_GET_REQUEST_TABLE:
605	case SG_SET_KEEP_ORPHAN:
606	case SG_GET_KEEP_ORPHAN:
607	case SG_GET_ACCESS_COUNT:
608	case SG_SET_FORCE_LOW_DMA:
609	case SG_GET_LOW_DMA:
610	case SG_GET_SG_TABLESIZE:
611	case SG_SET_FORCE_PACK_ID:
612	case SG_GET_PACK_ID:
613	case SG_SET_RESERVED_SIZE:
614	case SG_GET_COMMAND_Q:
615	case SG_SET_COMMAND_Q:
616	case SG_SET_DEBUG:
617	case SG_NEXT_CMD_LEN:
618	case LINUX_SG_EMULATED_HOST:
619	case LINUX_SG_SET_TRANSFORM:
620	case LINUX_SG_GET_TRANSFORM:
621	case LINUX_SG_GET_NUM_WAITING:
622	case LINUX_SG_SCSI_RESET:
623	case LINUX_SG_GET_REQUEST_TABLE:
624	case LINUX_SG_SET_KEEP_ORPHAN:
625	case LINUX_SG_GET_KEEP_ORPHAN:
626	case LINUX_SG_GET_ACCESS_COUNT:
627	case LINUX_SG_SET_FORCE_LOW_DMA:
628	case LINUX_SG_GET_LOW_DMA:
629	case LINUX_SG_GET_SG_TABLESIZE:
630	case LINUX_SG_SET_FORCE_PACK_ID:
631	case LINUX_SG_GET_PACK_ID:
632	case LINUX_SG_SET_RESERVED_SIZE:
633	case LINUX_SG_GET_COMMAND_Q:
634	case LINUX_SG_SET_COMMAND_Q:
635	case LINUX_SG_SET_DEBUG:
636	case LINUX_SG_NEXT_CMD_LEN:
637	default:
638#ifdef CAMDEBUG
639		printf("sgioctl: rejecting cmd 0x%lx\n", cmd);
640#endif
641		error = ENODEV;
642		break;
643	}
644
645	cam_periph_unlock(periph);
646	return (error);
647}
648
649static int
650sgwrite(struct cdev *dev, struct uio *uio, int ioflag)
651{
652	union ccb *ccb;
653	struct cam_periph *periph;
654	struct ccb_scsiio *csio;
655	struct sg_softc *sc;
656	struct sg_header *hdr;
657	struct sg_rdwr *rdwr;
658	u_char cdb_cmd;
659	char *buf;
660	int error = 0, cdb_len, buf_len, dir;
661
662	periph = dev->si_drv1;
663	rdwr = malloc(sizeof(*rdwr), M_DEVBUF, M_WAITOK | M_ZERO);
664	hdr = &rdwr->hdr.hdr;
665
666	/* Copy in the header block and sanity check it */
667	if (uio->uio_resid < sizeof(*hdr)) {
668		error = EINVAL;
669		goto out_hdr;
670	}
671	error = uiomove(hdr, sizeof(*hdr), uio);
672	if (error)
673		goto out_hdr;
674
675	ccb = xpt_alloc_ccb();
676	if (ccb == NULL) {
677		error = ENOMEM;
678		goto out_hdr;
679	}
680	csio = &ccb->csio;
681
682	/*
683	 * Copy in the CDB block.  The designers of the interface didn't
684	 * bother to provide a size for this in the header, so we have to
685	 * figure it out ourselves.
686	 */
687	if (uio->uio_resid < 1)
688		goto out_ccb;
689	error = uiomove(&cdb_cmd, 1, uio);
690	if (error)
691		goto out_ccb;
692	if (hdr->twelve_byte)
693		cdb_len = 12;
694	else
695		cdb_len = scsi_group_len(cdb_cmd);
696	/*
697	 * We've already read the first byte of the CDB and advanced the uio
698	 * pointer.  Just read the rest.
699	 */
700	csio->cdb_io.cdb_bytes[0] = cdb_cmd;
701	error = uiomove(&csio->cdb_io.cdb_bytes[1], cdb_len - 1, uio);
702	if (error)
703		goto out_ccb;
704
705	/*
706	 * Now set up the data block.  Again, the designers didn't bother
707	 * to make this reliable.
708	 */
709	buf_len = uio->uio_resid;
710	if (buf_len != 0) {
711		buf = malloc(buf_len, M_DEVBUF, M_WAITOK | M_ZERO);
712		error = uiomove(buf, buf_len, uio);
713		if (error)
714			goto out_buf;
715		dir = CAM_DIR_OUT;
716	} else if (hdr->reply_len != 0) {
717		buf = malloc(hdr->reply_len, M_DEVBUF, M_WAITOK | M_ZERO);
718		buf_len = hdr->reply_len;
719		dir = CAM_DIR_IN;
720	} else {
721		buf = NULL;
722		buf_len = 0;
723		dir = CAM_DIR_NONE;
724	}
725
726	cam_periph_lock(periph);
727	sc = periph->softc;
728	xpt_setup_ccb(&ccb->ccb_h, periph->path, /*priority*/5);
729	cam_fill_csio(csio,
730		      /*retries*/1,
731		      sgdone,
732		      dir|CAM_DEV_QFRZDIS,
733		      MSG_SIMPLE_Q_TAG,
734		      buf,
735		      buf_len,
736		      SG_MAX_SENSE,
737		      cdb_len,
738		      sc->sg_timeout);
739
740	/*
741	 * Send off the command and hope that it works. This path does not
742	 * go through sgstart because the I/O is supposed to be asynchronous.
743	 */
744	rdwr->buf = buf;
745	rdwr->buf_len = buf_len;
746	rdwr->tag = hdr->pack_id;
747	rdwr->ccb = ccb;
748	rdwr->state = SG_RDWR_INPROG;
749	ccb->ccb_h.ccb_rdwr = rdwr;
750	ccb->ccb_h.ccb_type = SG_CCB_RDWR_IO;
751	TAILQ_INSERT_TAIL(&sc->rdwr_done, rdwr, rdwr_link);
752	error = sgsendrdwr(periph, ccb);
753	cam_periph_unlock(periph);
754	return (error);
755
756out_buf:
757	free(buf, M_DEVBUF);
758out_ccb:
759	xpt_free_ccb(ccb);
760out_hdr:
761	free(rdwr, M_DEVBUF);
762	return (error);
763}
764
765static int
766sgread(struct cdev *dev, struct uio *uio, int ioflag)
767{
768	struct ccb_scsiio *csio;
769	struct cam_periph *periph;
770	struct sg_softc *sc;
771	struct sg_header *hdr;
772	struct sg_rdwr *rdwr;
773	u_short hstat, dstat;
774	int error, pack_len, reply_len, pack_id;
775
776	periph = dev->si_drv1;
777
778	/* XXX The pack len field needs to be updated and written out instead
779	 * of discarded.  Not sure how to do that.
780	 */
781	uio->uio_rw = UIO_WRITE;
782	if ((error = uiomove(&pack_len, 4, uio)) != 0)
783		return (error);
784	if ((error = uiomove(&reply_len, 4, uio)) != 0)
785		return (error);
786	if ((error = uiomove(&pack_id, 4, uio)) != 0)
787		return (error);
788	uio->uio_rw = UIO_READ;
789
790	cam_periph_lock(periph);
791	sc = periph->softc;
792search:
793	TAILQ_FOREACH(rdwr, &sc->rdwr_done, rdwr_link) {
794		if (rdwr->tag == pack_id)
795			break;
796	}
797	if ((rdwr == NULL) || (rdwr->state != SG_RDWR_DONE)) {
798		if (msleep(rdwr, periph->sim->mtx, PCATCH, "sgread", 0) == ERESTART)
799			return (EAGAIN);
800		goto search;
801	}
802	TAILQ_REMOVE(&sc->rdwr_done, rdwr, rdwr_link);
803	cam_periph_unlock(periph);
804
805	hdr = &rdwr->hdr.hdr;
806	csio = &rdwr->ccb->csio;
807	sg_scsiio_status(csio, &hstat, &dstat);
808	hdr->host_status = hstat;
809	hdr->driver_status = dstat;
810	hdr->target_status = csio->scsi_status >> 1;
811
812	switch (hstat) {
813	case DID_OK:
814	case DID_PASSTHROUGH:
815	case DID_SOFT_ERROR:
816		hdr->result = 0;
817		break;
818	case DID_NO_CONNECT:
819	case DID_BUS_BUSY:
820	case DID_TIME_OUT:
821		hdr->result = EBUSY;
822		break;
823	case DID_BAD_TARGET:
824	case DID_ABORT:
825	case DID_PARITY:
826	case DID_RESET:
827	case DID_BAD_INTR:
828	case DID_ERROR:
829	default:
830		hdr->result = EIO;
831		break;
832	}
833
834	if (dstat == DRIVER_SENSE) {
835		bcopy(&csio->sense_data, hdr->sense_buffer,
836		      min(csio->sense_len, SG_MAX_SENSE));
837#ifdef CAMDEBUG
838		scsi_sense_print(csio);
839#endif
840	}
841
842	error = uiomove(&hdr->result, sizeof(*hdr) -
843			offsetof(struct sg_header, result), uio);
844	if ((error == 0) && (hdr->result == 0))
845		error = uiomove(rdwr->buf, rdwr->buf_len, uio);
846
847	cam_periph_lock(periph);
848	xpt_free_ccb(rdwr->ccb);
849	cam_periph_unlock(periph);
850	free(rdwr->buf, M_DEVBUF);
851	free(rdwr, M_DEVBUF);
852	return (error);
853}
854
855static int
856sgsendccb(struct cam_periph *periph, union ccb *ccb)
857{
858	struct sg_softc *softc;
859	struct cam_periph_map_info mapinfo;
860	int error, need_unmap = 0;
861
862	softc = periph->softc;
863	if (((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE)
864	    && (ccb->csio.data_ptr != NULL)) {
865		bzero(&mapinfo, sizeof(mapinfo));
866
867		/*
868		 * cam_periph_mapmem calls into proc and vm functions that can
869		 * sleep as well as trigger I/O, so we can't hold the lock.
870		 * Dropping it here is reasonably safe.
871		 */
872		cam_periph_unlock(periph);
873		error = cam_periph_mapmem(ccb, &mapinfo);
874		cam_periph_lock(periph);
875		if (error)
876			return (error);
877		need_unmap = 1;
878	}
879
880	error = cam_periph_runccb(ccb,
881				  sgerror,
882				  CAM_RETRY_SELTO,
883				  SF_RETRY_UA,
884				  softc->device_stats);
885
886	if (need_unmap)
887		cam_periph_unmapmem(ccb, &mapinfo);
888
889	return (error);
890}
891
892static int
893sgsendrdwr(struct cam_periph *periph, union ccb *ccb)
894{
895	struct sg_softc *softc;
896
897	softc = periph->softc;
898	devstat_start_transaction(softc->device_stats, NULL);
899	xpt_action(ccb);
900	return (0);
901}
902
903static int
904sgerror(union ccb *ccb, uint32_t cam_flags, uint32_t sense_flags)
905{
906	struct cam_periph *periph;
907	struct sg_softc *softc;
908
909	periph = xpt_path_periph(ccb->ccb_h.path);
910	softc = (struct sg_softc *)periph->softc;
911
912	return (cam_periph_error(ccb, cam_flags, sense_flags,
913				 &softc->saved_ccb));
914}
915
916static void
917sg_scsiio_status(struct ccb_scsiio *csio, u_short *hoststat, u_short *drvstat)
918{
919	int status;
920
921	status = csio->ccb_h.status;
922
923	switch (status & CAM_STATUS_MASK) {
924	case CAM_REQ_CMP:
925		*hoststat = DID_OK;
926		*drvstat = 0;
927		break;
928	case CAM_REQ_CMP_ERR:
929		*hoststat = DID_ERROR;
930		*drvstat = 0;
931		break;
932	case CAM_REQ_ABORTED:
933		*hoststat = DID_ABORT;
934		*drvstat = 0;
935		break;
936	case CAM_REQ_INVALID:
937		*hoststat = DID_ERROR;
938		*drvstat = DRIVER_INVALID;
939		break;
940	case CAM_DEV_NOT_THERE:
941		*hoststat = DID_BAD_TARGET;
942		*drvstat = 0;
943	case CAM_SEL_TIMEOUT:
944		*hoststat = DID_NO_CONNECT;
945		*drvstat = 0;
946		break;
947	case CAM_CMD_TIMEOUT:
948		*hoststat = DID_TIME_OUT;
949		*drvstat = 0;
950		break;
951	case CAM_SCSI_STATUS_ERROR:
952		*hoststat = DID_ERROR;
953		*drvstat = 0;
954	case CAM_SCSI_BUS_RESET:
955		*hoststat = DID_RESET;
956		*drvstat = 0;
957		break;
958	case CAM_UNCOR_PARITY:
959		*hoststat = DID_PARITY;
960		*drvstat = 0;
961		break;
962	case CAM_SCSI_BUSY:
963		*hoststat = DID_BUS_BUSY;
964		*drvstat = 0;
965	default:
966		*hoststat = DID_ERROR;
967		*drvstat = DRIVER_ERROR;
968	}
969
970	if (status & CAM_AUTOSNS_VALID)
971		*drvstat = DRIVER_SENSE;
972}
973
974static int
975scsi_group_len(u_char cmd)
976{
977	int len[] = {6, 10, 10, 12, 12, 12, 10, 10};
978	int group;
979
980	group = (cmd >> 5) & 0x7;
981	return (len[group]);
982}
983
984