M boot/local.c => boot/local.c +5 -2
@@ 57,6 57,7 @@ int
connectlocal(void)
{
int p[2];
+ Dir dir;
char d[DIRLEN];
char partition[2*NAMELEN];
char *dev;
@@ 67,11 68,13 @@ connectlocal(void)
dev = disk ? disk : bootdisk;
sprint(partition, "%sfs", dev);
- if(stat(partition, d) < 0){
+ if(dirstat(partition, &dir) < 0){
strcpy(partition, dev);
- if(stat(partition, d) < 0)
+ if(dirstat(partition, &dir) < 0)
return -1;
}
+ if(dir.mode & CHDIR)
+ return -1;
print("kfs...");
if(bind("#c", "/dev", MREPL) < 0)
M pc/io.h => pc/io.h +32 -30
@@ 86,7 86,7 @@ enum {
enum {
MaxEISA = 16,
- EISAconfig = 0xC80,
+ CfgEISA = 0xC80,
};
/*
@@ 146,6 146,26 @@ enum { /* type 1 pre-defined header */
PciBCR = 0x3E, /* bridge control register */
};
+enum { /* type 2 pre-defined header */
+ PciCBExCA = 0x10,
+ PciCBSPSR = 0x16,
+ PciCBPBN = 0x18, /* primary bus number */
+ PciCBSBN = 0x19, /* secondary bus number */
+ PciCBUBN = 0x1A, /* subordinate bus number */
+ PciCBSLTR = 0x1B, /* secondary latency timer */
+ PciCBMBR0 = 0x1C,
+ PciCBMLR0 = 0x20,
+ PciCBMBR1 = 0x24,
+ PciCBMLR1 = 0x28,
+ PciCBIBR0 = 0x2C, /* I/O base */
+ PciCBILR0 = 0x30, /* I/O limit */
+ PciCBIBR1 = 0x34, /* I/O base */
+ PciCBILR1 = 0x38, /* I/O limit */
+ PciCBSVID = 0x40, /* subsystem vendor ID */
+ PciCBSID = 0x42, /* subsystem ID */
+ PciCBLMBAR = 0x44, /* legacy mode base address */
+};
+
typedef struct Pcisiz Pcisiz;
struct Pcisiz
{
@@ 161,16 181,17 @@ struct Pcidev
ushort vid; /* vendor ID */
ushort did; /* device ID */
+ uchar rid;
+ uchar ccrp;
+ uchar ccru;
+ uchar ccrb;
+
struct {
ulong bar; /* base address */
int size;
} mem[6];
- ulong pcr;
uchar intl; /* interrupt line */
- uchar ccrp;
- uchar ccru;
- uchar ccrb;
Pcidev* list;
Pcidev* link; /* next device on this bno */
@@ 180,8 201,14 @@ struct Pcidev
ulong bar;
int size;
} ioa, mema;
+ ulong pcr;
};
+#define PCIWINDOW 0
+#define PCIWADDR(va) (PADDR(va)+PCIWINDOW)
+#define ISAWINDOW 0
+#define ISAWADDR(va) (PADDR(va)+ISAWINDOW)
+
/*
* PCMCIA support code.
*/
@@ 197,31 224,6 @@ struct PCMmap {
int ref;
};
-/*
- * SCSI bus
- */
-enum {
- MaxScsi = 8,
- NTarget = 8, /* should be 16... */
-};
-struct Target {
- int ctlrno;
- int target;
- uchar* inq;
- uchar* scratch;
-
- Rendez rendez;
-
- int ok;
-};
-
-typedef int (*Scsiio)(Target*, int, uchar*, int, void*, int*);
-
-typedef struct SCSIdev {
- char* type;
- Scsiio (*reset)(int, ISAConf*);
-} SCSIdev;
-
/* SMBus transactions */
enum
{
M pc/pci.c => pc/pci.c +16 -17
@@ 142,13 142,13 @@ pcibarsize(Pcidev *p, int rno)
}
static int
-pcisizcmp(void* va, void* vb)
+pcisizcmp(void *a, void *b)
{
- Pcisiz *a, *b;
+ Pcisiz *aa, *bb;
- a = va;
- b = vb;
- return a->siz - b->siz;
+ aa = a;
+ bb = b;
+ return aa->siz - bb->siz;
}
static ulong
@@ 185,7 185,7 @@ pcibusmap(Pcidev *root, ulong *pmema, ulong *pioa, int wrreg)
ioa = *pioa;
mema = *pmema;
- DBG("pcibusmap wr=%d %T mem=%lux io=%lux\n",
+ DBG("pcibusmap wr=%d %T mem=%luX io=%luX\n",
wrreg, root->tbdf, mema, ioa);
ntb = 0;
@@ 202,7 202,8 @@ pcibusmap(Pcidev *root, ulong *pmema, ulong *pioa, int wrreg)
*/
for(p = root; p != nil; p = p->link) {
if(p->ccrb == 0x06) {
- if(p->ccru != 0x04 || p->bridge == nil) {
+ if((p->ccru != 0x04 && p->ccru != 0x07)
+ || p->bridge == nil) {
// DBG("pci: ignored bridge %T\n", p->tbdf);
continue;
}
@@ 393,12 394,14 @@ pciscan(int bno, Pcidev** list)
pcilist = p;
pcitail = p;
- p->intl = pcicfgr8(p, PciINTL);
+ p->rid = pcicfgr8(p, PciRID);
p->ccrp = pcicfgr8(p, PciCCRp);
p->ccru = pcicfgr8(p, PciCCRu);
p->ccrb = pcicfgr8(p, PciCCRb);
p->pcr = pcicfgr32(p, PciPCR);
+ p->intl = pcicfgr8(p, PciINTL);
+
/*
* If the device is a multi-function device adjust the
* loop count so all possible functions are checked.
@@ 452,7 455,7 @@ pciscan(int bno, Pcidev** list)
/*
* Find PCI-PCI bridges and recursively descend the tree.
*/
- if(p->ccrb != 0x06 || p->ccru != 0x04)
+ if(p->ccrb != 0x06 || (p->ccru != 0x04 && p->ccru != 0x07))
continue;
/*
@@ 553,7 556,8 @@ pcicfginit(void)
ioa = 0;
pcibusmap(pciroot, &mema, &ioa, 0);
-DBG("Sizes: mem=%8.8lux size=%8.8lux io=%8.8lux\n", mema, pcimask(mema), ioa);
+ DBG("Sizes: mem=%8.8lux size=%8.8lux io=%8.8lux\n",
+ mema, pcimask(mema), ioa);
/*
* Align the windows and map it
@@ 564,7 568,7 @@ DBG("Sizes: mem=%8.8lux size=%8.8lux io=%8.8lux\n", mema, pcimask(mema), ioa);
pcilog("Mask sizes: mem=%lux io=%lux\n", mema, ioa);
pcibusmap(pciroot, &mema, &ioa, 1);
-DBG("Sizes2: mem=%lux io=%lux\n", mema, ioa);
+ DBG("Sizes2: mem=%lux io=%lux\n", mema, ioa);
unlock(&pcicfginitlock);
return;
@@ 785,7 789,7 @@ pcihinv(Pcidev* p)
Pcidev *t;
if(p == nil) {
-putstrn(PCICONS.output, PCICONS.ptr);
+ putstrn(PCICONS.output, PCICONS.ptr);
p = pciroot;
print("bus dev type vid did intl memory\n");
}
@@ 808,16 812,11 @@ putstrn(PCICONS.output, PCICONS.ptr);
print("->%d", BUSBNO(t->bridge->tbdf));
print("\n");
}
-#define notdef
-#ifdef notdef
while(p != nil) {
if(p->bridge != nil)
pcihinv(p->bridge);
p = p->link;
}
-#else
-print("more...\n");
-#endif /* notdef */
}
void
D pc/scsi.c => pc/scsi.c +0 -468
@@ 1,468 0,0 @@
-#include "u.h"
-#include "../port/lib.h"
-#include "mem.h"
-#include "dat.h"
-#include "fns.h"
-#include "io.h"
-#include "ureg.h"
-#include "../port/error.h"
-
-enum {
- Ninq = 255,
- Nscratch = 255,
-
- CMDtest = 0x00,
- CMDreqsense = 0x03,
- CMDinquire = 0x12,
- CMDstart = 0x1B,
- CMDcapacity = 0x25,
- CMDread10 = 0x28,
- CMDwrite10 = 0x2A,
-};
-
-typedef struct {
- ISAConf;
- Scsiio io;
-
- Target target[NTarget];
-} Ctlr;
-
-static Ctlr* scsi[MaxScsi];
-
-extern SCSIdev* scsidev[];
-
-void
-scsireset(void)
-{
- Ctlr *ctlr;
- int ctlrno, i, t;
-
- for(ctlr = 0, ctlrno = 0; ctlrno < MaxScsi; ctlrno++){
- if(ctlr == 0)
- ctlr = malloc(sizeof(Ctlr));
- memset(ctlr, 0, sizeof(Ctlr));
- if(isaconfig("scsi", ctlrno, ctlr) == 0)
- continue;
- for(i = 0; scsidev[i]; i++){
- if(cistrcmp(ctlr->type, scsidev[i]->type))
- continue;
- if((ctlr->io = scsidev[i]->reset(ctlrno, ctlr)) == 0)
- break;
-
- print("scsi#%d: %s: port 0x%luX irq %lud",
- ctlrno, ctlr->type, ctlr->port,
- ctlr->irq);
- if(ctlr->mem)
- print(" addr 0x%luX", PADDR(ctlr->mem));
- if(ctlr->size)
- print(" size 0x%luX", ctlr->size);
- print("\n");
-
- for(t = 0; t < NTarget; t++){
- ctlr->target[t].ctlrno = ctlrno;
- ctlr->target[t].target = t;
- ctlr->target[t].inq = xalloc(Ninq);
- ctlr->target[t].scratch = xalloc(Nscratch);
- }
-
- scsi[ctlrno] = ctlr;
- ctlr = 0;
- break;
- }
- }
- if(ctlr)
- free(ctlr);
-}
-
-static uchar lastcmd[16];
-static int lastcmdsz;
-
-int
-scsiexec(Target *t, int rw, uchar *cmd, int cbytes, void *data, int *dbytes)
-{
- int s;
-
- /*
- * Call the device-specific I/O routine.
- * There should be no calls to 'error()' below this
- * which percolate back up.
- */
- switch(s = scsi[t->ctlrno]->io(t, rw, cmd, cbytes, data, dbytes)){
-
- case STcheck:
- memmove(lastcmd, cmd, cbytes);
- lastcmdsz = cbytes;
- /*FALLTHROUGH*/
-
- default:
- /*
- * It's more complicated than this. There are conditions
- * which are 'ok' but for which the returned status code
- * is not 'STok'.
- * Also, not all conditions require a reqsense, might
- * need to do a reqsense here and make it available to the
- * caller somehow.
- *
- * Later.
- */
- break;
- }
-
- return s;
-}
-
-Target*
-scsiunit(int ctlr, int unit)
-{
- Target *t;
-
- if(ctlr < 0 || ctlr >= MaxScsi || scsi[ctlr] == 0)
- return 0;
- if(unit < 0 || unit >= NTarget)
- return 0;
- t = &scsi[ctlr]->target[unit];
- if(t->ok == 0)
- return 0;
- return t;
-}
-
-static void
-scsiprobe(Ctlr *ctlr)
-{
- Target *t;
- int i, s, nbytes;
-
- for(i = 0; i < NTarget; i++) {
- t = &ctlr->target[i];
-
- if(scsitest(t, 0) < 0)
- continue;
-
- /*
- * Determine if the drive exists and is not ready or
- * is simply not responding
- */
- nbytes = Nscratch;
- s = scsireqsense(t, 0, t->scratch, &nbytes, 0);
- if(s != STok){
- print("scsi#%d: unit %d unavailable, status %d\n",
- t->ctlrno, i, s);
- continue;
- }
-
- /*
- * Inquire to find out what the device is.
- * Drivers then use the result to attach to targets
- */
- memset(t->inq, 0, Ninq);
- nbytes = Ninq;
- s = scsiinquiry(t, 0, t->inq, &nbytes);
- if(s != STok) {
- print("scsi#%d: unit %d inquire failed, status %d\n",
- t->ctlrno, i, s);
- continue;
- }
- print("scsi#%d: unit %d: %s\n",
- t->ctlrno, i, (char*)(t->inq+8));
- t->ok = 1;
- }
-}
-
-static void
-inventory(void)
-{
- int i;
- static Lock ilock;
- static int inited;
-
- lock(&ilock);
- if(inited) {
- unlock(&ilock);
- return;
- }
- inited = 1;
- unlock(&ilock);
-
- for(i = 0; i < MaxScsi; i++){
- if(scsi[i])
- scsiprobe(scsi[i]);
- }
-}
-
-int
-scsiinv(int devno, int *type, Target **rt, uchar **inq, char *id)
-{
- Target *t;
- int ctlr, *i, unit;
-
- inventory();
-
- for(;;){
- ctlr = devno/NTarget;
- unit = devno%NTarget;
- if(ctlr >= MaxScsi || scsi[ctlr] == 0)
- return -1;
-
- t = &scsi[ctlr]->target[unit];
- devno++;
- if(t->ok){
- for(i = type; *i >= 0; i++){
- if((t->inq[0]&0x1F) != *i)
- continue;
- *rt = t;
- *inq = t->inq;
- if(id)
- sprint(id, "scsi%d: unit %d",
- ctlr, unit);
- return devno;
- }
- }
- }
- return -1;
-}
-
-int
-scsitest(Target *t, int lun)
-{
- uchar cmd[6];
-
- memset(cmd, 0, sizeof(cmd));
- cmd[0] = CMDtest;
- cmd[1] = lun<<5;
- return scsiexec(t, SCSIread, cmd, sizeof(cmd), 0, 0);
-}
-
-int
-scsistart(Target *t, int lun, int s)
-{
- uchar cmd[6];
-
- memset(cmd, 0, sizeof cmd);
- cmd[0] = CMDstart;
- cmd[1] = lun<<5;
- cmd[4] = s? 1: 0;
- return scsiexec(t, SCSIread, cmd, sizeof(cmd), 0, 0);
-}
-
-int
-scsiinquiry(Target *t, int lun, void *data, int *datalen)
-{
- uchar cmd[6];
-
- memset(cmd, 0, sizeof cmd);
- cmd[0] = CMDinquire;
- cmd[1] = lun<<5;
- cmd[4] = *datalen;
- return scsiexec(t, SCSIread, cmd, sizeof(cmd), data, datalen);
-}
-
-int
-scsicap(Target *t, int lun, ulong *size, ulong *bsize)
-{
- int s, nbytes;
- uchar cmd[10], *d;
-
- memset(cmd, 0, sizeof(cmd));
- cmd[0] = CMDcapacity;
- cmd[1] = lun<<5;
-
- nbytes = 8;
- d = scsialloc(nbytes);
- if(d == 0)
- return scsierrstr(STnomem);
-
- s = scsiexec(t, SCSIread, cmd, sizeof(cmd), d, &nbytes);
- if(s == STok) {
- *size = (d[0]<<24)|(d[1]<<16)|(d[2]<<8)|d[3];
- *bsize = (d[4]<<24)|(d[5]<<16)|(d[6]<<8)|d[7];
- }
- scsifree(d);
- return s;
-}
-
-int
-scsibio(Target *t, int lun, int dir, void *b, long n, long bsize, long bno)
-{
- uchar cmd[10];
- int s, cdbsiz, nbytes;
-
- cmd[0] = CMDwrite10;
- if(dir == SCSIread)
- cmd[0] = CMDread10;
- cmd[1] = (lun<<5);
- cmd[2] = bno>>24;
- cmd[3] = bno>>16;
- cmd[4] = bno>>8;
- cmd[5] = bno;
- cmd[6] = 0;
- cmd[7] = n>>8;
- cmd[8] = n;
- cmd[9] = 0;
- cdbsiz = 10;
-
- nbytes = n*bsize;
- s = scsiexec(t, dir, cmd, cdbsiz, b, &nbytes);
- if(s != STok) {
- nbytes = Nscratch;
- scsireqsense(t, lun, t->scratch, &nbytes, 0);
- return scsierrstr(s);
- }
- return nbytes;
-}
-
-static char *key[] =
-{
- "no sense",
- "recovered error",
- "not ready",
- "medium error",
- "hardware error",
- "illegal request",
- "unit attention",
- "data protect",
- "blank check",
- "vendor specific",
- "copy aborted",
- "aborted command",
- "equal",
- "volume overflow",
- "miscompare",
- "reserved"
-};
-
-int
-scsireqsense(Target *t, int lun, void *data, int *nbytes, int quiet)
-{
- char *s;
- int n, status, try;
- uchar cmd[6], *sense;
-
- n = *nbytes;
- sense = malloc(n);
-
- for(try = 0; try < 20; try++) {
- memset(cmd, 0, sizeof(cmd));
- cmd[0] = CMDreqsense;
- cmd[1] = lun<<5;
- cmd[4] = n;
- memset(sense, 0, n);
-
- *nbytes = n;
- status = scsiexec(t, SCSIread, cmd, sizeof(cmd), sense, nbytes);
- if(status != STok){
- free(sense);
- return status;
- }
- *nbytes = sense[0x07]+8;
- memmove(data, sense, *nbytes);
-
- switch(sense[2] & 0x0F){
-
- case 6: /* unit attention */
- /*
- * 0x28 - not ready to ready transition,
- * medium may have changed.
- * 0x29 - power on, RESET or BUS DEVICE RESET occurred.
- */
- if(sense[12] != 0x28 && sense[12] != 0x29)
- goto buggery;
- /*FALLTHROUGH*/
- case 0: /* no sense */
- case 1: /* recovered error */
- free(sense);
- return STok;
-
- case 2: /* not ready */
- if(sense[12] == 0x3A) /* medium not present */
- goto buggery;
- /*FALLTHROUGH*/
-
- default:
- /*
- * If unit is becoming ready, rather than not ready,
- * then wait a little then poke it again; should this
- * be here or in the caller?
- */
- if((sense[12] == 0x04 && sense[13] == 0x01)){
- while(waserror())
- ;
- tsleep(&t->rendez, return0, 0, 500);
- poperror();
- scsitest(t, lun);
- break;
- }
- goto buggery;
- }
- }
-
-buggery:
- if(quiet == 0){
- s = key[sense[2]&0x0F];
- print("scsi#%d: unit %d reqsense: '%s' code #%2.2ux #%2.2ux\n",
- t->ctlrno, t->target, s, sense[12], sense[13]);
- print("scsi#%d: byte 2: #%2.2uX, bytes 15-17: #%2.2uX #%2.2uX #%2.2uX\n",
- t->ctlrno, sense[2], sense[15], sense[16], sense[17]);
- print("lastcmd (%d): ", lastcmdsz);
- for(n = 0; n < lastcmdsz; n++)
- print(" #%2.2uX", lastcmd[n]);
- print("\n");
- }
- free(sense);
- return STcheck;
-}
-
-
-int
-scsierrstr(int errno)
-{
- char *p;
-
- switch(errno){
- case STnomem:
- p = Enomem;
- break;
- case STtimeout:
- p = "bus timeout";
- break;
- case STownid:
- p = "playing with myself";
- break;
- case STharderr:
- p = Eio;
- break;
- case STok:
- p = Enoerror;
- break;
- case STcheck:
- p = "check condition";
- break;
- case STcondmet:
- p = "condition met/good";
- break;
- case STbusy:
- p = "busy";
- break;
- case STintok:
- p = "intermediate/good";
- break;
- case STintcondmet:
- p = "intermediate/condition met/good";
- break;
- case STresconf:
- p = "reservation conflict";
- break;
- case STterminated:
- p = "command terminated";
- break;
- case STqfull:
- p = "queue full";
- break;
-
- default:
- p = "unknown SCSI error";
- break;
- }
- strncpy(up->error, p, NAMELEN);
-
- return -1;
-}
A pc/sd53c8xx.c => pc/sd53c8xx.c +2068 -0
@@ 0,0 1,2068 @@
+/*
+ * NCR 53c8xx driver for Plan 9
+ * Nigel Roles (ngr@cotswold.demon.co.uk)
+ *
+ * 08/07/99 Ultra2 fixed. Brazil #ifdefs added. Fixed script error 6 diagnostics.
+ *
+ * 09/06/99 Enhancements to support 895 and 896 correctly. Attempt at Ultra 2 negotiation,
+ * though no device to test with yet.
+ * Variant now contains the number of valid chip registers to assist
+ * dumpncrregs()
+ *
+ * 06/10/98 Various bug fixes and Brazil compiler inspired changes from jmk
+ *
+ * 05/10/98 Small fix to handle command length being greater than expected by device
+ *
+ * 04/08/98 Added missing locks to interrupt handler. Marked places where
+ * multiple controller extensions could go
+ *
+ * 18/05/97 Fixed overestimate in size of local SCRIPT RAM
+ *
+ * 17/05/97 Bug fix to return status
+ *
+ * 06/10/96 Enhanced list of chip IDs. 875 revision 1 has no clock doubler, so assume it
+ * is shipped with 80MHz crystal. Use bit 3 of the GPREG to recognise differential
+ * boards. This is Symbios specific, but since they are about the only suppliers of
+ * differential cards.
+ *
+ * 23/9/96 Wide and Ultra supported. 825A and 860 added to variants. Dual compiling
+ * version for fileserver and cpu. 80MHz default clock for 860
+ *
+ * 5/8/96 Waits for an Inquiry message before initiating synchronous negotiation
+ * in case capabilities byte [7] indicates device does not support it. Devices
+ * which do target initiated negotiation will typically get in first; a few
+ * bugs in handling this have been fixed
+ *
+ * 3/8/96 Added differential support (put scsi0=diff in plan9.ini)
+ * Split exec() into exec() and io(). Exec() is small, and Io() does not
+ * use any Plan 9 specific data structures, so alternate exec() functions
+ * may be done for other environments, such as the fileserver
+ *
+ * GENERAL
+ *
+ * Works on 810 and 875
+ * Should work on 815, 825, 810A, 825A, 860A
+ * Uses local RAM, large FIFO, prefetch, burst opcode fetch, and 16 byte synch. offset
+ * where applicable
+ * Supports multi-target, wide, Ultra
+ * Differential mode can be enabled by putting scsi0=diff in plan9.ini
+ * NO SUPPORT FOR tagged queuing (yet)
+ *
+ * Known problems
+ */
+
+#define MAXTARGET 16 /* can be 8 or 16 */
+
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "sd.h"
+extern SDifc sd53c8xxifc;
+
+/**********************************/
+/* Portable configuration macros */
+/**********************************/
+
+//#define BOOTDEBUG
+//#define ASYNC_ONLY
+//#define INTERNAL_SCLK
+//#define ALWAYS_DO_WDTR
+#define WMR_DEBUG
+
+/**********************************/
+/* CPU specific macros */
+/**********************************/
+
+#ifdef BOOTDEBUG
+
+#define KPRINT oprint
+#define IPRINT intrprint
+#define DEBUG(n) 0
+#define IFLUSH() iflush()
+
+#else
+
+#define KPRINT if(0)print
+#define IPRINT if(0)print
+#define DEBUG(n) 0
+#define IFLUSH()
+
+#endif /* BOOTDEBUG */
+
+/*******************************/
+/* General */
+/*******************************/
+
+#ifndef DMASEG
+#define DMASEG(x) PADDR(x)
+#define legetl(x) (*(ulong*)(x))
+#define lesetl(x,v) (*(ulong*)(x) = (v))
+#define swabl(a,b,c)
+#else
+#endif /*DMASEG */
+#define DMASEG_TO_KADDR(x) KADDR(PADDR(x))
+#define KPTR(x) ((x) == 0 ? 0 : DMASEG_TO_KADDR(x))
+
+#define MEGA 1000000L
+#ifdef INTERNAL_SCLK
+#define SCLK (33 * MEGA)
+#else
+#define SCLK (40 * MEGA)
+#endif /* INTERNAL_SCLK */
+#define ULTRA_NOCLOCKDOUBLE_SCLK (80 * MEGA)
+
+#define MAXSYNCSCSIRATE (5 * MEGA)
+#define MAXFASTSYNCSCSIRATE (10 * MEGA)
+#define MAXULTRASYNCSCSIRATE (20 * MEGA)
+#define MAXULTRA2SYNCSCSIRATE (40 * MEGA)
+#define MAXASYNCCORERATE (25 * MEGA)
+#define MAXSYNCCORERATE (25 * MEGA)
+#define MAXFASTSYNCCORERATE (50 * MEGA)
+#define MAXULTRASYNCCORERATE (80 * MEGA)
+#define MAXULTRA2SYNCCORERATE (160 * MEGA)
+
+
+#define X_MSG 1
+#define X_MSG_SDTR 1
+#define X_MSG_WDTR 3
+
+struct na_patch {
+ unsigned lwoff;
+ unsigned char type;
+};
+
+typedef struct Ncr {
+ uchar scntl0; /* 00 */
+ uchar scntl1;
+ uchar scntl2;
+ uchar scntl3;
+
+ uchar scid; /* 04 */
+ uchar sxfer;
+ uchar sdid;
+ uchar gpreg;
+
+ uchar sfbr; /* 08 */
+ uchar socl;
+ uchar ssid;
+ uchar sbcl;
+
+ uchar dstat; /* 0c */
+ uchar sstat0;
+ uchar sstat1;
+ uchar sstat2;
+
+ uchar dsa[4]; /* 10 */
+
+ uchar istat; /* 14 */
+ uchar istatpad[3];
+
+ uchar ctest0; /* 18 */
+ uchar ctest1;
+ uchar ctest2;
+ uchar ctest3;
+
+ uchar temp[4]; /* 1c */
+
+ uchar dfifo; /* 20 */
+ uchar ctest4;
+ uchar ctest5;
+ uchar ctest6;
+
+ union { /* 24 */
+ ulong dbc; /* only 24 bits usable LONG */
+ struct {
+ uchar dcmdpad[3];
+ uchar dcmd;
+ };
+ };
+
+ uchar dnad[4]; /* 28 */
+ uchar dsp[4]; /* 2c */
+ uchar dsps[4]; /* 30 */
+
+ uchar scratcha[4]; /* 34 */
+
+ uchar dmode; /* 38 */
+ uchar dien;
+ uchar dwt;
+ uchar dcntl;
+
+ uchar adder[4]; /* 3c */
+
+ uchar sien0; /* 40 */
+ uchar sien1;
+ uchar sist0;
+ uchar sist1;
+
+ uchar slpar; /* 44 */
+ uchar slparpad0;
+ uchar macntl;
+ uchar gpcntl;
+
+ uchar stime0; /* 48 */
+ uchar stime1;
+ uchar respid;
+ uchar respidpad0;
+
+ uchar stest0; /* 4c */
+ uchar stest1;
+ uchar stest2;
+ uchar stest3;
+
+ uchar sidl; /* 50 */
+ uchar sidlpad[3];
+
+ uchar sodl; /* 54 */
+ uchar sodlpad[3];
+
+ uchar sbdl; /* 58 */
+ uchar sbdlpad[3];
+
+ union { /* 5c */
+ uchar scratchb[4];
+ ulong scratchbncr;
+ };
+} Ncr;
+
+typedef struct Movedata {
+ uchar dbc[4];
+ uchar pa[4];
+} Movedata;
+
+typedef enum NegoState {
+ NeitherDone, WideInit, WideResponse, WideDone,
+ SyncInit, SyncResponse, BothDone
+} NegoState;
+
+typedef enum State {
+ Allocated, Queued, Active, Done
+} State;
+
+typedef struct Dsa {
+ union {
+ uchar state[4];
+ struct {
+ uchar stateb;
+ uchar result;
+ uchar dmablks;
+ uchar flag; /* setbyte(state,3,...) */
+ };
+ };
+
+ union {
+ ulong dmancr; /* For block transfer: NCR order (little-endian) */
+ uchar dmaaddr[4];
+ };
+
+ uchar target; /* Target */
+ uchar pad0[3];
+
+ uchar lun; /* Logical Unit Number */
+ uchar pad1[3];
+
+ uchar scntl3;
+ uchar sxfer;
+ uchar pad2[2];
+
+ uchar next[4]; /* chaining for SCRIPT (NCR byte order) */
+ struct Dsa *freechain; /* chaining for freelist */
+ Rendez;
+ uchar scsi_id_buf[4];
+ Movedata msg_out_buf;
+ Movedata cmd_buf;
+ Movedata data_buf;
+ Movedata status_buf;
+ uchar msg_out[10]; /* enough to include SDTR */
+ uchar status;
+ int p9status;
+ uchar parityerror;
+} Dsa;
+
+typedef enum Feature {
+ BigFifo = 1, /* 536 byte fifo */
+ BurstOpCodeFetch = 2, /* burst fetch opcodes */
+ Prefetch = 4, /* prefetch 8 longwords */
+ LocalRAM = 8, /* 4K longwords of local RAM */
+ Differential = 16, /* Differential support */
+ Wide = 32, /* Wide capable */
+ Ultra = 64, /* Ultra capable */
+ ClockDouble = 128, /* Has clock doubler */
+ ClockQuad = 256, /* Has clock quadrupler (same as Ultra2) */
+ Ultra2 = 256,
+} Feature;
+
+typedef enum Burst {
+ Burst2 = 0,
+ Burst4 = 1,
+ Burst8 = 2,
+ Burst16 = 3,
+ Burst32 = 4,
+ Burst64 = 5,
+ Burst128 = 6
+} Burst;
+
+typedef struct Variant {
+ ushort did;
+ uchar maxrid; /* maximum allowed revision ID */
+ char *name;
+ Burst burst; /* codings for max burst */
+ uchar maxsyncoff; /* max synchronous offset */
+ uchar registers; /* number of 32 bit registers */
+ unsigned feature;
+} Variant;
+
+static unsigned char cf2[] = { 6, 2, 3, 4, 6, 8, 12, 16 };
+#define NULTRA2SCF (sizeof(cf2)/sizeof(cf2[0]))
+#define NULTRASCF (NULTRA2SCF - 2)
+#define NSCF (NULTRASCF - 1)
+
+typedef struct Controller {
+ Lock;
+ uchar synctab[NULTRA2SCF - 1][8];/* table of legal tpfs */
+ NegoState s[MAXTARGET];
+ uchar scntl3[MAXTARGET];
+ uchar sxfer[MAXTARGET];
+ uchar cap[MAXTARGET]; /* capabilities byte from Identify */
+ ushort capvalid; /* bit per target for validity of cap[] */
+ ushort wide; /* bit per target set if wide negotiated */
+ ulong sclk; /* clock speed of controller */
+ uchar clockmult; /* set by synctabinit */
+ uchar ccf; /* CCF bits */
+ uchar tpf; /* best tpf value for this controller */
+ uchar feature; /* requested features */
+ int running; /* is the script processor running? */
+ int ssm; /* single step mode */
+ Ncr *n; /* pointer to registers */
+ Variant *v; /* pointer to variant type */
+ ulong *script; /* where the real script is */
+ ulong scriptpa; /* where the real script is */
+ Pcidev* pcidev;
+ SDev* sdev;
+
+ struct {
+ Lock;
+ uchar head[4]; /* head of free list (NCR byte order) */
+ Dsa *tail;
+ Dsa *free;
+ } dsalist;
+
+ QLock q[MAXTARGET]; /* queues for each target */
+} Controller;
+
+static Controller controller;
+
+/* ISTAT */
+enum { Abrt = 0x80, Srst = 0x40, Sigp = 0x20, Sem = 0x10, Con = 0x08, Intf = 0x04, Sip = 0x02, Dip = 0x01 };
+
+/* DSTAT */
+enum { Dfe = 0x80, Mdpe = 0x40, Bf = 0x20, Abrted = 0x10, Ssi = 0x08, Sir = 0x04, Iid = 0x01 };
+
+/* SSTAT */
+enum { DataOut, DataIn, Cmd, Status, ReservedOut, ReservedIn, MessageOut, MessageIn };
+
+static void setmovedata(Movedata*, ulong, ulong);
+static void advancedata(Movedata*, long);
+
+static char *phase[] = {
+ "data out", "data in", "command", "status",
+ "reserved out", "reserved in", "message out", "message in"
+};
+
+#ifdef BOOTDEBUG
+#define DEBUGSIZE 10240
+char debugbuf[DEBUGSIZE];
+char *debuglast;
+
+static void
+intrprint(char *format, ...)
+{
+ if (debuglast == 0)
+ debuglast = debugbuf;
+ debuglast = doprint(debuglast, debugbuf + (DEBUGSIZE - 1), format, (&format + 1));
+}
+
+static void
+iflush()
+{
+ int s;
+ char *endp;
+ s = splhi();
+ if (debuglast == 0)
+ debuglast = debugbuf;
+ if (debuglast == debugbuf) {
+ splx(s);
+ return;
+ }
+ endp = debuglast;
+ splx(s);
+ screenputs(debugbuf, endp - debugbuf);
+ s = splhi();
+ memmove(debugbuf, endp, debuglast - endp);
+ debuglast -= endp - debugbuf;
+ splx(s);
+}
+
+static void
+oprint(char *format, ...)
+{
+ int s;
+
+ iflush();
+ s = splhi();
+ if (debuglast == 0)
+ debuglast = debugbuf;
+ debuglast = doprint(debuglast, debugbuf + (DEBUGSIZE - 1), format, (&format + 1));
+ splx(s);
+ iflush();
+}
+#endif
+
+#include "sd53c8xx.i"
+
+static Dsa *
+dsaalloc(Controller *c, int target, int lun)
+{
+ Dsa *d;
+
+ ilock(&c->dsalist);
+ if ((d = c->dsalist.free) == 0) {
+ d = xalloc(sizeof(*d));
+ if (DEBUG(1))
+ KPRINT("sd53c8xx: %d/%d: allocated new dsa %lux\n", target, lun, d);
+ lesetl(d->next, 0);
+ lesetl(d->state, A_STATE_ALLOCATED);
+ if (legetl(c->dsalist.head) == 0)
+ lesetl(c->dsalist.head, DMASEG(d)); /* ATOMIC?!? */
+ else
+ lesetl(c->dsalist.tail->next, DMASEG(d)); /* ATOMIC?!? */
+ c->dsalist.tail = d;
+ }
+ else {
+ if (DEBUG(1))
+ KPRINT("sd53c8xx: %d/%d: reused dsa %lux\n", target, lun, d);
+ c->dsalist.free = d->freechain;
+ lesetl(d->state, A_STATE_ALLOCATED);
+ }
+ iunlock(&c->dsalist);
+ d->target = target;
+ d->lun = lun;
+ return d;
+}
+
+static void
+dsafree(Controller *c, Dsa *d)
+{
+ ilock(&c->dsalist);
+ d->freechain = c->dsalist.free;
+ c->dsalist.free = d;
+ lesetl(d->state, A_STATE_FREE);
+ iunlock(&c->dsalist);
+}
+
+static Dsa *
+dsafind(Controller *c, uchar target, uchar lun, uchar state)
+{
+ Dsa *d;
+ for (d = KPTR(legetl(c->dsalist.head)); d; d = KPTR(legetl(d->next))) {
+ if (d->target != 0xff && d->target != target)
+ continue;
+ if (lun != 0xff && d->lun != lun)
+ continue;
+ if (state != 0xff && d->stateb != state)
+ continue;
+ break;
+ }
+ return d;
+}
+
+static void
+dumpncrregs(Controller *c, int intr)
+{
+ int i;
+ Ncr *n = c->n;
+ int depth = c->v->registers / 4;
+
+ KPRINT("sa = %.8lux\n", c->scriptpa);
+ for (i = 0; i < depth; i++) {
+ int j;
+ for (j = 0; j < 4; j++) {
+ int k = j * depth + i;
+ uchar *p;
+
+ /* display little-endian to make 32-bit values readable */
+ p = (uchar*)n+k*4;
+ if (intr)
+ IPRINT(" %.2x%.2x%.2x%.2x %.2x %.2x", p[3], p[2], p[1], p[0], k * 4, (k * 4) + 0x80);
+ else
+ KPRINT(" %.2x%.2x%.2x%.2x %.2x %.2x", p[3], p[2], p[1], p[0], k * 4, (k * 4) + 0x80);
+ USED(p);
+ }
+ if (intr)
+ IPRINT("\n");
+ else
+ KPRINT("\n");
+ }
+}
+
+static int
+chooserate(Controller *c, int tpf, int *scfp, int *xferpp)
+{
+ /* find lowest entry >= tpf */
+ int besttpf = 1000;
+ int bestscfi = 0;
+ int bestxferp = 0;
+ int scf, xferp;
+ int maxscf;
+
+ if (c->v->feature & Ultra2)
+ maxscf = NULTRA2SCF;
+ else if (c->v->feature & Ultra)
+ maxscf = NULTRASCF;
+ else
+ maxscf = NSCF;
+
+ /*
+ * search large clock factors first since this should
+ * result in more reliable transfers
+ */
+ for (scf = maxscf; scf >= 1; scf--) {
+ for (xferp = 0; xferp < 8; xferp++) {
+ unsigned char v = c->synctab[scf - 1][xferp];
+ if (v == 0)
+ continue;
+ if (v >= tpf && v < besttpf) {
+ besttpf = v;
+ bestscfi = scf;
+ bestxferp = xferp;
+ }
+ }
+ }
+ if (besttpf == 1000)
+ return 0;
+ if (scfp)
+ *scfp = bestscfi;
+ if (xferpp)
+ *xferpp = bestxferp;
+ return besttpf;
+}
+
+static void
+synctabinit(Controller *c)
+{
+ int scf;
+ unsigned long scsilimit;
+ int xferp;
+ unsigned long cr, sr;
+ int tpf;
+ int fast;
+ int maxscf;
+
+ if (c->v->feature & Ultra2)
+ maxscf = NULTRA2SCF;
+ else if (c->v->feature & Ultra)
+ maxscf = NULTRASCF;
+ else
+ maxscf = NSCF;
+
+ /*
+ * for chips with no clock doubler, but Ultra capable (e.g. 860, or interestingly the
+ * first spin of the 875), assume 80MHz
+ * otherwise use the internal (33 Mhz) or external (40MHz) default
+ */
+
+ if ((c->v->feature & Ultra) != 0 && (c->v->feature & (ClockDouble | ClockQuad)) == 0)
+ c->sclk = ULTRA_NOCLOCKDOUBLE_SCLK;
+ else
+ c->sclk = SCLK;
+
+ /*
+ * otherwise, if the chip is Ultra capable, but has a slow(ish) clock,
+ * invoke the doubler
+ */
+
+ if (SCLK <= 40000000) {
+ if (c->v->feature & ClockDouble) {
+ c->sclk *= 2;
+ c->clockmult = 1;
+ }
+ else if (c->v->feature & ClockQuad) {
+ c->sclk *= 4;
+ c->clockmult = 1;
+ }
+ else
+ c->clockmult = 0;
+ }
+ else
+ c->clockmult = 0;
+
+ /* derive CCF from sclk */
+ /* woebetide anyone with SCLK < 16.7 or > 80MHz */
+ if (c->sclk <= 25 * MEGA)
+ c->ccf = 1;
+ else if (c->sclk <= 3750000)
+ c->ccf = 2;
+ else if (c->sclk <= 50 * MEGA)
+ c->ccf = 3;
+ else if (c->sclk <= 75 * MEGA)
+ c->ccf = 4;
+ else if ((c->v->feature & ClockDouble) && c->sclk <= 80 * MEGA)
+ c->ccf = 5;
+ else if ((c->v->feature & ClockQuad) && c->sclk <= 120 * MEGA)
+ c->ccf = 6;
+ else if ((c->v->feature & ClockQuad) && c->sclk <= 160 * MEGA)
+ c->ccf = 7;
+
+ for (scf = 1; scf < maxscf; scf++) {
+ /* check for legal core rate */
+ /* round up so we run slower for safety */
+ cr = (c->sclk * 2 + cf2[scf] - 1) / cf2[scf];
+ if (cr <= MAXSYNCCORERATE) {
+ scsilimit = MAXSYNCSCSIRATE;
+ fast = 0;
+ }
+ else if (cr <= MAXFASTSYNCCORERATE) {
+ scsilimit = MAXFASTSYNCSCSIRATE;
+ fast = 1;
+ }
+ else if ((c->v->feature & Ultra) && cr <= MAXULTRASYNCCORERATE) {
+ scsilimit = MAXULTRASYNCSCSIRATE;
+ fast = 2;
+ }
+ else if ((c->v->feature & Ultra2) && cr <= MAXULTRA2SYNCCORERATE) {
+ scsilimit = MAXULTRA2SYNCSCSIRATE;
+ fast = 3;
+ }
+ else
+ continue;
+ for (xferp = 11; xferp >= 4; xferp--) {
+ int ok;
+ int tp;
+ /* calculate scsi rate - round up again */
+ /* start from sclk for accuracy */
+ int totaldivide = xferp * cf2[scf];
+ sr = (c->sclk * 2 + totaldivide - 1) / totaldivide;
+ if (sr > scsilimit)
+ break;
+ /*
+ * now work out transfer period
+ * round down now so that period is pessimistic
+ */
+ tp = (MEGA * 1000) / sr;
+ /*
+ * bounds check it
+ */
+ if (tp < 25 || tp > 255 * 4)
+ continue;
+ /*
+ * spot stupid special case for Ultra or Ultra2
+ * while working out factor
+ */
+ if (tp == 25)
+ tpf = 10;
+ else if (tp == 50)
+ tpf = 12;
+ else if (tp < 52)
+ continue;
+ else
+ tpf = tp / 4;
+ /*
+ * now check tpf looks sensible
+ * given core rate
+ */
+ switch (fast) {
+ case 0:
+ /* scf must be ccf for SCSI 1 */
+ ok = tpf >= 50 && scf == c->ccf;
+ break;
+ case 1:
+ ok = tpf >= 25 && tpf < 50;
+ break;
+ case 2:
+ /*
+ * must use xferp of 4, or 5 at a pinch
+ * for an Ultra transfer
+ */
+ ok = xferp <= 5 && tpf >= 12 && tpf < 25;
+ break;
+ case 3:
+ ok = xferp == 4 && (tpf == 10 || tpf == 11);
+ break;
+ default:
+ ok = 0;
+ }
+ if (!ok)
+ continue;
+ c->synctab[scf - 1][xferp - 4] = tpf;
+ }
+ }
+
+#ifndef NO_ULTRA2
+ if (c->v->feature & Ultra2)
+ tpf = 10;
+ else
+#endif
+ if (c->v->feature & Ultra)
+ tpf = 12;
+ else
+ tpf = 25;
+ for (; tpf < 256; tpf++) {
+ if (chooserate(c, tpf, &scf, &xferp) == tpf) {
+ unsigned tp = tpf == 10 ? 25 : (tpf == 12 ? 50 : tpf * 4);
+ unsigned long khz = (MEGA + tp - 1) / (tp);
+ KPRINT("sd53c8xx: tpf=%d scf=%d.%.1d xferp=%d mhz=%ld.%.3ld\n",
+ tpf, cf2[scf] / 2, (cf2[scf] & 1) ? 5 : 0,
+ xferp + 4, khz / 1000, khz % 1000);
+ USED(khz);
+ if (c->tpf == 0)
+ c->tpf = tpf; /* note lowest value for controller */
+ }
+ }
+}
+
+static void
+synctodsa(Dsa *dsa, Controller *c)
+{
+/*
+ KPRINT("synctodsa(dsa=%lux, target=%d, scntl3=%.2lx sxfer=%.2x)\n",
+ dsa, dsa->target, c->scntl3[dsa->target], c->sxfer[dsa->target]);
+*/
+ dsa->scntl3 = c->scntl3[dsa->target];
+ dsa->sxfer = c->sxfer[dsa->target];
+}
+
+static void
+setsync(Dsa *dsa, Controller *c, int target, uchar ultra, uchar scf, uchar xferp, uchar reqack)
+{
+ c->scntl3[target] =
+ (c->scntl3[target] & 0x08) | (((scf << 4) | c->ccf | (ultra << 7)) & ~0x08);
+ c->sxfer[target] = (xferp << 5) | reqack;
+ c->s[target] = BothDone;
+ if (dsa) {
+ synctodsa(dsa, c);
+ c->n->scntl3 = c->scntl3[target];
+ c->n->sxfer = c->sxfer[target];
+ }
+}
+
+static void
+setasync(Dsa *dsa, Controller *c, int target)
+{
+ setsync(dsa, c, target, 0, c->ccf, 0, 0);
+}
+
+static void
+setwide(Dsa *dsa, Controller *c, int target, uchar wide)
+{
+ c->scntl3[target] = wide ? (1 << 3) : 0;
+ setasync(dsa, c, target);
+ c->s[target] = WideDone;
+}
+
+static int
+buildsdtrmsg(uchar *buf, uchar tpf, uchar offset)
+{
+ *buf++ = X_MSG;
+ *buf++ = 3;
+ *buf++ = X_MSG_SDTR;
+ *buf++ = tpf;
+ *buf = offset;
+ return 5;
+}
+
+static int
+buildwdtrmsg(uchar *buf, uchar expo)
+{
+ *buf++ = X_MSG;
+ *buf++ = 2;
+ *buf++ = X_MSG_WDTR;
+ *buf = expo;
+ return 4;
+}
+
+static void
+start(Controller *c, long entry)
+{
+ ulong p;
+
+ if (c->running)
+ panic("sd53c8xx: start called while running");
+ c->running = 1;
+ p = c->scriptpa + entry;
+ lesetl(c->n->dsp, p);
+ if (c->ssm)
+ c->n->dcntl |= 0x4; /* start DMA in SSI mode */
+}
+
+static void
+ncrcontinue(Controller *c)
+{
+ if (c->running)
+ panic("sd53c8xx: ncrcontinue called while running");
+ /* set the start DMA bit to continue execution */
+ c->running = 1;
+ c->n->dcntl |= 0x4;
+}
+
+static void
+softreset(Controller *c)
+{
+ Ncr *n = c->n;
+
+ n->istat = Srst; /* software reset */
+ n->istat = 0;
+ /* general initialisation */
+ n->scid = (1 << 6) | 7; /* respond to reselect, ID 7 */
+ n->respid = 1 << 7; /* response ID = 7 */
+
+#ifdef INTERNAL_SCLK
+ n->stest1 = 0x80; /* disable external scsi clock */
+#else
+ n->stest1 = 0x00;
+#endif
+
+ n->stime0 = 0xdd; /* about 0.5 second timeout on each device */
+ n->scntl0 |= 0x8; /* Enable parity checking */
+
+ /* continued setup */
+ n->sien0 = 0x8f;
+ n->sien1 = 0x04;
+ n->dien = 0x7d;
+ n->stest3 = 0x80; /* TolerANT enable */
+ c->running = 0;
+
+ if (c->v->feature & BigFifo)
+ n->ctest5 = (1 << 5);
+ n->dmode = c->v->burst << 6; /* set burst length bits */
+ if (c->v->burst & 4)
+ n->ctest5 |= (1 << 2); /* including overflow into ctest5 bit 2 */
+ if (c->v->feature & Prefetch)
+ n->dcntl |= (1 << 5); /* prefetch enable */
+ else if (c->v->feature & BurstOpCodeFetch)
+ n->dmode |= (1 << 1); /* burst opcode fetch */
+ if (c->v->feature & Differential) {
+ /* chip capable */
+ if ((c->feature & Differential) || (n->gpreg & 0x8) == 0) {
+ /* user enabled, or bit 3 of GPREG clear (Symbios cards) */
+ if (n->sstat2 & (1 << 2))
+ print("sd53c8xx: can't go differential; wrong cable\n");
+ else {
+ n->stest2 = (1 << 5);
+ print("sd53c8xx: differential mode set\n");
+ }
+ }
+ }
+ if (c->clockmult) {
+ n->stest1 |= (1 << 3); /* power up doubler */
+ delay(2);
+ n->stest3 |= (1 << 5); /* stop clock */
+ n->stest1 |= (1 << 2); /* enable doubler */
+ n->stest3 &= ~(1 << 5); /* start clock */
+ /* pray */
+ }
+}
+
+static void
+msgsm(Dsa *dsa, Controller *c, int msg, int *cont, int *wakeme)
+{
+ uchar histpf, hisreqack;
+ int tpf;
+ int scf, xferp;
+ int len;
+
+ Ncr *n = c->n;
+
+ switch (c->s[dsa->target]) {
+ case SyncInit:
+ switch (msg) {
+ case A_SIR_MSG_SDTR:
+ /* reply to my SDTR */
+ histpf = n->scratcha[2];
+ hisreqack = n->scratcha[3];
+ KPRINT("sd53c8xx: %d: SDTN response %d %d\n",
+ dsa->target, histpf, hisreqack);
+
+ if (hisreqack == 0)
+ setasync(dsa, c, dsa->target);
+ else {
+ /* hisreqack should be <= c->v->maxsyncoff */
+ tpf = chooserate(c, histpf, &scf, &xferp);
+ KPRINT("sd53c8xx: %d: SDTN: using %d %d\n",
+ dsa->target, tpf, hisreqack);
+ setsync(dsa, c, dsa->target, tpf < 25, scf, xferp, hisreqack);
+ }
+ *cont = -2;
+ return;
+ case A_SIR_EV_PHASE_SWITCH_AFTER_ID:
+ /* target ignored ATN for message after IDENTIFY - not SCSI-II */
+ KPRINT("sd53c8xx: %d: illegal phase switch after ID message - SCSI-1 device?\n", dsa->target);
+ KPRINT("sd53c8xx: %d: SDTN: async\n", dsa->target);
+ setasync(dsa, c, dsa->target);
+ *cont = E_to_decisions;
+ return;
+ case A_SIR_MSG_REJECT:
+ /* rejection of my SDTR */
+ KPRINT("sd53c8xx: %d: SDTN: rejected SDTR\n", dsa->target);
+ //async:
+ KPRINT("sd53c8xx: %d: SDTN: async\n", dsa->target);
+ setasync(dsa, c, dsa->target);
+ *cont = -2;
+ return;
+ }
+ break;
+ case WideInit:
+ switch (msg) {
+ case A_SIR_MSG_WDTR:
+ /* reply to my WDTR */
+ KPRINT("sd53c8xx: %d: WDTN: response %d\n",
+ dsa->target, n->scratcha[2]);
+ setwide(dsa, c, dsa->target, n->scratcha[2]);
+ *cont = -2;
+ return;
+ case A_SIR_EV_PHASE_SWITCH_AFTER_ID:
+ /* target ignored ATN for message after IDENTIFY - not SCSI-II */
+ KPRINT("sd53c8xx: %d: illegal phase switch after ID message - SCSI-1 device?\n", dsa->target);
+ setwide(dsa, c, dsa->target, 0);
+ *cont = E_to_decisions;
+ return;
+ case A_SIR_MSG_REJECT:
+ /* rejection of my SDTR */
+ KPRINT("sd53c8xx: %d: WDTN: rejected WDTR\n", dsa->target);
+ setwide(dsa, c, dsa->target, 0);
+ *cont = -2;
+ return;
+ }
+ break;
+
+ case NeitherDone:
+ case WideDone:
+ case BothDone:
+ switch (msg) {
+ case A_SIR_MSG_WDTR: {
+ uchar hiswide, mywide;
+ hiswide = n->scratcha[2];
+ mywide = (c->v->feature & Wide) != 0;
+ KPRINT("sd53c8xx: %d: WDTN: target init %d\n",
+ dsa->target, hiswide);
+ if (hiswide < mywide)
+ mywide = hiswide;
+ KPRINT("sd53c8xx: %d: WDTN: responding %d\n",
+ dsa->target, mywide);
+ setwide(dsa, c, dsa->target, mywide);
+ len = buildwdtrmsg(dsa->msg_out, mywide);
+ setmovedata(&dsa->msg_out_buf, DMASEG(dsa->msg_out), len);
+ *cont = E_response;
+ c->s[dsa->target] = WideResponse;
+ return;
+ }
+ case A_SIR_MSG_SDTR:
+#ifdef ASYNC_ONLY
+ *cont = E_reject;
+ return;
+#else
+ /* target decides to renegotiate */
+ histpf = n->scratcha[2];
+ hisreqack = n->scratcha[3];
+ KPRINT("sd53c8xx: %d: SDTN: target init %d %d\n",
+ dsa->target, histpf, hisreqack);
+ if (hisreqack == 0) {
+ /* he wants asynchronous */
+ setasync(dsa, c, dsa->target);
+ tpf = 0;
+ }
+ else {
+ /* he wants synchronous */
+ tpf = chooserate(c, histpf, &scf, &xferp);
+ if (hisreqack > c->v->maxsyncoff)
+ hisreqack = c->v->maxsyncoff;
+ KPRINT("sd53c8xx: %d: using %d %d\n",
+ dsa->target, tpf, hisreqack);
+ setsync(dsa, c, dsa->target, tpf < 25, scf, xferp, hisreqack);
+ }
+ /* build my SDTR message */
+ len = buildsdtrmsg(dsa->msg_out, tpf, hisreqack);
+ setmovedata(&dsa->msg_out_buf, DMASEG(dsa->msg_out), len);
+ *cont = E_response;
+ c->s[dsa->target] = SyncResponse;
+ return;
+#endif
+ }
+ break;
+ case WideResponse:
+ switch (msg) {
+ case A_SIR_EV_RESPONSE_OK:
+ c->s[dsa->target] = WideDone;
+ KPRINT("sd53c8xx: %d: WDTN: response accepted\n", dsa->target);
+ *cont = -2;
+ return;
+ case A_SIR_MSG_REJECT:
+ setwide(dsa, c, dsa->target, 0);
+ KPRINT("sd53c8xx: %d: WDTN: response REJECTed\n", dsa->target);
+ *cont = -2;
+ return;
+ }
+ break;
+ case SyncResponse:
+ switch (msg) {
+ case A_SIR_EV_RESPONSE_OK:
+ c->s[dsa->target] = BothDone;
+ KPRINT("sd53c8xx: %d: SDTN: response accepted (%s)\n",
+ dsa->target, phase[n->sstat1 & 7]);
+ *cont = -2;
+ return; /* chf */
+ case A_SIR_MSG_REJECT:
+ setasync(dsa, c, dsa->target);
+ KPRINT("sd53c8xx: %d: SDTN: response REJECTed\n", dsa->target);
+ *cont = -2;
+ return;
+ }
+ break;
+ }
+ KPRINT("sd53c8xx: %d: msgsm: state %d msg %d\n",
+ dsa->target, c->s[dsa->target], msg);
+ *wakeme = 1;
+ return;
+}
+
+static void
+calcblockdma(Dsa *d, ulong base, ulong count)
+{
+ ulong blocks;
+ if (DEBUG(3))
+ blocks = 0;
+ else {
+ blocks = count / A_BSIZE;
+ if (blocks > 255)
+ blocks = 255;
+ }
+ d->dmablks = blocks;
+ d->dmaaddr[0] = base;
+ d->dmaaddr[1] = base >> 8;
+ d->dmaaddr[2] = base >> 16;
+ d->dmaaddr[3] = base >> 24;
+ setmovedata(&d->data_buf, base + blocks * A_BSIZE, count - blocks * A_BSIZE);
+ if (legetl(d->data_buf.dbc) == 0)
+ d->flag = 1;
+}
+
+static ulong
+read_mismatch_recover(Controller *c, Ncr *n, Dsa *dsa)
+{
+ ulong dbc = n->dbc & 0xffffff;
+ uchar dfifo = n->dfifo;
+ int inchip;
+ if (n->ctest5 & (1 << 5))
+ inchip = ((dfifo | ((n->ctest5 & 3) << 8)) - (dbc & 0x3ff)) & 0x3ff;
+ else
+ inchip = ((dfifo & 0x7f) - (dbc & 0x7f)) & 0x7f;
+ if (inchip) {
+ IPRINT("sd53c8xx: %d/%d: read_mismatch_recover: DMA FIFO = %d\n",
+ dsa->target, dsa->lun, inchip);
+ }
+ if (n->sxfer & 0xf) {
+ /* SCSI FIFO */
+ uchar fifo = n->sstat1 >> 4;
+ if (c->v->maxsyncoff > 8)
+ fifo |= (n->sstat2 & (1 << 4));
+ if (fifo) {
+ inchip += fifo;
+ IPRINT("sd53c8xx: %d/%d: read_mismatch_recover: SCSI FIFO = %d\n",
+ dsa->target, dsa->lun, fifo);
+ }
+ }
+ else {
+ if (n->sstat0 & (1 << 7)) {
+ inchip++;
+ IPRINT("sd53c8xx: %d/%d: read_mismatch_recover: SIDL full\n",
+ dsa->target, dsa->lun);
+ }
+ if (n->sstat2 & (1 << 7)) {
+ inchip++;
+ IPRINT("sd53c8xx: %d/%d: read_mismatch_recover: SIDL msb full\n",
+ dsa->target, dsa->lun);
+ }
+ }
+ USED(inchip);
+ return dbc;
+}
+
+static ulong
+write_mismatch_recover(Ncr *n, Dsa *dsa)
+{
+ ulong dbc = n->dbc & 0xffffff;
+ uchar dfifo = n->dfifo;
+ int inchip;
+
+ USED(dsa);
+ if (n->ctest5 & (1 << 5))
+ inchip = ((dfifo | ((n->ctest5 & 3) << 8)) - (dbc & 0x3ff)) & 0x3ff;
+ else
+ inchip = ((dfifo & 0x7f) - (dbc & 0x7f)) & 0x7f;
+#ifdef WMR_DEBUG
+ if (inchip) {
+ IPRINT("sd53c8xx: %d/%d: write_mismatch_recover: DMA FIFO = %d\n",
+ dsa->target, dsa->lun, inchip);
+ }
+#endif
+ if (n->sstat0 & (1 << 5)) {
+ inchip++;
+#ifdef WMR_DEBUG
+ IPRINT("sd53c8xx: %d/%d: write_mismatch_recover: SODL full\n", dsa->target, dsa->lun);
+#endif
+ }
+ if (n->sstat2 & (1 << 5)) {
+ inchip++;
+#ifdef WMR_DEBUG
+ IPRINT("sd53c8xx: %d/%d: write_mismatch_recover: SODL msb full\n", dsa->target, dsa->lun);
+#endif
+ }
+ if (n->sxfer & 0xf) {
+ /* synchronous SODR */
+ if (n->sstat0 & (1 << 6)) {
+ inchip++;
+#ifdef WMR_DEBUG
+ IPRINT("sd53c8xx: %d/%d: write_mismatch_recover: SODR full\n",
+ dsa->target, dsa->lun);
+#endif
+ }
+ if (n->sstat2 & (1 << 6)) {
+ inchip++;
+#ifdef WMR_DEBUG
+ IPRINT("sd53c8xx: %d/%d: write_mismatch_recover: SODR msb full\n",
+ dsa->target, dsa->lun);
+#endif
+ }
+ }
+ /* clear the dma fifo */
+ n->ctest3 |= (1 << 2);
+ /* wait till done */
+ while ((n->dstat & Dfe) == 0)
+ ;
+ return dbc + inchip;
+}
+
+static void
+interrupt(Ureg *ur, void *a)
+{
+ uchar istat;
+ ushort sist;
+ uchar dstat;
+ int wakeme = 0;
+ int cont = -1;
+ Dsa *dsa;
+ Controller *c = a;
+ Ncr *n = c->n;
+
+ USED(ur);
+ if (DEBUG(1))
+ IPRINT("sd53c8xx: int\n");
+ ilock(c);
+ istat = n->istat;
+ if (istat & Intf) {
+ Dsa *d;
+ int wokesomething = 0;
+ if (DEBUG(1))
+ IPRINT("sd53c8xx: Intfly\n");
+ n->istat = Intf;
+ /* search for structures in A_STATE_DONE */
+ for (d = KPTR(legetl(c->dsalist.head)); d; d = KPTR(legetl(d->next))) {
+ if (d->stateb == A_STATE_DONE) {
+ d->p9status = d->status;
+ if (DEBUG(1))
+ IPRINT("sd53c8xx: waking up dsa %lux\n", d);
+ wakeup(d);
+ wokesomething = 1;
+ }
+ }
+ if (!wokesomething)
+ IPRINT("sd53c8xx: nothing to wake up\n");
+ }
+
+ if ((istat & (Sip | Dip)) == 0) {
+ if (DEBUG(1))
+ IPRINT("sd53c8xx: int end %x\n", istat);
+ iunlock(c);
+ return;
+ }
+
+ sist = (n->sist1<<8)|n->sist0; /* BUG? can two-byte read be inconsistent? */
+ dstat = n->dstat;
+ dsa = (Dsa *)DMASEG_TO_KADDR(legetl(n->dsa));
+ c->running = 0;
+ if (istat & Sip) {
+ if (DEBUG(1))
+ IPRINT("sist = %.4x\n", sist);
+ if (sist & 0x80) {
+ ulong addr;
+ ulong sa;
+ ulong dbc;
+ ulong tbc;
+ int dmablks;
+ ulong dmaaddr;
+
+ addr = legetl(n->dsp);
+ sa = addr - c->scriptpa;
+ if (DEBUG(1) || DEBUG(2))
+ IPRINT("sd53c8xx: %d/%d: Phase Mismatch sa=%.8lux\n",
+ dsa->target, dsa->lun, sa);
+ /*
+ * now recover
+ */
+ if (sa == E_data_in_mismatch) {
+ dbc = read_mismatch_recover(c, n, dsa);
+ tbc = legetl(dsa->data_buf.dbc) - dbc;
+ advancedata(&dsa->data_buf, tbc);
+ if (DEBUG(1) || DEBUG(2))
+ IPRINT("sd53c8xx: %d/%d: transferred = %ld residue = %ld\n",
+ dsa->target, dsa->lun, tbc, legetl(dsa->data_buf.dbc));
+ cont = E_to_decisions;
+ }
+ else if (sa == E_data_in_block_mismatch) {
+ dbc = read_mismatch_recover(c, n, dsa);
+ tbc = A_BSIZE - dbc;
+ /* recover current state from registers */
+ dmablks = n->scratcha[2];
+ dmaaddr = legetl(n->scratchb);
+ /* we have got to dmaaddr + tbc */
+ /* we have dmablks * A_BSIZE - tbc + residue left to do */
+ /* so remaining transfer is */
+ IPRINT("in_block_mismatch: dmaaddr = 0x%lux tbc=%lud dmablks=%d\n",
+ dmaaddr, tbc, dmablks);
+ calcblockdma(dsa, dmaaddr + tbc,
+ dmablks * A_BSIZE - tbc + legetl(dsa->data_buf.dbc));
+ /* copy changes into scratch registers */
+ IPRINT("recalc: dmablks %d dmaaddr 0x%lx pa 0x%lx dbc %ld\n",
+ dsa->dmablks, legetl(dsa->dmaaddr),
+ legetl(dsa->data_buf.pa), legetl(dsa->data_buf.dbc));
+ n->scratcha[2] = dsa->dmablks;
+ n->scratchbncr = dsa->dmancr;
+ cont = E_data_block_mismatch_recover;
+ }
+ else if (sa == E_data_out_mismatch) {
+ dbc = write_mismatch_recover(n, dsa);
+ tbc = legetl(dsa->data_buf.dbc) - dbc;
+ advancedata(&dsa->data_buf, tbc);
+ if (DEBUG(1) || DEBUG(2))
+ IPRINT("sd53c8xx: %d/%d: transferred = %ld residue = %ld\n",
+ dsa->target, dsa->lun, tbc, legetl(dsa->data_buf.dbc));
+ cont = E_to_decisions;
+ }
+ else if (sa == E_data_out_block_mismatch) {
+ dbc = write_mismatch_recover(n, dsa);
+ tbc = legetl(dsa->data_buf.dbc) - dbc;
+ /* recover current state from registers */
+ dmablks = n->scratcha[2];
+ dmaaddr = legetl(n->scratchb);
+ /* we have got to dmaaddr + tbc */
+ /* we have dmablks blocks - tbc + residue left to do */
+ /* so remaining transfer is */
+ IPRINT("out_block_mismatch: dmaaddr = %lux tbc=%lud dmablks=%d\n",
+ dmaaddr, tbc, dmablks);
+ calcblockdma(dsa, dmaaddr + tbc,
+ dmablks * A_BSIZE - tbc + legetl(dsa->data_buf.dbc));
+ /* copy changes into scratch registers */
+ n->scratcha[2] = dsa->dmablks;
+ n->scratchbncr = dsa->dmancr;
+ cont = E_data_block_mismatch_recover;
+ }
+ else if (sa == E_id_out_mismatch) {
+ /*
+ * target switched phases while attention held during
+ * message out. The possibilities are:
+ * 1. It didn't like the last message. This is indicated
+ * by the new phase being message_in. Use script to recover
+ *
+ * 2. It's not SCSI-II compliant. The new phase will be other
+ * than message_in. We should also indicate that the device
+ * is asynchronous, if it's the SDTR that got ignored
+ *
+ * For now, if the phase switch is not to message_in, and
+ * and it happens after IDENTIFY and before SDTR, we
+ * notify the negotiation state machine.
+ */
+ ulong lim = legetl(dsa->msg_out_buf.dbc);
+ uchar p = n->sstat1 & 7;
+ dbc = write_mismatch_recover(n, dsa);
+ tbc = lim - dbc;
+ IPRINT("sd53c8xx: %d/%d: msg_out_mismatch: %lud/%lud sent, phase %s\n",
+ dsa->target, dsa->lun, tbc, lim, phase[p]);
+ if (p != MessageIn && tbc == 1) {
+ msgsm(dsa, c, A_SIR_EV_PHASE_SWITCH_AFTER_ID, &cont, &wakeme);
+ }
+ else
+ cont = E_id_out_mismatch_recover;
+ }
+ else if (sa == E_cmd_out_mismatch) {
+ /*
+ * probably the command count is longer than the device wants ...
+ */
+ ulong lim = legetl(dsa->cmd_buf.dbc);
+ uchar p = n->sstat1 & 7;
+ dbc = write_mismatch_recover(n, dsa);
+ tbc = lim - dbc;
+ IPRINT("sd53c8xx: %d/%d: cmd_out_mismatch: %lud/%lud sent, phase %s\n",
+ dsa->target, dsa->lun, tbc, lim, phase[p]);
+ USED(p, tbc);
+ cont = E_to_decisions;
+ }
+ else {
+ IPRINT("sd53c8xx: %d/%d: ma sa=%.8lux wanted=%s got=%s\n",
+ dsa->target, dsa->lun, sa,
+ phase[n->dcmd & 7],
+ phase[n->sstat1 & 7]);
+ dumpncrregs(c, 1);
+ dsa->p9status = SDeio; /* chf */
+ wakeme = 1;
+ }
+ }
+ /*else*/ if (sist & 0x400) {
+ if (DEBUG(0))
+ IPRINT("sd53c8xx: %d/%d Sto\n", dsa->target, dsa->lun);
+ dsa->p9status = SDtimeout;
+ dsa->stateb = A_STATE_DONE;
+ softreset(c);
+ cont = E_issue_check;
+ wakeme = 1;
+ }
+ if (sist & 0x1) {
+ IPRINT("sd53c8xx: %d/%d: parity error\n", dsa->target, dsa->lun);
+ dsa->parityerror = 1;
+ }
+ if (sist & 0x4) {
+ IPRINT("sd53c8xx: %d/%d: unexpected disconnect\n",
+ dsa->target, dsa->lun);
+ dumpncrregs(c, 1);
+ //wakeme = 1;
+ dsa->p9status = SDeio;
+ }
+ }
+ if (istat & Dip) {
+ if (DEBUG(1))
+ IPRINT("dstat = %.2x\n", dstat);
+ /*else*/ if (dstat & Ssi) {
+ ulong *p = DMASEG_TO_KADDR(legetl(n->dsp));
+ ulong w = (uchar *)p - (uchar *)c->script;
+ IPRINT("[%lux]", w);
+ USED(w);
+ cont = -2; /* restart */
+ }
+ if (dstat & Sir) {
+ switch (legetl(n->dsps)) {
+ case A_SIR_MSG_IO_COMPLETE:
+ dsa->p9status = dsa->status;
+ wakeme = 1;
+ break;
+ case A_SIR_MSG_SDTR:
+ case A_SIR_MSG_WDTR:
+ case A_SIR_MSG_REJECT:
+ case A_SIR_EV_RESPONSE_OK:
+ msgsm(dsa, c, legetl(n->dsps), &cont, &wakeme);
+ break;
+ case A_SIR_MSG_IGNORE_WIDE_RESIDUE:
+ /* back up one in the data transfer */
+ IPRINT("sd53c8xx: %d/%d: ignore wide residue %d, WSR = %d\n",
+ dsa->target, dsa->lun, n->scratcha[1], n->scntl2 & 1);
+ if (dsa->dmablks == 0 && dsa->flag)
+ IPRINT("sd53c8xx: %d/%d: transfer over; residue ignored\n",
+ dsa->target, dsa->lun);
+ else
+ calcblockdma(dsa, legetl(dsa->dmaaddr) - 1,
+ dsa->dmablks * A_BSIZE + legetl(dsa->data_buf.dbc) + 1);
+ cont = -2;
+ break;
+ case A_SIR_ERROR_NOT_MSG_IN_AFTER_RESELECT:
+ IPRINT("sd53c8xx: %d: not msg_in after reselect (%s)",
+ n->ssid & 7, phase[n->sstat1 & 7]);
+ dsa = dsafind(c, n->ssid & 7, -1, A_STATE_DISCONNECTED);
+ dumpncrregs(c, 1);
+ wakeme = 1;
+ break;
+ case A_SIR_NOTIFY_MSG_IN:
+ IPRINT("sd53c8xx: %d/%d: msg_in %d\n",
+ dsa->target, dsa->lun, n->sfbr);
+ cont = -2;
+ break;
+ case A_SIR_NOTIFY_DISC:
+ IPRINT("sd53c8xx: %d/%d: disconnect:", dsa->target, dsa->lun);
+ goto dsadump;
+ case A_SIR_NOTIFY_STATUS:
+ IPRINT("sd53c8xx: %d/%d: status\n", dsa->target, dsa->lun);
+ cont = -2;
+ break;
+ case A_SIR_NOTIFY_COMMAND:
+ IPRINT("sd53c8xx: %d/%d: commands\n", dsa->target, dsa->lun);
+ cont = -2;
+ break;
+ case A_SIR_NOTIFY_DATA_IN:
+ IPRINT("sd53c8xx: %d/%d: data in a %lx b %lx\n",
+ dsa->target, dsa->lun, legetl(n->scratcha), legetl(n->scratchb));
+ cont = -2;
+ break;
+ case A_SIR_NOTIFY_BLOCK_DATA_IN:
+ IPRINT("sd53c8xx: %d/%d: block data in: a2 %x b %lx\n",
+ dsa->target, dsa->lun, n->scratcha[2], legetl(n->scratchb));
+ cont = -2;
+ break;
+ case A_SIR_NOTIFY_DATA_OUT:
+ IPRINT("sd53c8xx: %d/%d: data out\n", dsa->target, dsa->lun);
+ cont = -2;
+ break;
+ case A_SIR_NOTIFY_DUMP:
+ IPRINT("sd53c8xx: %d/%d: dump\n", dsa->target, dsa->lun);
+ dumpncrregs(c, 1);
+ cont = -2;
+ break;
+ case A_SIR_NOTIFY_DUMP2:
+ IPRINT("sd53c8xx: %d/%d: dump2:", dsa->target, dsa->lun);
+ IPRINT(" sa %lux", legetl(n->dsp) - c->scriptpa);
+ IPRINT(" dsa %lux", legetl(n->dsa));
+ IPRINT(" sfbr %ux", n->sfbr);
+ IPRINT(" a %lux", n->scratcha);
+ IPRINT(" b %lux", legetl(n->scratchb));
+ IPRINT(" ssid %ux", n->ssid);
+ IPRINT("\n");
+ cont = -2;
+ break;
+ case A_SIR_NOTIFY_WAIT_RESELECT:
+ IPRINT("sd53c8xx: wait reselect\n");
+ cont = -2;
+ break;
+ case A_SIR_NOTIFY_RESELECT:
+ IPRINT("sd53c8xx: reselect: ssid %.2x sfbr %.2x at %ld\n",
+ n->ssid, n->sfbr, TK2MS(m->ticks));
+ cont = -2;
+ break;
+ case A_SIR_NOTIFY_ISSUE:
+ IPRINT("sd53c8xx: %d/%d: issue:", dsa->target, dsa->lun);
+ dsadump:
+ IPRINT(" tgt=%d", dsa->target);
+ IPRINT(" time=%ld", TK2MS(m->ticks));
+ IPRINT("\n");
+ cont = -2;
+ break;
+ case A_SIR_NOTIFY_ISSUE_CHECK:
+ IPRINT("sd53c8xx: issue check\n");
+ cont = -2;
+ break;
+ case A_SIR_NOTIFY_SIGP:
+ IPRINT("sd53c8xx: responded to SIGP\n");
+ cont = -2;
+ break;
+ case A_SIR_NOTIFY_DUMP_NEXT_CODE: {
+ ulong *dsp = DMASEG_TO_KADDR(legetl(n->dsp));
+ int x;
+ IPRINT("sd53c8xx: code at %lux", dsp - c->script);
+ for (x = 0; x < 6; x++)
+ IPRINT(" %.8lux", dsp[x]);
+ IPRINT("\n");
+ USED(dsp);
+ cont = -2;
+ break;
+ }
+ case A_SIR_NOTIFY_WSR:
+ IPRINT("sd53c8xx: %d/%d: WSR set\n", dsa->target, dsa->lun);
+ cont = -2;
+ break;
+ case A_SIR_NOTIFY_LOAD_SYNC:
+ IPRINT("sd53c8xx: %d/%d: scntl=%.2x sxfer=%.2x\n",
+ dsa->target, dsa->lun, n->scntl3, n->sxfer);
+ cont = -2;
+ break;
+ case A_SIR_NOTIFY_RESELECTED_ON_SELECT:
+ IPRINT("sd53c8xx: %d/%d: reselected during select\n",
+ dsa->target, dsa->lun);
+ cont = -2;
+ break;
+ default:
+ IPRINT("sd53c8xx: %d/%d: script error %ld\n",
+ dsa->target, dsa->lun, legetl(n->dsps));
+ dumpncrregs(c, 1);
+ wakeme = 1;
+ }
+ }
+ /*else*/ if (dstat & Iid) {
+ ulong addr = legetl(n->dsp);
+ IPRINT("sd53c8xx: %d/%d: Iid pa=%.8lux sa=%.8lux dbc=%lux\n",
+ dsa->target, dsa->lun,
+ addr, addr - c->scriptpa, n->dbc);
+ addr = (ulong)DMASEG_TO_KADDR(addr);
+ IPRINT("%.8lux %.8lux %.8lux\n",
+ *(ulong *)(addr - 12), *(ulong *)(addr - 8), *(ulong *)(addr - 4));
+ USED(addr);
+ dsa->p9status = SDeio;
+ wakeme = 1;
+ }
+ /*else*/ if (dstat & Bf) {
+ IPRINT("sd53c8xx: %d/%d: Bus Fault\n", dsa->target, dsa->lun);
+ dumpncrregs(c, 1);
+ dsa->p9status = SDeio;
+ wakeme = 1;
+ }
+ }
+ if (cont == -2)
+ ncrcontinue(c);
+ else if (cont >= 0)
+ start(c, cont);
+ if (wakeme){
+ if(dsa->p9status == SDnostatus)
+ dsa->p9status = SDeio;
+ wakeup(dsa);
+ }
+ iunlock(c);
+ if (DEBUG(1)) {
+ IPRINT("sd53c8xx: int end 1\n");
+ }
+}
+
+static int
+done(void *arg)
+{
+ return ((Dsa *)arg)->p9status != SDnostatus;
+}
+
+static void
+setmovedata(Movedata *d, ulong pa, ulong bc)
+{
+ d->pa[0] = pa;
+ d->pa[1] = pa>>8;
+ d->pa[2] = pa>>16;
+ d->pa[3] = pa>>24;
+ d->dbc[0] = bc;
+ d->dbc[1] = bc>>8;
+ d->dbc[2] = bc>>16;
+ d->dbc[3] = bc>>24;
+}
+
+static void
+advancedata(Movedata *d, long v)
+{
+ lesetl(d->pa, legetl(d->pa) + v);
+ lesetl(d->dbc, legetl(d->dbc) - v);
+}
+
+static void
+dumpwritedata(uchar *data, int datalen)
+{
+ int i;
+ uchar *bp;
+ if (!DEBUG(0)){
+ USED(data, datalen);
+ return;
+ }
+
+ if (datalen) {
+ KPRINT("sd53c8xx:write:");
+ for (i = 0, bp = data; i < 50 && i < datalen; i++, bp++)
+ KPRINT("%.2ux", *bp);
+ if (i < datalen) {
+ KPRINT("...");
+ }
+ KPRINT("\n");
+ }
+}
+
+static void
+dumpreaddata(uchar *data, int datalen)
+{
+ int i;
+ uchar *bp;
+ if (!DEBUG(0)){
+ USED(data, datalen);
+ return;
+ }
+
+ if (datalen) {
+ KPRINT("sd53c8xx:read:");
+ for (i = 0, bp = data; i < 50 && i < datalen; i++, bp++)
+ KPRINT("%.2ux", *bp);
+ if (i < datalen) {
+ KPRINT("...");
+ }
+ KPRINT("\n");
+ }
+}
+
+static void
+busreset(Controller *c)
+{
+ int x, ntarget;
+
+ /* bus reset */
+ c->n->scntl1 |= (1 << 3);
+ delay(500);
+ c->n->scntl1 &= ~(1 << 3);
+ if(!(c->v->feature & Wide))
+ ntarget = 8;
+ else
+ ntarget = MAXTARGET;
+ for (x = 0; x < ntarget; x++) {
+ setwide(0, c, x, 0);
+#ifndef ASYNC_ONLY
+ c->s[x] = NeitherDone;
+#endif
+ }
+ c->capvalid = 0;
+}
+
+static void
+reset(Controller *c)
+{
+ /* should wakeup all pending tasks */
+ softreset(c);
+ busreset(c);
+}
+
+static int
+symrio(SDreq* r)
+{
+ Dsa *d;
+ uchar *bp;
+ Controller *c;
+ uchar target_expo, my_expo;
+ int bc, check, status, target;
+
+ if((target = r->unit->subno) == 0x07)
+ return r->status = SDtimeout; /* assign */
+ c = r->unit->dev->ctlr;
+
+ check = 0;
+ d = dsaalloc(c, target, r->lun);
+
+ qlock(&c->q[target]); /* obtain access to target */
+docheck:
+ /* load the transfer control stuff */
+ d->scsi_id_buf[0] = 0;
+ d->scsi_id_buf[1] = c->sxfer[target];
+ d->scsi_id_buf[2] = target;
+ d->scsi_id_buf[3] = c->scntl3[target];
+ synctodsa(d, c);
+
+ bc = 0;
+
+ d->msg_out[bc] = 0x80 | r->lun;
+
+#ifndef NO_DISCONNECT
+ d->msg_out[bc] |= (1 << 6);
+#endif
+ bc++;
+
+ /* work out what to do about negotiation */
+ switch (c->s[target]) {
+ default:
+ KPRINT("sd53c8xx: %d: strange nego state %d\n", target, c->s[target]);
+ c->s[target] = NeitherDone;
+ /* fall through */
+ case NeitherDone:
+ if ((c->capvalid & (1 << target)) == 0)
+ break;
+ target_expo = (c->cap[target] >> 5) & 3;
+ my_expo = (c->v->feature & Wide) != 0;
+ if (target_expo < my_expo)
+ my_expo = target_expo;
+#ifdef ALWAYS_DO_WDTR
+ bc += buildwdtrmsg(d->msg_out + bc, my_expo);
+ KPRINT("sd53c8xx: %d: WDTN: initiating expo %d\n", target, my_expo);
+ c->s[target] = WideInit;
+ break;
+#else
+ if (my_expo) {
+ bc += buildwdtrmsg(d->msg_out + bc, (c->v->feature & Wide) ? 1 : 0);
+ KPRINT("sd53c8xx: %d: WDTN: initiating expo %d\n", target, my_expo);
+ c->s[target] = WideInit;
+ break;
+ }
+ KPRINT("sd53c8xx: %d: WDTN: narrow\n", target);
+ /* fall through */
+#endif
+ case WideDone:
+ if (c->cap[target] & (1 << 4)) {
+ KPRINT("sd53c8xx: %d: SDTN: initiating %d %d\n", target, c->tpf, c->v->maxsyncoff);
+ bc += buildsdtrmsg(d->msg_out + bc, c->tpf, c->v->maxsyncoff);
+ c->s[target] = SyncInit;
+ break;
+ }
+ KPRINT("sd53c8xx: %d: SDTN: async only\n", target);
+ c->s[target] = BothDone;
+ break;
+
+ case BothDone:
+ break;
+ }
+
+ setmovedata(&d->msg_out_buf, DMASEG(d->msg_out), bc);
+ setmovedata(&d->cmd_buf, DMASEG(r->cmd), r->clen);
+ calcblockdma(d, DMASEG(r->data), r->dlen);
+
+ if (DEBUG(0)) {
+ KPRINT("sd53c8xx: %d/%d: exec: ", target, r->lun);
+ for (bp = r->cmd; bp < &r->cmd[r->clen]; bp++)
+ KPRINT("%.2ux", *bp);
+ KPRINT("\n");
+ if (!r->write)
+ KPRINT("sd53c8xx: %d/%d: exec: limit=(%d)%ld\n",
+ target, r->lun, d->dmablks, legetl(d->data_buf.dbc));
+ else
+ dumpwritedata(r->data, r->dlen);
+ }
+
+ setmovedata(&d->status_buf, DMASEG(&d->status), 1);
+
+ d->p9status = SDnostatus;
+ d->parityerror = 0;
+
+ d->stateb = A_STATE_ISSUE; /* start operation */
+
+ ilock(c);
+ if (c->ssm)
+ c->n->dcntl |= 0x10; /* SSI */
+ if (c->running) {
+ c->n->istat |= Sigp;
+ }
+ else {
+ start(c, E_issue_check);
+ }
+ iunlock(c);
+
+ while(waserror())
+ ;
+ tsleep(d, done, d, 30 * 1000);
+ poperror();
+
+ if (!done(d)) {
+ KPRINT("sd53c8xx: %d/%d: exec: Timed out", target, r->lun);
+ dumpncrregs(c, 0);
+ dsafree(c, d);
+ reset(c);
+ qunlock(&c->q[target]);
+ r->status = SDtimeout;
+ return r->status = SDtimeout; /* assign */
+ }
+
+ if((status = d->p9status) == SDeio)
+ c->s[target] = NeitherDone;
+ if (d->parityerror) {
+ status = SDeio;
+ }
+
+ /*
+ * adjust datalen
+ */
+ r->rlen = r->dlen;
+ if (d->dmablks > 0)
+ r->rlen -= d->dmablks * A_BSIZE;
+ else if (d->flag == 0)
+ r->rlen -= legetl(d->data_buf.dbc);
+ if(!r->write)
+ dumpreaddata(r->data, r->rlen);
+ if (DEBUG(0))
+ KPRINT("53c8xx: %d/%d: exec: p9status=%d status %d rlen %ld\n",
+ target, r->lun, d->p9status, status, r->rlen);
+ /*
+ * spot the identify
+ */
+ if ((c->capvalid & (1 << target)) == 0
+ && (status == SDok || status == SDcheck)
+ && r->cmd[0] == 0x12 && r->dlen >= 8) {
+ c->capvalid |= 1 << target;
+ bp = r->data;
+ c->cap[target] = bp[7];
+ KPRINT("sd53c8xx: %d: capabilities %.2x\n", target, bp[7]);
+ }
+ if(!check && status == SDcheck && !(r->flags & SDnosense)){
+ check = 1;
+ r->write = 0;
+ memset(r->cmd, 0, sizeof(r->cmd));
+ r->cmd[0] = 0x03;
+ r->cmd[1] = r->lun<<5;
+ r->cmd[4] = sizeof(r->sense)-1;
+ r->clen = 6;
+ r->data = r->sense;
+ r->dlen = sizeof(r->sense)-1;
+ /*
+ * Clear out the microcode state
+ * so the Dsa can be re-used.
+ */
+ lesetl(d->state, A_STATE_ALLOCATED);
+ goto docheck;
+ }
+ qunlock(&c->q[target]);
+ dsafree(c, d);
+
+ if(status == SDok && check){
+ status = SDcheck;
+ r->flags |= SDvalidsense;
+ }
+ KPRINT("sd53c8xx: %d: r flags %8.8uX status %d rlen %d\n",
+ target, r->flags, r->status, r->dlen);
+ return status;
+}
+
+#define NCR_VID 0x1000
+#define NCR_810_DID 0x0001
+#define NCR_820_DID 0x0002 /* don't know enough about this one to support it */
+#define NCR_825_DID 0x0003
+#define NCR_815_DID 0x0004
+#define SYM_810AP_DID 0x0005
+#define SYM_860_DID 0x0006
+#define SYM_896_DID 0x000b
+#define SYM_895_DID 0x000c
+#define SYM_885_DID 0x000d /* ditto */
+#define SYM_875_DID 0x000f /* ditto */
+#define SYM_875J_DID 0x008f
+
+static Variant variant[] = {
+{ NCR_810_DID, 0x0f, "NCR53C810", Burst16, 8, 24, 0 },
+{ NCR_810_DID, 0x1f, "SYM53C810ALV", Burst16, 8, 24, Prefetch },
+{ NCR_810_DID, 0xff, "SYM53C810A", Burst16, 8, 24, Prefetch },
+{ SYM_810AP_DID, 0xff, "SYM53C810AP", Burst16, 8, 24, Prefetch },
+{ NCR_815_DID, 0xff, "NCR53C815", Burst16, 8, 24, BurstOpCodeFetch },
+{ NCR_825_DID, 0x0f, "NCR53C825", Burst16, 8, 24, Wide|BurstOpCodeFetch|Differential },
+{ NCR_825_DID, 0xff, "SYM53C825A", Burst128, 16, 24, Prefetch|LocalRAM|BigFifo|Differential|Wide },
+{ SYM_860_DID, 0x0f, "SYM53C860", Burst16, 8, 24, Prefetch|Ultra },
+{ SYM_860_DID, 0xff, "SYM53C860LV", Burst16, 8, 24, Prefetch|Ultra },
+{ SYM_875_DID, 0x01, "SYM53C875r1", Burst128, 16, 24, Prefetch|LocalRAM|BigFifo|Differential|Wide|Ultra },
+{ SYM_875_DID, 0xff, "SYM53C875", Burst128, 16, 24, Prefetch|LocalRAM|BigFifo|Differential|Wide|Ultra|ClockDouble },
+{ SYM_875J_DID, 0xff, "SYM53C875j", Burst128, 16, 24, Prefetch|LocalRAM|BigFifo|Differential|Wide|Ultra|ClockDouble },
+{ SYM_885_DID, 0xff, "SYM53C885", Burst128, 16, 24, Prefetch|LocalRAM|BigFifo|Wide|Ultra|ClockDouble },
+{ SYM_895_DID, 0xff, "SYM53C895", Burst128, 16, 24, Prefetch|LocalRAM|BigFifo|Wide|Ultra|Ultra2 },
+{ SYM_896_DID, 0xff, "SYM53C896", Burst128, 16, 64, Prefetch|LocalRAM|BigFifo|Wide|Ultra|Ultra2 },
+};
+
+#define offsetof(s, t) ((ulong)&((s *)0)->t)
+
+static int
+xfunc(Controller *c, enum na_external x, unsigned long *v)
+{
+ switch (x)
+ {
+ case X_scsi_id_buf:
+ *v = offsetof(Dsa, scsi_id_buf[0]); return 1;
+ case X_msg_out_buf:
+ *v = offsetof(Dsa, msg_out_buf); return 1;
+ case X_cmd_buf:
+ *v = offsetof(Dsa, cmd_buf); return 1;
+ case X_data_buf:
+ *v = offsetof(Dsa, data_buf); return 1;
+ case X_status_buf:
+ *v = offsetof(Dsa, status_buf); return 1;
+ case X_dsa_head:
+ *v = DMASEG(&c->dsalist.head[0]); return 1;
+ default:
+ print("xfunc: can't find external %d\n", x);
+ return 0;
+ }
+ return 1;
+}
+
+static int
+na_fixup(Controller *c, ulong pa_reg,
+ struct na_patch *patch, int patches,
+ int (*externval)(Controller*, int, ulong*))
+{
+ int p;
+ int v;
+ ulong *script, pa_script;
+ unsigned long lw, lv;
+
+ script = c->script;
+ pa_script = c->scriptpa;
+ for (p = 0; p < patches; p++) {
+ switch (patch[p].type) {
+ case 1:
+ /* script relative */
+ script[patch[p].lwoff] += pa_script;
+ break;
+ case 2:
+ /* register i/o relative */
+ script[patch[p].lwoff] += pa_reg;
+ break;
+ case 3:
+ /* data external */
+ lw = script[patch[p].lwoff];
+ v = (lw >> 8) & 0xff;
+ if (!(*externval)(c, v, &lv))
+ return 0;
+ v = lv & 0xff;
+ script[patch[p].lwoff] = (lw & 0xffff00ffL) | (v << 8);
+ break;
+ case 4:
+ /* 32 bit external */
+ lw = script[patch[p].lwoff];
+ if (!(*externval)(c, lw, &lv))
+ return 0;
+ script[patch[p].lwoff] = lv;
+ break;
+ case 5:
+ /* 24 bit external */
+ lw = script[patch[p].lwoff];
+ if (!(*externval)(c, lw & 0xffffff, &lv))
+ return 0;
+ script[patch[p].lwoff] = (lw & 0xff000000L) | (lv & 0xffffffL);
+ break;
+ }
+ }
+ return 1;
+}
+
+static SDev*
+sympnp(void)
+{
+ Pcidev *p;
+ Variant *v;
+ int scriptba;
+ void *scriptma;
+ Controller *ctlr;
+ SDev *sdev, *head, *tail;
+ ulong regpa, *script, scriptpa;
+
+ p = nil;
+ head = tail = nil;
+ while(p = pcimatch(p, NCR_VID, 0)){
+ for(v = variant; v < &variant[nelem(variant)]; v++){
+ if(p->did == v->did && p->rid <= v->maxrid)
+ break;
+ }
+ if(v >= &variant[nelem(variant)])
+ continue;
+ print("sd53c8xx: %s rev. 0x%2.2x intr=%d command=%4.4luX\n",
+ v->name, p->rid, p->intl, p->pcr);
+
+ regpa = p->mem[1].bar;
+ scriptba = 2;
+ if(regpa & 0x04){
+ if(p->mem[2].bar)
+ continue;
+ scriptba++;
+ }
+ regpa = upamalloc(regpa & ~0x0F, p->mem[1].size, 0);
+ if(regpa == 0)
+ continue;
+
+ script = nil;
+ scriptpa = 0;
+ scriptma = nil;
+ if((v->feature & LocalRAM) && sizeof(na_script) <= 4096){
+ scriptpa = p->mem[scriptba].bar;
+ if((scriptpa & 0x04) && p->mem[scriptba+1].bar){
+ upafree(regpa, p->mem[1].size);
+ continue;
+ }
+ scriptpa = upamalloc(scriptpa & ~0x0F,
+ p->mem[scriptba].size, 0);
+ if(scriptpa)
+ script = KADDR(scriptpa);
+ }
+ if(scriptpa == 0){
+ /*
+ * Either the map failed, or this chip does not have
+ * local RAM. It will need a copy of the microcode.
+ */
+ scriptma = malloc(sizeof(na_script));
+ if(scriptma == nil){
+ upafree(regpa, p->mem[1].size);
+ continue;
+ }
+ scriptpa = DMASEG(scriptma);
+ script = scriptma;
+ }
+
+ ctlr = malloc(sizeof(Controller));
+ sdev = malloc(sizeof(SDev));
+ if(ctlr == nil || sdev == nil){
+buggery:
+ if(ctlr)
+ free(ctlr);
+ if(sdev)
+ free(sdev);
+ if(scriptma)
+ free(scriptma);
+ else
+ upafree(scriptpa, p->mem[scriptba].size);
+ upafree(regpa, p->mem[1].size);
+ continue;
+ }
+
+ ctlr->n = KADDR(regpa);
+ ctlr->v = v;
+ ctlr->script = script;
+ memmove(ctlr->script, na_script, sizeof(na_script));
+ ctlr->scriptpa = scriptpa;
+ if(!na_fixup(ctlr, regpa, na_patches, NA_PATCHES, xfunc)){
+ print("script fixup failed\n");
+ goto buggery;
+ }
+ swabl(ctlr->script, ctlr->script, sizeof(na_script));
+
+ ctlr->dsalist.free = 0;
+ lesetl(ctlr->dsalist.head, 0);
+
+ ctlr->pcidev = p;
+
+ sdev->ifc = &sd53c8xxifc;
+ sdev->ctlr = ctlr;
+ if(!(v->feature & Wide))
+ sdev->nunit = 8;
+ else
+ sdev->nunit = MAXTARGET;
+ ctlr->sdev = sdev;
+
+ if(head != nil)
+ tail->next = sdev;
+ else
+ head = sdev;
+ tail = sdev;
+ }
+
+ return head;
+}
+
+static SDev*
+symid(SDev* sdev)
+{
+ return scsiid(sdev, &sd53c8xxifc);
+}
+
+static int
+symenable(SDev* sdev)
+{
+ Pcidev *pcidev;
+ Controller *ctlr;
+ char name[NAMELEN];
+
+ ctlr = sdev->ctlr;
+ pcidev = ctlr->pcidev;
+
+ pcisetbme(pcidev);
+ snprint(name, NAMELEN, "%s (%s)", sdev->name, sdev->ifc->name);
+ intrenable(pcidev->intl, interrupt, ctlr, pcidev->tbdf, name);
+
+ ilock(ctlr);
+ synctabinit(ctlr);
+ reset(ctlr);
+ iunlock(ctlr);
+
+ return 1;
+}
+
+SDifc sd53c8xxifc = {
+ "53c8xx", /* name */
+
+ sympnp, /* pnp */
+ nil, /* legacy */
+ symid, /* id */
+ symenable, /* enable */
+ nil, /* disable */
+
+ scsiverify, /* verify */
+ scsionline, /* online */
+ symrio, /* rio */
+ nil, /* rctl */
+ nil, /* wctl */
+
+ scsibio, /* bio */
+};
+
A pc/sdata.c => pc/sdata.c +1581 -0
@@ 0,0 1,1581 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "ureg.h"
+#include "../port/error.h"
+
+#include "sd.h"
+
+extern SDifc sdataifc;
+
+#define DEBUG 0
+#define DPRINT if(DEBUG)print
+
+enum { /* I/O ports */
+ Data = 0,
+ Error = 1, /* (read) */
+ Features = 1, /* (write) */
+ Count = 2, /* sector count */
+ Ir = 2, /* interrupt reason (PACKET) */
+ Sector = 3, /* sector number, LBA<7-0> */
+ Cyllo = 4, /* cylinder low, LBA<15-8> */
+ Bytelo = 4, /* byte count low (PACKET) */
+ Cylhi = 5, /* cylinder high, LBA<23-16> */
+ Bytehi = 5, /* byte count hi (PACKET) */
+ Dh = 6, /* Device/Head, LBA<32-14> */
+ Status = 7, /* (read) */
+ Command = 7, /* (write) */
+
+ As = 2, /* Alternate Status (read) */
+ Dc = 2, /* Device Control (write) */
+};
+
+enum { /* Error */
+ Med = 0x01, /* Media error */
+ Ili = 0x01, /* command set specific (PACKET) */
+ Nm = 0x02, /* No Media */
+ Eom = 0x02, /* command set specific (PACKET) */
+ Abrt = 0x04, /* Aborted command */
+ Mcr = 0x08, /* Media Change Request */
+ Idnf = 0x10, /* no user-accessible address */
+ Mc = 0x20, /* Media Change */
+ Unc = 0x40, /* Uncorrectable data error */
+ Wp = 0x40, /* Write Protect */
+ Icrc = 0x80, /* Interface CRC error */
+};
+
+enum { /* Features */
+ Dma = 0x01, /* data transfer via DMA (PACKET) */
+ Ovl = 0x02, /* command overlapped (PACKET) */
+};
+
+enum { /* Interrupt Reason */
+ Cd = 0x01, /* Command/Data */
+ Io = 0x02, /* I/O direction */
+ Rel = 0x04, /* Bus Release */
+};
+
+enum { /* Device/Head */
+ Dev0 = 0xA0, /* Master */
+ Dev1 = 0xB0, /* Slave */
+ Lba = 0x40, /* LBA mode */
+};
+
+enum { /* Status, Alternate Status */
+ Err = 0x01, /* Error */
+ Chk = 0x01, /* Check error (PACKET) */
+ Drq = 0x08, /* Data Request */
+ Dsc = 0x10, /* Device Seek Complete */
+ Serv = 0x10, /* Service */
+ Df = 0x20, /* Device Fault */
+ Dmrd = 0x20, /* DMA ready (PACKET) */
+ Drdy = 0x40, /* Device Ready */
+ Bsy = 0x80, /* Busy */
+};
+
+enum { /* Command */
+ Cnop = 0x00, /* NOP */
+ Cdr = 0x08, /* Device Reset */
+ Crs = 0x20, /* Read Sectors */
+ Cws = 0x30, /* Write Sectors */
+ Cedd = 0x90, /* Execute Device Diagnostics */
+ Cpkt = 0xA0, /* Packet */
+ Cidpkt = 0xA1, /* Identify Packet Device */
+ Crsm = 0xC4, /* Read Multiple */
+ Cwsm = 0xC5, /* Write Multiple */
+ Crdq = 0xC7, /* Read DMA queued */
+ Crd = 0xC8, /* Read DMA */
+ Cwd = 0xCA, /* Write DMA */
+ Cwdq = 0xCC, /* Write DMA queued */
+ Cid = 0xEC, /* Identify Device */
+ Csf = 0xEF, /* Set Features */
+};
+
+enum { /* Device Control */
+ Nien = 0x02, /* (not) Interrupt Enable */
+ Srst = 0x04, /* Software Reset */
+};
+
+enum { /* PCI Configuration Registers */
+ Bmiba = 0x20, /* Bus Master Interface Base Address */
+ Idetim = 0x40, /* IE Timing */
+ Sidetim = 0x44, /* Slave IE Timing */
+ Udmactl = 0x48, /* Ultra DMA/33 Control */
+ Udmatim = 0x4A, /* Ultra DMA/33 Timing */
+};
+
+enum { /* Bus Master IDE I/O Ports */
+ Bmicx = 0, /* Command */
+ Bmisx = 2, /* Status */
+ Bmidtpx = 4, /* Descriptor Table Pointer */
+};
+
+enum { /* Bmicx */
+ Ssbm = 0x01, /* Start/Stop Bus Master */
+ Rwcon = 0x08, /* Read/Write Control */
+};
+
+enum { /* Bmisx */
+ Bmidea = 0x01, /* Bus Master IDE Active */
+ Idedmae = 0x02, /* IDE DMA Error (R/WC) */
+ Ideints = 0x04, /* IDE Interrupt Status (R/WC) */
+ Dma0cap = 0x20, /* Drive 0 DMA Capable */
+ Dma1cap = 0x40, /* Drive 0 DMA Capable */
+};
+enum { /* Physical Region Descriptor */
+ PrdEOT = 0x80000000, /* Bus Master IDE Active */
+};
+
+enum { /* offsets into the identify info. */
+ Iconfig = 0, /* general configuration */
+ Ilcyl = 1, /* logical cylinders */
+ Ilhead = 3, /* logical heads */
+ Ilsec = 6, /* logical sectors per logical track */
+ Iserial = 10, /* serial number */
+ Ifirmware = 23, /* firmware revision */
+ Imodel = 27, /* model number */
+ Imaxrwm = 47, /* max. read/write multiple sectors */
+ Icapabilities = 49, /* capabilities */
+ Istandby = 50, /* device specific standby timer */
+ Ipiomode = 51, /* PIO data transfer mode number */
+ Ivalid = 53,
+ Iccyl = 54, /* cylinders if (valid&0x01) */
+ Ichead = 55, /* heads if (valid&0x01) */
+ Icsec = 56, /* sectors if (valid&0x01) */
+ Iccap = 57, /* capacity if (valid&0x01) */
+ Irwm = 59, /* read/write multiple */
+ Ilba0 = 60, /* LBA size */
+ Ilba1 = 61, /* LBA size */
+ Imwdma = 63, /* multiword DMA mode */
+ Iapiomode = 64, /* advanced PIO modes supported */
+ Iminmwdma = 65, /* min. multiword DMA cycle time */
+ Irecmwdma = 66, /* rec. multiword DMA cycle time */
+ Iminpio = 67, /* min. PIO cycle w/o flow control */
+ Iminiordy = 68, /* min. PIO cycle with IORDY */
+ Ipcktbr = 71, /* time from PACKET to bus release */
+ Iserbsy = 72, /* time from SERVICE to !Bsy */
+ Iqdepth = 75, /* max. queue depth */
+ Imajor = 80, /* major version number */
+ Iminor = 81, /* minor version number */
+ Icmdset0 = 82, /* command sets supported */
+ Icmdset1 = 83, /* command sets supported */
+ Icmdset2 = 84, /* command sets supported extension */
+ Icmdset3 = 85, /* command sets enabled */
+ Icmdset4 = 86, /* command sets enabled */
+ Icmdset5 = 87, /* command sets enabled extension */
+ Iudma = 88, /* ultra DMA mode */
+ Ierase = 89, /* time for security erase */
+ Ieerase = 90, /* time for enhanced security erase */
+ Ipower = 91, /* current advanced power management */
+ Irmsn = 127, /* removable status notification */
+ Istatus = 128, /* security status */
+};
+
+typedef struct Ctlr Ctlr;
+typedef struct Drive Drive;
+
+typedef struct Prd {
+ ulong pa; /* Physical Base Address */
+ int count;
+} Prd;
+
+enum {
+ Nprd = SDmaxio/(64*1024)+2,
+};
+
+typedef struct Ctlr {
+ int cmdport;
+ int ctlport;
+ int irq;
+ int bmiba; /* bus master interface base address */
+
+ Pcidev* pcidev;
+ void (*ienable)(Ctlr*);
+ SDev* sdev;
+
+ Drive* drive[2];
+
+ Prd* prdt; /* physical region descriptor table */
+
+ QLock; /* current command */
+ Drive* curdrive;
+ Rendez;
+ int done;
+
+ Lock; /* register access */
+} Ctlr;
+
+typedef struct Drive {
+ Ctlr* ctlr;
+
+ int dev;
+ ushort info[256];
+ int c; /* cylinder */
+ int h; /* head */
+ int s; /* sector */
+ int sectors; /* total */
+ int secsize; /* sector size */
+ int block; /* R/W multiple size */
+ int pior; /* PIO read command */
+ int piow; /* PIO write command */
+ int dma; /* DMA R/W possible */
+ int pkt; /* PACKET device, length of pktcmd */
+
+ uchar sense[18];
+ uchar inquiry[48];
+
+ QLock; /* drive access */
+ int command; /* current command */
+ int write;
+ uchar* data;
+ int dlen;
+ uchar* limit;
+ int count; /* sectors */
+ int status;
+ int error;
+
+ uchar pktcmd[16];
+ int pktdma;
+} Drive;
+
+static void
+pc87415ienable(Ctlr* ctlr)
+{
+ Pcidev *p;
+ int x;
+
+ p = ctlr->pcidev;
+ if(p == nil)
+ return;
+
+ x = pcicfgr32(p, 0x40);
+ if(ctlr->cmdport == p->mem[0].bar)
+ x &= ~0x00000100;
+ else
+ x &= ~0x00000200;
+ pcicfgw32(p, 0x40, x);
+}
+
+static int
+atadebug(int cmdport, int ctlport, char* fmt, ...)
+{
+ int i, n;
+ va_list arg;
+ char buf[PRINTSIZE];
+
+ if(!DEBUG){
+ USED(cmdport, ctlport, fmt);
+ return 0;
+ }
+
+ va_start(arg, fmt);
+ n = doprint(buf, buf+sizeof(buf), fmt, arg) - buf;
+ va_end(arg);
+
+ if(cmdport){
+ if(buf[n-1] == '\n')
+ n--;
+ n += snprint(buf+n, PRINTSIZE-n, " ataregs 0x%uX:",
+ cmdport);
+ for(i = 1; i < 7; i++)
+ n += snprint(buf+n, PRINTSIZE-n, " 0x%2.2uX",
+ inb(cmdport+i));
+ if(ctlport)
+ n += snprint(buf+n, PRINTSIZE-n, " 0x%2.2uX",
+ inb(ctlport+As));
+ n += snprint(buf+n, PRINTSIZE-n, "\n");
+ }
+ putstrn(buf, n);
+
+ return n;
+}
+
+static int
+ataready(int cmdport, int ctlport, int dev, int reset, int ready, int micro)
+{
+ int as;
+
+ atadebug(cmdport, ctlport, "ataready: dev %uX reset %uX ready %uX",
+ dev, reset, ready);
+
+ for(;;){
+ /*
+ * Wait for the controller to become not busy and
+ * possibly for a status bit to become true (usually
+ * Drdy). Must change to the appropriate device
+ * register set before testing for ready.
+ * Always run through the loop at least once so it
+ * can be used as a test for !Bsy.
+ */
+ as = inb(ctlport+As);
+ if(dev && !(as & (Bsy|Drq))){
+ outb(cmdport+Dh, dev);
+ dev = 0;
+ continue;
+ }
+ else if(!(as & reset)){
+ if(ready == 0 || (as & ready)){
+ atadebug(0, 0, "ataready: %d 0x%2.2uX\n",
+ micro, as);
+ return as;
+ }
+ }
+
+ if(micro-- <= 0){
+ atadebug(0, 0, "ataready: %d 0x%2.2uX\n",
+ micro, as);
+ break;
+ }
+ microdelay(1);
+ }
+ atadebug(cmdport, ctlport, "ataready: timeout");
+
+ return -1;
+}
+
+static int
+atacsfenabled(Drive* drive, vlong csf)
+{
+ int cmdset, i, x;
+
+ for(i = 0; i < 3; i++){
+ x = (csf>>(16*i)) & 0xFFFF;
+ if(x == 0)
+ continue;
+ cmdset = drive->info[Icmdset3+i];
+ if(cmdset == 0 || cmdset == 0xFFFF)
+ return 0;
+ return cmdset & x;
+ }
+
+ return 0;
+}
+
+static Drive*
+ataidentify(int cmdport, int ctlport, int dev)
+{
+ Drive *drive;
+ uchar buf[512], *p;
+ int command, dma, i, as;
+ ushort *sp;
+
+ atadebug(0, 0, "identify: port 0x%uX dev 0x%2.2uX\n", cmdport, dev);
+ command = Cidpkt;
+retry:
+ as = ataready(cmdport, ctlport, dev, Bsy|Drq, 0, 100*1000);
+ if(as < 0)
+ return nil;
+ outb(cmdport+Command, command);
+ microdelay(1);
+
+ as = ataready(cmdport, ctlport, dev, Bsy, Drq|Err, 100*1000);
+ if(as < 0)
+ return nil;
+ if(as & Err){
+ if(command == Cid)
+ return nil;
+ command = Cid;
+ goto retry;
+ }
+ memset(buf, 0, sizeof(buf));
+ inss(cmdport+Data, buf, 256);
+ inb(cmdport+Status);
+
+ if(DEBUG > 1){
+ int i;
+ ushort *sp;
+
+ sp = (ushort*)buf;
+ for(i = 0; i < 256; i++){
+ if(i && (i%16) == 0)
+ print("\n");
+ print("%4.4uX ", *sp);
+ sp++;
+ }
+ print("\n");
+ }
+
+ if((drive = malloc(sizeof(Drive))) == nil)
+ return nil;
+ drive->dev = dev;
+ memmove(drive->info, buf, sizeof(drive->info));
+ drive->sense[0] = 0x70;
+ drive->sense[7] = sizeof(drive->sense)-7;
+
+ drive->inquiry[2] = 2;
+ drive->inquiry[3] = 2;
+ drive->inquiry[4] = sizeof(drive->inquiry)-4;
+ p = &drive->inquiry[8];
+ sp = &drive->info[Imodel];
+ for(i = 0; i < 20; i++){
+ *p++ = *sp>>8;
+ *p++ = *sp++;
+ }
+ if((drive->info[Iconfig] & 0xC000) == 0x8000){
+ if(drive->info[Iconfig] & 0x01)
+ drive->pkt = 16;
+ else
+ drive->pkt = 12;
+ }
+ else{
+ if(drive->info[Ivalid] & 0x0001){
+ drive->c = drive->info[Ilcyl];
+ drive->h = drive->info[Ilhead];
+ drive->s = drive->info[Ilsec];
+ }
+ else{
+ drive->c = drive->info[Iccyl];
+ drive->h = drive->info[Ichead];
+ drive->s = drive->info[Icsec];
+ }
+ if(drive->info[Icapabilities] & 0x0200){
+ drive->sectors = (drive->info[Ilba1]<<16)
+ |drive->info[Ilba0];
+ drive->dev |= Lba;
+ }
+ else
+ drive->sectors = drive->c*drive->h*drive->s;
+ }
+ drive->secsize = 512;
+
+ if((drive->info[Imaxrwm] & 0xFF) && (drive->info[Irwm] & 0x0100)){
+ drive->block = drive->info[Imaxrwm] & 0x00FF;
+ drive->pior = Crsm;
+ drive->piow = Cwsm;
+ }
+ else{
+ drive->block = 1;
+ drive->pior = Crs;
+ drive->piow = Cws;
+ }
+ drive->block *= drive->secsize;
+
+ /*
+ * Check if any DMA mode enabled.
+ * Assumes the BIOS has picked and enabled the best.
+ */
+ dma = drive->info[Imwdma] & 0x0707;
+ drive->dma = (dma>>8) & dma;
+ if(drive->dma == 0 && (drive->info[Ivalid] & 0x04)){
+ dma = drive->info[Iudma] & 0x1F1F;
+ drive->dma = (dma>>8) & dma;
+ if(drive->dma)
+ drive->dma |= 'U'<<16;
+ }
+print("dev %4.4uX capabilities %4.4uX config %4.4uX mwdma %4.4uX dma %8.8uX\n",
+ dev, drive->info[Icapabilities], drive->info[Iconfig],
+ drive->info[Imwdma], drive->dma);
+
+ return drive;
+}
+
+static void
+atasrst(int ctlport)
+{
+ /*
+ * Srst is a big stick and may cause problems if further
+ * commands are tried before the drives become ready again.
+ * Also, there will be problems here if overlapped commands
+ * are ever supported.
+ */
+ microdelay(5);
+ outb(ctlport+Dc, Srst);
+ microdelay(5);
+ outb(ctlport+Dc, 0);
+ microdelay(2*1000);
+}
+
+static SDev*
+ataprobe(int cmdport, int ctlport, int irq)
+{
+ Ctlr* ctlr;
+ SDev *sdev;
+ Drive *drive;
+ int dev, error, rhi, rlo;
+
+ if(ioalloc(cmdport, 8, 0, "atacmd") < 0)
+ return nil;
+ if(ioalloc(ctlport+As, 1, 0, "atactl") < 0){
+ iofree(cmdport);
+ return nil;
+ }
+
+ /*
+ * Try to detect a floating bus.
+ * Bsy should be cleared. If not, see if the cylinder registers
+ * are read/write capable.
+ * If the master fails, try the slave to catch slave-only
+ * configurations.
+ * There's no need to restore the tested registers as they will
+ * be reset on any detected drives by the Cedd command.
+ * All this indicates is that there is at least one drive on the
+ * controller; when the non-existent drive is selected in a
+ * single-drive configuration the registers of the existing drive
+ * are often seen, only command execution fails.
+ */
+ dev = Dev0;
+ if(inb(ctlport+As) & Bsy){
+ outb(cmdport+Dh, dev);
+trydev1:
+ atadebug(cmdport, ctlport, "ataprobe bsy");
+ outb(cmdport+Cyllo, 0xAA);
+ outb(cmdport+Cylhi, 0x55);
+ outb(cmdport+Sector, 0xFF);
+ rlo = inb(cmdport+Cyllo);
+ rhi = inb(cmdport+Cylhi);
+ if(rlo != 0xAA && (rlo == 0xFF || rhi != 0x55)){
+ if(dev == Dev1){
+release:
+ iofree(cmdport);
+ iofree(ctlport+As);
+ return nil;
+ }
+ dev = Dev1;
+ if(ataready(cmdport, ctlport, dev, Bsy, 0, 20*1000) < 0)
+ goto trydev1;
+ }
+ }
+
+ /*
+ * Disable interrupts on any detected controllers.
+ */
+ outb(ctlport+Dc, Nien);
+tryedd1:
+ if(ataready(cmdport, ctlport, dev, Bsy|Drq, 0, 100*1000) < 0){
+ /*
+ * There's something there, but it didn't come up clean,
+ * so try hitting it with a big stick. The timing here is
+ * wrong but this is a last-ditch effort and it sometimes
+ * gets some marginal hardware back online.
+ */
+ atasrst(ctlport);
+ if(ataready(cmdport, ctlport, dev, Bsy|Drq, 0, 100*1000) < 0)
+ goto release;
+ }
+
+ /*
+ * Can only get here if controller is not busy.
+ * If there are drives Bsy will be set within 400nS,
+ * must wait 2mS before testing Status.
+ * Wait for the command to complete (6 seconds max).
+ */
+ outb(cmdport+Command, Cedd);
+ delay(2);
+ if(ataready(cmdport, ctlport, dev, Bsy|Drq, 0, 6*1000*1000) < 0)
+ goto release;
+
+ /*
+ * If bit 0 of the error register is set then the selected drive
+ * exists. This is enough to detect single-drive configurations.
+ * However, if the master exists there is no way short of executing
+ * a command to determine if a slave is present.
+ * It appears possible to get here testing Dev0 although it doesn't
+ * exist and the EDD won't take, so try again with Dev1.
+ */
+ error = inb(cmdport+Error);
+ atadebug(cmdport, ctlport, "ataprobe: dev %uX", dev);
+ if((error & ~0x80) != 0x01){
+ if(dev == Dev1)
+ goto release;
+ dev = Dev1;
+ goto tryedd1;
+ }
+
+ /*
+ * At least one drive is known to exist, try to
+ * identify it. If that fails, don't bother checking
+ * any further.
+ * If the one drive found is Dev0 and the EDD command
+ * didn't indicate Dev1 doesn't exist, check for it.
+ */
+ if((drive = ataidentify(cmdport, ctlport, dev)) == nil)
+ goto release;
+ if((ctlr = malloc(sizeof(Ctlr))) == nil){
+ free(drive);
+ goto release;
+ }
+ if((sdev = malloc(sizeof(SDev))) == nil){
+ free(ctlr);
+ free(drive);
+ goto release;
+ }
+ drive->ctlr = ctlr;
+ if(dev == Dev0){
+ ctlr->drive[0] = drive;
+ if(!(error & 0x80)){
+ /*
+ * Always leave Dh pointing to a valid drive,
+ * otherwise a subsequent call to ataready on
+ * this controller may try to test a bogus Status.
+ * Ataprobe is the only place possibly invalid
+ * drives should be selected.
+ */
+ drive = ataidentify(cmdport, ctlport, Dev1);
+ if(drive != nil){
+ drive->ctlr = ctlr;
+ ctlr->drive[1] = drive;
+ }
+ else
+ outb(cmdport+Dh, Dev0);
+ }
+ }
+ else
+ ctlr->drive[1] = drive;
+
+ ctlr->cmdport = cmdport;
+ ctlr->ctlport = ctlport;
+ ctlr->irq = irq;
+
+ sdev->ifc = &sdataifc;
+ sdev->ctlr = ctlr;
+ sdev->nunit = 2;
+ ctlr->sdev = sdev;
+
+ return sdev;
+}
+
+static int
+atasetsense(Drive* drive, int status, int key, int asc, int ascq)
+{
+ drive->sense[2] = key;
+ drive->sense[12] = asc;
+ drive->sense[13] = ascq;
+
+ return status;
+}
+
+static int
+atamodesense(Drive* drive, uchar* cmd)
+{
+ int len;
+
+ /*
+ * Fake a vendor-specific request with page code 0,
+ * return the drive info.
+ */
+ if((cmd[2] & 0x3F) != 0 && (cmd[2] & 0x3F) != 0x3F)
+ return atasetsense(drive, SDcheck, 0x05, 0x24, 0);
+ len = (cmd[7]<<8)|cmd[8];
+ if(len == 0)
+ return SDok;
+ if(len < 8+sizeof(drive->info))
+ return atasetsense(drive, SDcheck, 0x05, 0x1A, 0);
+ if(drive->data == nil || drive->dlen < len)
+ return atasetsense(drive, SDcheck, 0x05, 0x20, 1);
+ memset(drive->data, 0, 8);
+ drive->data[0] = sizeof(drive->info)>>8;
+ drive->data[1] = sizeof(drive->info);
+ memmove(drive->data+8, drive->info, sizeof(drive->info));
+ drive->data += 8+sizeof(drive->info);
+
+ return SDok;
+}
+
+static void
+atanop(Drive* drive, int subcommand)
+{
+ Ctlr* ctlr;
+ int as, cmdport, ctlport, timeo;
+
+ /*
+ * Attempt to abort a command by using NOP.
+ * In response, the drive is supposed to set Abrt
+ * in the Error register, set (Drdy|Err) in Status
+ * and clear Bsy when done. However, some drives
+ * (e.g. ATAPI Zip) just go Bsy then clear Status
+ * when done, hence the timeout loop only on Bsy
+ * and the forced setting of drive->error.
+ */
+ ctlr = drive->ctlr;
+ cmdport = ctlr->cmdport;
+ outb(cmdport+Features, subcommand);
+ outb(cmdport+Dh, drive->dev);
+ outb(cmdport+Command, 0);
+
+ microdelay(1);
+ ctlport = ctlr->ctlport;
+ for(timeo = 0; timeo < 1000; timeo++){
+ as = inb(ctlport+As);
+ if(!(as & Bsy))
+ break;
+ microdelay(1);
+ }
+ drive->error |= Abrt;
+}
+
+static void
+ataabort(Drive* drive)
+{
+ /*
+ * If NOP is available (packet commands) use it otherwise
+ * must try a software reset.
+ */
+ if(atacsfenabled(drive, 0x0000000000004000LL))
+ atanop(drive, 0);
+ else{
+ atasrst(drive->ctlr->ctlport);
+ drive->error |= Abrt;
+ }
+}
+
+static int
+atadmasetup(Drive* drive, int len)
+{
+ Prd *prd;
+ ulong pa;
+ Ctlr *ctlr;
+ int bmiba, bmisx, count;
+
+ pa = PCIWADDR(drive->data);
+ if(pa & 0x03)
+ return -1;
+ ctlr = drive->ctlr;
+ prd = ctlr->prdt;
+
+ /*
+ * Sometimes drives identify themselves as being DMA capable
+ * although they are not on a busmastering controller.
+ */
+ if(prd == nil){
+ drive->dma = 0;
+ return -1;
+ }
+
+ for(;;){
+ prd->pa = pa;
+ count = 64*1024 - (pa & 0xFFFF);
+ if(count >= len){
+ prd->count = PrdEOT|(len & 0xFFFF);
+ break;
+ }
+ prd->count = count;
+ len -= count;
+ pa += count;
+ prd++;
+ }
+
+ bmiba = ctlr->bmiba;
+ outl(bmiba+Bmidtpx, PCIWADDR(ctlr->prdt));
+ if(drive->write)
+ outb(ctlr->bmiba+Bmicx, 0);
+ else
+ outb(ctlr->bmiba+Bmicx, Rwcon);
+ bmisx = inb(bmiba+Bmisx);
+ outb(bmiba+Bmisx, bmisx|Ideints|Idedmae);
+
+ return 0;
+}
+
+static void
+atadmastart(Ctlr* ctlr, int write)
+{
+ if(write)
+ outb(ctlr->bmiba+Bmicx, Ssbm);
+ else
+ outb(ctlr->bmiba+Bmicx, Rwcon|Ssbm);
+}
+
+static void
+atadmastop(Ctlr* ctlr)
+{
+ int bmiba;
+
+ bmiba = ctlr->bmiba;
+ outb(bmiba+Bmicx, inb(bmiba+Bmicx) & ~Ssbm);
+ outb(bmiba+Bmisx, inb(bmiba+Bmisx)|Ideints|Idedmae);
+}
+
+static void
+atadmainterrupt(Drive* drive, int count)
+{
+ Ctlr* ctlr;
+ int bmiba, bmisx;
+
+ ctlr = drive->ctlr;
+ bmiba = ctlr->bmiba;
+ bmisx = inb(bmiba+Bmisx);
+ switch(bmisx & (Ideints|Idedmae|Bmidea)){
+ case Bmidea:
+ /*
+ * Data transfer still in progress, nothing to do
+ * (this should never happen).
+ */
+ return;
+
+ case Ideints:
+ case Ideints|Bmidea:
+ /*
+ * Normal termination, tidy up.
+ */
+ drive->data += count;
+ break;
+
+ default:
+ /*
+ * What's left are error conditions (memory transfer
+ * problem) and the device is not done but the PRD is
+ * exhausted. For both cases must somehow tell the
+ * drive to abort.
+ */
+ ataabort(drive);
+ break;
+ }
+ atadmastop(ctlr);
+ ctlr->done = 1;
+}
+
+static int
+atapktiodone(void* arg)
+{
+ return ((Ctlr*)arg)->done;
+}
+
+static void
+atapktinterrupt(Drive* drive)
+{
+ Ctlr* ctlr;
+ int cmdport, len;
+
+ ctlr = drive->ctlr;
+ cmdport = ctlr->cmdport;
+ switch(inb(cmdport+Ir) & (/*Rel|*/Io|Cd)){
+ case Cd:
+ outss(cmdport+Data, drive->pktcmd, drive->pkt/2);
+ break;
+
+ case 0:
+ len = (inb(cmdport+Bytehi)<<8)|inb(cmdport+Bytelo);
+ if(drive->data+len > drive->limit){
+ atanop(drive, 0);
+ break;
+ }
+ outss(cmdport+Data, drive->data, len/2);
+ drive->data += len;
+ break;
+
+ case Io:
+ len = (inb(cmdport+Bytehi)<<8)|inb(cmdport+Bytelo);
+ if(drive->data+len > drive->limit){
+ atanop(drive, 0);
+ break;
+ }
+ inss(cmdport+Data, drive->data, len/2);
+ drive->data += len;
+ break;
+
+ case Io|Cd:
+ if(drive->pktdma)
+ atadmainterrupt(drive, drive->dlen);
+ else
+ ctlr->done = 1;
+ break;
+ }
+}
+
+static int
+atapktio(Drive* drive, uchar* cmd, int clen)
+{
+ Ctlr *ctlr;
+ int as, cmdport, ctlport, len, r, timeo;
+
+ if(cmd[0] == 0x5A && (cmd[2] & 0x3F) == 0)
+ return atamodesense(drive, cmd);
+
+ r = SDok;
+
+ drive->command = Cpkt;
+ memmove(drive->pktcmd, cmd, clen);
+ memset(drive->pktcmd+clen, 0, drive->pkt-clen);
+ drive->limit = drive->data+drive->dlen;
+
+ ctlr = drive->ctlr;
+ cmdport = ctlr->cmdport;
+ ctlport = ctlr->ctlport;
+
+ qlock(ctlr);
+
+ if(ataready(cmdport, ctlport, drive->dev, Bsy|Drq, 0, 100*1000) < 0){
+ qunlock(ctlr);
+ return -1;
+ }
+
+ ilock(ctlr);
+ if(drive->dlen && drive->dma && !atadmasetup(drive, drive->dlen))
+ drive->pktdma = Dma;
+ else
+ drive->pktdma = 0;
+
+ outb(cmdport+Features, drive->pktdma);
+ outb(cmdport+Count, 0);
+ outb(cmdport+Sector, 0);
+ len = 16*drive->secsize;
+ outb(cmdport+Bytelo, len);
+ outb(cmdport+Bytehi, len>>8);
+ outb(cmdport+Dh, drive->dev);
+ ctlr->done = 0;
+ if(drive->pktdma)
+ atadmastart(ctlr, drive->write);
+ outb(cmdport+Command, Cpkt);
+
+ if((drive->info[Iconfig] & 0x0060) != 0x0020){
+ as = ataready(cmdport, ctlport,
+ drive->dev, Bsy, Drq|Chk, 4*1000);
+ if(as < 0)
+ r = SDtimeout;
+ else if(as & Chk)
+ r = SDcheck;
+ else
+ atapktinterrupt(drive);
+ }
+ ctlr->curdrive = drive;
+ iunlock(ctlr);
+
+ while(waserror())
+ ;
+ if(!drive->pktdma)
+ sleep(ctlr, atapktiodone, ctlr);
+ else for(timeo = 0; !ctlr->done; timeo++){
+ tsleep(ctlr, atapktiodone, ctlr, 1000);
+ if(ctlr->done)
+ break;
+ ilock(ctlr);
+ atadmainterrupt(drive, 0);
+ if(!drive->error && timeo > 10){
+ ataabort(drive);
+ atadmastop(ctlr);
+ drive->dma = 0;
+ drive->error |= Abrt;
+ }
+ if(drive->error){
+ drive->status |= Chk;
+ ctlr->curdrive = nil;
+ }
+ iunlock(ctlr);
+ }
+ poperror();
+
+ qunlock(ctlr);
+
+ if(drive->status & Chk)
+ r = SDcheck;
+
+ return r;
+}
+
+static int
+atageniodone(void* arg)
+{
+ return ((Ctlr*)arg)->done;
+}
+
+static int
+atageniostart(Drive* drive, int lba)
+{
+ Ctlr *ctlr;
+ int as, c, cmdport, ctlport, h, len, s;
+
+ if(drive->dev & Lba){
+ c = (lba>>8) & 0xFFFF;
+ h = (lba>>24) & 0x0F;
+ s = lba & 0xFF;
+ }
+ else{
+ c = lba/(drive->s*drive->h);
+ h = ((lba/drive->s) % drive->h);
+ s = (lba % drive->s) + 1;
+ }
+
+ ctlr = drive->ctlr;
+ cmdport = ctlr->cmdport;
+ ctlport = ctlr->ctlport;
+ if(ataready(cmdport, ctlport, drive->dev, Bsy|Drq, 0, 100*1000) < 0)
+ return -1;
+
+ ilock(ctlr);
+ if(drive->dma && !atadmasetup(drive, drive->count*drive->secsize)){
+ if(drive->write)
+ drive->command = Cwd;
+ else
+ drive->command = Crd;
+ }
+ else if(drive->write)
+ drive->command = drive->piow;
+ else
+ drive->command = drive->pior;
+
+ outb(cmdport+Count, drive->count);
+ outb(cmdport+Sector, s);
+ outb(cmdport+Dh, drive->dev|h);
+ outb(cmdport+Cyllo, c);
+ outb(cmdport+Cylhi, c>>8);
+ ctlr->done = 0;
+ outb(cmdport+Command, drive->command);
+ microdelay(1);
+
+ switch(drive->command){
+ case Cws:
+ case Cwsm:
+ as = ataready(cmdport, ctlport, drive->dev, Bsy, Drq|Err, 1000);
+ if(as < 0 || (as & Err)){
+ iunlock(ctlr);
+ return -1;
+ }
+ len = drive->block;
+ if(drive->data+len > drive->limit)
+ len = drive->limit-drive->data;
+ outss(cmdport+Data, drive->data, len/2);
+ break;
+
+ case Crd:
+ case Cwd:
+ atadmastart(ctlr, drive->write);
+ break;
+ }
+ ctlr->curdrive = drive;
+ iunlock(ctlr);
+
+ return 0;
+}
+
+static int
+atagenio(Drive* drive, uchar* cmd, int)
+{
+ uchar *p;
+ Ctlr *ctlr;
+ int count, lba, len;
+
+ /*
+ * Map SCSI commands into ATA commands for discs.
+ * Fail any command with a LUN except INQUIRY which
+ * will return 'logical unit not supported'.
+ */
+ if((cmd[1]>>5) && cmd[0] != 0x12)
+ return atasetsense(drive, SDcheck, 0x05, 0x25, 0);
+
+ switch(cmd[0]){
+ default:
+ return atasetsense(drive, SDcheck, 0x05, 0x20, 0);
+
+ case 0x00: /* test unit ready */
+ return SDok;
+
+ case 0x03: /* request sense */
+ if(cmd[4] < sizeof(drive->sense))
+ len = cmd[4];
+ else
+ len = sizeof(drive->sense);
+ if(drive->data && drive->dlen >= len){
+ memmove(drive->data, drive->sense, len);
+ drive->data += len;
+ }
+ return SDok;
+
+ case 0x12: /* inquiry */
+ if(cmd[4] < sizeof(drive->inquiry))
+ len = cmd[4];
+ else
+ len = sizeof(drive->inquiry);
+ if(drive->data && drive->dlen >= len){
+ memmove(drive->data, drive->inquiry, len);
+ drive->data += len;
+ }
+ return SDok;
+
+ case 0x1B: /* start/stop unit */
+ /*
+ * NOP for now, can use the power management feature
+ * set later.
+ */
+ return SDok;
+
+ case 0x25: /* read capacity */
+ if((cmd[1] & 0x01) || cmd[2] || cmd[3])
+ return atasetsense(drive, SDcheck, 0x05, 0x24, 0);
+ if(drive->data == nil || drive->dlen < 8)
+ return atasetsense(drive, SDcheck, 0x05, 0x20, 1);
+ /*
+ * Read capacity returns the LBA of the last sector.
+ */
+ len = drive->sectors-1;
+ p = drive->data;
+ *p++ = len>>24;
+ *p++ = len>>16;
+ *p++ = len>>8;
+ *p++ = len;
+ len = drive->secsize;
+ *p++ = len>>24;
+ *p++ = len>>16;
+ *p++ = len>>8;
+ *p = len;
+ drive->data += 8;
+ return SDok;
+
+ case 0x28: /* read */
+ case 0x2A: /* write */
+ break;
+
+ case 0x5A:
+ return atamodesense(drive, cmd);
+ }
+
+ ctlr = drive->ctlr;
+ lba = (cmd[2]<<24)|(cmd[3]<<16)|(cmd[4]<<8)|cmd[5];
+ count = (cmd[7]<<8)|cmd[8];
+ if(drive->data == nil)
+ return SDok;
+ if(drive->dlen < count*drive->secsize)
+ count = drive->dlen/drive->secsize;
+ qlock(ctlr);
+ while(count){
+ if(count > 256)
+ drive->count = 256;
+ else
+ drive->count = count;
+ drive->limit += drive->count*drive->secsize;
+ if(atageniostart(drive, lba)){
+ qunlock(ctlr);
+ return atasetsense(drive, SDcheck, 2, 5, 0);
+ }
+ lba += drive->count;
+
+ while(waserror())
+ ;
+ tsleep(ctlr, atageniodone, ctlr, 10*1000);
+ poperror();
+ if(!ctlr->done){
+ /*
+ * What should the above timeout be? In
+ * standby and sleep modes it could take as
+ * long as 30 seconds for a drive to respond.
+ * Very hard to get out of this cleanly.
+ * Let's see if it ever happens first...
+ */
+ panic("atagenio");
+ }
+
+ if(drive->status & Err){
+ qunlock(ctlr);
+ return atasetsense(drive, SDcheck, 4, 8, drive->error);
+ }
+ if(drive->data != drive->limit)
+ print("%s: atagenio: %p != %p\n",
+ ctlr->sdev->name,
+ drive->data, drive->limit);
+ count -= drive->count;
+ }
+ qunlock(ctlr);
+
+ return SDok;
+}
+
+static int
+atario(SDreq* r)
+{
+ Ctlr *ctlr;
+ Drive *drive;
+ SDunit *unit;
+ uchar cmd10[10], *cmdp, *p;
+ int clen, reqstatus, status;
+
+ unit = r->unit;
+ if((ctlr = unit->dev->ctlr) == nil || ctlr->drive[unit->subno] == nil){
+ r->status = SDtimeout;
+ return SDtimeout;
+ }
+ drive = ctlr->drive[unit->subno];
+
+ /*
+ * Most SCSI commands can be passed unchanged except for
+ * the padding on the end. The few which require munging
+ * are not used internally. Mode select/sense(6) could be
+ * converted to the 10-byte form but it's not worth the
+ * effort. Read/write(6) are easy.
+ */
+ switch(r->cmd[0]){
+ case 0x08: /* read */
+ case 0x0A: /* write */
+ cmdp = cmd10;
+ memset(cmdp, 0, sizeof(cmd10));
+ cmdp[0] = r->cmd[0]|0x20;
+ cmdp[1] = r->cmd[1] & 0xE0;
+ cmdp[5] = r->cmd[3];
+ cmdp[4] = r->cmd[2];
+ cmdp[3] = r->cmd[1] & 0x0F;
+ cmdp[8] = r->cmd[4];
+ clen = sizeof(cmd10);
+ break;
+
+ default:
+ cmdp = r->cmd;
+ clen = r->clen;
+ break;
+ }
+
+ qlock(drive);
+ drive->write = r->write;
+ drive->data = r->data;
+ drive->dlen = r->dlen;
+ drive->limit = r->data;
+
+ drive->status = 0;
+ drive->error = 0;
+ if(drive->pkt)
+ status = atapktio(drive, cmdp, clen);
+ else
+ status = atagenio(drive, cmdp, clen);
+ if(status == SDok){
+ atasetsense(drive, SDok, 0, 0, 0);
+ if(drive->data){
+ p = r->data;
+ r->rlen = drive->data - p;
+ }
+ else
+ r->rlen = 0;
+ }
+ else if(status == SDcheck && !(r->flags & SDnosense)){
+ drive->write = 0;
+ memset(cmd10, 0, sizeof(cmd10));
+ cmd10[0] = 0x03;
+ cmd10[1] = r->lun<<5;
+ cmd10[4] = sizeof(r->sense)-1;
+ drive->data = r->sense;
+ drive->dlen = sizeof(r->sense)-1;
+ drive->limit = r->sense;
+ drive->status = 0;
+ drive->error = 0;
+ if(drive->pkt)
+ reqstatus = atapktio(drive, cmd10, 6);
+ else
+ reqstatus = atagenio(drive, cmd10, 6);
+ if(reqstatus == SDok){
+ r->flags |= SDvalidsense;
+ atasetsense(drive, SDok, 0, 0, 0);
+ }
+ }
+ qunlock(drive);
+ r->status = status;
+ if(status != SDok)
+ return status;
+
+ /*
+ * Fix up any results.
+ * Many ATAPI CD-ROMs ignore the LUN field completely and
+ * return valid INQUIRY data. Patch the response to indicate
+ * 'logical unit not supported' if the LUN is non-zero.
+ */
+ switch(cmdp[0]){
+ case 0x12: /* inquiry */
+ if((p = r->data) == nil)
+ break;
+ if((cmdp[1]>>5) && (!drive->pkt || (p[0] & 0x1F) == 0x05))
+ p[0] = 0x7F;
+ /*FALLTHROUGH*/
+ default:
+ break;
+ }
+
+ return SDok;
+}
+
+static void
+atainterrupt(Ureg*, void* arg)
+{
+ Ctlr *ctlr;
+ Drive *drive;
+ int cmdport, len, status;
+
+ ctlr = arg;
+
+ ilock(ctlr);
+ if(inb(ctlr->ctlport+As) & Bsy){
+ iunlock(ctlr);
+ return;
+ }
+ cmdport = ctlr->cmdport;
+ status = inb(cmdport+Status);
+ if((drive = ctlr->curdrive) == nil){
+ iunlock(ctlr);
+ return;
+ }
+
+ if(status & Err)
+ drive->error = inb(cmdport+Error);
+ else switch(drive->command){
+ default:
+ drive->error = Abrt;
+ break;
+
+ case Crs:
+ case Crsm:
+ if(!(status & Drq)){
+ drive->error = Abrt;
+ break;
+ }
+ len = drive->block;
+ if(drive->data+len > drive->limit)
+ len = drive->limit-drive->data;
+ inss(cmdport+Data, drive->data, len/2);
+ drive->data += len;
+ if(drive->data >= drive->limit)
+ ctlr->done = 1;
+ break;
+
+ case Cws:
+ case Cwsm:
+ len = drive->block;
+ if(drive->data+len > drive->limit)
+ len = drive->limit-drive->data;
+ drive->data += len;
+ if(drive->data >= drive->limit){
+ ctlr->done = 1;
+ break;
+ }
+ if(!(status & Drq)){
+ drive->error = Abrt;
+ break;
+ }
+ len = drive->block;
+ if(drive->data+len > drive->limit)
+ len = drive->limit-drive->data;
+ outss(cmdport+Data, drive->data, len/2);
+ break;
+
+ case Cpkt:
+ atapktinterrupt(drive);
+ break;
+
+ case Crd:
+ case Cwd:
+ atadmainterrupt(drive, drive->count*drive->secsize);
+ break;
+ }
+ iunlock(ctlr);
+
+ if(drive->error){
+ status |= Err;
+ ctlr->done = 1;
+ }
+
+ if(ctlr->done){
+ ctlr->curdrive = nil;
+ drive->status = status;
+ wakeup(ctlr);
+ }
+}
+
+static SDev*
+atapnp(void)
+{
+ Ctlr *ctlr;
+ Pcidev *p;
+ int channel, ispc87415, pi;
+ SDev *legacy[2], *sdev, *head, *tail;
+
+ legacy[0] = legacy[1] = head = tail = nil;
+ if(sdev = ataprobe(0x1F0, 0x3F4, IrqATA0)){
+ head = tail = sdev;
+ legacy[0] = sdev;
+ }
+ if(sdev = ataprobe(0x170, 0x374, IrqATA1)){
+ if(head != nil)
+ tail->next = sdev;
+ else
+ head = sdev;
+ tail = sdev;
+ legacy[1] = sdev;
+ }
+
+ p = nil;
+ while(p = pcimatch(p, 0, 0)){
+ /*
+ * Look for devices with the correct class and sub-class
+ * code and known device and vendor ID; add native-mode
+ * channels to the list to be probed, save info for the
+ * compatibility mode channels.
+ * Note that the legacy devices should not be considered
+ * PCI devices by the interrupt controller.
+ * For both native and legacy, save info for busmastering
+ * if capable.
+ * Promise Ultra ATA/66 (PDC20262) appears to
+ * 1) give a sub-class of 'other mass storage controller'
+ * instead of 'IDE controller', regardless of whether it's
+ * the only controller or not;
+ * 2) put 0 in the programming interface byte (probably
+ * as a consequence of 1) above).
+ */
+ if(p->ccrb != 0x01 || (p->ccru != 0x01 && p->ccru != 0x80))
+ continue;
+ pi = p->ccrp;
+ ispc87415 = 0;
+
+ switch((p->did<<16)|p->vid){
+ default:
+ continue;
+
+ case (0x0002<<16)|0x100B: /* NS PC87415 */
+ /*
+ * Disable interrupts on both channels until
+ * after they are probed for drives.
+ * This must be called before interrupts are
+ * enabled because the IRQ may be shared.
+ */
+ ispc87415 = 1;
+ pcicfgw32(p, 0x40, 0x00000300);
+ break;
+
+ case (0x4D38<<16)|0x105A: /* Promise PDC20262 */
+ pi = 0x85;
+ break;
+ case (0x1230<<16)|0x8086: /* 82371FB (PIIX) */
+ case (0x7010<<16)|0x8086: /* 82371SB (PIIX3) */
+ case (0x7111<<16)|0x8086: /* 82371[AE]B (PIIX4[E]) */
+ case (0x0646<<16)|0x1095: /* CMD 646 */
+ break;
+ case (0x0571<<16)|0x1106: /* VIA 82C686 */
+ break;
+ }
+
+ for(channel = 0; channel < 2; channel++){
+ if(pi & (1<<(2*channel))){
+ sdev = ataprobe(p->mem[0+2*channel].bar & ~0x01,
+ p->mem[1+2*channel].bar & ~0x01,
+ p->intl);
+ if(sdev == nil)
+ continue;
+
+ ctlr = sdev->ctlr;
+ if(ispc87415)
+ ctlr->ienable = pc87415ienable;
+
+ if(head != nil)
+ tail->next = sdev;
+ else
+ head = sdev;
+ tail = sdev;
+ ctlr->pcidev = p;
+ }
+ else if((sdev = legacy[channel]) == nil)
+ continue;
+ else
+ ctlr = sdev->ctlr;
+
+ if(!(pi & 0x80))
+ continue;
+ ctlr->bmiba = (p->mem[4].bar & ~0x01) + channel*8;
+ }
+ }
+
+ return head;
+}
+
+static SDev*
+atalegacy(int port, int irq)
+{
+ return ataprobe(port, port+0x204, irq);
+}
+
+static SDev*
+ataid(SDev* sdev)
+{
+ int i;
+ Ctlr *ctlr;
+
+ /*
+ * Legacy controllers are always 'C' and 'D' and if
+ * they exist and have drives will be first in the list.
+ * If there are no active legacy controllers, native
+ * controllers start at 'C'.
+ */
+ if(sdev == nil)
+ return nil;
+ ctlr = sdev->ctlr;
+ if(ctlr->cmdport == 0x1F0 || ctlr->cmdport == 0x170)
+ i = 2;
+ else
+ i = 0;
+ while(sdev){
+ if(sdev->ifc == &sdataifc){
+ ctlr = sdev->ctlr;
+ if(ctlr->cmdport == 0x1F0)
+ sdev->idno = 'C';
+ else if(ctlr->cmdport == 0x170)
+ sdev->idno = 'D';
+ else{
+ sdev->idno = 'C'+i;
+ i++;
+ }
+ snprint(sdev->name, NAMELEN, "sd%c", sdev->idno);
+ }
+ sdev = sdev->next;
+ }
+
+ return nil;
+}
+
+static int
+ataenable(SDev* sdev)
+{
+ int tbdf;
+ Ctlr *ctlr;
+ char name[NAMELEN];
+
+ ctlr = sdev->ctlr;
+
+ if(ctlr->bmiba){
+ if(ctlr->pcidev != nil)
+ pcisetbme(ctlr->pcidev);
+ ctlr->prdt = xspanalloc(Nprd*sizeof(Prd), 4, 4*1024);
+ }
+ if(ctlr->pcidev)
+ tbdf = ctlr->pcidev->tbdf;
+ else
+ tbdf = BUSUNKNOWN;
+ snprint(name, NAMELEN, "%s (%s)", sdev->name, sdev->ifc->name);
+ intrenable(ctlr->irq, atainterrupt, ctlr, tbdf, name);
+ outb(ctlr->ctlport+Dc, 0);
+ if(ctlr->ienable)
+ ctlr->ienable(ctlr);
+
+ return 1;
+}
+
+static int
+atarctl(SDunit* unit, char* p, int l)
+{
+ int n;
+ Ctlr *ctlr;
+ Drive *drive;
+
+ if((ctlr = unit->dev->ctlr) == nil || ctlr->drive[unit->subno] == nil)
+ return 0;
+ drive = ctlr->drive[unit->subno];
+
+ n = snprint(p, l, "geometry %ld %ld", unit->sectors, unit->secsize);
+ if(drive->pkt == 0)
+ n += snprint(p+n, l-n, " %d %d %d",
+ drive->c, drive->h, drive->s);
+ n += snprint(p+n, l-n, "\n");
+
+ return n;
+}
+
+SDifc sdataifc = {
+ "ata", /* name */
+
+ atapnp, /* pnp */
+ atalegacy, /* legacy */
+ ataid, /* id */
+ ataenable, /* enable */
+ nil, /* disable */
+
+ scsiverify, /* verify */
+ scsionline, /* online */
+ atario, /* rio */
+ atarctl, /* rctl */
+ nil, /* wctl */
+
+ scsibio, /* bio */
+};
A pc/sdmylex.c => pc/sdmylex.c +1222 -0
@@ 0,0 1,1222 @@
+/*
+ * Mylex MultiMaster (Buslogic BT-*) SCSI Host Adapter
+ * in both 24-bit and 32-bit mode.
+ * 24-bit mode works for Adaptec AHA-154xx series too.
+ *
+ * To do:
+ * tidy the PCI probe and do EISA;
+ * allocate more Ccb's as needed, up to NMbox-1;
+ * add nmbox and nccb to Ctlr struct for the above;
+ * 64-bit LUN/explicit wide support necessary?
+ *
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "ureg.h"
+#include "../port/error.h"
+
+#include "sd.h"
+
+#define K2BPA(va, tbdf) PADDR(va)
+#define BPA2K(pa, tbdf) KADDR(pa)
+
+extern SDifc sdmylexifc;
+
+enum { /* registers */
+ Rcontrol = 0x00, /* WO: control register */
+ Rstatus = 0x00, /* RO: status register */
+ Rcpr = 0x01, /* WO: command/parameter register */
+ Rdatain = 0x01, /* RO: data-in register */
+ Rinterrupt = 0x02, /* RO: interrupt register */
+};
+
+enum { /* Rcontrol */
+ Rsbus = 0x10, /* SCSI Bus Reset */
+ Rint = 0x20, /* Interrupt Reset */
+ Rsoft = 0x40, /* Soft Reset */
+ Rhard = 0x80, /* Hard Reset */
+};
+
+enum { /* Rstatus */
+ Cmdinv = 0x01, /* Command Invalid */
+ Dirrdy = 0x04, /* Data In Register Ready */
+ Cprbsy = 0x08, /* Command/Parameter Register Busy */
+ Hardy = 0x10, /* Host Adapter Ready */
+ Inreq = 0x20, /* Initialisation Required */
+ Dfail = 0x40, /* Diagnostic Failure */
+ Dact = 0x80, /* Diagnostic Active */
+};
+
+enum { /* Rcpr */
+ Cinitialise = 0x01, /* Initialise Mailbox */
+ Cstart = 0x02, /* Start Mailbox Command */
+ Cinquiry = 0x04, /* Adapter Anquiry */
+ Ceombri = 0x05, /* Enable OMBR Interrupt */
+ Cinquire = 0x0B, /* Inquire Configuration */
+ Cextbios = 0x28, /* AHA-1542: extended BIOS info. */
+ Cmbienable = 0x29, /* AHA-1542: Mailbox interface enable */
+ Ciem = 0x81, /* Initialise Extended Mailbox */
+ Ciesi = 0x8D, /* Inquire Extended Setup Information */
+ Cerrm = 0x8F, /* Enable strict round-robin mode */
+ Cwide = 0x96, /* Wide CCB */
+};
+
+enum { /* Rinterrupt */
+ Imbl = 0x01, /* Incoming Mailbox Loaded */
+ Mbor = 0x02, /* Mailbox Out Ready */
+ Cmdc = 0x04, /* Command Complete */
+ Rsts = 0x08, /* SCSI Reset State */
+ Intv = 0x80, /* Interrupt Valid */
+};
+
+typedef struct {
+ uchar code; /* action/completion code */
+ uchar ccb[3]; /* CCB pointer (MSB, ..., LSB) */
+} Mbox24;
+
+typedef struct {
+ uchar ccb[4]; /* CCB pointer (LSB, ..., MSB) */
+ uchar btstat; /* BT-7[45]7[SD] status */
+ uchar sdstat; /* SCSI device status */
+ uchar pad;
+ uchar code; /* action/completion code */
+} Mbox32;
+
+enum { /* mailbox commands */
+ Mbfree = 0x00, /* Mailbox not in use */
+
+ Mbostart = 0x01, /* Start a mailbox command */
+ Mboabort = 0x02, /* Abort a mailbox command */
+
+ Mbiok = 0x01, /* CCB completed without error */
+ Mbiabort = 0x02, /* CCB aborted at request of host */
+ Mbinx = 0x03, /* Aborted CCB not found */
+ Mbierror = 0x04, /* CCB completed with error */
+};
+
+typedef struct Ccb24 Ccb24;
+typedef struct Ccb32 Ccb32;
+typedef union Ccb Ccb;
+
+typedef struct Ccb24 {
+ uchar opcode; /* Operation code */
+ uchar datadir; /* Data direction control */
+ uchar cdblen; /* Length of CDB */
+ uchar senselen; /* Length of sense area */
+ uchar datalen[3]; /* Data length (MSB, ..., LSB) */
+ uchar dataptr[3]; /* Data pointer (MSB, ..., LSB) */
+ uchar linkptr[3]; /* Link pointer (MSB, ..., LSB) */
+ uchar linkid; /* command linking identifier */
+ uchar btstat; /* BT-* adapter status */
+ uchar sdstat; /* SCSI device status */
+ uchar reserved[2]; /* */
+ uchar cs[12+0xFF]; /* Command descriptor block + Sense */
+
+ void* data; /* buffer if address > 24-bits */
+
+ Rendez;
+ int done; /* command completed */
+
+ Ccb* ccb; /* link on free list */
+} Ccb24;
+
+
+typedef struct Ccb32 {
+ uchar opcode; /* Operation code */
+ uchar datadir; /* Data direction control */
+ uchar cdblen; /* Length of CDB */
+ uchar senselen; /* Length of sense area */
+ uchar datalen[4]; /* Data length (LSB, ..., MSB) */
+ uchar dataptr[4]; /* Data pointer (LSB, ..., MSB) */
+ uchar reserved[2];
+ uchar btstat; /* BT-* adapter status */
+ uchar sdstat; /* SCSI device status */
+ uchar targetid; /* Target ID */
+ uchar luntag; /* LUN & tag */
+ uchar cdb[12]; /* Command descriptor block */
+ uchar ccbctl; /* CCB control */
+ uchar linkid; /* command linking identifier */
+ uchar linkptr[4]; /* Link pointer (LSB, ..., MSB) */
+ uchar senseptr[4]; /* Sense pointer (LSB, ..., MSB) */
+ uchar sense[0xFF]; /* Sense bytes */
+
+ Rendez;
+ int done; /* command completed */
+
+ Ccb* ccb; /* link on free list */
+} Ccb32;
+
+typedef union Ccb {
+ Ccb24;
+ Ccb32;
+} Ccb;
+
+enum { /* opcode */
+ OInitiator = 0x00, /* initiator CCB */
+ Ordl = 0x03, /* initiator CCB with
+ * residual data length returned
+ */
+};
+
+enum { /* datadir */
+ CCBdatain = 0x08, /* inbound, length is checked */
+ CCBdataout = 0x10, /* outbound, length is checked */
+};
+
+enum { /* btstat */
+ Eok = 0x00, /* normal completion with no errors */
+};
+
+enum { /* luntag */
+ TagEnable = 0x20, /* Tag enable */
+ SQTag = 0x00, /* Simple Queue Tag */
+ HQTag = 0x40, /* Head of Queue Tag */
+ OQTag = 0x80, /* Ordered Queue Tag */
+};
+
+enum { /* CCB control */
+ NoDisc = 0x08, /* No disconnect */
+ NoUnd = 0x10, /* No underrrun error report */
+ NoData = 0x20, /* No data transfer */
+ NoStat = 0x40, /* No CCB status if zero */
+ NoIntr = 0x80, /* No Interrupts */
+};
+
+typedef struct {
+ int port; /* I/O port */
+ int id; /* adapter SCSI id */
+ int bus; /* 24 or 32 -bit */
+ int irq;
+ int wide;
+ Pcidev* pcidev;
+ SDev* sdev;
+ int spurious;
+
+ Lock issuelock;
+
+ Lock ccblock;
+ QLock ccbq;
+ Rendez ccbr;
+
+ Lock mboxlock;
+ void* mb; /* mailbox out + mailbox in */
+ int mbox; /* current mailbox out index into mb */
+ int mbix; /* current mailbox in index into mb */
+
+ Lock cachelock;
+ Ccb* ccb; /* list of free Ccb's */
+ Ccb** cache; /* last completed Ccb */
+} Ctlr;
+
+/*
+ * The number of mailboxes should be a multiple of 8 (4 for Mbox32)
+ * to ensure the boundary between the out and in mailboxes doesn't
+ * straddle a cache-line boundary.
+ * The number of Ccb's should be less than the number of mailboxes to
+ * ensure no queueing is necessary on mailbox allocation.
+ */
+enum {
+ NMbox = 8*8, /* number of Mbox's */
+ NCcb = NMbox-1, /* number of Ccb's */
+};
+
+#define PADDR24(a, n) (PADDR(a)+n <= (1<<24))
+
+static void
+ccbfree(Ctlr* ctlr, Ccb* ccb)
+{
+ lock(&ctlr->ccblock);
+ if(ctlr->bus == 24)
+ ((Ccb24*)ccb)->ccb = ctlr->ccb;
+ else
+ ((Ccb32*)ccb)->ccb = ctlr->ccb;
+ if(ctlr->ccb == nil)
+ wakeup(&ctlr->ccbr);
+ ctlr->ccb = ccb;
+ unlock(&ctlr->ccblock);
+}
+
+static int
+ccbavailable(void* a)
+{
+ return ((Ctlr*)a)->ccb != nil;
+}
+
+static Ccb*
+ccballoc(Ctlr* ctlr)
+{
+ Ccb *ccb;
+
+ for(;;){
+ lock(&ctlr->ccblock);
+ if(ccb = ctlr->ccb){
+ if(ctlr->bus == 24)
+ ctlr->ccb = ((Ccb24*)ccb)->ccb;
+ else
+ ctlr->ccb = ((Ccb32*)ccb)->ccb;
+ unlock(&ctlr->ccblock);
+ break;
+ }
+
+ unlock(&ctlr->ccblock);
+ qlock(&ctlr->ccbq);
+ if(waserror()){
+ qunlock(&ctlr->ccbq);
+ continue;
+ }
+ sleep(&ctlr->ccbr, ccbavailable, ctlr);
+ qunlock(&ctlr->ccbq);
+ poperror();
+ }
+
+ return ccb;
+}
+
+static int
+done24(void* arg)
+{
+ return ((Ccb24*)arg)->done;
+}
+
+static int
+mylex24rio(SDreq* r)
+{
+ ulong p;
+ Ctlr *ctlr;
+ Ccb24 *ccb;
+ Mbox32 *mb;
+ uchar *data, lun, *sense;
+ int d, n, btstat, sdstat, target;
+
+ ctlr = r->unit->dev->ctlr;
+ target = r->unit->subno;
+ lun = (r->cmd[1]>>5) & 0x07;
+
+ /*
+ * Ctlr->cache holds the last completed Ccb for this target if it
+ * returned 'check condition'.
+ * If this command is a request-sense and there is valid sense data
+ * from the last completed Ccb, return it immediately.
+ */
+ lock(&ctlr->cachelock);
+ if(ccb = ctlr->cache[target]){
+ ctlr->cache[target] = nil;
+ if(r->cmd[0] == 0x03
+ && ccb->sdstat == SDcheck && lun == ((ccb->cs[1]>>5) & 0x07)){
+ unlock(&ctlr->cachelock);
+ if(r->dlen){
+ sense = &ccb->cs[ccb->cdblen];
+ n = 8+sense[7];
+ if(n > r->dlen)
+ n = r->dlen;
+ memmove(r->data, sense, n);
+ r->rlen = n;
+ }
+ ccbfree(ctlr, (Ccb*)ccb);
+ return SDok;
+ }
+ }
+ unlock(&ctlr->cachelock);
+ if(ccb == nil)
+ ccb = ccballoc(ctlr);
+
+ /*
+ * Check if the transfer is to memory above the 24-bit limit the
+ * controller can address. If it is, try to allocate a temporary
+ * buffer as a staging area.
+ */
+ n = r->dlen;
+ if(n && !PADDR24(r->data, n)){
+ ccb->data = r->data;
+ data = mallocz(n, 0);
+ if(data == nil || !PADDR24(data, n)){
+ if(data != nil){
+ free(data);
+ ccb->data = nil;
+ }
+ ccbfree(ctlr, (Ccb*)ccb);
+ return SDmalloc;
+ }
+ if(r->write)
+ memmove(data, ccb->data, n);
+ }
+ else
+ data = r->data;
+
+ /*
+ * Fill in the ccb.
+ */
+ ccb->opcode = Ordl;
+
+ ccb->datadir = (target<<5)|lun;
+ if(n == 0)
+ ccb->datadir |= CCBdataout|CCBdatain;
+ else if(!r->write)
+ ccb->datadir |= CCBdatain;
+ else
+ ccb->datadir |= CCBdataout;
+
+ ccb->cdblen = r->clen;
+ ccb->senselen = 0xFF;
+
+ ccb->datalen[0] = n>>16;
+ ccb->datalen[1] = n>>8;
+ ccb->datalen[2] = n;
+ p = PADDR(data);
+ ccb->dataptr[0] = p>>16;
+ ccb->dataptr[1] = p>>8;
+ ccb->dataptr[2] = p;
+
+ ccb->linkptr[0] = ccb->linkptr[1] = ccb->linkptr[2] = 0;
+ ccb->linkid = 0;
+ ccb->btstat = ccb->sdstat = 0;
+ ccb->reserved[0] = ccb->reserved[1] = 0;
+
+ memmove(ccb->cs, r->cmd, r->clen);
+
+ /*
+ * There's one more mbox than there there is
+ * ccb so there is always one free.
+ */
+ lock(&ctlr->mboxlock);
+ mb = ctlr->mb;
+ mb += ctlr->mbox;
+ p = PADDR(ccb);
+ mb->ccb[0] = p>>16;
+ mb->ccb[1] = p>>8;
+ mb->ccb[2] = p;
+ mb->code = Mbostart;
+ ctlr->mbox++;
+ if(ctlr->mbox >= NMbox)
+ ctlr->mbox = 0;
+
+ /*
+ * This command does not require Hardy
+ * and doesn't generate a Cmdc interrupt.
+ */
+ ccb->done = 0;
+ outb(ctlr->port+Rcpr, Cstart);
+ unlock(&ctlr->mboxlock);
+
+ /*
+ * Wait for the request to complete and return the status.
+ * Since the buffer is not reference counted cannot return
+ * until the DMA is done writing into the buffer so the caller
+ * cannot free the buffer prematurely.
+ */
+ while(waserror())
+ ;
+ sleep(ccb, done24, ccb);
+ poperror();
+
+ /*
+ * Save the status and patch up the number of
+ * bytes actually transferred.
+ * There's a firmware bug on some 956C controllers
+ * which causes the return count from a successful
+ * READ CAPACITY not be updated, so fix it here.
+ */
+ sdstat = ccb->sdstat;
+ btstat = ccb->btstat;
+
+ d = ccb->datalen[0]<<16;
+ d |= ccb->datalen[1]<<8;
+ d |= ccb->datalen[2];
+ if(ccb->cs[0] == 0x25 && sdstat == SDok)
+ d = 0;
+ n -= d;
+ r->rlen = n;
+
+ /*
+ * Tidy things up if a staging area was used for the data,
+ */
+ if(ccb->data){
+ if(sdstat == SDok && btstat == 0 && !r->write)
+ memmove(ccb->data, data, n);
+ free(data);
+ ccb->data = nil;
+ }
+
+ /*
+ * If there was a check-condition, save the
+ * ccb for a possible request-sense command.
+ */
+ if(sdstat == SDcheck){
+ if(r->flags & SDnosense){
+ lock(&ctlr->cachelock);
+ if(ctlr->cache[target])
+ ccbfree(ctlr, ctlr->cache[target]);
+ ctlr->cache[target] = (Ccb*)ccb;
+ unlock(&ctlr->cachelock);
+ return SDcheck;
+ }
+ sense = &ccb->cs[ccb->cdblen];
+ n = 8+sense[7];
+ if(n > sizeof(r->sense)-1)
+ n = sizeof(r->sense)-1;
+ memmove(r->sense, sense, n);
+ r->flags |= SDvalidsense;
+ }
+ ccbfree(ctlr, (Ccb*)ccb);
+
+ if(btstat){
+ if(btstat == 0x11)
+ return SDtimeout;
+ return SDeio;
+ }
+ return sdstat;
+}
+
+static void
+mylex24interrupt(Ureg*, void* arg)
+{
+ ulong pa;
+ Ctlr *ctlr;
+ Ccb24 *ccb;
+ Mbox24 *mb, *mbox;
+ int port, rinterrupt, rstatus;
+
+ ctlr = arg;
+ port = ctlr->port;
+
+ /*
+ * Save and clear the interrupt(s). The only
+ * interrupts expected are Cmdc, which is ignored,
+ * and Imbl which means something completed.
+ * There's one spurious interrupt left over from
+ * initialisation, ignore it.
+ */
+ rinterrupt = inb(port+Rinterrupt);
+ rstatus = inb(port+Rstatus);
+ outb(port+Rcontrol, Rint);
+ if((rinterrupt & ~(Cmdc|Imbl)) != Intv && ctlr->spurious++)
+ print("%s: interrupt 0x%2.2ux\n",
+ ctlr->sdev->name, rinterrupt);
+ if((rinterrupt & Cmdc) && (rstatus & Cmdinv))
+ print("%s: command invalid\n", ctlr->sdev->name);
+
+ /*
+ * Look for something in the mail.
+ * If there is, save the status, free the mailbox
+ * and wakeup whoever.
+ */
+ mb = ctlr->mb;
+ for(mbox = &mb[ctlr->mbix]; mbox->code; mbox = &mb[ctlr->mbix]){
+ pa = (mbox->ccb[0]<<16)|(mbox->ccb[1]<<8)|mbox->ccb[2];
+ ccb = BPA2K(pa, BUSUNKNOWN);
+ mbox->code = 0;
+ ccb->done = 1;
+ wakeup(ccb);
+
+ ctlr->mbix++;
+ if(ctlr->mbix >= NMbox+NMbox)
+ ctlr->mbix = NMbox;
+ }
+}
+
+static int
+done32(void* arg)
+{
+ return ((Ccb32*)arg)->done;
+}
+
+static int
+mylex32rio(SDreq* r)
+{
+ ulong p;
+ uchar lun;
+ Ctlr *ctlr;
+ Ccb32 *ccb;
+ Mbox32 *mb;
+ int d, n, btstat, sdstat, target;
+
+ ctlr = r->unit->dev->ctlr;
+ target = r->unit->subno;
+ lun = (r->cmd[1]>>5) & 0x07;
+
+ /*
+ * Ctlr->cache holds the last completed Ccb for this target if it
+ * returned 'check condition'.
+ * If this command is a request-sense and there is valid sense data
+ * from the last completed Ccb, return it immediately.
+ */
+ lock(&ctlr->cachelock);
+ if(ccb = ctlr->cache[target]){
+ ctlr->cache[target] = nil;
+ if(r->cmd[0] == 0x03
+ && ccb->sdstat == SDcheck && lun == (ccb->luntag & 0x07)){
+ unlock(&ctlr->cachelock);
+ if(r->dlen){
+ n = 8+ccb->sense[7];
+ if(n > r->dlen)
+ n = r->dlen;
+ memmove(r->data, ccb->sense, n);
+ r->rlen = n;
+ }
+ ccbfree(ctlr, (Ccb*)ccb);
+ return SDok;
+ }
+ }
+ unlock(&ctlr->cachelock);
+ if(ccb == nil)
+ ccb = ccballoc(ctlr);
+
+ /*
+ * Fill in the ccb.
+ */
+ ccb->opcode = Ordl;
+
+ n = r->dlen;
+ if(n == 0)
+ ccb->datadir |= CCBdataout|CCBdatain;
+ else if(!r->write)
+ ccb->datadir |= CCBdatain;
+ else
+ ccb->datadir |= CCBdataout;
+
+ ccb->cdblen = r->clen;
+
+ ccb->datalen[0] = n;
+ ccb->datalen[1] = n>>8;
+ ccb->datalen[2] = n>>16;
+ ccb->datalen[3] = n>>24;
+ p = PADDR(r->data);
+ ccb->dataptr[0] = p;
+ ccb->dataptr[1] = p>>8;
+ ccb->dataptr[2] = p>>16;
+ ccb->dataptr[3] = p>>24;
+
+ ccb->targetid = target;
+ ccb->luntag = lun;
+ if(r->unit->inquiry[7] & 0x02)
+ ccb->luntag |= SQTag|TagEnable;
+ memmove(ccb->cdb, r->cmd, r->clen);
+ ccb->btstat = ccb->sdstat = 0;
+ ccb->ccbctl = 0;
+
+ /*
+ * There's one more mbox than there there is
+ * ccb so there is always one free.
+ */
+ lock(&ctlr->mboxlock);
+ mb = ctlr->mb;
+ mb += ctlr->mbox;
+ p = PADDR(ccb);
+ mb->ccb[0] = p;
+ mb->ccb[1] = p>>8;
+ mb->ccb[2] = p>>16;
+ mb->ccb[3] = p>>24;
+ mb->code = Mbostart;
+ ctlr->mbox++;
+ if(ctlr->mbox >= NMbox)
+ ctlr->mbox = 0;
+
+ /*
+ * This command does not require Hardy
+ * and doesn't generate a Cmdc interrupt.
+ */
+ ccb->done = 0;
+ outb(ctlr->port+Rcpr, Cstart);
+ unlock(&ctlr->mboxlock);
+
+ /*
+ * Wait for the request to complete and return the status.
+ * Since the buffer is not reference counted cannot return
+ * until the DMA is done writing into the buffer so the caller
+ * cannot free the buffer prematurely.
+ */
+ while(waserror())
+ ;
+ sleep(ccb, done32, ccb);
+ poperror();
+
+ /*
+ * Save the status and patch up the number of
+ * bytes actually transferred.
+ * There's a firmware bug on some 956C controllers
+ * which causes the return count from a successful
+ * READ CAPACITY not to be updated, so fix it here.
+ */
+ sdstat = ccb->sdstat;
+ btstat = ccb->btstat;
+
+ d = ccb->datalen[0];
+ d |= (ccb->datalen[1]<<8);
+ d |= (ccb->datalen[2]<<16);
+ d |= (ccb->datalen[3]<<24);
+ if(ccb->cdb[0] == 0x25 && sdstat == SDok)
+ d = 0;
+ n -= d;
+ r->rlen = n;
+
+ /*
+ * If there was a check-condition, save the
+ * ccb for a possible request-sense command.
+ */
+ if(sdstat == SDcheck){
+ if(r->flags & SDnosense){
+ lock(&ctlr->cachelock);
+ if(ctlr->cache[target])
+ ccbfree(ctlr, ctlr->cache[target]);
+ ctlr->cache[target] = (Ccb*)ccb;
+ unlock(&ctlr->cachelock);
+ return SDcheck;
+ }
+ n = 8+ccb->sense[7];
+ if(n > sizeof(r->sense)-1)
+ n = sizeof(r->sense)-1;
+ memmove(r->sense, ccb->sense, n);
+ r->flags |= SDvalidsense;
+ }
+ ccbfree(ctlr, (Ccb*)ccb);
+
+ if(btstat){
+ if(btstat == 0x11)
+ return SDtimeout;
+ return SDeio;
+ }
+ return sdstat;
+}
+
+static void
+mylex32interrupt(Ureg*, void* arg)
+{
+ ulong pa;
+ Ctlr *ctlr;
+ Ccb32 *ccb;
+ Mbox32 *mb, *mbox;
+ int port, rinterrupt, rstatus;
+
+ ctlr = arg;
+ port = ctlr->port;
+
+ /*
+ * Save and clear the interrupt(s). The only
+ * interrupts expected are Cmdc, which is ignored,
+ * and Imbl which means something completed.
+ * There's one spurious interrupt left over from
+ * initialisation, ignore it.
+ */
+ rinterrupt = inb(port+Rinterrupt);
+ rstatus = inb(port+Rstatus);
+ outb(port+Rcontrol, Rint);
+ if((rinterrupt & ~(Cmdc|Imbl)) != Intv && ctlr->spurious++)
+ print("%s: interrupt 0x%2.2ux\n",
+ ctlr->sdev->name, rinterrupt);
+ if((rinterrupt & Cmdc) && (rstatus & Cmdinv))
+ print("%s: command invalid\n", ctlr->sdev->name);
+
+ /*
+ * Look for something in the mail.
+ * If there is, free the mailbox and wakeup whoever.
+ */
+ mb = ctlr->mb;
+ for(mbox = &mb[ctlr->mbix]; mbox->code; mbox = &mb[ctlr->mbix]){
+ pa = (mbox->ccb[3]<<24)
+ |(mbox->ccb[2]<<16)
+ |(mbox->ccb[1]<<8)
+ |mbox->ccb[0];
+ if(ctlr->pcidev)
+ ccb = BPA2K(pa, ctlr->pcidev->tbdf);
+ else
+ ccb = BPA2K(pa, BUSUNKNOWN);
+ mbox->code = 0;
+ ccb->done = 1;
+ wakeup(ccb);
+
+ ctlr->mbix++;
+ if(ctlr->mbix >= NMbox+NMbox)
+ ctlr->mbix = NMbox;
+ }
+}
+
+static int
+mylexrio(SDreq* r)
+{
+ Ctlr *ctlr;
+
+ ctlr = r->unit->dev->ctlr;
+ if((r->unit->subno) == ctlr->id)
+ r->status = SDtimeout;
+ else if(ctlr->bus == 24)
+ r->status = mylex24rio(r);
+ else
+ r->status = mylex32rio(r);
+ return r->status;
+}
+
+/*
+ * Issue a command to a controller. The command and its length is
+ * contained in cmd and cmdlen. If any data is to be
+ * returned, datalen should be non-zero, and the returned data
+ * will be placed in data.
+ * If Cmdc is set, bail out, the invalid command will be handled
+ * when the interrupt is processed.
+ */
+static void
+issueio(int port, uchar* cmd, int cmdlen, uchar* data, int datalen)
+{
+ int len;
+
+ if(cmd[0] != Cstart && cmd[0] != Ceombri){
+ while(!(inb(port+Rstatus) & Hardy))
+ ;
+ }
+ outb(port+Rcpr, cmd[0]);
+
+ len = 1;
+ while(len < cmdlen){
+ if(!(inb(port+Rstatus) & Cprbsy)){
+ outb(port+Rcpr, cmd[len]);
+ len++;
+ }
+ if(inb(port+Rinterrupt) & Cmdc)
+ return;
+ }
+
+ if(datalen){
+ len = 0;
+ while(len < datalen){
+ if(inb(port+Rstatus) & Dirrdy){
+ data[len] = inb(port+Rdatain);
+ len++;
+ }
+ if(inb(port+Rinterrupt) & Cmdc)
+ return;
+ }
+ }
+}
+
+/*
+ * Issue a command to a controller, wait for it to complete then
+ * try to reset the interrupt. Should only be called at initialisation.
+ */
+static int
+issue(Ctlr* ctlr, uchar* cmd, int cmdlen, uchar* data, int datalen)
+{
+ int port;
+ uchar rinterrupt, rstatus;
+ static Lock mylexissuelock;
+
+ port = ctlr->port;
+
+ ilock(&ctlr->issuelock);
+ issueio(port, cmd, cmdlen, data, datalen);
+
+ while(!((rinterrupt = inb(port+Rinterrupt)) & Cmdc))
+ ;
+
+ rstatus = inb(port+Rstatus);
+ outb(port+Rcontrol, Rint);
+ iunlock(&ctlr->issuelock);
+
+ if((rinterrupt & Cmdc) && (rstatus & Cmdinv))
+ return 0;
+ return 1;
+}
+
+static SDev*
+mylexprobe(int port, int irq)
+{
+ SDev *sdev;
+ Ctlr *ctlr;
+ uchar cmd[6], data[256];
+ int clen, dlen, timeo;
+
+ if(ioalloc(port, 0x3, 0, "mylex") < 0)
+ return nil;
+ ctlr = nil;
+
+ /*
+ * Attempt to hard-reset the board and reset
+ * the SCSI bus. If the board state doesn't settle to
+ * idle with mailbox initialisation required, either
+ * it isn't a compatible board or it's broken.
+ * If the controller has SCAM set this can take a while.
+ */
+ outb(port+Rcontrol, Rhard|Rsbus);
+ for(timeo = 0; timeo < 100; timeo++){
+ if(inb(port+Rstatus) == (Inreq|Hardy))
+ break;
+ delay(100);
+ }
+ if(inb(port+Rstatus) != (Inreq|Hardy)){
+buggery:
+ if(ctlr != nil)
+ free(ctlr);
+ iofree(port);
+ return nil;
+ }
+
+ if((ctlr = malloc(sizeof(Ctlr))) == nil)
+ goto buggery;
+ ctlr->port = port;
+ ctlr->irq = irq;
+ ctlr->bus = 24;
+ ctlr->wide = 0;
+
+ /*
+ * Try to determine if this is a 32-bit MultiMaster controller
+ * by attempting to obtain the extended inquiry information;
+ * this command is not implemented on Adaptec 154xx
+ * controllers. If successful, the first byte of the returned
+ * data is the host adapter bus type, 'E' for 32-bit EISA,
+ * PCI and VLB buses.
+ */
+ cmd[0] = Ciesi;
+ cmd[1] = 4;
+ clen = 2;
+ dlen = 256;
+ if(issue(ctlr, cmd, clen, data, dlen)){
+ if(data[0] == 'E')
+ ctlr->bus = 32;
+ ctlr->wide = data[0x0D] & 0x01;
+ }
+ else{
+ /*
+ * Inconceivable though it may seem, a hard controller reset
+ * is necessary here to clear out the command queue. Every
+ * board seems to lock-up in a different way if you give an
+ * invalid command and then try to clear out the
+ * command/parameter and/or data-in register.
+ * Soft reset doesn't do the job either. Fortunately no
+ * serious initialisation has been done yet so there's nothing
+ * to tidy up.
+ */
+ outb(port+Rcontrol, Rhard);
+ for(timeo = 0; timeo < 100; timeo++){
+ if(inb(port+Rstatus) == (Inreq|Hardy))
+ break;
+ delay(100);
+ }
+ if(inb(port+Rstatus) != (Inreq|Hardy))
+ goto buggery;
+ }
+
+ /*
+ * If the BIOS is enabled on the AHA-1542C/CF and BIOS options for
+ * support of drives > 1Gb, dynamic scanning of the SCSI bus or more
+ * than 2 drives under DOS 5.0 are enabled, the BIOS disables
+ * accepting Cmbinit to protect against running with drivers which
+ * don't support those options. In order to unlock the interface it
+ * is necessary to read a lock-code using Cextbios and write it back
+ * using Cmbienable; the lock-code is non-zero.
+ */
+ cmd[0] = Cinquiry;
+ clen = 1;
+ dlen = 4;
+ if(issue(ctlr, cmd, clen, data, dlen) == 0)
+ goto buggery;
+ if(data[0] >= 0x43){
+ cmd[0] = Cextbios;
+ clen = 1;
+ dlen = 2;
+ if(issue(ctlr, cmd, clen, data, dlen) == 0)
+ goto buggery;
+
+ /*
+ * Lock-code returned in data[1]. If it's non-zero write
+ * it back along with bit 0 of byte 0 cleared to enable
+ * mailbox initialisation.
+ */
+ if(data[1]){
+ cmd[0] = Cmbienable;
+ cmd[1] = 0;
+ cmd[2] = data[1];
+ clen = 3;
+ if(issue(ctlr, cmd, clen, 0, 0) == 0)
+ goto buggery;
+ }
+ }
+
+ /*
+ * Get the id, DMA and IRQ info from the board. This will
+ * cause an interrupt which will hopefully not cause any
+ * trouble because the interrupt number isn't known yet.
+ * This is necessary as the DMA won't be set up if the
+ * board has the BIOS disabled.
+ *
+ * If the IRQ is already known, this must be a 32-bit PCI
+ * or EISA card, in which case the returned DMA and IRQ can
+ * be ignored.
+ */
+ cmd[0] = Cinquire;
+ clen = 1;
+ dlen = 3;
+ if(issue(ctlr, cmd, clen, data, dlen) == 0)
+ goto buggery;
+
+ ctlr->id = data[2] & 0x07;
+ if(ctlr->irq < 0){
+ switch(data[0]){ /* DMA Arbitration Priority */
+ case 0x80: /* Channel 7 */
+ outb(0xD6, 0xC3);
+ outb(0xD4, 0x03);
+ break;
+ case 0x40: /* Channel 6 */
+ outb(0xD6, 0xC2);
+ outb(0xD4, 0x02);
+ break;
+ case 0x20: /* Channel 5 */
+ outb(0xD6, 0xC1);
+ outb(0xD4, 0x01);
+ break;
+ case 0x01: /* Channel 0 */
+ outb(0x0B, 0xC0);
+ outb(0x0A, 0x00);
+ break;
+ default:
+ break;
+ }
+
+ switch(data[1]){ /* Interrupt Channel */
+ case 0x40:
+ ctlr->irq = 15;
+ break;
+ case 0x20:
+ ctlr->irq = 14;
+ break;
+ case 0x08:
+ ctlr->irq = 12;
+ break;
+ case 0x04:
+ ctlr->irq = 11;
+ break;
+ case 0x02:
+ ctlr->irq = 10;
+ break;
+ case 0x01:
+ ctlr->irq = 9;
+ break;
+ default:
+ goto buggery;
+ }
+ }
+
+ if((sdev = malloc(sizeof(SDev))) == nil)
+ goto buggery;
+ sdev->ifc = &sdmylexifc;
+ sdev->ctlr = ctlr;
+ ctlr->sdev = sdev;
+ if(!ctlr->wide)
+ sdev->nunit = 8;
+ else
+ sdev->nunit = 16;
+
+ return sdev;
+}
+
+static int mylexport[8] = {
+ 0x330, 0x334, 0x230, 0x234, 0x130, 0x134, 0x000, 0x000,
+};
+static int mylexirq[8] = {
+ 9, 10, 11, 12, 14, 15, 0, 0,
+};
+
+static SDev*
+mylexpnp(void)
+{
+ Pcidev *p;
+ Ctlr *ctlr;
+ int cfg, i, x;
+ SDev *sdev, *head, *tail;
+
+ p = nil;
+ head = tail = nil;
+ while(p = pcimatch(p, 0x104B, 0)){
+ if((sdev = mylexprobe(p->mem[0].bar & ~0x01, p->intl)) == nil)
+ continue;
+
+ ctlr = sdev->ctlr;
+ ctlr->pcidev = p;
+
+ if(head != nil)
+ tail->next = sdev;
+ else
+ head = sdev;
+ tail = sdev;
+ }
+
+ if(strncmp(KADDR(0xFFFD9), "EISA", 4))
+ return head;
+ for(cfg = 0x1000; cfg < MaxEISA*0x1000; cfg += 0x1000){
+ x = 0;
+ for(i = 0; i < 4; i++)
+ x |= inb(cfg+CfgEISA+i)<<(i*8);
+ if(x != 0x0142B30A && x != 0x0242B30A)
+ continue;
+
+ x = inb(cfg+0xC8C);
+ if((sdev = mylexprobe(mylexport[x & 0x07], -1)) == nil)
+ continue;
+
+ if(head != nil)
+ tail->next = sdev;
+ else
+ head = sdev;
+ tail = sdev;
+ }
+
+ return head;
+}
+
+static SDev*
+mylexid(SDev* sdev)
+{
+ return scsiid(sdev, &sdmylexifc);
+}
+
+static int
+mylex24enable(Ctlr* ctlr)
+{
+ ulong p;
+ Ccb24 *ccb, *ccbp;
+ uchar cmd[6], *v;
+ int len;
+
+ len = (sizeof(Mbox24)*NMbox*2)+(sizeof(Ccb24)*NCcb);
+ v = xspanalloc(len, 32, 0);
+
+ if(!PADDR24(ctlr, sizeof(Ctlr)) || !PADDR24(v, len))
+ return 0;
+
+ ctlr->mb = v;
+ v += sizeof(Mbox24)*NMbox*2;
+
+ ccb = (Ccb24*)v;
+ for(ccbp = ccb; ccbp < &ccb[NCcb]; ccbp++){
+ ccbp->ccb = ctlr->ccb;
+ ctlr->ccb = (Ccb*)ccbp;
+ }
+
+ /*
+ * Initialise the software controller and
+ * set the board scanning the mailboxes.
+ */
+ ctlr->mbix = NMbox;
+
+ cmd[0] = Cinitialise;
+ cmd[1] = NMbox;
+ p = K2BPA(ctlr->mb, BUSUNKNOWN);
+ cmd[2] = p>>16;
+ cmd[3] = p>>8;
+ cmd[4] = p;
+ len = 5;
+
+ return issue(ctlr, cmd, len, 0, 0);
+}
+
+static int
+mylex32enable(Ctlr* ctlr)
+{
+ ulong p;
+ Ccb32 *ccb, *ccbp;
+ uchar cmd[6], *v;
+
+ v = xspanalloc((sizeof(Mbox32)*NMbox*2)+(sizeof(Ccb32)*NCcb), 32, 0);
+
+ ctlr->mb = v;
+ v += sizeof(Mbox32)*NMbox*2;
+
+ ccb = (Ccb32*)v;
+ for(ccbp = ccb; ccbp < &ccb[NCcb]; ccbp++){
+ /*
+ * Fill in some stuff that doesn't change.
+ */
+ ccbp->senselen = sizeof(ccbp->sense);
+ p = PADDR(ccbp->sense);
+ ccbp->senseptr[0] = p;
+ ccbp->senseptr[1] = p>>8;
+ ccbp->senseptr[2] = p>>16;
+ ccbp->senseptr[3] = p>>24;
+
+ ccbp->ccb = ctlr->ccb;
+ ctlr->ccb = (Ccb*)ccbp;
+ }
+
+ /*
+ * Attempt wide mode setup.
+ */
+ if(ctlr->wide){
+ cmd[0] = Cwide;
+ cmd[1] = 1;
+ if(!issue(ctlr, cmd, 2, 0, 0))
+ ctlr->wide = 0;
+ }
+
+ /*
+ * Initialise the software controller and
+ * set the board scanning the mailboxes.
+ */
+ ctlr->mbix = NMbox;
+
+ cmd[0] = Ciem;
+ cmd[1] = NMbox;
+ if(ctlr->pcidev)
+ p = K2BPA(ctlr->mb, ctlr->tbdf);
+ else
+ p = K2BPA(ctlr->mb, BUSUNKNOWN);
+ cmd[2] = p;
+ cmd[3] = p>>8;
+ cmd[4] = p>>16;
+ cmd[5] = p>>24;
+
+ return issue(ctlr, cmd, 6, 0, 0);
+}
+
+static int
+mylexenable(SDev* sdev)
+{
+ int tbdf;
+ Ctlr *ctlr;
+ void (*interrupt)(Ureg*, void*);
+ char name[NAMELEN];
+
+ ctlr = sdev->ctlr;
+ if(ctlr->cache == nil){
+ if((ctlr->cache = malloc(sdev->nunit*sizeof(Ccb*))) == nil)
+ return 0;
+ }
+
+ tbdf = BUSUNKNOWN;
+ if(ctlr->bus == 32){
+ if(ctlr->pcidev){
+ tbdf = ctlr->pcidev->tbdf;
+ pcisetbme(ctlr->pcidev);
+ }
+ if(!mylex32enable(ctlr))
+ return 0;
+ interrupt = mylex32interrupt;
+ }
+ else if(mylex24enable(ctlr))
+ interrupt = mylex24interrupt;
+ else
+ return 0;
+
+ snprint(name, NAMELEN, "sd%c (%s)", sdev->idno, sdev->ifc->name);
+ intrenable(ctlr->irq, interrupt, ctlr, tbdf, name);
+
+ return 1;
+}
+
+SDifc sdmylexifc = {
+ "mylex", /* name */
+
+ mylexpnp, /* pnp */
+ nil, /* legacy */
+ mylexid, /* id */
+ mylexenable, /* enable */
+ nil, /* disable */
+
+ scsiverify, /* verify */
+ scsionline, /* online */
+ mylexrio, /* rio */
+ nil, /* rctl */
+ nil, /* wctl */
+
+ scsibio, /* bio */
+};
A pc/sdscsi.c => pc/sdscsi.c +377 -0
@@ 0,0 1,377 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "ureg.h"
+#include "../port/error.h"
+
+#include "sd.h"
+
+static int
+scsitest(SDreq* r)
+{
+ r->write = 0;
+ memset(r->cmd, 0, sizeof(r->cmd));
+ r->cmd[1] = r->lun<<5;
+ r->clen = 6;
+ r->data = nil;
+ r->dlen = 0;
+ r->flags = 0;
+
+ r->status = ~0;
+
+ return r->unit->dev->ifc->rio(r);
+}
+
+int
+scsiverify(SDunit* unit)
+{
+ SDreq *r;
+ int i, status;
+ uchar *inquiry;
+
+ if((r = malloc(sizeof(SDreq))) == nil)
+ return 0;
+ if((inquiry = sdmalloc(sizeof(unit->inquiry))) == nil){
+ free(r);
+ return 0;
+ }
+ r->unit = unit;
+ r->lun = 0; /* ??? */
+
+ memset(unit->inquiry, 0, sizeof(unit->inquiry));
+ r->write = 0;
+ r->cmd[0] = 0x12;
+ r->cmd[1] = r->lun<<5;
+ r->cmd[4] = sizeof(unit->inquiry)-1;
+ r->clen = 6;
+ r->data = inquiry;
+ r->dlen = sizeof(unit->inquiry)-1;
+ r->flags = 0;
+
+ r->status = ~0;
+ if(unit->dev->ifc->rio(r) != SDok){
+ free(r);
+ return 0;
+ }
+ memmove(unit->inquiry, inquiry, r->dlen);
+ free(inquiry);
+
+ SET(status);
+ for(i = 0; i < 3; i++){
+ while((status = scsitest(r)) == SDbusy)
+ ;
+ if(status == SDok || status != SDcheck)
+ break;
+ if(!(r->flags & SDvalidsense))
+ break;
+ if((r->sense[2] & 0x0F) != 0x02)
+ continue;
+ /*
+ * Unit is 'not ready'.
+ * If it needs an initialising command, set status
+ * so it will be spun-up below.
+ * If there's no medium, that's OK too, but don't
+ * try to spin it up.
+ */
+ if(r->sense[12] == 0x04 && r->sense[13] == 0x02){
+ status = SDok;
+ break;
+ }
+ if(r->sense[12] == 0x3A)
+ break;
+ }
+
+ if(status == SDok){
+ /*
+ * Try to ensure a direct-access device is spinning.
+ * Don't wait for completion, ignore the result.
+ */
+ if((unit->inquiry[0] & 0x1F) == 0){
+ memset(r->cmd, 0, sizeof(r->cmd));
+ r->write = 0;
+ r->cmd[0] = 0x1B;
+ r->cmd[1] = (r->lun<<5)|0x01;
+ r->cmd[4] = 1;
+ r->clen = 6;
+ r->data = nil;
+ r->dlen = 0;
+ r->flags = 0;
+
+ r->status = ~0;
+ unit->dev->ifc->rio(r);
+ }
+ }
+ free(r);
+
+ if(status == SDok || status == SDcheck)
+ return 1;
+ return 0;
+}
+
+static int
+scsirio(SDreq* r)
+{
+ /*
+ * Perform an I/O request, returning
+ * -1 failure
+ * 0 ok
+ * 1 no medium present
+ * 2 retry
+ * The contents of r may be altered so the
+ * caller should re-initialise if necesary.
+ */
+ r->status = ~0;
+ switch(r->unit->dev->ifc->rio(r)){
+ default:
+ return -1;
+ case SDcheck:
+ if(!(r->flags & SDvalidsense))
+ return -1;
+ switch(r->sense[2] & 0x0F){
+ case 0x00: /* no sense */
+ case 0x01: /* recovered error */
+ return 2;
+ case 0x06: /* check condition */
+ /*
+ * 0x28 - not ready to ready transition,
+ * medium may have changed.
+ * 0x29 - power on or some type of reset.
+ */
+ if(r->sense[12] == 0x28 && r->sense[13] == 0)
+ return 2;
+ if(r->sense[12] == 0x29)
+ return 2;
+ return -1;
+ case 0x02: /* not ready */
+ /*
+ * If no medium present, bail out.
+ * If unit is becoming ready, rather than not
+ * not ready, wait a little then poke it again. */
+ if(r->sense[12] == 0x3A)
+ return 1;
+ if(r->sense[12] != 0x04 || r->sense[13] != 0x01)
+ return -1;
+
+ while(waserror())
+ ;
+ tsleep(&r->unit->rendez, return0, 0, 500);
+ poperror();
+ scsitest(r);
+ return 2;
+ default:
+ return -1;
+ }
+ return -1;
+ case SDok:
+ return 0;
+ }
+ return -1;
+}
+
+int
+scsionline(SDunit* unit)
+{
+ SDreq *r;
+ uchar *p;
+ int ok, retries;
+
+ if((r = malloc(sizeof(SDreq))) == nil)
+ return 0;
+ if((p = sdmalloc(8)) == nil){
+ free(r);
+ return 0;
+ }
+
+ ok = 0;
+
+ r->unit = unit;
+ r->lun = 0; /* ??? */
+ for(retries = 0; retries < 10; retries++){
+ /*
+ * Read-capacity is mandatory for DA, WORM, CD-ROM and
+ * MO. It may return 'not ready' if type DA is not
+ * spun up, type MO or type CD-ROM are not loaded or just
+ * plain slow getting their act together after a reset.
+ */
+ r->write = 0;
+ memset(r->cmd, 0, sizeof(r->cmd));
+ r->cmd[0] = 0x25;
+ r->cmd[1] = r->lun<<5;
+ r->clen = 10;
+ r->data = p;
+ r->dlen = 8;
+ r->flags = 0;
+
+ r->status = ~0;
+ switch(scsirio(r)){
+ default:
+ break;
+ case 0:
+ unit->sectors = (p[0]<<24)|(p[1]<<16)|(p[2]<<8)|p[3];
+ /*
+ * Read-capacity returns the LBA of the last sector,
+ * therefore the number of sectors must be incremented.
+ */
+ unit->sectors++;
+ unit->secsize = (p[4]<<24)|(p[5]<<16)|(p[6]<<8)|p[7];
+ ok = 1;
+ break;
+ case 1:
+ ok = 1;
+ break;
+ case 2:
+ continue;
+ }
+ break;
+ }
+ free(p);
+ free(r);
+
+ return ok;
+}
+
+int
+scsiexec(SDunit* unit, int write, uchar* cmd, int clen, void* data, int* dlen)
+{
+ SDreq *r;
+ int status;
+
+ if((r = malloc(sizeof(SDreq))) == nil)
+ return SDmalloc;
+ r->unit = unit;
+ r->lun = cmd[1]>>5; /* ??? */
+ r->write = write;
+ memmove(r->cmd, cmd, clen);
+ r->clen = clen;
+ r->data = data;
+ if(dlen)
+ r->dlen = *dlen;
+ r->flags = 0;
+
+ r->status = ~0;
+
+ /*
+ * Call the device-specific I/O routine.
+ * There should be no calls to 'error()' below this
+ * which percolate back up.
+ */
+ switch(status = unit->dev->ifc->rio(r)){
+ case SDok:
+ if(dlen)
+ *dlen = r->rlen;
+ /*FALLTHROUGH*/
+ case SDcheck:
+ /*FALLTHROUGH*/
+ default:
+ /*
+ * It's more complicated than this. There are conditions
+ * which are 'ok' but for which the returned status code
+ * is not 'SDok'.
+ * Also, not all conditions require a reqsense, might
+ * need to do a reqsense here and make it available to the
+ * caller somehow.
+ *
+ * Mañana.
+ */
+ break;
+ }
+ sdfree(r);
+
+ return status;
+}
+
+long
+scsibio(SDunit* unit, int lun, int write, void* data, long nb, long bno)
+{
+ SDreq *r;
+ long rlen;
+
+ if((r = malloc(sizeof(SDreq))) == nil)
+ return SDmalloc;
+ r->unit = unit;
+ r->lun = lun;
+again:
+ r->write = write;
+ if(write == 0)
+ r->cmd[0] = 0x28;
+ else
+ r->cmd[0] = 0x2A;
+ r->cmd[1] = (lun<<5);
+ r->cmd[2] = bno>>24;
+ r->cmd[3] = bno>>16;
+ r->cmd[4] = bno>>8;
+ r->cmd[5] = bno;
+ r->cmd[6] = 0;
+ r->cmd[7] = nb>>8;
+ r->cmd[8] = nb;
+ r->cmd[9] = 0;
+ r->clen = 10;
+ r->data = data;
+ r->dlen = nb*unit->secsize;
+ r->flags = 0;
+
+ r->status = ~0;
+ switch(scsirio(r)){
+ default:
+ rlen = -1;
+ break;
+ case 0:
+ rlen = r->rlen;
+ break;
+ case 2:
+ rlen = -1;
+ if(!(r->flags & SDvalidsense))
+ break;
+ switch(r->sense[2] & 0x0F){
+ default:
+ break;
+ case 0x06: /* check condition */
+ /*
+ * Check for a removeable media change.
+ * If so, mark it and zap the geometry info
+ * to force an online request.
+ */
+ if(r->sense[12] != 0x28 || r->sense[13] != 0)
+ break;
+ if(unit->inquiry[1] & 0x80){
+ unit->changed = 1;
+ unit->sectors = 0;
+ }
+ break;
+ case 0x02: /* not ready */
+ /*
+ * If unit is becoming ready,
+ * rather than not not ready, try again.
+ */
+ if(r->sense[12] == 0x04 && r->sense[13] == 0x01)
+ goto again;
+ break;
+ }
+ break;
+ }
+ free(r);
+
+ return rlen;
+}
+
+SDev*
+scsiid(SDev* sdev, SDifc* ifc)
+{
+ static char idno[16] = "0123456789abcdef";
+ static char *p = idno;
+
+ while(sdev){
+ if(sdev->ifc == ifc){
+ sdev->idno = *p++;
+ snprint(sdev->name, NAMELEN, "sd%c", sdev->idno);
+ if(p >= &idno[sizeof(idno)])
+ break;
+ }
+ sdev = sdev->next;
+ }
+
+ return nil;
+}
M port/devsd.c => port/devsd.c +793 -353
@@ 1,461 1,901 @@
/*
- * SCSI disc driver
+ * Storage Device.
*/
-#include "u.h"
-#include "../port/lib.h"
-#include "mem.h"
-#include "dat.h"
-#include "fns.h"
-#include "../port/error.h"
-
-enum
-{
- TypeDA = 0x00, /* Direct Access */
- TypeSA = 0x01, /* Sequential Access */
- TypeWO = 0x04, /* Worm */
- TypeCD = 0x05, /* CD-ROM */
- TypeMO = 0x07, /* rewriteable Magneto-Optical */
- TYpeMC = 0x08, /* Medium Changer */
-};
-
-enum
-{
- LogNpart = 4,
- Npart = 1<<LogNpart,
- Ndisk = 32,
- Nlun = 8,
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "ureg.h"
+#include "../port/error.h"
+
+#include "sd.h"
+
+extern Dev sddevtab;
+extern SDifc* sdifc[];
+
+static QLock sdqlock;
+static SDev* sdlist;
+static SDunit** sdunit;
+static int* sdunitflg;
+static int sdnunit;
+
+enum {
+ Rawcmd,
+ Rawdata,
+ Rawstatus,
};
-#define DRIVE(qid) ((qid).path>>LogNpart)
-#define PART(qid) ((qid).path&(Npart-1))
-typedef struct Part Part;
-typedef struct Disk Disk;
+enum {
+ Qtopdir = 1, /* top level directory */
+ Qtopbase,
-struct Part
-{
- ulong beg;
- ulong end;
- char name[NAMELEN];
+ Qunitdir, /* directory per unit */
+ Qunitbase,
+ Qctl = Qunitbase,
+ Qraw,
+ Qpart,
};
-struct Disk
+#define TYPE(q) ((q).path & 0x0F)
+#define PART(q) (((q).path>>4) & 0x0F)
+#define UNIT(q) (((q).path>>8) & 0xFF)
+#define QID(u, p, t) (((u)<<8)|((p)<<4)|(t))
+
+static void
+sdaddpart(SDunit* unit, char* name, ulong start, ulong end)
{
- QLock;
- Target* t;
- int lun;
- char id[NAMELEN];
- char vol[NAMELEN];
-
- uchar* inquire;
- int partok;
- ulong version;
- ulong size;
- ulong bsize;
-
- int npart;
- Part table[Npart];
-};
+ SDpart *pp;
+ int i, partno;
-int ndisk;
-Disk disk[Ndisk];
+ /*
+ * Check name not already used
+ * and look for a free slot.
+ */
+ if(unit->part != nil){
+ partno = -1;
+ for(i = 0; i < SDnpart; i++){
+ pp = &unit->part[i];
+ if(!pp->valid){
+ if(partno == -1)
+ partno = i;
+ break;
+ }
+ if(strcmp(name, pp->name) == 0){
+ if(pp->start == start && pp->end == end)
+ return;
+ error(Ebadctl);
+ }
+ }
+ }
+ else{
+ if((unit->part = malloc(sizeof(SDpart)*SDnpart)) == nil)
+ error(Enomem);
+ partno = 0;
+ }
-static int sdrdpart(Disk*);
-static long sdio(Chan*, int, char*, ulong, vlong);
+ /*
+ * Check there is a free slot and size and extent are valid.
+ */
+ if(partno == -1 || start > end || end > unit->sectors)
+ error(Eio);
+ pp = &unit->part[partno];
+ pp->start = start;
+ pp->end = end;
+ strncpy(pp->name, name, NAMELEN);
+ strncpy(pp->user, eve, NAMELEN);
+ pp->perm = 0640;
+ pp->valid = 1;
+ unit->npart++;
+}
-static int types[] =
+static void
+sddelpart(SDunit* unit, char* name)
{
- TypeDA, TypeCD, TypeMO,
- -1,
-};
+ int i;
+ SDpart *pp;
+
+ /*
+ * Look for the partition to delete.
+ * Can't delete if someone still has it open.
+ * If it's the last valid partition zap the
+ * whole table.
+ */
+ pp = unit->part;
+ for(i = 0; i < SDnpart; i++){
+ if(strncmp(name, pp->name, NAMELEN) == 0)
+ break;
+ pp++;
+ }
+ if(i >= SDnpart)
+ error(Ebadctl);
+ if(pp->nopen)
+ error(Einuse);
+ pp->valid = 0;
+
+ unit->npart--;
+ if(unit->npart == 0){
+ free(unit->part);
+ unit->part = nil;
+ }
+}
static int
-sdgen(Chan *c, Dirtab*, int, int s, Dir *dirp)
+sdinitpart(SDunit* unit)
{
- Qid qid;
- Disk *d;
- Part *p;
- int unit;
- char name[2*NAMELEN];
- vlong l;
+ int nf;
+ ulong start, end;
+ char *f[4], *p, *q;
+
+ unit->sectors = unit->secsize = 0;
+ unit->npart = 0;
+ if(unit->part){
+ free(unit->part);
+ unit->part = nil;
+ }
- if(s == DEVDOTDOT){
- devdir(c, qid, "#w", 0, eve, 0555, dirp);
- return 1;
+ if(unit->inquiry[0] & 0xC0)
+ return 0;
+ switch(unit->inquiry[0] & 0x1F){
+ case 0x00: /* DA */
+ case 0x04: /* WORM */
+ case 0x05: /* CD-ROM */
+ case 0x07: /* MO */
+ break;
+ default:
+ return 0;
}
- d = disk;
- while(s >= d->npart) {
- s -= d->npart;
- d++;
+ if(unit->dev->ifc->online)
+ unit->dev->ifc->online(unit);
+ if(unit->sectors){
+ sdaddpart(unit, "data", 0, unit->sectors);
+
+ /*
+ * Use partitions passed from boot program,
+ * e.g.
+ * sdC0=dos 63 123123/plan9 123123 456456
+ */
+ for(p = getconf(unit->name); p != nil; p = q){
+ if(q = strchr(p, '/'))
+ *q++ = '\0';
+ nf = getfields(p, f, nelem(f), 1, " \t\r");
+ if(nf < 3)
+ continue;
+
+ start = strtoul(f[1], 0, 0);
+ end = strtoul(f[2], 0, 0);
+ if(!waserror()){
+ sdaddpart(unit, f[0], start, end);
+ poperror();
+ }
+ }
}
- unit = d - disk;
- if(unit > ndisk)
- return -1;
- p = d->table+s;
- sprint(name, "%s%s", d->vol, p->name);
- name[NAMELEN-1] = '\0';
- qid = (Qid){(unit<<LogNpart)+s, 0};
- l = (p->end - p->beg) * (vlong)d->bsize;
- devdir(c, qid, name, l, eve, 0666, dirp);
return 1;
}
-static void
-sdinit(void)
+static SDunit*
+sdgetunit(SDev* sdev, int subno)
{
- Disk *d;
- uchar *scratch, type;
- int dev, i, nbytes;
-
- dev = 0;
- scratch = scsialloc(0xFF);
- for(;;) {
- d = &disk[ndisk];
- dev = scsiinv(dev, types, &d->t, &d->inquire, d->id);
- if(dev < 0)
- break;
+ int index;
+ SDunit *unit;
+ /*
+ * Associate a unit with a given device and sub-unit
+ * number on that device.
+ * The device will be probed if it has not already been
+ * successfully accessed.
+ */
+ qlock(&sdqlock);
+ index = sdev->index+subno;
+ unit = sdunit[index];
+ if(unit == nil){
/*
- * Search for all the lun's.
+ * Probe the unit only once. This decision
+ * may be a little severe and reviewed later.
*/
- for(i = 0; i < Nlun; i++) {
- d->lun = i;
+ if(sdunitflg[index]){
+ qunlock(&sdqlock);
+ return nil;
+ }
+ if((unit = malloc(sizeof(SDunit))) == nil){
+ qunlock(&sdqlock);
+ return nil;
+ }
+ sdunitflg[index] = 1;
- /*
- * A SCSI target does not support a lun if the
- * the peripheral device type and qualifier fields
- * in the response to an inquiry command are 0x7F.
- */
- nbytes = 0xFF;
- memset(scratch, 0, nbytes);
+ if(sdev->enabled == 0 && sdev->ifc->enable)
+ sdev->ifc->enable(sdev);
+ sdev->enabled = 1;
- if(scsiinquiry(d->t, d->lun, scratch, &nbytes) != STok)
- continue;
- if(scratch[0] == 0x7F)
- continue;
- type = scratch[0] & 0x1F;
+ snprint(unit->name, NAMELEN, "%s%d", sdev->name, subno);
+ unit->subno = subno;
+ unit->dev = sdev;
- /*
- * Read-capacity is mandatory for TypeDA, TypeMO and
- * TypeCD. It may return 'not ready' if TypeDA is not
- * spun up, TypeMO or TypeCD are not loaded or just
- * plain slow getting their act together after a reset.
- * If 'not ready' comes back, try starting a TypeDA and
- * punt the get capacity until the drive is attached.
- */
- if(scsicap(d->t, d->lun, &d->size, &d->bsize) != STok) {
- nbytes = 0xFF;
- memset(scratch, 0, nbytes);
- scsireqsense(d->t, 0, scratch, &nbytes, 1);
- switch(scratch[2] & 0x0F){
-
- case 0x00:
- case 0x01:
- case 0x02:
- break;
- case 0x06:
- if(scratch[12] == 0x28 && !scratch[13])
- break;
- if(scratch[12] == 0x29 && !scratch[13])
- break;
- /*FALLTHROUGH*/
- default:
- continue;
- }
- if(type == TypeDA)
- scsistart(d->t, d->lun, 0);
- d->size = d->bsize = 0;
- }
+ /*
+ * No need to lock anything here as this is only
+ * called before the unit is made available in the
+ * sdunit[] array.
+ */
+ if(unit->dev->ifc->verify(unit) == 0){
+ qunlock(&sdqlock);
+ free(unit);
+ return nil;
+ }
+ sdunit[index] = unit;
+ }
+ qunlock(&sdqlock);
- switch(type){
+ return unit;
+}
- case TypeDA:
- case TypeMO:
- sprint(d->vol, "sd%d", ndisk);
- break;
+static SDunit*
+sdindex2unit(int index)
+{
+ SDev *sdev;
- case TypeCD:
- sprint(d->vol, "cd%d", ndisk);
- break;
+ /*
+ * Associate a unit with a given index into the top-level
+ * device directory.
+ * The device will be probed if it has not already been
+ * successfully accessed.
+ */
+ for(sdev = sdlist; sdev != nil; sdev = sdev->next){
+ if(index >= sdev->index && index < sdev->index+sdev->nunit)
+ return sdgetunit(sdev, index-sdev->index);
+ }
- default:
- continue;
- }
+ return nil;
- if(++ndisk >= Ndisk)
- break;
- d++;
- d->t = d[-1].t;
- d->inquire = d[-1].inquire;
- strcpy(d->id, d[-1].id);
+}
+
+static void
+sdreset(void)
+{
+ int i;
+ SDev *sdev, *tail;
+
+ /*
+ * Probe all configured controllers and make a list
+ * of devices found, accumulating a possible maximum number
+ * of units attached and marking each device with an index
+ * into the linear top-level directory array of units.
+ */
+ tail = nil;
+ for(i = 0; sdifc[i] != nil; i++){
+ if(sdifc[i]->pnp == nil || (sdev = sdifc[i]->pnp()) == nil)
+ continue;
+ if(sdlist != nil)
+ tail->next = sdev;
+ else
+ sdlist = sdev;
+ for(tail = sdev; tail->next != nil; tail = tail->next){
+ sdev->index = sdnunit;
+ sdnunit += tail->nunit;
}
+ tail->index = sdnunit;
+ sdnunit += tail->nunit;
+ }
+
+ /*
+ * Legacy and option code goes here. This will be hard...
+ */
+
+ /*
+ * The maximum number of possible units is known, allocate
+ * placeholders for their datastructures; the units will be
+ * probed and structures allocated when attached.
+ * Allocate controller names for the different types.
+ */
+ if(sdnunit == 0)
+ return;
+ if((sdunit = malloc(sdnunit*sizeof(SDunit*))) == nil)
+ return;
+ if((sdunitflg = malloc(sdnunit*sizeof(int))) == nil){
+ free(sdunit);
+ sdunit = nil;
+ return;
+ }
+ for(i = 0; sdifc[i] != nil; i++){
+ if(sdifc[i]->id)
+ sdifc[i]->id(sdlist);
+ }
+}
+
+static int
+sd2gen(Chan* c, int i, Dir* dp)
+{
+ Qid q;
+ vlong l;
+ SDpart *pp;
+ SDunit *unit;
+
+ switch(i){
+ case Qctl:
+ q = (Qid){QID(UNIT(c->qid), PART(c->qid), Qctl), c->qid.vers};
+ devdir(c, q, "ctl", 0, eve, 0640, dp);
+ return 1;
+ case Qraw:
+ q = (Qid){QID(UNIT(c->qid), PART(c->qid), Qraw), c->qid.vers};
+ devdir(c, q, "raw", 0, eve, 0600, dp);
+ return 1;
+ case Qpart:
+ unit = sdunit[UNIT(c->qid)];
+ if(unit->changed)
+ break;
+ pp = &unit->part[PART(c->qid)];
+ l = (pp->end - pp->start) * (vlong)unit->secsize;
+ q = (Qid){QID(UNIT(c->qid), PART(c->qid), Qpart), c->qid.vers};
+ devdir(c, q, pp->name, l, pp->user, pp->perm, dp);
+ return 1;
+ }
+ return -1;
+}
- if(ndisk >= Ndisk) {
- print("devsd: configure more disks\n");
+static int
+sd1gen(Chan*, int i, Dir*)
+{
+ switch(i){
+ default:
+ return -1;
+ }
+ return -1;
+}
+
+static int
+sdgen(Chan* c, Dirtab*, int, int s, Dir* dp)
+{
+ Qid q;
+ vlong l;
+ int i, r;
+ SDpart *pp;
+ SDunit *unit;
+ char name[NAMELEN];
+
+ switch(TYPE(c->qid)){
+ case Qtopdir:
+ if(s == DEVDOTDOT){
+ q = (Qid){QID(0, 0, Qtopdir)|CHDIR, 0};
+ snprint(name, NAMELEN, "#%C", sddevtab.dc);
+ devdir(c, q, name, 0, eve, 0555, dp);
+ return 1;
+ }
+ if(s < sdnunit){
+ if(sdunit[s] == nil && sdindex2unit(s) == nil)
+ return 0;
+ q = (Qid){QID(s, 0, Qunitdir)|CHDIR, 0};
+ devdir(c, q, sdunit[s]->name, 0, eve, 0555, dp);
+ return 1;
+ }
+ s -= sdnunit;
+ return sd1gen(c, s+Qtopbase, dp);
+ case Qunitdir:
+ if(s == DEVDOTDOT){
+ q = (Qid){QID(0, 0, Qtopdir)|CHDIR, 0};
+ snprint(name, NAMELEN, "#%C", sddevtab.dc);
+ devdir(c, q, name, 0, eve, 0555, dp);
+ return 1;
+ }
+ unit = sdunit[UNIT(c->qid)];
+ qlock(&unit->ctl);
+ if(!unit->changed && unit->sectors == 0)
+ sdinitpart(unit);
+ i = s+Qunitbase;
+ if(i < Qpart){
+ r = sd2gen(c, i, dp);
+ qunlock(&unit->ctl);
+ return r;
+ }
+ i -= Qpart;
+ if(unit->npart == 0 || i >= SDnpart){
+ qunlock(&unit->ctl);
break;
}
+ pp = &unit->part[i];
+ if(unit->changed || !pp->valid){
+ qunlock(&unit->ctl);
+ return 0;
+ }
+ l = (pp->end - pp->start) * (vlong)unit->secsize;
+ q = (Qid){QID(UNIT(c->qid), i, Qpart), c->qid.vers};
+ devdir(c, q, pp->name, l, pp->user, pp->perm, dp);
+ qunlock(&unit->ctl);
+ return 1;
+ case Qraw:
+ case Qctl:
+ case Qpart:
+ unit = sdunit[UNIT(c->qid)];
+ qlock(&unit->ctl);
+ r = sd2gen(c, TYPE(c->qid), dp);
+ qunlock(&unit->ctl);
+ return r;
+ default:
+ break;
}
- scsifree(scratch);
+
+ return -1;
}
static Chan*
-sdattach(char *spec)
+sdattach(char* spec)
{
- int i;
+ Chan *c;
+ char *p;
+ SDev *sdev;
+ int idno, subno;
+
+ if(sdnunit == 0 || *spec == '\0'){
+ c = devattach(sddevtab.dc, spec);
+ c->qid = (Qid){QID(0, 0, Qtopdir)|CHDIR, 0};
+ return c;
+ }
- for(i = 0; i < ndisk; i++){
- qlock(&disk[i]);
- if(disk[i].partok == 0)
- sdrdpart(&disk[i]);
- qunlock(&disk[i]);
+ if(spec[0] != 's' || spec[1] != 'd')
+ error(Ebadspec);
+ idno = spec[2];
+ subno = strtol(&spec[3], &p, 0);
+ if(p == &spec[3])
+ error(Ebadspec);
+ for(sdev = sdlist; sdev != nil; sdev = sdev->next){
+ if(sdev->idno == idno && subno < sdev->nunit)
+ break;
}
+ if(sdev == nil || sdgetunit(sdev, subno) == nil)
+ error(Enonexist);
- return devattach('w', spec);
+ c = devattach(sddevtab.dc, spec);
+ c->qid = (Qid){QID(sdev->index+subno, 0, Qunitdir)|CHDIR, 0};
+ c->dev = sdev->index+subno;
+ return c;
+}
+
+static Chan*
+sdclone(Chan* c, Chan* nc)
+{
+ return devclone(c, nc);
}
static int
-sdwalk(Chan *c, char *name)
+sdwalk(Chan* c, char* name)
{
- return devwalk(c, name, 0, 0, sdgen);
+ return devwalk(c, name, nil, 0, sdgen);
}
static void
-sdstat(Chan *c, char *db)
+sdstat(Chan* c, char* db)
{
- devstat(c, db, 0, 0, sdgen);
+ devstat(c, db, nil, 0, sdgen);
}
static Chan*
-sdopen(Chan *c, int omode)
+sdopen(Chan* c, int omode)
{
- return devopen(c, omode, 0, 0, sdgen);
+ SDpart *pp;
+ SDunit *unit;
+
+ c = devopen(c, omode, 0, 0, sdgen);
+ switch(TYPE(c->qid)){
+ default:
+ break;
+ case Qpart:
+ unit = sdunit[UNIT(c->qid)];
+ qlock(&unit->ctl);
+ if(waserror()){
+ qunlock(&unit->ctl);
+ c->flag &= ~COPEN;
+ nexterror();
+ }
+ if(unit->changed)
+ error(Eio);
+ pp = &unit->part[PART(c->qid)];
+ pp->nopen++;
+ unit->nopen++;
+ qunlock(&unit->ctl);
+ poperror();
+ break;
+ }
+ return c;
}
static void
-sdclose(Chan *c)
+sdclose(Chan* c)
{
- Disk *d;
- Part *p;
+ SDpart *pp;
+ SDunit *unit;
if(c->qid.path & CHDIR)
return;
+ if(!(c->flag & COPEN))
+ return;
- d = &disk[DRIVE(c->qid)];
- p = &d->table[PART(c->qid)];
- if((c->mode&3) != OREAD && strcmp(p->name, "partition") == 0){
- qlock(d);
- d->partok = 0;
- sdrdpart(d);
- qunlock(d);
+ switch(TYPE(c->qid)){
+ default:
+ break;
+ case Qraw:
+ unit = sdunit[UNIT(c->qid)];
+ if(canqlock(&unit->raw) || unit->pid == up->pid){
+ unit->pid = 0;
+ unit->state = Rawcmd;
+ qunlock(&unit->raw);
+ }
+ break;
+ case Qpart:
+ unit = sdunit[UNIT(c->qid)];
+ qlock(&unit->ctl);
+ if(waserror()){
+ qunlock(&unit->ctl);
+ c->flag &= ~COPEN;
+ nexterror();
+ }
+ pp = &unit->part[PART(c->qid)];
+ pp->nopen--;
+ unit->nopen--;
+ if(unit->nopen == 0)
+ unit->changed = 0;
+ qunlock(&unit->ctl);
+ poperror();
+ break;
}
}
static long
-sdread(Chan *c, void *a, long n, vlong off)
+sdbio(Chan* c, int write, char* a, long len, vlong off)
{
+ long l;
+ uchar *b;
+ SDpart *pp;
+ SDunit *unit;
+ ulong bno, max, nb, offset;
- if(c->qid.path & CHDIR)
- return devdirread(c, a, n, 0, 0, sdgen);
+ unit = sdunit[UNIT(c->qid)];
- return sdio(c, 0, a, n, off);
-}
+ qlock(&unit->ctl);
+ if(waserror()){
+ qunlock(&unit->ctl);
+ nexterror();
+ }
+ if(unit->changed)
+ error(Eio);
-static long
-sdwrite(Chan *c, void *a, long n, vlong off)
-{
- Disk *d;
+ /*
+ * Check the request is within bounds.
+ * Removeable drives are locked throughout the I/O
+ * in case the media changes unexpectedly.
+ * Non-removeable drives are not locked during the I/O
+ * to allow the hardware to optimise if it can; this is
+ * a little fast and loose.
+ * It's assumed that non-removeable media parameters
+ * (sectors, secsize) can't change once the drive has
+ * been brought online.
+ */
+ pp = &unit->part[PART(c->qid)];
+ bno = (off/unit->secsize) + pp->start;
+ nb = ((off+len+unit->secsize-1)/unit->secsize) + pp->start - bno;
+ max = SDmaxio/unit->secsize;
+ if(nb > max)
+ nb = max;
+ if(bno+nb > pp->end)
+ nb = pp->end - bno;
+ if(bno >= pp->end || nb == 0){
+ if(write)
+ error(Eio);
+ qunlock(&unit->ctl);
+ poperror();
+ return 0;
+ }
+ if(!(unit->inquiry[1] & 0x80)){
+ qunlock(&unit->ctl);
+ poperror();
+ }
- d = &disk[DRIVE(c->qid)];
+ b = sdmalloc(nb*unit->secsize);
+ if(b == nil)
+ error(Enomem);
+ if(waserror()){
+ sdfree(b);
+ nexterror();
+ }
- if((d->inquire[0] & 0x1F) == TypeCD)
- error(Eperm);
- return sdio(c, 1, a, n, off);
-}
+ offset = off%unit->secsize;
+ if(write){
+ if(offset || (len%unit->secsize)){
+ if((l = unit->dev->ifc->bio(unit, 0, 0, b, nb, bno)) < 0)
+ error(Eio);
+ if(l < (nb*unit->secsize)){
+ nb = l/unit->secsize;
+ l = nb*unit->secsize - offset;
+ if(len > l)
+ len = l;
+ }
+ }
+ memmove(b+offset, a, len);
+ if((l = unit->dev->ifc->bio(unit, 0, 1, b, nb, bno)) < 0)
+ error(Eio);
+ if(l < offset)
+ len = 0;
+ else if(len > l - offset)
+ len = l - offset;
+ }
+ else {
+ if((l = unit->dev->ifc->bio(unit, 0, 0, b, nb, bno)) < 0)
+ error(Eio);
+ if(l < offset)
+ len = 0;
+ else if(len > l - offset)
+ len = l - offset;
+ memmove(a, b+offset, len);
+ }
+ sdfree(b);
+ poperror();
-Dev sddevtab = {
- 'w',
- "sd",
+ if(unit->inquiry[1] & 0x80){
+ qunlock(&unit->ctl);
+ poperror();
+ }
- devreset,
- sdinit,
- sdattach,
- devclone,
- sdwalk,
- sdstat,
- sdopen,
- devcreate,
- sdclose,
- sdread,
- devbread,
- sdwrite,
- devbwrite,
- devremove,
- devwstat,
-};
+ return len;
+}
-static int
-sdrdpart(Disk *d)
+static long
+sdrio(SDreq* r, void* a, long n)
{
- Part *p;
- int n, i;
- char *b, *line[Npart+2], *field[3];
- static char MAGIC[] = "plan9 partitions";
+ void *data;
- if(d->partok)
- return STok;
+ if(n >= SDmaxio || n < 0)
+ error(Etoobig);
- p = d->table;
- strcpy(p->name, "disk");
- p->beg = 0;
- p->end = 0;
- d->npart = 1;
+ data = nil;
+ if(n){
+ if((data = sdmalloc(n)) == nil)
+ error(Enomem);
+ if(r->write)
+ memmove(data, a, n);
+ }
+ r->data = data;
+ r->dlen = n;
- /*
- * If the drive wasn't ready when a read-capacity was tried
- * earlier (in sdinit()), try again.
- */
- if(d->size == 0 || d->bsize == 0){
- n = scsicap(d->t, d->lun, &d->size, &d->bsize);
- if(n != STok){
- d->size = d->bsize = 0;
- return scsierrstr(n);
+ if(waserror()){
+ if(data != nil){
+ sdfree(data);
+ r->data = nil;
}
+ nexterror();
}
- p->end = d->size + 1;
+ if(r->unit->dev->ifc->rio(r) != SDok)
+ error(Eio);
- if((d->inquire[0] & 0x1F) == TypeCD)
- return STok;
+ if(!r->write && r->rlen > 0)
+ memmove(a, data, r->rlen);
+ if(data != nil){
+ sdfree(data);
+ r->data = nil;
+ }
+ poperror();
- b = scsialloc(d->bsize);
- if(b == 0)
- return scsierrstr(STnomem);
+ return r->rlen;
+}
- p++;
- strcpy(p->name, "partition");
- p->beg = d->table[0].end - 2;
- p->end = d->table[0].end - 1;
- p++;
- d->npart = 2;
+static long
+sdread(Chan *c, void *a, long n, vlong off)
+{
+ char *p;
+ SDpart *pp;
+ SDunit *unit;
+ ulong offset;
+ int i, l, status;
- /*
- * Read second last sector from disk, null terminate.
- * The last sector used to hold the partition tables.
- * However, this sector is special on some PC's so we've
- * started to use the second last sector as the partition
- * table instead. To avoid reconfiguring all our old systems
- * we still check if there is a valid partition table in
- * the last sector if none is found in the second last.
- */
- scsibio(d->t, d->lun, SCSIread, b, 1, d->bsize, d->table[0].end-2);
- b[d->bsize-1] = '\0';
- n = getfields(b, line, nelem(line), 1, "\n");
- if(n <= 0 || strncmp(line[0], MAGIC, sizeof(MAGIC)-1) != 0){
- /* try the last */
- scsibio(d->t, d->lun, SCSIread, b, 1, d->bsize, d->table[0].end-1);
- b[d->bsize-1] = '\0';
- n = getfields(b, line, nelem(line), 1, "\n");
- /* only point partition file at last sector if there is one there */
- if(n > 0 && strncmp(line[0], MAGIC, sizeof(MAGIC)-1) == 0){
- d->table[1].beg++;
- d->table[1].end++;
+ offset = off;
+ switch(TYPE(c->qid)){
+ default:
+ error(Eperm);
+ case Qtopdir:
+ case Qunitdir:
+ return devdirread(c, a, n, 0, 0, sdgen);
+ case Qctl:
+ unit = sdunit[UNIT(c->qid)];
+ p = malloc(READSTR);
+ l = snprint(p, READSTR, "inquiry %.48s\n",
+ (char*)unit->inquiry+8);
+ qlock(&unit->ctl);
+ if(!unit->changed && unit->sectors){
+ /*
+ * If there's a device specific routine it must
+ * provide all information pertaining to night geometry
+ * and the garscadden trains.
+ */
+ if(unit->dev->ifc->rctl)
+ l += unit->dev->ifc->rctl(unit, p+l, READSTR-l);
+ else
+ l += snprint(p+l, READSTR-l,
+ "geometry %ld %ld\n",
+ unit->sectors, unit->secsize);
+ pp = unit->part;
+ for(i = 0; i < SDnpart; i++){
+ if(pp->valid)
+ l += snprint(p+l, READSTR-l,
+ "part %.*s %lud %lud\n",
+ NAMELEN, pp->name,
+ pp->start, pp->end);
+ pp++;
+ }
}
- }
-
- /*
- * parse partition table.
- */
- if(n > 0 && strncmp(line[0], MAGIC, sizeof(MAGIC)-1) == 0) {
- for(i = 1; i < n; i++) {
- if(getfields(line[i], field, nelem(field), 1, " ") != 3)
- break;
- if(p >= &d->table[Npart])
- break;
- strncpy(p->name, field[0], NAMELEN);
- p->beg = strtoul(field[1], 0, 0);
- p->end = strtoul(field[2], 0, 0);
- if(p->beg > p->end || p->beg >= d->table[0].end)
- break;
- p++;
+ qunlock(&unit->ctl);
+ l = readstr(offset, a, n, p);
+ free(p);
+ return l;
+ case Qraw:
+ unit = sdunit[UNIT(c->qid)];
+ if(canqlock(&unit->raw)){
+ qunlock(&unit->raw);
+ error(Ebadusefd);
+ }
+ if(unit->pid != up->pid)
+ error(Eperm);
+ if(unit->state == Rawdata){
+ unit->state = Rawstatus;
+ return sdrio(unit->req, a, n);
}
+ else if(unit->state == Rawstatus){
+ status = unit->req->status;
+ unit->pid = 0;
+ unit->state = Rawcmd;
+ free(unit->req);
+ unit->req = nil;
+ qunlock(&unit->raw);
+ return readnum(0, a, n, status, NUMSIZE);
+ }
+ break;
+ case Qpart:
+ return sdbio(c, 0, a, n, off);
}
- d->npart = p - d->table;
- scsifree(b);
-
- d->partok = 1;
- return STok;
+ return 0;
}
static long
-sdio(Chan *c, int write, char *a, ulong len, vlong off)
+sdwrite(Chan *c, void *a, long n, vlong off)
{
- Disk *d;
- Part *p;
- uchar *b;
- ulong block, n, max, x;
- ulong offset;
-
- d = &disk[DRIVE(c->qid)];
- p = &d->table[PART(c->qid)];
-
- block = (off / d->bsize) + p->beg;
- n = (off + len + d->bsize - 1) / d->bsize + p->beg - block;
- max = SCSImaxxfer / d->bsize;
- if(n > max)
- n = max;
- if(block + n > p->end)
- n = p->end - block;
- if(block >= p->end || n == 0)
- return 0;
+ Cmdbuf *cb;
+ SDreq *req;
+ SDunit *unit;
+ ulong end, start;
- b = scsialloc(n*d->bsize);
- if(b == 0)
- return scsierrstr(STnomem);
-
- offset = off % d->bsize;
- if(write) {
- if(offset || len % d->bsize) {
- x = scsibio(d->t, d->lun, SCSIread, b, n, d->bsize, block);
- if(x < 0){
- len = -1;
- goto buggery;
+ switch(TYPE(c->qid)){
+ default:
+ error(Eperm);
+ case Qctl:
+ cb = parsecmd(a, n);
+ unit = sdunit[UNIT(c->qid)];
+
+ qlock(&unit->ctl);
+ if(waserror()){
+ qunlock(&unit->ctl);
+ free(cb);
+ nexterror();
+ }
+ if(unit->changed)
+ error(Eio);
+
+ if(cb->nf < 1)
+ error(Ebadctl);
+ if(strcmp(cb->f[0], "part") == 0){
+ if(cb->nf != 4 || unit->npart >= SDnpart)
+ error(Ebadctl);
+ if(unit->sectors == 0 && !sdinitpart(unit))
+ error(Eio);
+ start = strtoul(cb->f[2], 0, 0);
+ end = strtoul(cb->f[3], 0, 0);
+ sdaddpart(unit, cb->f[1], start, end);
+ }
+ else if(strcmp(cb->f[0], "delpart") == 0){
+ if(cb->nf != 2 || unit->part == nil)
+ error(Ebadctl);
+ sddelpart(unit, cb->f[1]);
+ }
+ else if(unit->dev->ifc->wctl)
+ unit->dev->ifc->wctl(unit, cb);
+ else
+ error(Ebadctl);
+ qunlock(&unit->ctl);
+ poperror();
+ free(cb);
+ break;
+ case Qraw:
+ unit = sdunit[UNIT(c->qid)];
+ if(canqlock(&unit->raw)){
+ if(unit->state != Rawcmd){
+ qunlock(&unit->raw);
+ error(Ebadusefd);
+ }
+ if(n < 6 || n > sizeof(req->cmd)){
+ qunlock(&unit->raw);
+ error(Ebadarg);
}
- if(x < n * d->bsize) {
- n = x / d->bsize;
- x = n * d->bsize - offset;
- if(len > x)
- len = x;
+ if((req = malloc(sizeof(SDreq))) == nil){
+ qunlock(&unit->raw);
+ error(Enomem);
}
+ req->unit = unit;
+ memmove(req->cmd, a, n);
+ req->clen = n;
+ req->flags = SDnosense;
+ req->status = ~0;
+
+ unit->req = req;
+ unit->pid = up->pid;
+ unit->state = Rawdata;
}
- memmove(b + offset, a, len);
- x = scsibio(d->t, d->lun, SCSIwrite, b, n, d->bsize, block);
- if(x < 0){
- len = -1;
- goto buggery;
+ else{
+ if(unit->pid != up->pid)
+ error(Eperm);
+ if(unit->state != Rawdata)
+ error(Ebadusefd);
+ unit->state = Rawstatus;
+
+ unit->req->write = 1;
+ return sdrio(unit->req, a, n);
}
- if(x < offset)
- len = 0;
- else
- if(len > x - offset)
- len = x - offset;
+ break;
+ case Qpart:
+ return sdbio(c, 1, a, n, off);
}
- else {
- x = scsibio(d->t, d->lun, SCSIread, b, n, d->bsize, block);
- if(x < 0){
- len = -1;
- goto buggery;
- }
- if(x < offset)
- len = 0;
- else
- if(len > x - offset)
- len = x - offset;
- memmove(a, b+offset, len);
+
+ return n;
+}
+
+static void
+sdwstat(Chan* c, char* dp)
+{
+ Dir d;
+ SDpart *pp;
+ SDunit *unit;
+
+ if((c->qid.path & CHDIR) || TYPE(c->qid) != Qpart)
+ error(Eperm);
+
+ unit = sdunit[UNIT(c->qid)];
+ qlock(&unit->ctl);
+ if(waserror()){
+ qunlock(&unit->ctl);
+ nexterror();
}
-buggery:
- scsifree(b);
- return len;
+ if(unit->changed)
+ error(Enonexist);
+ pp = &unit->part[PART(c->qid)];
+ if(!pp->valid)
+ error(Enonexist);
+ if(strcmp(up->user, pp->user) && !iseve())
+ error(Eperm);
+
+ convM2D(dp, &d);
+ strncpy(pp->user, d.uid, NAMELEN);
+ pp->perm = d.mode&0777;
+
+ qunlock(&unit->ctl);
+ poperror();
}
+
+Dev sddevtab = {
+ 'S',
+ "sd",
+
+ sdreset,
+ devinit,
+ sdattach,
+ sdclone,
+ sdwalk,
+ sdstat,
+ sdopen,
+ devcreate,
+ sdclose,
+ sdread,
+ devbread,
+ sdwrite,
+ devbwrite,
+ devremove,
+ sdwstat,
+};
M port/portdat.h => port/portdat.h +0 -23
@@ 682,29 682,6 @@ enum
AUTHLEN = 8,
};
-/* Things scsi */
-enum
-{
- STnomem =-4, /* buffer allocation failed */
- STtimeout =-3, /* bus timeout */
- STownid =-2, /* playing with myself */
- STharderr =-1, /* controller error of some kind */
- STok = 0, /* good */
- STcheck = 0x02, /* check condition */
- STcondmet = 0x04, /* condition met/good */
- STbusy = 0x08, /* busy */
- STintok = 0x10, /* intermediate/good */
- STintcondmet = 0x14, /* intermediate/condition met/good */
- STresconf = 0x18, /* reservation conflict */
- STterminated = 0x22, /* command terminated */
- STqfull = 0x28, /* queue full */
-
- SCSIread = 0,
- SCSIwrite,
-
- SCSImaxxfer = 2048*1024,
-};
-
enum
{
LRESPROF = 3,
M port/portfns.h => port/portfns.h +0 -14
@@ 127,7 127,6 @@ long ibrk(ulong, int);
void ilock(Lock*);
void iunlock(Lock*);
int incref(Ref*);
-void initscsi(void);
void initseg(void);
void isdir(Chan*);
int iseve(void);
@@ 258,7 257,6 @@ void ready(Proc*);
void relocateseg(Segment*, ulong);
void renameuser(char*, char*);
void resched(char*);
-void resetscsi(void);
void resrcwait(char*);
int return0(void*);
void rlock(RWlock*);
@@ 272,18 270,6 @@ void sched(void);
void scheddump(void);
void schedinit(void);
int screenbits(void);
-#define scsialloc(n) mallocz((n)+512, 0)
-int scsibio(Target*, int, int, void*, long, long, long);
-int scsicap(Target*, int, ulong*, ulong*);
-int scsierrstr(int);
-int scsiexec(Target*, int, uchar*, int, void*, int*);
-#define scsifree(p) free(p)
-int scsiinquiry(Target*, int, void*, int*);
-int scsiinv(int, int*, Target**, uchar**, char*);
-int scsireqsense(Target*, int, void*, int*, int);
-int scsistart(Target*, int, int);
-int scsitest(Target*, int);
-Target* scsiunit(int, int);
long seconds(void);
ulong segattach(Proc*, ulong, char *, ulong, ulong);
void segclock(ulong);
A port/sd.h => port/sd.h +115 -0
@@ 0,0 1,115 @@
+/*
+ * Storage Device.
+ */
+typedef struct SDev SDev;
+typedef struct SDifc SDifc;
+typedef struct SDpart SDpart;
+typedef struct SDreq SDreq;
+typedef struct SDunit SDunit;
+
+typedef struct SDpart {
+ ulong start;
+ ulong end;
+ char name[NAMELEN];
+ char user[NAMELEN];
+ ulong perm;
+ int valid;
+ int nopen; /* of this partition */
+} SDpart;
+
+typedef struct SDunit {
+ SDev* dev;
+ int subno;
+ uchar inquiry[256]; /* format follows SCSI spec */
+ char name[NAMELEN];
+ Rendez rendez;
+
+ QLock ctl;
+ ulong sectors;
+ ulong secsize;
+ SDpart* part;
+ int npart; /* of valid partitions */
+ int nopen; /* of partitions on this unit */
+ int changed;
+
+ QLock raw;
+ int state;
+ ulong pid;
+ SDreq* req;
+} SDunit;
+
+typedef struct SDev {
+ SDifc* ifc; /* pnp/legacy */
+ void *ctlr;
+ int idno;
+ char name[NAMELEN];
+ int index; /* into unit space */
+ int nunit;
+ SDev* next;
+
+ QLock; /* enable/disable */
+ int enabled;
+} SDev;
+
+typedef struct SDifc {
+ char* name;
+
+ SDev* (*pnp)(void);
+ SDev* (*legacy)(int, int);
+ SDev* (*id)(SDev*);
+ int (*enable)(SDev*);
+ int (*disable)(SDev*);
+
+ int (*verify)(SDunit*);
+ int (*online)(SDunit*);
+ int (*rio)(SDreq*);
+ int (*rctl)(SDunit*, char*, int);
+ int (*wctl)(SDunit*, Cmdbuf*);
+
+ long (*bio)(SDunit*, int, int, void*, long, long);
+} SDifc;
+
+typedef struct SDreq {
+ SDunit* unit;
+ int lun;
+ int write;
+ uchar cmd[16];
+ int clen;
+ void* data;
+ int dlen;
+
+ int flags;
+
+ int status;
+ long rlen;
+ uchar sense[256];
+} SDreq;
+
+enum {
+ SDnosense = 0x00000001,
+ SDvalidsense = 0x00010000,
+};
+
+enum {
+ SDmalloc = -4,
+ SDeio = -3,
+ SDtimeout = -2,
+ SDnostatus = -1,
+
+ SDok = 0,
+
+ SDcheck = 0x02, /* check condition */
+ SDbusy = 0x08, /* busy */
+
+ SDmaxio = 2048*1024,
+ SDnpart = 16,
+};
+
+#define sdmalloc(n) malloc(n)
+#define sdfree(p) free(p)
+
+/* sdscsi.c */
+extern int scsiverify(SDunit*);
+extern int scsionline(SDunit*);
+extern long scsibio(SDunit*, int, int, void*, long, long);
+extern SDev* scsiid(SDev*, SDifc*);