* Contributed by HD Associates (hd@world.std.com).
* Copyright (c) 1992, 1993 HD Associates
* Berkeley style copyright. I've just snarfed it out of stdio.h:
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
#include <scsi/scsi_all.h>
#include <scsi/scsiconf.h>
#include <scsi/scsi_generic.h>
/* Use one of the implementation defined spare bits
* to indicate the escape op:
#define DSRQ_ESCAPE DSRQ_CTRL1
struct scsi_switch
*sc_sw
;
long int ad_info
; /* info about the adapter */
int cmdscount
; /* cmds allowed outstanding by the board */
struct scsi_xfer
*free_xfer
;
/* This is used to associate a struct dsreq and a struct buf.
/* I think this is a portable way to get back to the base of
* the enclosing structure:
# define DSBUF_P(BP) ((dsbuf_t *)((caddr_t)(BP) - (caddr_t)&((dsbuf_t *)0)->buf))
# define DSBUF_MAGIC 0xDBFACDBF
/* The host adapter unit is encoded in the upper 2 bits of the minor number
#error "NSG can't be > 4 unless the method of encoding the board unit changes"
#define SG(DEV) sgs[G_SCSI_UNIT(DEV)]
struct sg
*sg_new(int lun
)
struct sg
*sg
= (struct sg
*)malloc(sizeof(*sg
),M_TEMP
, M_NOWAIT
);
bzero(sg
, sizeof(struct sg
));
int sg_attach(ctlr
, scsi_addr
, scsi_switch
)
struct scsi_switch
*scsi_switch
;
struct scsi_xfer
*scsi_xfer
;
static int next_sg_unit
= 0;
int unit
= next_sg_unit
++;
printf("Too many generic SCSIs (%d > %d); reconfigure the kernel.\n",
"You have hit the max of 4. You will have to change the driver.\n");
if ((sg
= sg_new(0)) == 0)
/* This is a bit confusing. It looks like Julian calls back into the
* adapter to find out how many outstanding transactions it can
* handle. How does he handle a tape/disk combo?
if (sg
->sc_sw
->adapter_info
)
sg
->ad_info
= ( (*(sg
->sc_sw
->adapter_info
))(unit
));
sg
->cmdscount
= sg
->ad_info
& AD_INF_MAX_CMDS
;
if(sg
->cmdscount
> SGOUTSTANDING
)
sg
->cmdscount
= SGOUTSTANDING
;
scsi_xfer
= (struct scsi_xfer
*)malloc(sizeof(struct scsi_xfer
) *
printf("scsi_generic: Can't malloc.\n");
scsi_xfer
->next
= sg
->free_xfer
;
sg
->free_xfer
= scsi_xfer
;
printf(" /dev/gs%d (instance 0) generic SCSI via controller %d\n",
printf(" /dev/gs%d-%d generic SCSI via controller %d\n",
unit
, scsi_addr
, sg
->ctlr
);
/* It is trivial to add support for processor target devices
* here - enable target mode on open and disable on close
* if a flag bit is set in the minor number
/* Free a scsi_xfer, wake processes waiting for it
void sg_free_xs(dev_t dev
, struct scsi_xfer
*xs
, int flags
)
printf("sg_free_xs: doing a wakeup from NOMASK mode!\n");
wakeup((caddr_t
)&sg
->free_xfer
);
xs
->next
= sg
->free_xfer
;
wakeup((caddr_t
)&sg
->free_xfer
);
xs
->next
= sg
->free_xfer
;
/* Get ownership of a scsi_xfer
* If need be, sleep on it, until it comes free
struct scsi_xfer
*sg_get_xs(dev_t dev
, int flags
)
if(flags
& (SCSI_NOSLEEP
| SCSI_NOMASK
))
sg
->free_xfer
= xs
->next
;
while (!(xs
= sg
->free_xfer
))
sg
->free_xfer_wait
++; /* someone waiting! */
sleep((caddr_t
)&sg
->free_xfer
, PRIBIO
+1);
sg
->free_xfer
= xs
->next
;
/* We let the user interpret his own sense in the
int sg_interpret_sense(dev_t dev
, struct scsi_xfer
*xs
, int *flag_p
)
/* ITSDONE is really used for things that are marked one
* in the interrupt. I'll leave the logic in in case I want
* to move done processing (and therefore have a start queue)
* back into the interrupt.
int sg_submit_cmd(dev_t dev
, struct scsi_xfer
*xs
, dsreq_t
*dsreq
)
xs
->bp
= 0; /* This bp doesn't seem to be used except to
* disable sleeping in the host adapter code.
* "st" does set it up, though.
retval
= (*(sg
->sc_sw
->scsi_cmd
))(xs
);
case SUCCESSFULLY_QUEUED
:
while(!(xs
->flags
& ITSDONE
))
dsreq
->ds_status
= xs
->status
;
dsreq
->ds_datasent
= dsreq
->ds_datalen
- xs
->resid
;
retval
= (sg_interpret_sense(dev
,xs
, (int *)0));
dsreq
->ds_sensesent
= sizeof(xs
->sense
);
dsreq
->ds_ret
= DSRT_SENSE
;
dsreq
->ds_ret
= DSRT_HOST
;
printf("sg%d: host adapter code inconsistency\n" ,G_SCSI_UNIT(dev
));
dsreq
->ds_ret
= DSRT_TIMEOUT
;
printf("sg%d: unknown error category from host adapter code\n"
dsreq
->ds_datasent
= dsreq
->ds_datalen
- xs
->resid
;
case ESCAPE_NOT_SUPPORTED
:
retval
= ENOSYS
; /* "Function not implemented" */
printf("sg%d: illegal return from host adapter code\n",
/* sg_escape: Do a generic SCSI escape
int sg_escape(dev_t dev
, int op_code
, u_char
*b
, int nb
)
struct scsi_generic scsi_generic
;
xs
= sg_get_xs(dev
, flags
);
printf("sg_target%d: controller busy"
" (this should never happen)\n",G_SCSI_UNIT(dev
));
scsi_generic
.opcode
= op_code
;
bcopy(b
, scsi_generic
.bytes
, nb
);
/* Fill out the scsi_xfer structure
xs
->flags
= (flags
|INUSE
);
xs
->targ
= G_SCSI_ID(dev
);
xs
->lu
= G_SCSI_LUN(dev
);
xs
->retries
= SG_RETRIES
;
xs
->when_done
= (flags
& SCSI_NOMASK
)
retval
= sg_submit_cmd(dev
, xs
, 0);
bcopy(scsi_generic
.bytes
, b
, nb
);
sg_free_xs(dev
,xs
,flags
);
/* sg_target: Turn on / off target mode
int sg_target(dev_t dev
, int enable
)
return sg_escape(dev
, SCSI_OP_TARGET
, &b0
, 1);
/* This should REALLY be a select call!
* This is used in a stand alone system without an O/S. I didn't
* have the time to add select, which the system was missing,
* so I added this stuff to poll for the async arrival of
* connections for target mode.
int sg_poll(dev_t dev
, int *send
, int *recv
)
ret
= sg_escape(dev
, SCSI_OP_POLL
, (u_char
*)&s
, sizeof(s
));
int sg_scsi_cmd(dev_t dev
,
struct scsi_generic
*scsi_cmd
,
struct scsi_sense_data
*scsi_sense
)
if (dsreq
->ds_flags
& DSRQ_READ
)
if (dsreq
->ds_flags
& DSRQ_WRITE
)
if (dsreq
->ds_flags
& DSRQ_TARGET
)
if (dsreq
->ds_flags
& DSRQ_ESCAPE
)
if (dsreq
->ds_flags
& DSRQ_PHYSADDR
)
xs
= sg_get_xs(dev
, flags
);
printf("sg_scsi_cmd%d: controller busy"
" (this should never happen)\n",G_SCSI_UNIT(dev
));
/* Fill out the scsi_xfer structure
xs
->flags
|= (flags
|INUSE
);
xs
->targ
= G_SCSI_ID(dev
);
xs
->lu
= G_SCSI_LUN(dev
);
xs
->retries
= SG_RETRIES
;
xs
->timeout
= dsreq
->ds_time
;
xs
->cmdlen
= dsreq
->ds_cmdlen
;
xs
->when_done
= (flags
& SCSI_NOMASK
)
xs
->req_sense_length
= (dsreq
->ds_senselen
< sizeof(struct scsi_sense_data
))
: sizeof(struct scsi_sense_data
);
retval
= sg_submit_cmd(dev
, xs
, dsreq
);
if (dsreq
->ds_ret
== DSRT_SENSE
)
bcopy(&(xs
->sense
), scsi_sense
, sizeof(xs
->sense
));
sg_free_xs(dev
,xs
,flags
);
void sgerr(struct buf
*bp
, int err
)
* Should I reorganize this so it returns to physio instead
* of sleeping in sg_scsi_cmd? Is there any advantage, other
* than avoiding the probable duplicate wakeup in iodone?
* Don't create a block device entry point for this
* driver without making some fixes:
* you have to be able to go from the bp to the dsreq somehow.
void sgstrategy(struct buf
*bp
)
struct scsi_generic scsi_generic
;
struct scsi_sense_data scsi_sense
;
int lun
= G_SCSI_LUN(bp
->b_dev
);
dsbuf_t
*dsbuf
= DSBUF_P(bp
);
if (dsbuf
->magic
!= DSBUF_MAGIC
)
printf("sgstrategy: struct buf not magic.\n");
/* We're in trouble if physio tried to break up the
if (bp
->b_bcount
!= dsreq
->ds_datalen
)
printf("sgstrategy unit%d: Transfer broken up.\n",
/* Reject 0 length timeouts.
if (dsreq
->ds_cmdlen
> sizeof(struct scsi_generic
))
copyin(dsreq
->ds_cmdbuf
, (char *)&scsi_generic
, dsreq
->ds_cmdlen
);
/* Use device unit for the LUN. Using the one the user provided
* would be a huge security problem.
if ((dsreq
->ds_flags
& DSRQ_ESCAPE
) == 0)
scsi_generic
.bytes
[0] = (scsi_generic
.bytes
[0] & 0x1F) | (lun
<< 5);
err
= sg_scsi_cmd(bp
->b_dev
, dsreq
,
(u_char
*)bp
->b_un
.b_addr
,
if (dsreq
->ds_sensesent
> dsreq
->ds_senselen
)
dsreq
->ds_sensesent
= dsreq
->ds_senselen
;
copyout(&scsi_sense
, dsreq
->ds_sensebuf
, dsreq
->ds_sensesent
);
if (dsreq
->ds_ret
== DSRT_OK
)
dsreq
->ds_ret
= DSRT_DEVSCSI
;
/* This is a fake. It would be nice to know if the
* command was sent or not instead of pretending it was if
* we get this far. That would involve adding "sent" members
* to the xs so it could be set up down in the host adapter code.
dsreq
->ds_cmdsent
= dsreq
->ds_cmdlen
;
iodone(bp
); /* Shouldn't this iodone be done in the interrupt?
void sgminphys(struct buf
*bp
)
int sgioctl(dev_t dev
, int cmd
, caddr_t addr
, int f
)
dsreq_t
*dsreq
= (dsreq_t
*)addr
;
int rwflag
= (dsreq
->ds_flags
& DSRQ_READ
) ? B_READ
: B_WRITE
;
struct buf
*bp
= &dsbuf
.buf
;
bzero(&dsbuf
, sizeof(dsbuf
));
dsbuf
.magic
= DSBUF_MAGIC
;
#ifdef SCSI_PHYSADDR /* Physical memory addressing option */
phys
= (dsreq
->ds_flags
& DSRQ_PHYSADDR
);
bp
->b_un
.b_addr
= dsreq
->ds_databuf
;
bp
->b_bcount
= dsreq
->ds_datalen
;
else if (dsreq
->ds_datalen
)
iovec
.iov_base
= dsreq
->ds_databuf
;
iovec
.iov_len
= dsreq
->ds_datalen
;
uio
.uio_resid
= dsreq
->ds_datalen
;
uio
.uio_segflg
= UIO_USERSPACE
;
uio
.uio_rw
= (rwflag
== B_READ
) ? UIO_READ
: UIO_WRITE
;
if ((ret
= rawio(dev
, &uio
, bp
)) == 0)
ret
= sg_target(dev
, *(int *)addr
);