Revised drivers from Julian. Support for generic scsi. Fixed scsi tape (st.c)
[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 *
35 */
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>
42
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>
48
49#define SGOUTSTANDING 2
50#define SG_RETRIES 2
51#define SPLSG splbio
52
53/* Use one of the implementation defined spare bits
54 * to indicate the escape op:
55 */
56#define DSRQ_ESCAPE DSRQ_CTRL1
57
58struct sg
59{
60 int flags;
61 struct scsi_switch *sc_sw;
62 int ctlr;
63
64 long int ad_info; /* info about the adapter */
65 int cmdscount; /* cmds allowed outstanding by the board */
66
67 struct scsi_xfer *free_xfer;
68 int free_xfer_wait;
69};
70
71/* This is used to associate a struct dsreq and a struct buf.
72 */
73typedef struct dsbuf
74{
75 dsreq_t *dsreq;
76 struct buf buf;
77
78 /* I think this is a portable way to get back to the base of
79 * the enclosing structure:
80 */
81# define DSBUF_P(BP) ((dsbuf_t *)((caddr_t)(BP) - (caddr_t)&((dsbuf_t *)0)->buf))
82
83 int magic;
84
85# define DSBUF_MAGIC 0xDBFACDBF
86} dsbuf_t;
87
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).
91 */
92#error "NSG can't be > 4 unless the method of encoding the board unit changes"
93#endif
94
95struct sg *sgs[NSG];
96
97#define SG(DEV) sgs[G_SCSI_UNIT(DEV)]
98
99struct sg *sg_new(int lun)
100{
101 struct sg *sg = (struct sg *)malloc(sizeof(*sg),M_TEMP, M_NOWAIT);
102
103 if (sg == 0)
104 return 0;
105
106 bzero(sg, sizeof(struct sg));
107
108 return sg;
109}
110
111int sg_attach(ctlr, scsi_addr, scsi_switch)
112int ctlr,scsi_addr;
113struct scsi_switch *scsi_switch;
114{
115 struct sg *sg;
116 int i;
117 struct scsi_xfer *scsi_xfer;
118 static int next_sg_unit = 0;
119
120 int unit = next_sg_unit++;
121
122 if (unit >= NSG)
123 {
124 printf("Too many generic SCSIs (%d > %d); reconfigure the kernel.\n",
125 unit+1, NSG);
126
127 if (NSG == 4)
128 printf(
129 "You have hit the max of 4. You will have to change the driver.\n");
130
131 return 0;
132 }
133
134 if ((sg = sg_new(0)) == 0)
135 return 0;
136
137 sgs[unit] = sg;
138
139 sg->sc_sw = scsi_switch;
140 sg->ctlr = ctlr;
141
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?
145 */
146
147 if (sg->sc_sw->adapter_info)
148 {
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;
153 }
154 else
155 {
156 sg->ad_info = 1;
157 sg->cmdscount = 1;
158 }
159
160 i = sg->cmdscount;
161
162 scsi_xfer = (struct scsi_xfer *)malloc(sizeof(struct scsi_xfer) *
163 i, M_TEMP, M_NOWAIT);
164
165 if (scsi_xfer == 0)
166 {
167 printf("scsi_generic: Can't malloc.\n");
168 return 0;
169 }
170
171 while (i--)
172 {
173 scsi_xfer->next = sg->free_xfer;
174 sg->free_xfer = scsi_xfer;
175 scsi_xfer++;
176 }
177
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
186
187 return 1;
188}
189
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
193 */
194int sgopen(dev_t dev)
195{
196 if (SG(dev) == 0)
197 return ENXIO;
198
199 return 0;
200}
201
202int sgclose(dev_t dev)
203{
204 return 0;
205}
206
207
208/* Free a scsi_xfer, wake processes waiting for it
209 */
210void sg_free_xs(dev_t dev, struct scsi_xfer *xs, int flags)
211{
212 int s;
213 struct sg *sg = SG(dev);
214
215 if(flags & SCSI_NOMASK)
216 {
217 if (sg->free_xfer_wait)
218 {
219 printf("sg_free_xs: doing a wakeup from NOMASK mode!\n");
220 wakeup((caddr_t)&sg->free_xfer);
221 }
222 xs->next = sg->free_xfer;
223 sg->free_xfer = xs;
224 }
225 else
226 {
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);
233 }
234}
235
236/* Get ownership of a scsi_xfer
237 * If need be, sleep on it, until it comes free
238 */
239struct scsi_xfer *sg_get_xs(dev_t dev, int flags)
240{
241 struct scsi_xfer *xs;
242 int s;
243 struct sg *sg = SG(dev);
244
245 if(flags & (SCSI_NOSLEEP | SCSI_NOMASK))
246 {
247 if (xs = sg->free_xfer)
248 {
249 sg->free_xfer = xs->next;
250 xs->flags = 0;
251 }
252 }
253 else
254 {
255 s = SPLSG();
256 while (!(xs = sg->free_xfer))
257 {
258 sg->free_xfer_wait++; /* someone waiting! */
259 sleep((caddr_t)&sg->free_xfer, PRIBIO+1);
260 sg->free_xfer_wait--;
261 }
262 sg->free_xfer = xs->next;
263 splx(s);
264 xs->flags = 0;
265 }
266
267 return xs;
268}
269
270/* We let the user interpret his own sense in the
271 * generic scsi world
272 */
273int sg_interpret_sense(dev_t dev, struct scsi_xfer *xs, int *flag_p)
274{
275 return 0;
276}
277
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.
283 */
284
285int sg_done(dev_t dev,
286struct scsi_xfer *xs)
287{
288 xs->flags |= ITSDONE;
289 wakeup(xs);
290 return 0;
291}
292
293int sg_submit_cmd(dev_t dev, struct scsi_xfer *xs, dsreq_t *dsreq)
294{
295 int retval;
296
297 struct sg *sg = SG(dev);
298
299retry:
300 xs->error = XS_NOERROR;
301
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.
305 */
306
307 retval = (*(sg->sc_sw->scsi_cmd))(xs);
308
309 switch(retval)
310 {
311 case SUCCESSFULLY_QUEUED:
312 while(!(xs->flags & ITSDONE))
313 sleep(xs,PRIBIO+1);
314
315 /* Fall through... */
316
317 case HAD_ERROR:
318
319 if (dsreq)
320 dsreq->ds_status = xs->status;
321
322 switch(xs->error)
323 {
324 case XS_NOERROR:
325 if (dsreq)
326 dsreq->ds_datasent = dsreq->ds_datalen - xs->resid;
327 retval = 0;
328 break;
329
330 case XS_SENSE:
331 retval = (sg_interpret_sense(dev ,xs, (int *)0));
332 if (dsreq)
333 {
334 dsreq->ds_sensesent = sizeof(xs->sense);
335 dsreq->ds_ret = DSRT_SENSE;
336 }
337 retval = 0;
338 break;
339
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;
346
347 case XS_TIMEOUT:
348 if (dsreq)
349 dsreq->ds_ret = DSRT_TIMEOUT;
350 retval = ETIMEDOUT;
351 break;
352
353 case XS_BUSY:
354 if(xs->retries-- )
355 {
356 xs->flags &= ~ITSDONE;
357 goto retry;
358 }
359 retval = EBUSY;
360 break;
361
362 default:
363 printf("sg%d: unknown error category from host adapter code\n"
364 ,G_SCSI_UNIT(dev));
365 retval = EIO;
366 break;
367 }
368 break;
369
370 case COMPLETE:
371 if (dsreq)
372 dsreq->ds_datasent = dsreq->ds_datalen - xs->resid;
373 retval = 0;
374 break;
375
376 case TRY_AGAIN_LATER:
377 if(xs->retries-- )
378 {
379 xs->flags &= ~ITSDONE;
380 goto retry;
381 }
382 retval = EBUSY;
383 break;
384
385 case ESCAPE_NOT_SUPPORTED:
386 retval = ENOSYS; /* "Function not implemented" */
387 break;
388
389 default:
390 printf("sg%d: illegal return from host adapter code\n",
391 G_SCSI_UNIT(dev));
392 retval = EIO;
393 break;
394 }
395
396 return retval;
397}
398
399/* sg_escape: Do a generic SCSI escape
400 */
401int sg_escape(dev_t dev, int op_code, u_char *b, int nb)
402{
403 int retval;
404
405 struct scsi_generic scsi_generic;
406
407 int flags = SCSI_ESCAPE;
408
409 struct scsi_xfer *xs;
410 struct sg *sg = SG(dev);
411
412 xs = sg_get_xs(dev, flags);
413
414 if (xs == 0)
415 {
416 printf("sg_target%d: controller busy"
417 " (this should never happen)\n",G_SCSI_UNIT(dev));
418 return EBUSY;
419 }
420
421 scsi_generic.opcode = op_code;
422 bcopy(b, scsi_generic.bytes, nb);
423
424 /* Fill out the scsi_xfer structure
425 */
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;
438
439 xs->status = 0;
440
441 retval = sg_submit_cmd(dev, xs, 0);
442
443 bcopy(scsi_generic.bytes, b, nb);
444
445 sg_free_xs(dev,xs,flags);
446
447 return retval;
448}
449
450/* sg_target: Turn on / off target mode
451 */
452int sg_target(dev_t dev, int enable)
453{
454 u_char b0 = enable;
455 return sg_escape(dev, SCSI_OP_TARGET, &b0, 1);
456}
457
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.
464 */
465int sg_poll(dev_t dev, int *send, int *recv)
466{
467 scsi_op_poll_t s;
468 int ret;
469
470 ret = sg_escape(dev, SCSI_OP_POLL, (u_char *)&s, sizeof(s));
471
472 if (ret == 0)
473 {
474 *send = s.send;
475 *recv = s.recv;
476 }
477
478 return ret;
479}
480#endif /* EMBEDDED */
481
482int sg_scsi_cmd(dev_t dev,
483dsreq_t *dsreq,
484struct scsi_generic *scsi_cmd,
485u_char *d_addr,
486long d_count,
487struct scsi_sense_data *scsi_sense)
488{
489 int retval;
490
491 int flags = 0;
492 struct scsi_xfer *xs;
493 struct sg *sg = SG(dev);
494
495 if (sg->sc_sw == 0)
496 return ENODEV;
497
498 dsreq->ds_status = 0;
499 dsreq->ds_sensesent = 0;
500
501 if (dsreq->ds_flags & DSRQ_READ)
502 flags |= SCSI_DATA_IN;
503
504 if (dsreq->ds_flags & DSRQ_WRITE)
505 flags |= SCSI_DATA_OUT;
506
507 if (dsreq->ds_flags & DSRQ_TARGET)
508 flags |= SCSI_TARGET;
509
510 if (dsreq->ds_flags & DSRQ_ESCAPE)
511 flags |= SCSI_ESCAPE;
512
513#ifdef SCSI_PHYSADDR
514 if (dsreq->ds_flags & DSRQ_PHYSADDR)
515 flags |= SCSI_PHYSADDR;
516#endif
517
518 xs = sg_get_xs(dev, flags);
519
520 if (xs == 0)
521 {
522 printf("sg_scsi_cmd%d: controller busy"
523 " (this should never happen)\n",G_SCSI_UNIT(dev));
524
525 return EBUSY;
526 }
527
528 /* Fill out the scsi_xfer structure
529 */
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;
546
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;
551
552 retval = sg_submit_cmd(dev, xs, dsreq);
553
554 if (dsreq->ds_ret == DSRT_SENSE)
555 bcopy(&(xs->sense), scsi_sense, sizeof(xs->sense));
556
557 sg_free_xs(dev,xs,flags);
558
559 return retval;
560}
561
562void sgerr(struct buf *bp, int err)
563{
564 bp->b_error = err;
565 bp->b_flags |= B_ERROR;
566
567 iodone(bp);
568}
569
570/* strategy function
571 *
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?
575 *
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.
579 */
580void sgstrategy(struct buf *bp)
581{
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);
586
587 dsbuf_t *dsbuf = DSBUF_P(bp);
588 dsreq_t *dsreq;
589
590 if (dsbuf->magic != DSBUF_MAGIC)
591 {
592 printf("sgstrategy: struct buf not magic.\n");
593 sgerr(bp, EFAULT);
594 return;
595 }
596
597 dsreq = dsbuf->dsreq;
598
599 /* We're in trouble if physio tried to break up the
600 * transfer:
601 */
602 if (bp->b_bcount != dsreq->ds_datalen)
603 {
604 printf("sgstrategy unit%d: Transfer broken up.\n",
605 G_SCSI_UNIT(bp->b_dev));
606 sgerr(bp, EIO);
607 return;
608 }
609
610 dsreq->ds_ret = DSRT_OK;
611
612 /* Reject 0 length timeouts.
613 */
614 if (dsreq->ds_time == 0)
615 {
616 sgerr(bp, EINVAL);
617 return;
618 }
619
620 if (dsreq->ds_cmdlen > sizeof(struct scsi_generic))
621 {
622 sgerr(bp, EFAULT);
623 return;
624 }
625
626 copyin(dsreq->ds_cmdbuf, (char *)&scsi_generic, dsreq->ds_cmdlen);
627
628 /* Use device unit for the LUN. Using the one the user provided
629 * would be a huge security problem.
630 */
631 if ((dsreq->ds_flags & DSRQ_ESCAPE) == 0)
632 scsi_generic.bytes[0] = (scsi_generic.bytes[0] & 0x1F) | (lun << 5);
633
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);
639
640 if (dsreq->ds_sensesent)
641 {
642 if (dsreq->ds_sensesent > dsreq->ds_senselen)
643 dsreq->ds_sensesent = dsreq->ds_senselen;
644
645 copyout(&scsi_sense, dsreq->ds_sensebuf, dsreq->ds_sensesent);
646 }
647
648 if (err)
649 {
650 if (dsreq->ds_ret == DSRT_OK)
651 dsreq->ds_ret = DSRT_DEVSCSI;
652
653 sgerr(bp, err);
654 return;
655 }
656
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.
661 */
662 dsreq->ds_cmdsent = dsreq->ds_cmdlen;
663
664 if (dsreq->ds_ret == 0)
665 dsreq->ds_ret = DSRT_OK;
666
667 iodone(bp); /* Shouldn't this iodone be done in the interrupt?
668 */
669
670 return;
671}
672
673void sgminphys(struct buf *bp)
674{
675}
676
677int sgioctl(dev_t dev, int cmd, caddr_t addr, int f)
678{
679 int ret = 0;
680 int phys;
681
682 switch(cmd)
683 {
684 case DS_ENTER:
685 {
686 dsreq_t *dsreq = (dsreq_t *)addr;
687
688 int rwflag = (dsreq->ds_flags & DSRQ_READ) ? B_READ : B_WRITE;
689
690 struct dsbuf dsbuf;
691 struct buf *bp = &dsbuf.buf;
692
693 bzero(&dsbuf, sizeof(dsbuf));
694
695 dsbuf.dsreq = dsreq;
696 dsbuf.magic = DSBUF_MAGIC;
697
698#ifdef SCSI_PHYSADDR /* Physical memory addressing option */
699 phys = (dsreq->ds_flags & DSRQ_PHYSADDR);
700#else
701 phys = 0;
702#endif
703
704 if (phys)
705 {
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;
710
711 sgstrategy(bp);
712 ret = bp->b_error;
713 }
714 else if (dsreq->ds_datalen)
715 {
716 struct uio uio;
717 struct iovec iovec;
718
719 iovec.iov_base = dsreq->ds_databuf;
720 iovec.iov_len = dsreq->ds_datalen;
721
722 uio.uio_offset = 0;
723 uio.uio_resid = dsreq->ds_datalen;
724
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;
730
731 if ((ret = rawio(dev, &uio, bp)) == 0)
732 ret = bp->b_error;
733 }
734 else
735 {
736 bp->b_un.b_addr = 0;
737 bp->b_bcount = 0;
738 bp->b_dev = dev;
739 bp->b_flags = 0;
740
741 sgstrategy(bp);
742 ret = bp->b_error;
743 }
744
745 }
746 break;
747
748 case DS_TARGET:
749 ret = sg_target(dev, *(int *)addr);
750 break;
751
752 default:
753 ret = ENOTTY;
754 break;
755 }
756
757 return ret;
758}