1
0
mirror of https://git.FreeBSD.org/src.git synced 2024-12-16 10:20:30 +00:00

Add scsi target support.

This commit is contained in:
Peter Dufault 1995-04-14 15:13:46 +00:00
parent 8db34c841e
commit bc0cfeb0ce
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=7819

View File

@ -12,7 +12,7 @@
* on the understanding that TFS is not responsible for the correct * on the understanding that TFS is not responsible for the correct
* functioning of this software in any circumstances. * functioning of this software in any circumstances.
* *
* $Id: aha1542.c,v 1.42 1995/03/28 07:55:22 bde Exp $ * $Id: aha1542.c,v 1.43 1995/04/12 20:47:32 wollman Exp $
*/ */
/* /*
@ -33,6 +33,7 @@
#include <sys/user.h> #include <sys/user.h>
#include <machine/clock.h> #include <machine/clock.h>
#include <i386/isa/isa_device.h> #include <i386/isa/isa_device.h>
#include <machine/clock.h>
#endif /* KERNEL */ #endif /* KERNEL */
#include <scsi/scsi_all.h> #include <scsi/scsi_all.h>
#include <scsi/scsiconf.h> #include <scsi/scsiconf.h>
@ -151,6 +152,8 @@ struct aha_mbx {
#define AHA_MBI_UNKNOWN 0x3 /* Tried to abort invalid CCB */ #define AHA_MBI_UNKNOWN 0x3 /* Tried to abort invalid CCB */
#define AHA_MBI_ERROR 0x4 /* Completed with error */ #define AHA_MBI_ERROR 0x4 /* Completed with error */
#define AHA_MBI_TGT_NO_CCB 0x10 /* Target received, no CCB ready */
/* FOR OLD VERSIONS OF THE !%$@ this may have to be 16 (yuk) */ /* FOR OLD VERSIONS OF THE !%$@ this may have to be 16 (yuk) */
#define AHA_NSEG 17 /* Number of scatter gather segments <= 16 */ #define AHA_NSEG 17 /* Number of scatter gather segments <= 16 */
/* allow 64 K i/o (min) */ /* allow 64 K i/o (min) */
@ -283,9 +286,7 @@ struct aha_extbios
#define AHA_DMA_PAGES AHA_NSEG #define AHA_DMA_PAGES AHA_NSEG
#define PAGESIZ 4096 #define PAGESIZ 4096
#define INVALIDATE_CACHE {asm volatile( ".byte 0x0F ;.byte 0x08" ); }
u_char aha_scratch_buf[256];
#ifdef AHADEBUG #ifdef AHADEBUG
int aha_debug = 1; int aha_debug = 1;
#endif /*AHADEBUG */ #endif /*AHADEBUG */
@ -305,6 +306,7 @@ struct aha_data {
int aha_int; /* irq level */ int aha_int; /* irq level */
int aha_dma; /* DMA req channel */ int aha_dma; /* DMA req channel */
int aha_scsi_dev; /* scsi bus address */ int aha_scsi_dev; /* scsi bus address */
int flags;
/* We use different op codes for different revs of the board /* We use different op codes for different revs of the board
* if we think residual codes will work. * if we think residual codes will work.
@ -629,7 +631,7 @@ ahaattach(dev)
aha->sc_link.adapter_targ = aha->aha_scsi_dev; aha->sc_link.adapter_targ = aha->aha_scsi_dev;
aha->sc_link.adapter = &aha_switch; aha->sc_link.adapter = &aha_switch;
aha->sc_link.device = &aha_dev; aha->sc_link.device = &aha_dev;
aha->sc_link.flags = SDEV_BOUNCE; aha->sc_link.flags = aha->flags;;
/* /*
* ask the adapter what subunits are present * ask the adapter what subunits are present
@ -658,7 +660,6 @@ int
ahaintr(unit) ahaintr(unit)
int unit; int unit;
{ {
struct aha_ccb *ccb;
unsigned char stat; unsigned char stat;
register i; register i;
struct aha_data *aha = ahadata[unit]; struct aha_data *aha = ahadata[unit];
@ -667,7 +668,7 @@ ahaintr(unit)
printf("ahaintr "); printf("ahaintr ");
#endif /*AHADEBUG */ #endif /*AHADEBUG */
/* /*
* First acknowlege the interrupt, Then if it's not telling about * First acknowledge the interrupt, Then if it's not telling about
* a completed operation just return. * a completed operation just return.
*/ */
stat = inb(AHA_INTR_PORT); stat = inb(AHA_INTR_PORT);
@ -678,64 +679,95 @@ ahaintr(unit)
printf("mbxin "); printf("mbxin ");
#endif /*AHADEBUG */ #endif /*AHADEBUG */
/* /*
* If it IS then process the competed operation * If it IS then process the completed operation
*/ */
for (i = 0; i < AHA_MBX_SIZE; i++) { for (i = 0; i < AHA_MBX_SIZE; i++) {
if (aha->aha_mbx.mbi[i].stat != AHA_MBI_FREE) { struct aha_mbx_in *mbi = aha->aha_mbx.mbi + i;
ccb = (struct aha_ccb *) PHYSTOKV(
(scsi_3btou(aha->aha_mbx.mbi[i].ccb_addr)));
if ((stat = aha->aha_mbx.mbi[i].stat) != AHA_MBI_OK) { if (mbi->stat != AHA_MBI_FREE) {
switch (stat) { struct aha_ccb *ccb =
case AHA_MBI_ABORT: (struct aha_ccb *)PHYSTOKV(scsi_3btou(mbi->ccb_addr));
stat = mbi->stat;
switch (stat) {
case AHA_MBI_OK:
break;
case AHA_MBI_ABORT:
#ifdef AHADEBUG #ifdef AHADEBUG
if (aha_debug) if (aha_debug)
printf("abort"); printf("abort");
#endif /*AHADEBUG */ #endif /*AHADEBUG */
ccb->host_stat = AHA_ABORTED; ccb->host_stat = AHA_ABORTED;
break; break;
case AHA_MBI_UNKNOWN: case AHA_MBI_TGT_NO_CCB:
ccb = (struct aha_ccb *) 0; /* We enabled target mode and received a SEND
* or RECEIVE command from the initiator, but
* we don't have any CCB registered to handle the command.
* At this point it would be nice to wakeup a
* process sleeping on this event via an ioctl,
* returning whether it is a SEND or RECEIVE and the
* required length.
* However, I want to look at the CAM documentation before
* I start extending the API at all.
*/
#ifdef NOISE_WHEN_TGT_NO_CDB
printf("Target received, but no CCB ready.\n");
printf("Initiator & lun: %02x\n", mbi->ccb_addr[0]);
printf("Max data length: %06x\n",
(mbi->ccb_addr[1] << 16) | (mbi->ccb_addr[2] << 8)
+ 255);
#endif
#ifdef AHADEBUG #ifdef AHADEBUG
if (aha_debug) if (aha_debug)
printf("unknown ccb for abort "); printf("target-no-ccb");
#endif /*AHADEBUG */ #endif /*AHADEBUG */
/* may have missed it */ ccb = 0;
/* no such ccb known for abort */ break;
case AHA_MBI_ERROR: case AHA_MBI_UNKNOWN:
break; ccb = 0;
default:
panic("Impossible mbxi status");
}
#ifdef AHADEBUG #ifdef AHADEBUG
if (aha_debug && ccb) { if (aha_debug)
u_char *cp; printf("unknown ccb for abort ");
cp = (u_char *) (&(ccb->scsi_cmd));
printf("op=%x %x %x %x %x %x\n",
cp[0], cp[1], cp[2],
cp[3], cp[4], cp[5]);
printf("stat %x for mbi[%d]\n"
,aha->aha_mbx.mbi[i].stat, i);
printf("addr = 0x%x\n", ccb);
}
#endif /*AHADEBUG */ #endif /*AHADEBUG */
/* may have missed it */
/* no such ccb known for abort */
case AHA_MBI_ERROR:
/* XXX ccb is still set up? Driver fails without it? */
break;
default:
panic("Impossible mbxi status");
} }
#ifdef AHADEBUG
if (aha_debug && ccb && stat != AHA_MBI_OK) {
u_char *cp;
cp = (u_char *) (&(ccb->scsi_cmd));
printf("op=%x %x %x %x %x %x\n",
cp[0], cp[1], cp[2],
cp[3], cp[4], cp[5]);
printf("stat %x for mbi[%d]\n"
,mbi->stat, i);
printf("addr = 0x%x\n", ccb);
}
#endif /*AHADEBUG */
if (ccb) { if (ccb) {
untimeout(aha_timeout, (caddr_t)ccb); untimeout(aha_timeout, (caddr_t)ccb);
aha_done(unit, ccb); aha_done(unit, ccb);
} }
aha->aha_mbx.mbi[i].stat = AHA_MBI_FREE; mbi->stat = AHA_MBI_FREE;
} }
} }
return 1; return 1;
} }
/* /*
* A ccb (and hence a mbx-out is put onto the * A ccb (and hence a mbx-out) is put onto the
* free list. * free list.
*/ */
void void
@ -794,6 +826,38 @@ aha_get_ccb(unit, flags)
return (rc); return (rc);
} }
static void
put_host_stat(int host_stat)
{
int i;
struct { int host_stat; char *text; } tab[] = {
{ AHA_OK, "Cmd ok" },
{ AHA_LINK_OK, "Link cmd ok" },
{ AHA_LINK_IT, "Link cmd ok + int" },
{ AHA_SEL_TIMEOUT, "Selection time out" },
{ AHA_OVER_UNDER, "Data over/under run" },
{ AHA_BUS_FREE, "Bus dropped at unexpected time" },
{ AHA_INV_BUS, "Invalid bus phase/sequence" },
{ AHA_BAD_MBO, "Incorrect MBO cmd" },
{ AHA_BAD_CCB, "Incorrect ccb opcode" },
{ AHA_BAD_LINK, "Not same values of LUN for links" },
{ AHA_INV_TARGET, "Invalid target direction" },
{ AHA_CCB_DUP, "Duplicate CCB received" },
{ AHA_INV_CCB, "Invalid CCB or segment list" },
{ AHA_ABORTED, "Software abort" },
};
for (i = 0; i < sizeof(tab) / sizeof(tab[0]); i++) {
if (tab[i].host_stat == host_stat) {
printf("%s\n", tab[i].text);
return;
}
}
printf("Unknown host_stat %02x\n", host_stat);
}
/* /*
* We have a ccb which has been processed by the * We have a ccb which has been processed by the
* adaptor, now we look to see how the operation * adaptor, now we look to see how the operation
@ -842,6 +906,7 @@ aha_done(unit, ccb)
{ {
case AHA_TARGET_CCB: case AHA_TARGET_CCB:
xs->resid = xs->datalen - scsi_3btoi(ccb->data_length); xs->resid = xs->datalen - scsi_3btoi(ccb->data_length);
xs->flags |= SCSI_RESID_VALID;
if (xs->resid <= 0) if (xs->resid <= 0)
xs->error = XS_LENGTH; xs->error = XS_LENGTH;
break; break;
@ -849,11 +914,9 @@ aha_done(unit, ccb)
case AHA_INIT_RESID_CCB: case AHA_INIT_RESID_CCB:
case AHA_INIT_SG_RESID_CCB: case AHA_INIT_SG_RESID_CCB:
xs->resid = scsi_3btoi(ccb->data_length); xs->resid = scsi_3btoi(ccb->data_length);
xs->flags |= SCSI_RESID_VALID;
if (xs->resid <= 0) if (xs->resid <= 0)
xs->error = XS_LENGTH; xs->error = XS_LENGTH;
printf("aha over under: resid %d error %d.\n",
xs->resid, xs->error);
break; break;
default: default:
@ -863,8 +926,8 @@ aha_done(unit, ccb)
default: /* Other scsi protocol messes */ default: /* Other scsi protocol messes */
xs->error = XS_DRIVER_STUFFUP; xs->error = XS_DRIVER_STUFFUP;
printf("aha%d:host_stat%x\n", printf("aha%d: ", unit);
unit, ccb->host_stat); put_host_stat(ccb->host_stat);
} }
} else { } else {
SC_DEBUG(xs->sc_link, SDEV_DB3, ("target err 0x%x\n", SC_DEBUG(xs->sc_link, SDEV_DB3, ("target err 0x%x\n",
@ -891,6 +954,21 @@ aha_done(unit, ccb)
scsi_done(xs); scsi_done(xs);
} }
static char *board_rev(int type)
{
switch(type)
{
case 0x20: return "Buslogic 545?";
case 0x31: return "AHA-1540";
case 0x41: return "AHA-154x[AB]";
case 0x42: return "AHA-1640";
case 0x43: return "AHA-1542C";
case 0x44: return "AHA-1542CF";
case 0x45: return "AHA-1542CF BIOS v2.01";
default: return "Unknown board";
}
}
/* /*
* Start the board, ready for normal operation * Start the board, ready for normal operation
*/ */
@ -978,6 +1056,8 @@ aha_init(unit)
inquire.revision_1, inquire.revision_2); inquire.revision_1, inquire.revision_2);
#endif /* AHADEBUG */ #endif /* AHADEBUG */
aha->flags = SDEV_BOUNCE;
/* /*
* If we are a 1542C or 1542CF disable the extended bios so that the * If we are a 1542C or 1542CF disable the extended bios so that the
* mailbox interface is unlocked. * mailbox interface is unlocked.
@ -986,36 +1066,58 @@ aha_init(unit)
* No need to check the extended bios flags as some of the * No need to check the extended bios flags as some of the
* extensions that cause us problems are not flagged in that byte. * extensions that cause us problems are not flagged in that byte.
*/ */
printf("aha%d: %s-V%c.%c",
unit, board_rev(inquire.boardid), inquire.revision_1,
inquire.revision_2);
if ((inquire.boardid == 0x43) || (inquire.boardid == 0x44) || if ((inquire.boardid == 0x43) || (inquire.boardid == 0x44) ||
(inquire.boardid == 0x45) || (inquire.boardid == 0x41 (inquire.boardid == 0x45) || (inquire.boardid == 0x41
&& inquire.revision_1 == 0x31 && inquire.revision_2 == 0x34)) { && inquire.revision_1 == 0x31 && inquire.revision_2 == 0x34)) {
static char *revs[] =
{"154xB-3.2", "1640", "154xC", "154xCF", "154xCF-2.01"};
aha_cmd(unit, 0, sizeof(extbios), 0, &extbios, AHA_EXT_BIOS); aha_cmd(unit, 0, sizeof(extbios), 0, &extbios, AHA_EXT_BIOS);
#ifdef AHADEBUG #ifdef AHADEBUG
printf("aha%d: extended bios flags %x\n", unit, extbios.flags); printf("aha%d: extended bios flags %x\n", unit, extbios.flags);
#endif /* AHADEBUG */ #endif /* AHADEBUG */
/* Say exactly what we think this is in case we ever get rev printf(", enabling mailbox");
* dependent problems:
*/
printf("aha%d is a %s-V%c.%c: enabling mailbox and residuals\n",
unit, revs[inquire.boardid - 0x41], inquire.revision_1,
inquire.revision_2);
aha_cmd(unit, 2, 0, 0, 0, AHA_MBX_ENABLE, aha_cmd(unit, 2, 0, 0, 0, AHA_MBX_ENABLE,
0, extbios.mailboxlock); 0, extbios.mailboxlock);
}
/* Which boards support residuals? Some early 1542A's apparently
* don't. The 1542B with V0.5 of the software does, so I've
* arbitrarily set that as the earliest rev.
*/
if ((inquire.boardid == 0x43) || (inquire.boardid == 0x44) ||
(inquire.boardid == 0x45) || (inquire.boardid == 0x41
&& (inquire.revision_1 > '0' || inquire.revision_2 >= '5'))) {
printf(", enabling residuals");
aha->init_opcode = AHA_INIT_RESID_CCB; aha->init_opcode = AHA_INIT_RESID_CCB;
aha->sg_opcode = AHA_INIT_SG_RESID_CCB; aha->sg_opcode = AHA_INIT_SG_RESID_CCB;
} }
/* Which boards support target operations? The 1542C completely
* locks up the SCSI bus if you enable them. I'm only sure
* about the B.
*/
if (inquire.boardid == 0x41) {
printf(", target ops");
aha->flags |= SDEV_TARGET_OPS;
}
printf("\n");
/* /*
* setup dma channel from jumpers and save int * setup dma channel from jumpers and save int
* level * level
*/ */
printf("aha%d: reading board settings, ", unit); printf("aha%d: reading board settings, ", unit);
#define PRNT(x) printf(x) #define PRNT(x) printf(x)
DELAY(1000); /* for Bustek 545 */ if (inquire.boardid == 0x20) {
DELAY(1000); /* for Bustek 545 */
}
aha_cmd(unit, 0, sizeof(conf), 0, &conf, AHA_CONF_GET); aha_cmd(unit, 0, sizeof(conf), 0, &conf, AHA_CONF_GET);
switch (conf.chan) { switch (conf.chan) {
case CHAN0: case CHAN0:
@ -1136,6 +1238,91 @@ ahaminphys(bp)
} }
} }
int aha_escape(xs, ccb)
struct scsi_xfer *xs;
struct aha_ccb *ccb;
{
int ret = 0;
int s;
if (xs->cmd)
{
switch(xs->cmd->opcode)
{
case SCSI_OP_RESET:
ccb->opcode = AHA_RESET_CCB;
ret = 0;
break;
case SCSI_OP_TARGET:
s= splbio();
aha_cmd(xs->sc_link->adapter_unit, 2, 0, 0, 0, AHA_TARGET_EN,
(int)xs->cmd->bytes[0], (int)1);
splx(s);
ret = COMPLETE;
break;
default:
ret = ESCAPE_NOT_SUPPORTED;
break;
}
}
else
{
ccb->opcode = AHA_RESET_CCB;
ret = 0;
}
return ret;
}
#define physdb(ARG) (void)(ARG)
/* physcontig: Scan forward from a KV and return length to the
* end of physically contiguous addresses. This belongs in
* i386/.../something_or_other.c
* XXX: Find the right thing in the kernel.
*/
static int physcontig(int kv, int len)
{
int len_was = len;
u_long kvl = (u_long)kv;
int phys_len;
u_long phys, prev_phys;
prev_phys = KVTOPHYS(kvl);
/* We go at least to the end of this page:
*/
phys_len = PAGESIZ - (prev_phys & (PAGESIZ - 1));
len -= phys_len;
kvl += phys_len;
prev_phys &= ~(PAGESIZ - 1);
while (len > 0)
{
phys = KVTOPHYS(kvl);
if (phys != prev_phys + PAGESIZ)
{
physdb(("phys %08x != prev_phys %08x + PAGESIZ\n",
phys, prev_phys));
break;
}
prev_phys = phys;
kvl += PAGESIZ;
len -= PAGESIZ;
}
phys_len = (len < 0) ? len_was : (len_was - len);
physdb(("physcontig(%08x, %d) = %d\n", kv, len_was, phys_len));
return phys_len;
}
/* /*
* start a scsi operation given the command and * start a scsi operation given the command and
* the data address. Also needs the unit, target * the data address. Also needs the unit, target
@ -1193,16 +1380,87 @@ aha_scsi_cmd(xs)
* fall on the ground if you ask for anything but * fall on the ground if you ask for anything but
* an exact number of sense bytes (wiping out the * an exact number of sense bytes (wiping out the
* sense data) * sense data)
* XXX: This was lost at some point in scsi_ioctl.c.
*/ */
ccb->req_sense_length = (xs->req_sense_length) ccb->req_sense_length = (xs->req_sense_length)
? xs->req_sense_length ? xs->req_sense_length
: sizeof(ccb->scsi_sense); : sizeof(ccb->scsi_sense);
if ((xs->datalen) && (!(flags & SCSI_RESET))) { /* XXX: I propose we move the reset handling into the escape
/* can use S/G only if not zero length */ * handling.
scsi_uto3b(KVTOPHYS(ccb->scat_gath), ccb->data_addr); */
sg = ccb->scat_gath; if (flags & SCSI_RESET) {
seg = 0; flags |= SCSI_ESCAPE;
xs->cmd->opcode = SCSI_OP_RESET;
}
/* Set up the CCB. For an escape function, the escape hook may
* set it up for us.
*/
if (flags & SCSI_ESCAPE) {
int ret;
ret = aha_escape(xs, ccb);
if (ret)
return ret;
}
else if (flags & SCSI_TARGET)
{
ccb->opcode = AHA_TARGET_CCB;
/* These must be set up for target mode:
*/
if (flags & SCSI_DATA_IN)
ccb->data_in = 1;
if (flags & SCSI_DATA_OUT)
ccb->data_out = 1;
}
else
{
ccb->opcode = (xs->datalen? /* can't use S/G if zero length */
AHA_INIT_SCAT_GATH_CCB
:AHA_INITIATOR_CCB);
}
switch(ccb->opcode)
{
case AHA_TARGET_CCB:
if (xs->data)
scsi_uto3b(KVTOPHYS((int)xs->data), ccb->data_addr);
else
scsi_uto3b(0, ccb->data_addr);
/* For non scatter-gather I/O (and Target mode doesn't do
* scatter-gather) we need to truncate the transfer
* at the first non consecutive physical address.
*/
scsi_uto3b(physcontig((int)xs->data, xs->datalen), ccb->data_length);
break;
/* This should be folded in with TARGET_CCB once
* physcontig is debugged.
*/
case AHA_INITIATOR_CCB:
case AHA_INIT_RESID_CCB:
if (xs->data)
scsi_uto3b(KVTOPHYS((int)xs->data), ccb->data_addr);
else
scsi_uto3b(0, ccb->data_addr);
scsi_uto3b(xs->datalen, ccb->data_length);
break;
case AHA_RESET_CCB:
scsi_uto3b(0, ccb->data_addr);
scsi_uto3b(0, ccb->data_length);
break;
case AHA_INIT_SCAT_GATH_CCB:
case AHA_INIT_SG_RESID_CCB:
scsi_uto3b(KVTOPHYS(ccb->scat_gath), ccb->data_addr );
sg = ccb->scat_gath ;
seg = 0;
#ifdef TFS_ONLY #ifdef TFS_ONLY
if (flags & SCSI_DATA_UIO) { if (flags & SCSI_DATA_UIO) {
iovp = ((struct uio *) xs->data)->uio_iov; iovp = ((struct uio *) xs->data)->uio_iov;
@ -1226,7 +1484,7 @@ aha_scsi_cmd(xs)
*/ */
SC_DEBUG(xs->sc_link, SDEV_DB4, SC_DEBUG(xs->sc_link, SDEV_DB4,
("%d @0x%x:- ", xs->datalen, xs->data)); ("%ld @%p:- ", xs->datalen, xs->data));
datalen = xs->datalen; datalen = xs->datalen;
thiskv = (int) xs->data; thiskv = (int) xs->data;
thisphys = KVTOPHYS(thiskv); thisphys = KVTOPHYS(thiskv);
@ -1293,15 +1551,20 @@ aha_scsi_cmd(xs)
aha_free_ccb(unit, ccb, flags); aha_free_ccb(unit, ccb, flags);
return (HAD_ERROR); return (HAD_ERROR);
} }
} else { /* No data xfer, use non S/G values */ break;
scsi_uto3b(0, ccb->data_addr);
scsi_uto3b(0, ccb->data_length); default:
printf("aha_scsi_cmd%d: Illegal CCB opcode.\n", unit);
xs->error = XS_DRIVER_STUFFUP;
aha_free_ccb(unit,ccb,flags);
return HAD_ERROR;
} }
scsi_uto3b(0, ccb->link_addr); scsi_uto3b(0, ccb->link_addr);
/* /*
* Put the scsi command in the ccb and start it * Put the scsi command in the ccb and start it
*/ */
if (!(flags & SCSI_RESET)) if (!(flags & SCSI_ESCAPE))
bcopy(xs->cmd, &ccb->scsi_cmd, ccb->scsi_cmd_length); bcopy(xs->cmd, &ccb->scsi_cmd, ccb->scsi_cmd_length);
if (!(flags & SCSI_NOMASK)) { if (!(flags & SCSI_NOMASK)) {
s = splbio(); /* stop instant timeouts */ s = splbio(); /* stop instant timeouts */
@ -1469,6 +1732,8 @@ aha_set_bus_speed(unit)
*/ */
static char aha_test_string[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890abcdefghijklmnopqrstuvwxyz!@"; static char aha_test_string[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890abcdefghijklmnopqrstuvwxyz!@";
u_char aha_scratch_buf[256];
int int
aha_bus_speed_check(unit, speed) aha_bus_speed_check(unit, speed)
int unit, speed; int unit, speed;