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