Sync usage with man page.
[netbsd-mini2440.git] / sys / arch / mac68k / scsi / sg.c
blobe78e60353802ba7d2152d521cc1078f68d77744b
1 /*
2 * Contributed by HD Associates (hd@world.std.com).
3 * Copyright (c) 1992, 1993 HD Associates
5 * Berkeley style copyright. I've just snarfed it out of stdio.h:
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.
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.
36 #include <sys/types.h>
37 #include <sys/errno.h>
38 #include <sys/param.h>
39 #include <sys/malloc.h>
40 #include <sys/buf.h>
41 #include <sys/proc.h>
43 #include <scsi/scsi_all.h>
44 #include <scsi/scsiconf.h>
45 #include <scsi/scsi_generic.h>
46 #include "sg.h"
47 #include <sys/sgio.h>
49 #define SGOUTSTANDING 2
50 #define SG_RETRIES 2
51 #define SPLSG splbio
53 /* Use one of the implementation defined spare bits
54 * to indicate the escape op:
56 #define DSRQ_ESCAPE DSRQ_CTRL1
58 struct sg
60 int flags;
61 struct scsi_switch *sc_sw;
62 int ctlr;
64 long int ad_info; /* info about the adapter */
65 int cmdscount; /* cmds allowed outstanding by the board */
67 struct scsi_xfer *free_xfer;
68 int free_xfer_wait;
71 /* This is used to associate a struct dsreq and a struct buf.
73 typedef struct dsbuf
75 dsreq_t *dsreq;
76 struct buf buf;
78 /* I think this is a portable way to get back to the base of
79 * the enclosing structure:
81 # define DSBUF_P(BP) ((dsbuf_t *)((caddr_t)(BP) - (caddr_t)&((dsbuf_t *)0)->buf))
83 int magic;
85 # define DSBUF_MAGIC 0xDBFACDBF
86 } dsbuf_t;
88 #if NSG > 4
89 /* The host adapter unit is encoded in the upper 2 bits of the minor number
90 * (the SGI flag bits).
92 #error "NSG can't be > 4 unless the method of encoding the board unit changes"
93 #endif
95 struct sg *sgs[NSG];
97 #define SG(DEV) sgs[G_SCSI_UNIT(DEV)]
99 struct sg *sg_new(int lun)
101 struct sg *sg = (struct sg *)malloc(sizeof(*sg),M_TEMP, M_NOWAIT);
103 if (sg == 0)
104 return 0;
106 bzero(sg, sizeof(struct sg));
108 return sg;
111 int sg_attach(ctlr, scsi_addr, scsi_switch)
112 int ctlr,scsi_addr;
113 struct scsi_switch *scsi_switch;
115 struct sg *sg;
116 int i;
117 struct scsi_xfer *scsi_xfer;
118 static int next_sg_unit = 0;
120 int unit = next_sg_unit++;
122 if (unit >= NSG)
124 printf("Too many generic SCSIs (%d > %d); reconfigure the kernel.\n",
125 unit+1, NSG);
127 if (NSG == 4)
128 printf(
129 "You have hit the max of 4. You will have to change the driver.\n");
131 return 0;
134 if ((sg = sg_new(0)) == 0)
135 return 0;
137 sgs[unit] = sg;
139 sg->sc_sw = scsi_switch;
140 sg->ctlr = ctlr;
142 /* This is a bit confusing. It looks like Julian calls back into the
143 * adapter to find out how many outstanding transactions it can
144 * handle. How does he handle a tape/disk combo?
147 if (sg->sc_sw->adapter_info)
149 sg->ad_info = ( (*(sg->sc_sw->adapter_info))(unit));
150 sg->cmdscount = sg->ad_info & AD_INF_MAX_CMDS;
151 if(sg->cmdscount > SGOUTSTANDING)
152 sg->cmdscount = SGOUTSTANDING;
154 else
156 sg->ad_info = 1;
157 sg->cmdscount = 1;
160 i = sg->cmdscount;
162 scsi_xfer = (struct scsi_xfer *)malloc(sizeof(struct scsi_xfer) *
163 i, M_TEMP, M_NOWAIT);
165 if (scsi_xfer == 0)
167 printf("scsi_generic: Can't malloc.\n");
168 return 0;
171 while (i--)
173 scsi_xfer->next = sg->free_xfer;
174 sg->free_xfer = scsi_xfer;
175 scsi_xfer++;
178 #ifndef EMBEDDED
179 if (unit == 0)
180 printf(" /dev/gs%d (instance 0) generic SCSI via controller %d\n",
181 scsi_addr, sg->ctlr);
182 else
183 printf(" /dev/gs%d-%d generic SCSI via controller %d\n",
184 unit, scsi_addr, sg->ctlr);
185 #endif
187 return 1;
190 /* It is trivial to add support for processor target devices
191 * here - enable target mode on open and disable on close
192 * if a flag bit is set in the minor number
194 int sgopen(dev_t dev)
196 if (SG(dev) == 0)
197 return ENXIO;
199 return 0;
202 int sgclose(dev_t dev)
204 return 0;
208 /* Free a scsi_xfer, wake processes waiting for it
210 void sg_free_xs(dev_t dev, struct scsi_xfer *xs, int flags)
212 int s;
213 struct sg *sg = SG(dev);
215 if(flags & SCSI_NOMASK)
217 if (sg->free_xfer_wait)
219 printf("sg_free_xs: doing a wakeup from NOMASK mode!\n");
220 wakeup((caddr_t)&sg->free_xfer);
222 xs->next = sg->free_xfer;
223 sg->free_xfer = xs;
225 else
227 s = SPLSG();
228 if (sg->free_xfer_wait)
229 wakeup((caddr_t)&sg->free_xfer);
230 xs->next = sg->free_xfer;
231 sg->free_xfer = xs;
232 splx(s);
236 /* Get ownership of a scsi_xfer
237 * If need be, sleep on it, until it comes free
239 struct scsi_xfer *sg_get_xs(dev_t dev, int flags)
241 struct scsi_xfer *xs;
242 int s;
243 struct sg *sg = SG(dev);
245 if(flags & (SCSI_NOSLEEP | SCSI_NOMASK))
247 if (xs = sg->free_xfer)
249 sg->free_xfer = xs->next;
250 xs->flags = 0;
253 else
255 s = SPLSG();
256 while (!(xs = sg->free_xfer))
258 sg->free_xfer_wait++; /* someone waiting! */
259 sleep((caddr_t)&sg->free_xfer, PRIBIO+1);
260 sg->free_xfer_wait--;
262 sg->free_xfer = xs->next;
263 splx(s);
264 xs->flags = 0;
267 return xs;
270 /* We let the user interpret his own sense in the
271 * generic scsi world
273 int sg_interpret_sense(dev_t dev, struct scsi_xfer *xs, int *flag_p)
275 return 0;
278 /* ITSDONE is really used for things that are marked one
279 * in the interrupt. I'll leave the logic in in case I want
280 * to move done processing (and therefore have a start queue)
281 * back into the interrupt.
282 * BUG: No start queue.
285 int sg_done(dev_t dev,
286 struct scsi_xfer *xs)
288 xs->flags |= ITSDONE;
289 wakeup(xs);
290 return 0;
293 int sg_submit_cmd(dev_t dev, struct scsi_xfer *xs, dsreq_t *dsreq)
295 int retval;
297 struct sg *sg = SG(dev);
299 retry:
300 xs->error = XS_NOERROR;
302 xs->bp = 0; /* This bp doesn't seem to be used except to
303 * disable sleeping in the host adapter code.
304 * "st" does set it up, though.
307 retval = (*(sg->sc_sw->scsi_cmd))(xs);
309 switch(retval)
311 case SUCCESSFULLY_QUEUED:
312 while(!(xs->flags & ITSDONE))
313 sleep(xs,PRIBIO+1);
315 /* Fall through... */
317 case HAD_ERROR:
319 if (dsreq)
320 dsreq->ds_status = xs->status;
322 switch(xs->error)
324 case XS_NOERROR:
325 if (dsreq)
326 dsreq->ds_datasent = dsreq->ds_datalen - xs->resid;
327 retval = 0;
328 break;
330 case XS_SENSE:
331 retval = (sg_interpret_sense(dev ,xs, (int *)0));
332 if (dsreq)
334 dsreq->ds_sensesent = sizeof(xs->sense);
335 dsreq->ds_ret = DSRT_SENSE;
337 retval = 0;
338 break;
340 case XS_DRIVER_STUFFUP:
341 if (dsreq)
342 dsreq->ds_ret = DSRT_HOST;
343 printf("sg%d: host adapter code inconsistency\n" ,G_SCSI_UNIT(dev));
344 retval = EIO;
345 break;
347 case XS_TIMEOUT:
348 if (dsreq)
349 dsreq->ds_ret = DSRT_TIMEOUT;
350 retval = ETIMEDOUT;
351 break;
353 case XS_BUSY:
354 if(xs->retries-- )
356 xs->flags &= ~ITSDONE;
357 goto retry;
359 retval = EBUSY;
360 break;
362 default:
363 printf("sg%d: unknown error category from host adapter code\n"
364 ,G_SCSI_UNIT(dev));
365 retval = EIO;
366 break;
368 break;
370 case COMPLETE:
371 if (dsreq)
372 dsreq->ds_datasent = dsreq->ds_datalen - xs->resid;
373 retval = 0;
374 break;
376 case TRY_AGAIN_LATER:
377 if(xs->retries-- )
379 xs->flags &= ~ITSDONE;
380 goto retry;
382 retval = EBUSY;
383 break;
385 case ESCAPE_NOT_SUPPORTED:
386 retval = ENOSYS; /* "Function not implemented" */
387 break;
389 default:
390 printf("sg%d: illegal return from host adapter code\n",
391 G_SCSI_UNIT(dev));
392 retval = EIO;
393 break;
396 return retval;
399 /* sg_escape: Do a generic SCSI escape
401 int sg_escape(dev_t dev, int op_code, u_char *b, int nb)
403 int retval;
405 struct scsi_generic scsi_generic;
407 int flags = SCSI_ESCAPE;
409 struct scsi_xfer *xs;
410 struct sg *sg = SG(dev);
412 xs = sg_get_xs(dev, flags);
414 if (xs == 0)
416 printf("sg_target%d: controller busy"
417 " (this should never happen)\n",G_SCSI_UNIT(dev));
418 return EBUSY;
421 scsi_generic.opcode = op_code;
422 bcopy(b, scsi_generic.bytes, nb);
424 /* Fill out the scsi_xfer structure
426 xs->flags = (flags|INUSE);
427 xs->adapter = sg->ctlr;
428 xs->cmd = &scsi_generic;
429 xs->targ = G_SCSI_ID(dev);
430 xs->lu = G_SCSI_LUN(dev);
431 xs->retries = SG_RETRIES;
432 xs->timeout = 100;
433 xs->when_done = (flags & SCSI_NOMASK)
434 ?(int (*)())0
435 :(int (*)())sg_done;
436 xs->done_arg = dev;
437 xs->done_arg2 = (int)xs;
439 xs->status = 0;
441 retval = sg_submit_cmd(dev, xs, 0);
443 bcopy(scsi_generic.bytes, b, nb);
445 sg_free_xs(dev,xs,flags);
447 return retval;
450 /* sg_target: Turn on / off target mode
452 int sg_target(dev_t dev, int enable)
454 u_char b0 = enable;
455 return sg_escape(dev, SCSI_OP_TARGET, &b0, 1);
458 #ifdef EMBEDDED
459 /* This should REALLY be a select call!
460 * This is used in a stand alone system without an O/S. I didn't
461 * have the time to add select, which the system was missing,
462 * so I added this stuff to poll for the async arrival of
463 * connections for target mode.
465 int sg_poll(dev_t dev, int *send, int *recv)
467 scsi_op_poll_t s;
468 int ret;
470 ret = sg_escape(dev, SCSI_OP_POLL, (u_char *)&s, sizeof(s));
472 if (ret == 0)
474 *send = s.send;
475 *recv = s.recv;
478 return ret;
480 #endif /* EMBEDDED */
482 int sg_scsi_cmd(dev_t dev,
483 dsreq_t *dsreq,
484 struct scsi_generic *scsi_cmd,
485 u_char *d_addr,
486 long d_count,
487 struct scsi_sense_data *scsi_sense)
489 int retval;
491 int flags = 0;
492 struct scsi_xfer *xs;
493 struct sg *sg = SG(dev);
495 if (sg->sc_sw == 0)
496 return ENODEV;
498 dsreq->ds_status = 0;
499 dsreq->ds_sensesent = 0;
501 if (dsreq->ds_flags & DSRQ_READ)
502 flags |= SCSI_DATA_IN;
504 if (dsreq->ds_flags & DSRQ_WRITE)
505 flags |= SCSI_DATA_OUT;
507 if (dsreq->ds_flags & DSRQ_TARGET)
508 flags |= SCSI_TARGET;
510 if (dsreq->ds_flags & DSRQ_ESCAPE)
511 flags |= SCSI_ESCAPE;
513 #ifdef SCSI_PHYSADDR
514 if (dsreq->ds_flags & DSRQ_PHYSADDR)
515 flags |= SCSI_PHYSADDR;
516 #endif
518 xs = sg_get_xs(dev, flags);
520 if (xs == 0)
522 printf("sg_scsi_cmd%d: controller busy"
523 " (this should never happen)\n",G_SCSI_UNIT(dev));
525 return EBUSY;
528 /* Fill out the scsi_xfer structure
530 xs->flags |= (flags|INUSE);
531 xs->adapter = sg->ctlr;
532 xs->targ = G_SCSI_ID(dev);
533 xs->lu = G_SCSI_LUN(dev);
534 xs->retries = SG_RETRIES;
535 xs->timeout = dsreq->ds_time;
536 xs->cmd = scsi_cmd;
537 xs->cmdlen = dsreq->ds_cmdlen;
538 xs->data = d_addr;
539 xs->datalen = d_count;
540 xs->resid = d_count;
541 xs->when_done = (flags & SCSI_NOMASK)
542 ?(int (*)())0
543 :(int (*)())sg_done;
544 xs->done_arg = dev;
545 xs->done_arg2 = (int)xs;
547 xs->req_sense_length = (dsreq->ds_senselen < sizeof(struct scsi_sense_data))
548 ? dsreq->ds_senselen
549 : sizeof(struct scsi_sense_data);
550 xs->status = 0;
552 retval = sg_submit_cmd(dev, xs, dsreq);
554 if (dsreq->ds_ret == DSRT_SENSE)
555 bcopy(&(xs->sense), scsi_sense, sizeof(xs->sense));
557 sg_free_xs(dev,xs,flags);
559 return retval;
562 void sgerr(struct buf *bp, int err)
564 bp->b_error = err;
565 bp->b_flags |= B_ERROR;
567 iodone(bp);
570 /* strategy function
572 * Should I reorganize this so it returns to physio instead
573 * of sleeping in sg_scsi_cmd? Is there any advantage, other
574 * than avoiding the probable duplicate wakeup in iodone?
576 * Don't create a block device entry point for this
577 * driver without making some fixes:
578 * you have to be able to go from the bp to the dsreq somehow.
580 void sgstrategy(struct buf *bp)
582 int err;
583 struct scsi_generic scsi_generic;
584 struct scsi_sense_data scsi_sense;
585 int lun = G_SCSI_LUN(bp->b_dev);
587 dsbuf_t *dsbuf = DSBUF_P(bp);
588 dsreq_t *dsreq;
590 if (dsbuf->magic != DSBUF_MAGIC)
592 printf("sgstrategy: struct buf not magic.\n");
593 sgerr(bp, EFAULT);
594 return;
597 dsreq = dsbuf->dsreq;
599 /* We're in trouble if physio tried to break up the
600 * transfer:
602 if (bp->b_bcount != dsreq->ds_datalen)
604 printf("sgstrategy unit%d: Transfer broken up.\n",
605 G_SCSI_UNIT(bp->b_dev));
606 sgerr(bp, EIO);
607 return;
610 dsreq->ds_ret = DSRT_OK;
612 /* Reject 0 length timeouts.
614 if (dsreq->ds_time == 0)
616 sgerr(bp, EINVAL);
617 return;
620 if (dsreq->ds_cmdlen > sizeof(struct scsi_generic))
622 sgerr(bp, EFAULT);
623 return;
626 copyin(dsreq->ds_cmdbuf, (char *)&scsi_generic, dsreq->ds_cmdlen);
628 /* Use device unit for the LUN. Using the one the user provided
629 * would be a huge security problem.
631 if ((dsreq->ds_flags & DSRQ_ESCAPE) == 0)
632 scsi_generic.bytes[0] = (scsi_generic.bytes[0] & 0x1F) | (lun << 5);
634 err = sg_scsi_cmd(bp->b_dev, dsreq,
635 &scsi_generic,
636 (u_char *)bp->b_un.b_addr,
637 bp->b_bcount,
638 &scsi_sense);
640 if (dsreq->ds_sensesent)
642 if (dsreq->ds_sensesent > dsreq->ds_senselen)
643 dsreq->ds_sensesent = dsreq->ds_senselen;
645 copyout(&scsi_sense, dsreq->ds_sensebuf, dsreq->ds_sensesent);
648 if (err)
650 if (dsreq->ds_ret == DSRT_OK)
651 dsreq->ds_ret = DSRT_DEVSCSI;
653 sgerr(bp, err);
654 return;
657 /* This is a fake. It would be nice to know if the
658 * command was sent or not instead of pretending it was if
659 * we get this far. That would involve adding "sent" members
660 * to the xs so it could be set up down in the host adapter code.
662 dsreq->ds_cmdsent = dsreq->ds_cmdlen;
664 if (dsreq->ds_ret == 0)
665 dsreq->ds_ret = DSRT_OK;
667 iodone(bp); /* Shouldn't this iodone be done in the interrupt?
670 return;
673 void sgminphys(struct buf *bp)
677 int sgioctl(dev_t dev, int cmd, caddr_t addr, int f)
679 int ret = 0;
680 int phys;
682 switch(cmd)
684 case DS_ENTER:
686 dsreq_t *dsreq = (dsreq_t *)addr;
688 int rwflag = (dsreq->ds_flags & DSRQ_READ) ? B_READ : B_WRITE;
690 struct dsbuf dsbuf;
691 struct buf *bp = &dsbuf.buf;
693 bzero(&dsbuf, sizeof(dsbuf));
695 dsbuf.dsreq = dsreq;
696 dsbuf.magic = DSBUF_MAGIC;
698 #ifdef SCSI_PHYSADDR /* Physical memory addressing option */
699 phys = (dsreq->ds_flags & DSRQ_PHYSADDR);
700 #else
701 phys = 0;
702 #endif
704 if (phys)
706 bp->b_un.b_addr = dsreq->ds_databuf;
707 bp->b_bcount = dsreq->ds_datalen;
708 bp->b_dev = dev;
709 bp->b_flags = rwflag;
711 sgstrategy(bp);
712 ret = bp->b_error;
714 else if (dsreq->ds_datalen)
716 struct uio uio;
717 struct iovec iovec;
719 iovec.iov_base = dsreq->ds_databuf;
720 iovec.iov_len = dsreq->ds_datalen;
722 uio.uio_offset = 0;
723 uio.uio_resid = dsreq->ds_datalen;
725 uio.uio_segflg = UIO_USERSPACE;
726 uio.uio_procp = curproc;
727 uio.uio_rw = (rwflag == B_READ) ? UIO_READ : UIO_WRITE;
728 uio.uio_iov = &iovec;
729 uio.uio_iovcnt = 1;
731 /* if ((ret = rawio(dev, &uio, bp)) == 0)
732 ret = bp->b_error; */
734 else
736 bp->b_un.b_addr = 0;
737 bp->b_bcount = 0;
738 bp->b_dev = dev;
739 bp->b_flags = 0;
741 sgstrategy(bp);
742 ret = bp->b_error;
746 break;
748 case DS_TARGET:
749 ret = sg_target(dev, *(int *)addr);
750 break;
752 default:
753 ret = ENOTTY;
754 break;
757 return ret;