from John Dyson:
[unix-history] / sys / scsi / sg.c
CommitLineData
d4284689
RG
1/*
2 * Contributed by HD Associates (hd@world.std.com).
3 * Copyright (c) 1992, 1993 HD Associates
4 *
5 * Berkeley style copyright. I've just snarfed it out of stdio.h:
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by the University of
18 * California, Berkeley and its contributors.
19 * 4. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 *
01e1b05c
RG
35 * from: @(#)stdio.h 5.17 (Berkeley) 6/3/91
36 * $Id$
d4284689
RG
37 */
38#include <sys/types.h>
39#include <sys/errno.h>
40#include <sys/param.h>
41#include <sys/malloc.h>
42#include <sys/buf.h>
43#include <sys/proc.h>
44
45#include <scsi/scsi_all.h>
46#include <scsi/scsiconf.h>
47#include <scsi/scsi_generic.h>
48#include "sg.h"
49#include <sys/sgio.h>
50
51#define SGOUTSTANDING 2
52#define SG_RETRIES 2
53#define SPLSG splbio
54
55/* Use one of the implementation defined spare bits
56 * to indicate the escape op:
57 */
58#define DSRQ_ESCAPE DSRQ_CTRL1
59
60struct sg
61{
62 int flags;
63 struct scsi_switch *sc_sw;
64 int ctlr;
65
66 long int ad_info; /* info about the adapter */
67 int cmdscount; /* cmds allowed outstanding by the board */
68
69 struct scsi_xfer *free_xfer;
70 int free_xfer_wait;
71};
72
73/* This is used to associate a struct dsreq and a struct buf.
74 */
75typedef struct dsbuf
76{
77 dsreq_t *dsreq;
78 struct buf buf;
79
80 /* I think this is a portable way to get back to the base of
81 * the enclosing structure:
82 */
83# define DSBUF_P(BP) ((dsbuf_t *)((caddr_t)(BP) - (caddr_t)&((dsbuf_t *)0)->buf))
84
85 int magic;
86
87# define DSBUF_MAGIC 0xDBFACDBF
88} dsbuf_t;
89
90#if NSG > 4
91/* The host adapter unit is encoded in the upper 2 bits of the minor number
92 * (the SGI flag bits).
93 */
94#error "NSG can't be > 4 unless the method of encoding the board unit changes"
95#endif
96
97struct sg *sgs[NSG];
98
99#define SG(DEV) sgs[G_SCSI_UNIT(DEV)]
100
101struct sg *sg_new(int lun)
102{
103 struct sg *sg = (struct sg *)malloc(sizeof(*sg),M_TEMP, M_NOWAIT);
104
105 if (sg == 0)
106 return 0;
107
108 bzero(sg, sizeof(struct sg));
109
110 return sg;
111}
112
113int sg_attach(ctlr, scsi_addr, scsi_switch)
114int ctlr,scsi_addr;
115struct scsi_switch *scsi_switch;
116{
117 struct sg *sg;
118 int i;
119 struct scsi_xfer *scsi_xfer;
120 static int next_sg_unit = 0;
121
122 int unit = next_sg_unit++;
123
124 if (unit >= NSG)
125 {
126 printf("Too many generic SCSIs (%d > %d); reconfigure the kernel.\n",
127 unit+1, NSG);
128
129 if (NSG == 4)
130 printf(
131 "You have hit the max of 4. You will have to change the driver.\n");
132
133 return 0;
134 }
135
136 if ((sg = sg_new(0)) == 0)
137 return 0;
138
139 sgs[unit] = sg;
140
141 sg->sc_sw = scsi_switch;
142 sg->ctlr = ctlr;
143
144 /* This is a bit confusing. It looks like Julian calls back into the
145 * adapter to find out how many outstanding transactions it can
146 * handle. How does he handle a tape/disk combo?
147 */
148
149 if (sg->sc_sw->adapter_info)
150 {
151 sg->ad_info = ( (*(sg->sc_sw->adapter_info))(unit));
152 sg->cmdscount = sg->ad_info & AD_INF_MAX_CMDS;
153 if(sg->cmdscount > SGOUTSTANDING)
154 sg->cmdscount = SGOUTSTANDING;
155 }
156 else
157 {
158 sg->ad_info = 1;
159 sg->cmdscount = 1;
160 }
161
162 i = sg->cmdscount;
163
164 scsi_xfer = (struct scsi_xfer *)malloc(sizeof(struct scsi_xfer) *
165 i, M_TEMP, M_NOWAIT);
166
167 if (scsi_xfer == 0)
168 {
169 printf("scsi_generic: Can't malloc.\n");
170 return 0;
171 }
172
173 while (i--)
174 {
175 scsi_xfer->next = sg->free_xfer;
176 sg->free_xfer = scsi_xfer;
177 scsi_xfer++;
178 }
179
180#ifndef EMBEDDED
181 if (unit == 0)
182 printf(" /dev/gs%d (instance 0) generic SCSI via controller %d\n",
183 scsi_addr, sg->ctlr);
184 else
185 printf(" /dev/gs%d-%d generic SCSI via controller %d\n",
186 unit, scsi_addr, sg->ctlr);
187#endif
188
189 return 1;
190}
191
192/* It is trivial to add support for processor target devices
193 * here - enable target mode on open and disable on close
194 * if a flag bit is set in the minor number
195 */
196int sgopen(dev_t dev)
197{
198 if (SG(dev) == 0)
199 return ENXIO;
200
201 return 0;
202}
203
204int sgclose(dev_t dev)
205{
206 return 0;
207}
208
209
210/* Free a scsi_xfer, wake processes waiting for it
211 */
212void sg_free_xs(dev_t dev, struct scsi_xfer *xs, int flags)
213{
214 int s;
215 struct sg *sg = SG(dev);
216
217 if(flags & SCSI_NOMASK)
218 {
219 if (sg->free_xfer_wait)
220 {
221 printf("sg_free_xs: doing a wakeup from NOMASK mode!\n");
222 wakeup((caddr_t)&sg->free_xfer);
223 }
224 xs->next = sg->free_xfer;
225 sg->free_xfer = xs;
226 }
227 else
228 {
229 s = SPLSG();
230 if (sg->free_xfer_wait)
231 wakeup((caddr_t)&sg->free_xfer);
232 xs->next = sg->free_xfer;
233 sg->free_xfer = xs;
234 splx(s);
235 }
236}
237
238/* Get ownership of a scsi_xfer
239 * If need be, sleep on it, until it comes free
240 */
241struct scsi_xfer *sg_get_xs(dev_t dev, int flags)
242{
243 struct scsi_xfer *xs;
244 int s;
245 struct sg *sg = SG(dev);
246
247 if(flags & (SCSI_NOSLEEP | SCSI_NOMASK))
248 {
249 if (xs = sg->free_xfer)
250 {
251 sg->free_xfer = xs->next;
252 xs->flags = 0;
253 }
254 }
255 else
256 {
257 s = SPLSG();
258 while (!(xs = sg->free_xfer))
259 {
260 sg->free_xfer_wait++; /* someone waiting! */
261 sleep((caddr_t)&sg->free_xfer, PRIBIO+1);
262 sg->free_xfer_wait--;
263 }
264 sg->free_xfer = xs->next;
265 splx(s);
266 xs->flags = 0;
267 }
268
269 return xs;
270}
271
272/* We let the user interpret his own sense in the
273 * generic scsi world
274 */
275int sg_interpret_sense(dev_t dev, struct scsi_xfer *xs, int *flag_p)
276{
277 return 0;
278}
279
280/* ITSDONE is really used for things that are marked one
281 * in the interrupt. I'll leave the logic in in case I want
282 * to move done processing (and therefore have a start queue)
283 * back into the interrupt.
284 * BUG: No start queue.
285 */
286
287int sg_done(dev_t dev,
288struct scsi_xfer *xs)
289{
290 xs->flags |= ITSDONE;
291 wakeup(xs);
292 return 0;
293}
294
295int sg_submit_cmd(dev_t dev, struct scsi_xfer *xs, dsreq_t *dsreq)
296{
297 int retval;
298
299 struct sg *sg = SG(dev);
300
301retry:
302 xs->error = XS_NOERROR;
303
304 xs->bp = 0; /* This bp doesn't seem to be used except to
305 * disable sleeping in the host adapter code.
306 * "st" does set it up, though.
307 */
308
309 retval = (*(sg->sc_sw->scsi_cmd))(xs);
310
311 switch(retval)
312 {
313 case SUCCESSFULLY_QUEUED:
314 while(!(xs->flags & ITSDONE))
315 sleep(xs,PRIBIO+1);
316
317 /* Fall through... */
318
319 case HAD_ERROR:
320
321 if (dsreq)
322 dsreq->ds_status = xs->status;
323
324 switch(xs->error)
325 {
326 case XS_NOERROR:
327 if (dsreq)
328 dsreq->ds_datasent = dsreq->ds_datalen - xs->resid;
329 retval = 0;
330 break;
331
332 case XS_SENSE:
333 retval = (sg_interpret_sense(dev ,xs, (int *)0));
334 if (dsreq)
335 {
336 dsreq->ds_sensesent = sizeof(xs->sense);
337 dsreq->ds_ret = DSRT_SENSE;
338 }
339 retval = 0;
340 break;
341
342 case XS_DRIVER_STUFFUP:
343 if (dsreq)
344 dsreq->ds_ret = DSRT_HOST;
345 printf("sg%d: host adapter code inconsistency\n" ,G_SCSI_UNIT(dev));
346 retval = EIO;
347 break;
348
349 case XS_TIMEOUT:
350 if (dsreq)
351 dsreq->ds_ret = DSRT_TIMEOUT;
352 retval = ETIMEDOUT;
353 break;
354
355 case XS_BUSY:
356 if(xs->retries-- )
357 {
358 xs->flags &= ~ITSDONE;
359 goto retry;
360 }
361 retval = EBUSY;
362 break;
363
364 default:
365 printf("sg%d: unknown error category from host adapter code\n"
366 ,G_SCSI_UNIT(dev));
367 retval = EIO;
368 break;
369 }
370 break;
371
372 case COMPLETE:
373 if (dsreq)
374 dsreq->ds_datasent = dsreq->ds_datalen - xs->resid;
375 retval = 0;
376 break;
377
378 case TRY_AGAIN_LATER:
379 if(xs->retries-- )
380 {
381 xs->flags &= ~ITSDONE;
382 goto retry;
383 }
384 retval = EBUSY;
385 break;
386
387 case ESCAPE_NOT_SUPPORTED:
388 retval = ENOSYS; /* "Function not implemented" */
389 break;
390
391 default:
392 printf("sg%d: illegal return from host adapter code\n",
393 G_SCSI_UNIT(dev));
394 retval = EIO;
395 break;
396 }
397
398 return retval;
399}
400
401/* sg_escape: Do a generic SCSI escape
402 */
403int sg_escape(dev_t dev, int op_code, u_char *b, int nb)
404{
405 int retval;
406
407 struct scsi_generic scsi_generic;
408
409 int flags = SCSI_ESCAPE;
410
411 struct scsi_xfer *xs;
412 struct sg *sg = SG(dev);
413
414 xs = sg_get_xs(dev, flags);
415
416 if (xs == 0)
417 {
418 printf("sg_target%d: controller busy"
419 " (this should never happen)\n",G_SCSI_UNIT(dev));
420 return EBUSY;
421 }
422
423 scsi_generic.opcode = op_code;
424 bcopy(b, scsi_generic.bytes, nb);
425
426 /* Fill out the scsi_xfer structure
427 */
428 xs->flags = (flags|INUSE);
429 xs->adapter = sg->ctlr;
430 xs->cmd = &scsi_generic;
431 xs->targ = G_SCSI_ID(dev);
432 xs->lu = G_SCSI_LUN(dev);
433 xs->retries = SG_RETRIES;
434 xs->timeout = 100;
435 xs->when_done = (flags & SCSI_NOMASK)
436 ?(int (*)())0
437 :(int (*)())sg_done;
438 xs->done_arg = dev;
439 xs->done_arg2 = (int)xs;
440
441 xs->status = 0;
442
443 retval = sg_submit_cmd(dev, xs, 0);
444
445 bcopy(scsi_generic.bytes, b, nb);
446
447 sg_free_xs(dev,xs,flags);
448
449 return retval;
450}
451
452/* sg_target: Turn on / off target mode
453 */
454int sg_target(dev_t dev, int enable)
455{
456 u_char b0 = enable;
457 return sg_escape(dev, SCSI_OP_TARGET, &b0, 1);
458}
459
460#ifdef EMBEDDED
461/* This should REALLY be a select call!
462 * This is used in a stand alone system without an O/S. I didn't
463 * have the time to add select, which the system was missing,
464 * so I added this stuff to poll for the async arrival of
465 * connections for target mode.
466 */
467int sg_poll(dev_t dev, int *send, int *recv)
468{
469 scsi_op_poll_t s;
470 int ret;
471
472 ret = sg_escape(dev, SCSI_OP_POLL, (u_char *)&s, sizeof(s));
473
474 if (ret == 0)
475 {
476 *send = s.send;
477 *recv = s.recv;
478 }
479
480 return ret;
481}
482#endif /* EMBEDDED */
483
484int sg_scsi_cmd(dev_t dev,
485dsreq_t *dsreq,
486struct scsi_generic *scsi_cmd,
487u_char *d_addr,
488long d_count,
489struct scsi_sense_data *scsi_sense)
490{
491 int retval;
492
493 int flags = 0;
494 struct scsi_xfer *xs;
495 struct sg *sg = SG(dev);
496
497 if (sg->sc_sw == 0)
498 return ENODEV;
499
500 dsreq->ds_status = 0;
501 dsreq->ds_sensesent = 0;
502
503 if (dsreq->ds_flags & DSRQ_READ)
504 flags |= SCSI_DATA_IN;
505
506 if (dsreq->ds_flags & DSRQ_WRITE)
507 flags |= SCSI_DATA_OUT;
508
509 if (dsreq->ds_flags & DSRQ_TARGET)
510 flags |= SCSI_TARGET;
511
512 if (dsreq->ds_flags & DSRQ_ESCAPE)
513 flags |= SCSI_ESCAPE;
514
515#ifdef SCSI_PHYSADDR
516 if (dsreq->ds_flags & DSRQ_PHYSADDR)
517 flags |= SCSI_PHYSADDR;
518#endif
519
520 xs = sg_get_xs(dev, flags);
521
522 if (xs == 0)
523 {
524 printf("sg_scsi_cmd%d: controller busy"
525 " (this should never happen)\n",G_SCSI_UNIT(dev));
526
527 return EBUSY;
528 }
529
530 /* Fill out the scsi_xfer structure
531 */
532 xs->flags |= (flags|INUSE);
533 xs->adapter = sg->ctlr;
534 xs->targ = G_SCSI_ID(dev);
535 xs->lu = G_SCSI_LUN(dev);
536 xs->retries = SG_RETRIES;
537 xs->timeout = dsreq->ds_time;
538 xs->cmd = scsi_cmd;
539 xs->cmdlen = dsreq->ds_cmdlen;
540 xs->data = d_addr;
541 xs->datalen = d_count;
542 xs->resid = d_count;
543 xs->when_done = (flags & SCSI_NOMASK)
544 ?(int (*)())0
545 :(int (*)())sg_done;
546 xs->done_arg = dev;
547 xs->done_arg2 = (int)xs;
548
549 xs->req_sense_length = (dsreq->ds_senselen < sizeof(struct scsi_sense_data))
550 ? dsreq->ds_senselen
551 : sizeof(struct scsi_sense_data);
552 xs->status = 0;
553
554 retval = sg_submit_cmd(dev, xs, dsreq);
555
556 if (dsreq->ds_ret == DSRT_SENSE)
557 bcopy(&(xs->sense), scsi_sense, sizeof(xs->sense));
558
559 sg_free_xs(dev,xs,flags);
560
561 return retval;
562}
563
564void sgerr(struct buf *bp, int err)
565{
566 bp->b_error = err;
567 bp->b_flags |= B_ERROR;
568
569 iodone(bp);
570}
571
572/* strategy function
573 *
574 * Should I reorganize this so it returns to physio instead
575 * of sleeping in sg_scsi_cmd? Is there any advantage, other
576 * than avoiding the probable duplicate wakeup in iodone?
577 *
578 * Don't create a block device entry point for this
579 * driver without making some fixes:
580 * you have to be able to go from the bp to the dsreq somehow.
581 */
582void sgstrategy(struct buf *bp)
583{
584 int err;
585 struct scsi_generic scsi_generic;
586 struct scsi_sense_data scsi_sense;
587 int lun = G_SCSI_LUN(bp->b_dev);
588
589 dsbuf_t *dsbuf = DSBUF_P(bp);
590 dsreq_t *dsreq;
591
592 if (dsbuf->magic != DSBUF_MAGIC)
593 {
594 printf("sgstrategy: struct buf not magic.\n");
595 sgerr(bp, EFAULT);
596 return;
597 }
598
599 dsreq = dsbuf->dsreq;
600
601 /* We're in trouble if physio tried to break up the
602 * transfer:
603 */
604 if (bp->b_bcount != dsreq->ds_datalen)
605 {
606 printf("sgstrategy unit%d: Transfer broken up.\n",
607 G_SCSI_UNIT(bp->b_dev));
608 sgerr(bp, EIO);
609 return;
610 }
611
612 dsreq->ds_ret = DSRT_OK;
613
614 /* Reject 0 length timeouts.
615 */
616 if (dsreq->ds_time == 0)
617 {
618 sgerr(bp, EINVAL);
619 return;
620 }
621
622 if (dsreq->ds_cmdlen > sizeof(struct scsi_generic))
623 {
624 sgerr(bp, EFAULT);
625 return;
626 }
627
628 copyin(dsreq->ds_cmdbuf, (char *)&scsi_generic, dsreq->ds_cmdlen);
629
630 /* Use device unit for the LUN. Using the one the user provided
631 * would be a huge security problem.
632 */
633 if ((dsreq->ds_flags & DSRQ_ESCAPE) == 0)
634 scsi_generic.bytes[0] = (scsi_generic.bytes[0] & 0x1F) | (lun << 5);
635
636 err = sg_scsi_cmd(bp->b_dev, dsreq,
637 &scsi_generic,
638 (u_char *)bp->b_un.b_addr,
639 bp->b_bcount,
640 &scsi_sense);
641
642 if (dsreq->ds_sensesent)
643 {
644 if (dsreq->ds_sensesent > dsreq->ds_senselen)
645 dsreq->ds_sensesent = dsreq->ds_senselen;
646
647 copyout(&scsi_sense, dsreq->ds_sensebuf, dsreq->ds_sensesent);
648 }
649
650 if (err)
651 {
652 if (dsreq->ds_ret == DSRT_OK)
653 dsreq->ds_ret = DSRT_DEVSCSI;
654
655 sgerr(bp, err);
656 return;
657 }
658
659 /* This is a fake. It would be nice to know if the
660 * command was sent or not instead of pretending it was if
661 * we get this far. That would involve adding "sent" members
662 * to the xs so it could be set up down in the host adapter code.
663 */
664 dsreq->ds_cmdsent = dsreq->ds_cmdlen;
665
666 if (dsreq->ds_ret == 0)
667 dsreq->ds_ret = DSRT_OK;
668
669 iodone(bp); /* Shouldn't this iodone be done in the interrupt?
670 */
671
672 return;
673}
674
675void sgminphys(struct buf *bp)
676{
677}
678
679int sgioctl(dev_t dev, int cmd, caddr_t addr, int f)
680{
681 int ret = 0;
682 int phys;
683
684 switch(cmd)
685 {
686 case DS_ENTER:
687 {
688 dsreq_t *dsreq = (dsreq_t *)addr;
689
690 int rwflag = (dsreq->ds_flags & DSRQ_READ) ? B_READ : B_WRITE;
691
692 struct dsbuf dsbuf;
693 struct buf *bp = &dsbuf.buf;
694
695 bzero(&dsbuf, sizeof(dsbuf));
696
697 dsbuf.dsreq = dsreq;
698 dsbuf.magic = DSBUF_MAGIC;
699
700#ifdef SCSI_PHYSADDR /* Physical memory addressing option */
701 phys = (dsreq->ds_flags & DSRQ_PHYSADDR);
702#else
703 phys = 0;
704#endif
705
706 if (phys)
707 {
708 bp->b_un.b_addr = dsreq->ds_databuf;
709 bp->b_bcount = dsreq->ds_datalen;
710 bp->b_dev = dev;
711 bp->b_flags = rwflag;
712
713 sgstrategy(bp);
714 ret = bp->b_error;
715 }
716 else if (dsreq->ds_datalen)
717 {
718 struct uio uio;
719 struct iovec iovec;
720
721 iovec.iov_base = dsreq->ds_databuf;
722 iovec.iov_len = dsreq->ds_datalen;
723
724 uio.uio_offset = 0;
725 uio.uio_resid = dsreq->ds_datalen;
726
727 uio.uio_segflg = UIO_USERSPACE;
728 uio.uio_procp = curproc;
729 uio.uio_rw = (rwflag == B_READ) ? UIO_READ : UIO_WRITE;
730 uio.uio_iov = &iovec;
731 uio.uio_iovcnt = 1;
732
733 if ((ret = rawio(dev, &uio, bp)) == 0)
734 ret = bp->b_error;
735 }
736 else
737 {
738 bp->b_un.b_addr = 0;
739 bp->b_bcount = 0;
740 bp->b_dev = dev;
741 bp->b_flags = 0;
742
743 sgstrategy(bp);
744 ret = bp->b_error;
745 }
746
747 }
748 break;
749
750 case DS_TARGET:
751 ret = sg_target(dev, *(int *)addr);
752 break;
753
754 default:
755 ret = ENOTTY;
756 break;
757 }
758
759 return ret;
760}