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