xref: /netbsd-src/sys/dev/scsipi/scsipi_ioctl.c (revision 93f9db1b75d415b78f73ed629beeb86235153473)
1 /*	$NetBSD: scsipi_ioctl.c,v 1.34 1998/10/10 02:35:30 thorpej Exp $	*/
2 
3 /*-
4  * Copyright (c) 1998 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Charles M. Hannum.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *        This product includes software developed by the NetBSD
21  *        Foundation, Inc. and its contributors.
22  * 4. Neither the name of The NetBSD Foundation nor the names of its
23  *    contributors may be used to endorse or promote products derived
24  *    from this software without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  */
38 
39 /*
40  * Contributed by HD Associates (hd@world.std.com).
41  * Copyright (c) 1992, 1993 HD Associates
42  *
43  * Berkeley style copyright.
44  */
45 
46 #include "opt_compat_freebsd.h"
47 #include "opt_compat_netbsd.h"
48 
49 #include <sys/types.h>
50 #include <sys/errno.h>
51 #include <sys/param.h>
52 #include <sys/systm.h>
53 #include <sys/malloc.h>
54 #include <sys/buf.h>
55 #include <sys/proc.h>
56 #include <sys/device.h>
57 #include <sys/fcntl.h>
58 
59 #include <dev/scsipi/scsipi_all.h>
60 #include <dev/scsipi/scsi_all.h>
61 #include <dev/scsipi/scsipiconf.h>
62 #include <dev/scsipi/scsiconf.h>
63 #include <sys/scsiio.h>
64 
65 #include "scsibus.h"
66 #include "atapibus.h"
67 
68 struct scsi_ioctl {
69 	LIST_ENTRY(scsi_ioctl) si_list;
70 	struct buf si_bp;
71 	struct uio si_uio;
72 	struct iovec si_iov;
73 	scsireq_t si_screq;
74 	struct scsipi_link *si_sc_link;
75 };
76 
77 LIST_HEAD(, scsi_ioctl) si_head;
78 
79 struct	scsi_ioctl *si_find __P((struct buf *));
80 void	si_free __P((struct scsi_ioctl *));
81 struct	scsi_ioctl *si_get __P((void));
82 void	scsistrategy __P((struct buf *));
83 
84 struct scsi_ioctl *
85 si_get()
86 {
87 	struct scsi_ioctl *si;
88 	int s;
89 
90 	si = malloc(sizeof(struct scsi_ioctl), M_TEMP, M_WAITOK);
91 	bzero(si, sizeof(struct scsi_ioctl));
92 	s = splbio();
93 	LIST_INSERT_HEAD(&si_head, si, si_list);
94 	splx(s);
95 	return (si);
96 }
97 
98 void
99 si_free(si)
100 	struct scsi_ioctl *si;
101 {
102 	int s;
103 
104 	s = splbio();
105 	LIST_REMOVE(si, si_list);
106 	splx(s);
107 	free(si, M_TEMP);
108 }
109 
110 struct scsi_ioctl *
111 si_find(bp)
112 	struct buf *bp;
113 {
114 	struct scsi_ioctl *si;
115 	int s;
116 
117 	s = splbio();
118 	for (si = si_head.lh_first; si != 0; si = si->si_list.le_next)
119 		if (bp == &si->si_bp)
120 			break;
121 	splx(s);
122 	return (si);
123 }
124 
125 /*
126  * We let the user interpret his own sense in the generic scsi world.
127  * This routine is called at interrupt time if the SCSI_USER bit was set
128  * in the flags passed to scsi_scsipi_cmd(). No other completion processing
129  * takes place, even if we are running over another device driver.
130  * The lower level routines that call us here, will free the xs and restart
131  * the device's queue if such exists.
132  */
133 void
134 scsipi_user_done(xs)
135 	struct scsipi_xfer *xs;
136 {
137 	struct buf *bp;
138 	struct scsi_ioctl *si;
139 	scsireq_t *screq;
140 	struct scsipi_link *sc_link;
141 
142 	bp = xs->bp;
143 	if (bp == NULL) {	/* ALL user requests must have a buf */
144 		xs->sc_link->sc_print_addr(xs->sc_link);
145 		printf("User command with no buf\n");
146 		return;
147 	}
148 	si = si_find(bp);
149 	if (si == NULL) {
150 		xs->sc_link->sc_print_addr(xs->sc_link);
151 		printf("User command with no ioctl\n");
152 		return;
153 	}
154 	screq = &si->si_screq;
155 	sc_link = si->si_sc_link;
156 	SC_DEBUG(xs->sc_link, SDEV_DB2, ("user-done\n"));
157 
158 	screq->retsts = 0;
159 	screq->status = xs->status;
160 	switch (xs->error) {
161 	case XS_NOERROR:
162 		SC_DEBUG(sc_link, SDEV_DB3, ("no error\n"));
163 		screq->datalen_used =
164 		    xs->datalen - xs->resid;	/* probably rubbish */
165 		screq->retsts = SCCMD_OK;
166 		break;
167 	case XS_SENSE:
168 		SC_DEBUG(sc_link, SDEV_DB3, ("have sense\n"));
169 		screq->senselen_used = min(sizeof(xs->sense.scsi_sense),
170 		    SENSEBUFLEN);
171 		bcopy(&xs->sense.scsi_sense, screq->sense, screq->senselen);
172 		screq->retsts = SCCMD_SENSE;
173 		break;
174 	case XS_DRIVER_STUFFUP:
175 		sc_link->sc_print_addr(sc_link);
176 		printf("host adapter code inconsistency\n");
177 		screq->retsts = SCCMD_UNKNOWN;
178 		break;
179 	case XS_TIMEOUT:
180 		SC_DEBUG(sc_link, SDEV_DB3, ("timeout\n"));
181 		screq->retsts = SCCMD_TIMEOUT;
182 		break;
183 	case XS_BUSY:
184 		SC_DEBUG(sc_link, SDEV_DB3, ("busy\n"));
185 		screq->retsts = SCCMD_BUSY;
186 		break;
187 	default:
188 		sc_link->sc_print_addr(sc_link);
189 		printf("unknown error category from host adapter code\n");
190 		screq->retsts = SCCMD_UNKNOWN;
191 		break;
192 	}
193 	biodone(bp); 	/* we're waiting on it in scsi_strategy() */
194 }
195 
196 
197 /* Pseudo strategy function
198  * Called by scsipi_do_ioctl() via physio/physstrat if there is to
199  * be data transfered, and directly if there is no data transfer.
200  *
201  * Should I reorganize this so it returns to physio instead
202  * of sleeping in scsiio_scsipi_cmd?  Is there any advantage, other
203  * than avoiding the probable duplicate wakeup in iodone? [PD]
204  *
205  * No, seems ok to me... [JRE]
206  * (I don't see any duplicate wakeups)
207  *
208  * Can't be used with block devices or raw_read/raw_write directly
209  * from the cdevsw/bdevsw tables because they couldn't have added
210  * the screq structure. [JRE]
211  */
212 void
213 scsistrategy(bp)
214 	struct buf *bp;
215 {
216 	struct scsi_ioctl *si;
217 	scsireq_t *screq;
218 	struct scsipi_link *sc_link;
219 	int error;
220 	int flags = 0;
221 	int s;
222 
223 	si = si_find(bp);
224 	if (si == NULL) {
225 		printf("user_strat: No ioctl\n");
226 		error = EINVAL;
227 		goto bad;
228 	}
229 	screq = &si->si_screq;
230 	sc_link = si->si_sc_link;
231 	SC_DEBUG(sc_link, SDEV_DB2, ("user_strategy\n"));
232 
233 	/*
234 	 * We're in trouble if physio tried to break up the transfer.
235 	 */
236 	if (bp->b_bcount != screq->datalen) {
237 		sc_link->sc_print_addr(sc_link);
238 		printf("physio split the request.. cannot proceed\n");
239 		error = EIO;
240 		goto bad;
241 	}
242 
243 	if (screq->timeout == 0) {
244 		error = EINVAL;
245 		goto bad;
246 	}
247 
248 	if (screq->cmdlen > sizeof(struct scsipi_generic)) {
249 		sc_link->sc_print_addr(sc_link);
250 		printf("cmdlen too big\n");
251 		error = EFAULT;
252 		goto bad;
253 	}
254 
255 	if (screq->flags & SCCMD_READ)
256 		flags |= SCSI_DATA_IN;
257 	if (screq->flags & SCCMD_WRITE)
258 		flags |= SCSI_DATA_OUT;
259 	if (screq->flags & SCCMD_TARGET)
260 		flags |= SCSI_TARGET;
261 	if (screq->flags & SCCMD_ESCAPE)
262 		flags |= SCSI_ESCAPE;
263 
264 	error = scsipi_command(sc_link,
265 	    (struct scsipi_generic *)screq->cmd, screq->cmdlen,
266 	    (u_char *)bp->b_data, screq->datalen,
267 	    0, /* user must do the retries *//* ignored */
268 	    screq->timeout, bp, flags | SCSI_USER | SCSI_NOSLEEP);
269 
270 	/* because there is a bp, scsi_scsipi_cmd will return immediatly */
271 	if (error)
272 		goto bad;
273 
274 	SC_DEBUG(sc_link, SDEV_DB3, ("about to sleep\n"));
275 	s = splbio();
276 	while ((bp->b_flags & B_DONE) == 0)
277 		tsleep(bp, PRIBIO, "scistr", 0);
278 	splx(s);
279 	SC_DEBUG(sc_link, SDEV_DB3, ("back from sleep\n"));
280 
281 	return;
282 
283 bad:
284 	bp->b_flags |= B_ERROR;
285 	bp->b_error = error;
286 	biodone(bp);
287 }
288 
289 /*
290  * Something (e.g. another driver) has called us
291  * with an sc_link for a target/lun/adapter, and a scsi
292  * specific ioctl to perform, better try.
293  * If user-level type command, we must still be running
294  * in the context of the calling process
295  */
296 int
297 scsipi_do_ioctl(sc_link, dev, cmd, addr, flag, p)
298 	struct scsipi_link *sc_link;
299 	dev_t dev;
300 	u_long cmd;
301 	caddr_t addr;
302 	int flag;
303 	struct proc *p;
304 {
305 	int error;
306 
307 	SC_DEBUG(sc_link, SDEV_DB2, ("scsipi_do_ioctl(0x%lx)\n", cmd));
308 
309 	/* Check for the safe-ness of this request. */
310 	switch (cmd) {
311 	case OSCIOCIDENTIFY:
312 	case SCIOCIDENTIFY:
313 		break;
314 	case SCIOCCOMMAND:
315 		if ((((scsireq_t *)addr)->flags & SCCMD_READ) == 0 &&
316 		    (flag & FWRITE) == 0)
317 			return (EBADF);
318 		break;
319 	default:
320 		if ((flag & FWRITE) == 0)
321 			return (EBADF);
322 	}
323 
324 	switch (cmd) {
325 	case SCIOCCOMMAND: {
326 		scsireq_t *screq = (scsireq_t *)addr;
327 		struct scsi_ioctl *si;
328 		int len;
329 
330 		si = si_get();
331 		si->si_screq = *screq;
332 		si->si_sc_link = sc_link;
333 		len = screq->datalen;
334 		if (len) {
335 			si->si_iov.iov_base = screq->databuf;
336 			si->si_iov.iov_len = len;
337 			si->si_uio.uio_iov = &si->si_iov;
338 			si->si_uio.uio_iovcnt = 1;
339 			si->si_uio.uio_resid = len;
340 			si->si_uio.uio_offset = 0;
341 			si->si_uio.uio_segflg = UIO_USERSPACE;
342 			si->si_uio.uio_rw =
343 			    (screq->flags & SCCMD_READ) ? UIO_READ : UIO_WRITE;
344 			si->si_uio.uio_procp = p;
345 			error = physio(scsistrategy, &si->si_bp, dev,
346 			    (screq->flags & SCCMD_READ) ? B_READ : B_WRITE,
347 			    sc_link->adapter->scsipi_minphys, &si->si_uio);
348 		} else {
349 			/* if no data, no need to translate it.. */
350 			si->si_bp.b_flags = 0;
351 			si->si_bp.b_data = 0;
352 			si->si_bp.b_bcount = 0;
353 			si->si_bp.b_dev = dev;
354 			si->si_bp.b_proc = p;
355 			scsistrategy(&si->si_bp);
356 			error = si->si_bp.b_error;
357 		}
358 		*screq = si->si_screq;
359 		si_free(si);
360 		return (error);
361 	}
362 	case SCIOCDEBUG: {
363 		int level = *((int *)addr);
364 
365 		SC_DEBUG(sc_link, SDEV_DB3, ("debug set to %d\n", level));
366 		sc_link->flags &= ~SDEV_DBX; /* clear debug bits */
367 		if (level & 1)
368 			sc_link->flags |= SDEV_DB1;
369 		if (level & 2)
370 			sc_link->flags |= SDEV_DB2;
371 		if (level & 4)
372 			sc_link->flags |= SDEV_DB3;
373 		if (level & 8)
374 			sc_link->flags |= SDEV_DB4;
375 		return (0);
376 	}
377 	case SCIOCRECONFIG:
378 	case SCIOCDECONFIG:
379 		return (EINVAL);
380 	case SCIOCIDENTIFY: {
381 		struct scsi_addr *sca = (struct scsi_addr *)addr;
382 
383 		switch (sc_link->type) {
384 		case BUS_SCSI:
385 			sca->type = TYPE_SCSI;
386 			sca->addr.scsi.scbus = sc_link->scsipi_scsi.scsibus;
387 			sca->addr.scsi.target = sc_link->scsipi_scsi.target;
388 			sca->addr.scsi.lun = sc_link->scsipi_scsi.lun;
389 			return (0);
390 		case BUS_ATAPI:
391 			sca->type = TYPE_ATAPI;
392 			sca->addr.atapi.atbus = sc_link->scsipi_atapi.atapibus;
393 			sca->addr.atapi.drive = sc_link->scsipi_atapi.drive;
394 			return (0);
395 		}
396 		return (ENXIO);
397 	}
398 #if defined(COMPAT_12) || defined(COMPAT_FREEBSD)
399 	/* SCIOCIDENTIFY before ATAPI staff merge */
400 	case OSCIOCIDENTIFY: {
401 		struct oscsi_addr *sca = (struct oscsi_addr *)addr;
402 
403 		switch (sc_link->type) {
404 		case BUS_SCSI:
405 			sca->scbus = sc_link->scsipi_scsi.scsibus;
406 			sca->target = sc_link->scsipi_scsi.target;
407 			sca->lun = sc_link->scsipi_scsi.lun;
408 			return (0);
409 		}
410 		return (ENODEV);
411 	}
412 #endif
413 	default:
414 		return (ENOTTY);
415 	}
416 
417 #ifdef DIAGNOSTIC
418 	panic("scsipi_do_ioctl: impossible");
419 #endif
420 }
421