/* $NetBSD: sc.c,v 1.20 2024/09/25 09:08:22 rin Exp $ */
/*
* Copyright (c) 1992 OMRON Corporation.
*
* This code is derived from software contributed to Berkeley by
* OMRON Corporation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 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
* SUCH DAMAGE.
*
* @(#)sc.c 8.1 (Berkeley) 6/10/93
*/
/*
* Copyright (c) 1992, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* OMRON Corporation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 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. 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
* SUCH DAMAGE.
*
* @(#)sc.c 8.1 (Berkeley) 6/10/93
*/
/*
* XXX
* sensebuf and inqbuf may be uninitialized for some cases.
* Real fix should be to check return values everywhere in
* scsi_request_sense(), scsi_immed_command(), and functions
* called from them.
*/
#pragma GCC diagnostic push /* XXX { */
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
/*
* See if unit exists and is a disk then read block size & nblocks.
*/
while ((i = scsi_test_unit_rdy(ctlr, target, lun)) != 0) {
if (i < 0 || --tries < 0)
return false;
if (i == STS_CHECKCOND) {
uint8_t sensebuf[8];
struct scsi_xsense *sp = (struct scsi_xsense *)sensebuf;
scsi_request_sense(ctlr, target, lun, sensebuf, 8);
if (sp->class == 7 && sp->key == 6)
/* drive doing an RTZ -- give it a while */
DELAY(1000000);
}
DELAY(1000);
}
if (scsi_immed_command(ctlr, target, lun, &inq, (uint8_t *)&inqbuf,
sizeof(inqbuf)) ||
scsi_immed_command(ctlr, target, lun, &cap, (uint8_t *)&capbuf,
sizeof(capbuf)))
/* doesn't exist or not a CCS device */
return false;
switch (inqbuf.type) {
case 0: /* disk */
case 4: /* WORM */
case 5: /* CD-ROM */
case 7: /* Magneto-optical */
break;
default: /* not a disk */
return false;
}
if (inqout != NULL)
*inqout = inqbuf;
if (capout != NULL) {
/* assume big endian */
capout[0] = capbuf[0];
capout[1] = capbuf[1];
}
if (!scident(hs->sc_ctlr, target, lun, &inqbuf, capbuf))
return;
/* CMD_READ_CAPACITY returns the last logical data block address. */
blocks = capbuf[0] + 1;
blksize = capbuf[1];
memcpy(idstr, &inqbuf.vendor_id, 28);
for (i = 27; i > 23; --i)
if (idstr[i] != ' ')
break;
idstr[i + 1] = '\0';
for (i = 23; i > 7; --i)
if (idstr[i] != ' ')
break;
idstr[i + 1] = '\0';
for (i = 7; i >= 0; --i)
if (idstr[i] != ' ')
break;
idstr[i + 1] = '\0';
printf(" ID %d: %s %s rev %s", target, idstr, &idstr[8], &idstr[24]);
printf(", %d bytes/sect x %d sectors\n", blksize, blocks);
}
/*
* SPC Arbitration/Selection routine
*/
static int
issue_select(struct scsidevice *hd, uint8_t target)
{
static void
ixfer_out(struct scsidevice *hd, int len, uint8_t *buf)
{
for (; len > 0; len--) {
while (hd->scsi_ssts & SSTS_DREG_FULL) {
DELAY(5);
}
hd->scsi_dreg = *buf++;
}
}
static void
ixfer_in(struct scsidevice *hd, int len, uint8_t *buf)
{
for (; len > 0; len--) {
while (hd->scsi_ssts & SSTS_DREG_EMPTY) {
DELAY(5);
}
*buf++ = hd->scsi_dreg;
}
}
/*
* SPC drive routines
*/
static int
scrun(int ctlr, int target, uint8_t *cdb, int cdblen, uint8_t *buf, int len,
volatile int *lock)
{
struct scsi_softc *hs;
struct scsidevice *hd;
if (ctlr < 0 || ctlr >= NSC)
return 0;
hs = &scsi_softc[ctlr];
hd = hs->sc_spc;
if (hd == NULL)
return 0;
if (hd->scsi_ints != 0)
/* write register value back to register */
hd->scsi_ints = hd->scsi_ints;
if (hd->scsi_psns == 0 || (hd->scsi_ssts & SSTS_INITIATOR) == 0)
/* no longer connected to scsi target */
return;
/* get the number of bytes remaining in current xfer + fudge */
len = (hd->scsi_tch << 16) | (hd->scsi_tcm << 8) | hd->scsi_tcl;
/* for that many bus cycles, try to send an abort msg */
for (len += 1024;
((hd->scsi_ssts & SSTS_INITIATOR)) != 0 && --len >= 0;) {
hd->scsi_scmd = SCMD_SET_ATN;
while ((hd->scsi_psns & PSNS_REQ) == 0) {
if ((hd->scsi_ssts & SSTS_INITIATOR) == 0)
goto out;
DELAY(1);
}
if (hd->scsi_psns & PHASE_IO) {
/* one of the input phases - read & discard a byte */
hd->scsi_scmd = SCMD_SET_ACK;
while ((hd->scsi_psns & PSNS_REQ) != 0)
DELAY(1);
(void)hd->scsi_temp;
} else {
/* one of the output phases - send an abort msg */
hd->scsi_temp = MSG_ABORT;
hd->scsi_scmd = SCMD_SET_ACK;
while ((hd->scsi_psns & PSNS_REQ) != 0)
DELAY(1);
}
hd->scsi_scmd = SCMD_RST_ACK;
}
out:
/*
* Either the abort was successful & the bus is disconnected or
* the device didn't listen. If the latter, announce the problem.
* Either way, reset the card & the SPC.
*/
if (len < 0 && hs)
printf("sc%d: abort failed. phase=0x%x, ssts=0x%x\n",
hs->sc_ctlr, hd->scsi_psns, hd->scsi_ssts);
}
/*
* SCSI Command Handler
*/
int
scsi_test_unit_rdy(int ctlr, int target, int lun)
{
static struct scsi_cdb6 cdb = { CMD_TEST_UNIT_READY };
int status;
volatile int lock;
int
scsi_request_sense(int ctlr, int target, int lun, uint8_t *buf,
unsigned int len)
{
static struct scsi_cdb6 cdb = { CMD_REQUEST_SENSE };
int status;
volatile int lock;