ref: c2fc2fad133d51bc7dc86af015a20aed11a1817f
parent: ae00ac74659e69a1aee9dc3e3ab20d5ec70b8126
author: cinap_lenrek <cinap_lenrek@localhost>
date: Sun Jul 10 10:14:23 EDT 2011
merge sd changes from 9atom
--- /dev/null
+++ b/sys/include/fis.h
@@ -1,0 +1,163 @@
+#pragma lib "libfis.a"
+#pragma src "/sys/src/libfis"
+
+/* ata errors */
+enum {
+ Emed = 1<<0, /* media error */
+ Enm = 1<<1, /* no media */
+ Eabrt = 1<<2, /* abort */
+ Emcr = 1<<3, /* media change request */
+ Eidnf = 1<<4, /* no user-accessible address */
+ Emc = 1<<5, /* media change */
+ Eunc = 1<<6, /* data error */
+ Ewp = 1<<6, /* write protect */
+ Eicrc = 1<<7, /* interface crc error */
+
+ Efatal = Eidnf|Eicrc, /* must sw reset */
+};
+
+/* ata status */
+enum {
+ ASerr = 1<<0, /* error */
+ ASdrq = 1<<3, /* request */
+ ASdf = 1<<5, /* fault */
+ ASdrdy = 1<<6, /* ready */
+ ASbsy = 1<<7, /* busy */
+
+ ASobs = 1<<1|1<<2|1<<4,
+};
+
+enum {
+ /* fis types */
+ H2dev = 0x27,
+ D2host = 0x34,
+
+ /* fis flags bits */
+ Fiscmd = 0x80,
+
+ /* ata bits */
+ Ataobs = 0xa0,
+ Atalba = 0x40,
+
+ /* nominal fis size (fits any fis) */
+ Fissize = 0x20,
+};
+
+/* sata device-to-host (0x27) fis layout */
+enum {
+ Ftype,
+ Fflags,
+ Fcmd,
+ Ffeat,
+ Flba0,
+ Flba8,
+ Flba16,
+ Fdev,
+ Flba24,
+ Flba32,
+ Flba40,
+ Ffeat8,
+ Fsc,
+ Fsc8,
+ Ficc, /* isochronous cmd completion */
+ Fcontrol,
+};
+
+/* sata host-to-device fis (0x34) differences */
+enum{
+ Fioport = 1,
+ Fstatus,
+ Frerror,
+};
+
+/* ata protcol type */
+enum{
+ Pnd = 0<<0, /* data direction */
+ Pin = 1<<0,
+ Pout = 2<<0,
+ Pdatam = 3<<0,
+
+ Ppio = 1<<2, /* ata protocol */
+ Pdma = 2<<2,
+ Pdmq = 3<<2,
+ Preset = 4<<2,
+ Pdiag = 5<<2,
+ Ppkt = 6<<2,
+ Pprotom = 7<<2,
+
+ P48 = 0<<5, /* command “size” */
+ P28 = 1<<5,
+ Pcmdszm = 1<<5,
+
+ Pssn = 0<<6, /* sector size */
+ P512 = 1<<6,
+ Pssm = 1<<6,
+};
+
+typedef struct Sfis Sfis;
+struct Sfis {
+ ushort feat;
+ uchar udma;
+ uchar speeds;
+ uint sig;
+ uint lsectsz;
+ uint physshift; /* log2(log/phys) */
+ uint c; /* disgusting, no? */
+ uint h;
+ uint s;
+};
+
+enum {
+ Dlba = 1<<0, /* required for sata */
+ Dllba = 1<<1,
+ Dsmart = 1<<2,
+ Dpower = 1<<3,
+ Dnop = 1<<4,
+ Datapi = 1<<5,
+ Datapi16= 1<<6,
+ Data8 = 1<<7,
+ Dsct = 1<<8,
+ Dnflag = 9,
+};
+
+enum {
+ Pspinup = 1<<0,
+ Pidready = 1<<1,
+};
+
+void setfissig(Sfis*, uint);
+int txmodefis(Sfis*, uchar*, uchar);
+int atapirwfis(Sfis*, uchar*, uchar*, int, int);
+int featfis(Sfis*, uchar*, uchar);
+int flushcachefis(Sfis*, uchar*);
+int identifyfis(Sfis*, uchar*);
+int nopfis(Sfis*, uchar*, int);
+int rwfis(Sfis*, uchar*, int, int, uvlong);
+void skelfis(uchar*);
+void sigtofis(Sfis*, uchar*);
+uvlong fisrw(Sfis*, uchar*, int*);
+
+void idmove(char*, ushort*, int);
+vlong idfeat(Sfis*, ushort*);
+uvlong idwwn(Sfis*, ushort*);
+int idss(Sfis*, ushort*);
+int idpuis(ushort*);
+ushort id16(ushort*, int);
+uint id32(ushort*, int);
+uvlong id64(ushort*, int);
+char *pflag(char*, char*, Sfis*);
+uint fistosig(uchar*);
+
+/* scsi */
+typedef struct Cfis Cfis;
+struct Cfis {
+ uchar phyid;
+ uchar encid[8];
+ uchar tsasaddr[8];
+ uchar ssasaddr[8];
+ uchar ict[2];
+};
+
+void smpskelframe(Cfis*, uchar*, int);
+uint sashash(uvlong);
+uchar *sasbhash(uchar*, uchar*);
--- a/sys/src/9/pc/ahci.h
+++ b/sys/src/9/pc/ahci.h
@@ -1,34 +1,8 @@
/*
* advanced host controller interface (sata)
- * © 2007 coraid, inc
+ * © 2007-9 coraid, inc
*/
-/* ata errors */
-enum {
- Emed = 1<<0, /* media error */
- Enm = 1<<1, /* no media */
- Eabrt = 1<<2, /* abort */
- Emcr = 1<<3, /* media change request */
- Eidnf = 1<<4, /* no user-accessible address */
- Emc = 1<<5, /* media change */
- Eunc = 1<<6, /* data error */
- Ewp = 1<<6, /* write protect */
- Eicrc = 1<<7, /* interface crc error */
-
- Efatal = Eidnf|Eicrc, /* must sw reset */
-};
-
-/* ata status */
-enum {
- ASerr = 1<<0, /* error */
- ASdrq = 1<<3, /* request */
- ASdf = 1<<5, /* fault */
- ASdrdy = 1<<6, /* ready */
- ASbsy = 1<<7, /* busy */
-
- ASobs = 1<<1|1<<2|1<<4,
-};
-
/* pci configuration */
enum {
Abar = 5,
@@ -47,19 +21,18 @@
/* cap bits: supported features */
enum {
- Hs64a = 1<<31, /* 64-bit addressing */
- Hsncq = 1<<30, /* ncq */
- Hssntf = 1<<29, /* snotification reg. */
- Hsmps = 1<<28, /* mech pres switch */
- Hsss = 1<<27, /* staggered spinup */
- Hsalp = 1<<26, /* aggressive link pm */
- Hsal = 1<<25, /* activity led */
- Hsclo = 1<<24, /* command-list override */
+ H64a = 1<<31, /* 64-bit addressing */
+ Hncq = 1<<30, /* ncq */
+ Hsntf = 1<<29, /* snotification reg. */
+ Hmps = 1<<28, /* mech pres switch */
+ Hss = 1<<27, /* staggered spinup */
+ Halp = 1<<26, /* aggressive link pm */
+ Hal = 1<<25, /* activity led */
+ Hclo = 1<<24, /* command-list override */
Hiss = 1<<20, /* for interface speed */
-// Hsnzo = 1<<19,
- Hsam = 1<<18, /* ahci-mode only */
- Hspm = 1<<17, /* port multiplier */
-// Hfbss = 1<<16,
+ Ham = 1<<18, /* ahci-mode only */
+ Hpm = 1<<17, /* port multiplier */
+ Hfbs = 1<<16, /* fis-based switching */
Hpmb = 1<<15, /* multiple-block pio */
Hssc = 1<<14, /* slumber state */
Hpsc = 1<<13, /* partial-slumber state */
@@ -66,7 +39,7 @@
Hncs = 1<<8, /* n command slots */
Hcccs = 1<<7, /* coal */
Hems = 1<<6, /* enclosure mgmt. */
- Hsxs = 1<<5, /* external sata */
+ Hxs = 1<<5, /* external sata */
Hnp = 1<<0, /* n ports */
};
@@ -77,6 +50,29 @@
Hhr = 1<<0, /* hba reset */
};
+/* cap2 bits */
+enum {
+ Apts = 1<<2, /* automatic partial to slumber */
+ Nvmp = 1<<1, /* nvmhci present; nvram */
+ Boh = 1<<0, /* bios/os handoff supported */
+};
+
+/* emctl bits */
+enum {
+ Pm = 1<<27, /* port multiplier support */
+ Alhd = 1<<26, /* activity led hardware driven */
+ Xonly = 1<<25, /* rx messages not supported */
+ Smb = 1<<24, /* single msg buffer; rx limited */
+ Esgpio = 1<<19, /* sgpio messages supported */
+ Eses2 = 1<<18, /* ses-2 supported */
+ Esafte = 1<<17, /* saf-te supported */
+ Elmt = 1<<16, /* led msg types support */
+ Emrst = 1<<9, /* reset all em logic */
+ Tmsg = 1<<8, /* transmit message */
+ Mr = 1<<0, /* message rx'd */
+ Emtype = Esgpio | Eses2 | Esafte | Elmt,
+};
+
typedef struct {
ulong cap;
ulong ghc;
@@ -87,6 +83,8 @@
ulong cccports;
ulong emloc;
ulong emctl;
+ ulong cap2;
+ ulong bios;
} Ahba;
enum {
@@ -147,6 +145,8 @@
Aalpe = 1<<26, /* aggressive link pm enable */
Adlae = 1<<25, /* drive led on atapi */
Aatapi = 1<<24, /* device is atapi */
+ Apste = 1<<23, /* automatic slumber to partial cap */
+ Afbsc = 1<<22, /* fis-based switching capable */
Aesp = 1<<21, /* external sata port */
Acpd = 1<<20, /* cold presence detect */
Ampsp = 1<<19, /* mechanical pres. */
@@ -164,6 +164,7 @@
Ast = 1<<0, /* start */
Arun = Ast|Acr|Afre|Afr,
+ Apwr = Apod|Asud,
};
/* ctl register bits */
@@ -173,13 +174,41 @@
Adet = 1<<0, /* device detection */
};
+/* sstatus register bits */
+enum{
+ /* sstatus det */
+ Smissing = 0<<0,
+ Spresent = 1<<0,
+ Sphylink = 3<<0,
+ Sbist = 4<<0,
+ Smask = 7<<0,
+
+ /* sstatus speed */
+ Gmissing = 0<<4,
+ Gi = 1<<4,
+ Gii = 2<<4,
+ Giii = 3<<4,
+ Gmask = 7<<4,
+
+ /* sstatus ipm */
+ Imissing = 0<<8,
+ Iactive = 1<<8,
+ Isleepy = 2<<8,
+ Islumber = 6<<8,
+ Imask = 7<<8,
+
+ SImask = Smask | Imask,
+ SSmask = Smask | Isleepy,
+};
+
#define sstatus scr0
#define sctl scr2
#define serror scr1
#define sactive scr3
+#define ntf scr4
typedef struct {
- ulong list; /* PxCLB must be 1kb aligned. */
+ ulong list; /* PxCLB must be 1kb aligned */
ulong listhi;
ulong fis; /* 256-byte aligned */
ulong fishi;
@@ -194,9 +223,10 @@
ulong scr1;
ulong scr3;
ulong ci; /* command issue */
- ulong ntf;
- uchar res2[8];
- ulong vendor;
+ ulong scr4;
+ ulong fbs;
+ ulong res2[11];
+ ulong vendor[4];
} Aport;
/* in host's memory; not memory mapped */
@@ -244,26 +274,49 @@
Aprdt prdt;
} Actab;
+/* enclosure message header */
enum {
- Ferror = 1,
- Fdone = 2,
+ Mled = 0,
+ Msafte = 1,
+ Mses2 = 2,
+ Msgpio = 3,
};
+typedef struct {
+ uchar dummy;
+ uchar msize;
+ uchar dsize;
+ uchar type;
+ uchar hba; /* bits 0:4 are the port */
+ uchar pm;
+ uchar led[2];
+} Aledmsg;
+
enum {
- Dllba = 1,
- Dsmart = 1<<1,
- Dpower = 1<<2,
- Dnop = 1<<3,
- Datapi = 1<<4,
- Datapi16= 1<<5,
+ Aled = 1<<0,
+ Locled = 1<<3,
+ Errled = 1<<6,
+
+ Ledoff = 0,
+ Ledon = 1,
};
typedef struct {
+ uint encsz;
+ ulong *enctx;
+ ulong *encrx;
+} Aenc;
+
+enum {
+ Ferror = 1,
+ Fdone = 2,
+};
+
+typedef struct {
QLock;
Rendez;
uchar flag;
- uchar feat;
- uchar smart;
+ Sfis;
Afis fis;
Alist *list;
Actab *ctab;
--- a/sys/src/9/pc/mkfile
+++ b/sys/src/9/pc/mkfile
@@ -67,6 +67,7 @@
/$objtype/lib/libc.a\
/$objtype/lib/libsec.a\
/$objtype/lib/libmp.a\
+ /$objtype/lib/libfis.a\
ETHER=`{echo devether.c ether*.c | sed 's/\.c/.'$O'/g'}
VGA=`{echo devvga.c screen.c vga*.c | sed 's/\.c/.'$O'/g'}
--- a/sys/src/9/pc/pccpuf
+++ b/sys/src/9/pc/pccpuf
@@ -75,11 +75,13 @@
uarti8250
uartpci pci
- sdata pci sdscsi
+ sdaoe
+ sdide pci sdscsi
sd53c8xx pci sdscsi
sdmylex pci sdscsi
- sdiahci pci sdscsi
- sdaoe
+ sdiahci pci sdscsi led
+ sdodin pci sdscsi led
+ sdloop
vga3dfx +cur
vgaark2000pv +cur
--- a/sys/src/9/pc/pcf
+++ b/sys/src/9/pc/pcf
@@ -81,11 +81,13 @@
archmp mp apic
mtrr
- sdata pci sdscsi
+ sdaoe
+ sdide pci sdscsi
sd53c8xx pci sdscsi
sdmylex pci sdscsi
- sdiahci pci sdscsi
- sdaoe
+ sdiahci pci sdscsi led
+ sdodin pci sdscsi led
+ sdloop
uarti8250
uartpci pci
--- a/sys/src/9/pc/sdata.c
+++ /dev/null
@@ -1,2312 +1,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"
-
-#include "../port/sd.h"
-
-#define HOWMANY(x, y) (((x)+((y)-1))/(y))
-#define ROUNDUP(x, y) (HOWMANY((x), (y))*(y))
-
-extern SDifc sdataifc;
-
-enum {
- DbgCONFIG = 0x0001, /* detected drive config info */
- DbgIDENTIFY = 0x0002, /* detected drive identify info */
- DbgSTATE = 0x0004, /* dump state on panic */
- DbgPROBE = 0x0008, /* trace device probing */
- DbgDEBUG = 0x0080, /* the current problem... */
- DbgINL = 0x0100, /* That Inil20+ message we hate */
- Dbg48BIT = 0x0200, /* 48-bit LBA */
- DbgBsy = 0x0400, /* interrupt but Bsy (shared IRQ) */
-};
-#define DEBUG (DbgDEBUG|DbgSTATE)
-
-enum { /* I/O ports */
- Data = 0,
- Error = 1, /* (read) */
- Features = 1, /* (write) */
- Count = 2, /* sector count<7-0>, sector count<15-8> */
- Ir = 2, /* interrupt reason (PACKET) */
- Sector = 3, /* sector number */
- Lbalo = 3, /* LBA<7-0>, LBA<31-24> */
- Cyllo = 4, /* cylinder low */
- Bytelo = 4, /* byte count low (PACKET) */
- Lbamid = 4, /* LBA<15-8>, LBA<39-32> */
- Cylhi = 5, /* cylinder high */
- Bytehi = 5, /* byte count hi (PACKET) */
- Lbahi = 5, /* LBA<23-16>, LBA<47-40> */
- Dh = 6, /* Device/Head, LBA<27-24> */
- 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 */
- Crs48 = 0x24, /* Read Sectors Ext */
- Crd48 = 0x25, /* Read w/ DMA Ext */
- Crdq48 = 0x26, /* Read w/ DMA Queued Ext */
- Crsm48 = 0x29, /* Read Multiple Ext */
- Cws = 0x30, /* Write Sectors */
- Cws48 = 0x34, /* Write Sectors Ext */
- Cwd48 = 0x35, /* Write w/ DMA Ext */
- Cwdq48 = 0x36, /* Write w/ DMA Queued Ext */
- Cwsm48 = 0x39, /* Write Multiple Ext */
- Cedd = 0x90, /* Execute Device Diagnostics */
- Cpkt = 0xA0, /* Packet */
- Cidpkt = 0xA1, /* Identify Packet Device */
- Crsm = 0xC4, /* Read Multiple */
- Cwsm = 0xC5, /* Write Multiple */
- Csm = 0xC6, /* Set Multiple */
- Crdq = 0xC7, /* Read DMA queued */
- Crd = 0xC8, /* Read DMA */
- Cwd = 0xCA, /* Write DMA */
- Cwdq = 0xCC, /* Write DMA queued */
- Cstandby = 0xE2, /* Standby */
- Cid = 0xEC, /* Identify Device */
- Csf = 0xEF, /* Set Features */
-};
-
-enum { /* Device Control */
- Nien = 0x02, /* (not) Interrupt Enable */
- Srst = 0x04, /* Software Reset */
- Hob = 0x80, /* High Order Bit [sic] */
-};
-
-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, /* End of Transfer */
-};
-
-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 */
- Ilba = 60, /* 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 */
- Icsfs = 82, /* command set/feature supported */
- Icsfe = 85, /* command set/feature enabled */
- Iudma = 88, /* ultra DMA mode */
- Ierase = 89, /* time for security erase */
- Ieerase = 90, /* time for enhanced security erase */
- Ipower = 91, /* current advanced power management */
- Ilba48 = 100, /* 48-bit LBA size (64 bits in 100-103) */
- Irmsn = 127, /* removable status notification */
- Isecstat = 128, /* security status */
- Icfapwr = 160, /* CFA power mode */
- Imediaserial = 176, /* current media serial number */
- Icksum = 255, /* checksum */
-};
-
-enum { /* bit masks for config identify info */
- Mpktsz = 0x0003, /* packet command size */
- Mincomplete = 0x0004, /* incomplete information */
- Mdrq = 0x0060, /* DRQ type */
- Mrmdev = 0x0080, /* device is removable */
- Mtype = 0x1F00, /* device type */
- Mproto = 0x8000, /* command protocol */
-};
-
-enum { /* bit masks for capabilities identify info */
- Mdma = 0x0100, /* DMA supported */
- Mlba = 0x0200, /* LBA supported */
- Mnoiordy = 0x0400, /* IORDY may be disabled */
- Miordy = 0x0800, /* IORDY supported */
- Msoftrst = 0x1000, /* needs soft reset when Bsy */
- Mstdby = 0x2000, /* standby supported */
- Mqueueing = 0x4000, /* queueing overlap supported */
- Midma = 0x8000, /* interleaved DMA supported */
-};
-
-enum { /* bit masks for supported/enabled features */
- Msmart = 0x0001,
- Msecurity = 0x0002,
- Mrmmedia = 0x0004,
- Mpwrmgmt = 0x0008,
- Mpkt = 0x0010,
- Mwcache = 0x0020,
- Mlookahead = 0x0040,
- Mrelirq = 0x0080,
- Msvcirq = 0x0100,
- Mreset = 0x0200,
- Mprotected = 0x0400,
- Mwbuf = 0x1000,
- Mrbuf = 0x2000,
- Mnop = 0x4000,
- Mmicrocode = 0x0001,
- Mqueued = 0x0002,
- Mcfa = 0x0004,
- Mapm = 0x0008,
- Mnotify = 0x0010,
- Mstandby = 0x0020,
- Mspinup = 0x0040,
- Mmaxsec = 0x0100,
- Mautoacoustic = 0x0200,
- Maddr48 = 0x0400,
- Mdevconfov = 0x0800,
- Mflush = 0x1000,
- Mflush48 = 0x2000,
- Msmarterror = 0x0001,
- Msmartselftest = 0x0002,
- Mmserial = 0x0004,
- Mmpassthru = 0x0008,
- Mlogging = 0x0020,
-};
-
-typedef struct Ctlr Ctlr;
-typedef struct Drive Drive;
-
-typedef struct Prd { /* Physical Region Descriptor */
- ulong pa; /* Physical Base Address */
- int count;
-} Prd;
-
-enum {
- BMspan = 64*1024, /* must be power of 2 <= 64*1024 */
-
- Nprd = SDmaxio/BMspan+2,
-};
-
-typedef struct Ctlr {
- int cmdport;
- int ctlport;
- int irq;
- int tbdf;
- int bmiba; /* bus master interface base address */
- int maxio; /* sector count transfer maximum */
- int span; /* don't span this boundary with dma */
-
- Pcidev* pcidev;
- void (*ienable)(Ctlr*);
- void (*idisable)(Ctlr*);
- SDev* sdev;
-
- Drive* drive[2];
-
- Prd* prdt; /* physical region descriptor table */
-
- QLock; /* current command */
- Drive* curdrive;
- int command; /* last command issued (debugging) */
- 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 */
- vlong sectors; /* total */
- int secsize; /* sector size */
-
- int dma; /* DMA R/W possible */
- int dmactl;
- int rwm; /* read/write multiple possible */
- int rwmctl;
-
- int pkt; /* PACKET device, length of pktcmd */
- uchar pktcmd[16];
- int pktdma; /* this PACKET command using dma */
-
- 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 block; /* R/W bytes per block */
- int status;
- int error;
- int flags; /* internal flags */
-} Drive;
-
-enum { /* internal flags */
- Lba48 = 0x1, /* LBA48 mode */
- Lba48always = 0x2, /* ... */
-};
-enum {
- Last28 = (1<<28) - 1 - 1, /* all-ones mask is not addressible */
-};
-
-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 void
-atadumpstate(Drive* drive, uchar* cmd, vlong lba, int count)
-{
- Prd *prd;
- Pcidev *p;
- Ctlr *ctlr;
- int i, bmiba;
-
- if(!(DEBUG & DbgSTATE)){
- USED(drive, cmd, lba, count);
- return;
- }
-
- ctlr = drive->ctlr;
- print("sdata: command %2.2uX\n", ctlr->command);
- print("data %8.8p limit %8.8p dlen %d status %uX error %uX\n",
- drive->data, drive->limit, drive->dlen,
- drive->status, drive->error);
- if(cmd != nil){
- print("lba %d -> %lld, count %d -> %d (%d)\n",
- (cmd[2]<<24)|(cmd[3]<<16)|(cmd[4]<<8)|cmd[5], lba,
- (cmd[7]<<8)|cmd[8], count, drive->count);
- }
- if(!(inb(ctlr->ctlport+As) & Bsy)){
- for(i = 1; i < 7; i++)
- print(" 0x%2.2uX", inb(ctlr->cmdport+i));
- print(" 0x%2.2uX\n", inb(ctlr->ctlport+As));
- }
- if(drive->command == Cwd || drive->command == Crd){
- bmiba = ctlr->bmiba;
- prd = ctlr->prdt;
- print("bmicx %2.2uX bmisx %2.2uX prdt %8.8p\n",
- inb(bmiba+Bmicx), inb(bmiba+Bmisx), prd);
- for(;;){
- print("pa 0x%8.8luX count %8.8uX\n",
- prd->pa, prd->count);
- if(prd->count & PrdEOT)
- break;
- prd++;
- }
- }
- if(ctlr->pcidev && ctlr->pcidev->vid == 0x8086){
- p = ctlr->pcidev;
- print("0x40: %4.4uX 0x42: %4.4uX",
- pcicfgr16(p, 0x40), pcicfgr16(p, 0x42));
- print("0x48: %2.2uX\n", pcicfgr8(p, 0x48));
- print("0x4A: %4.4uX\n", pcicfgr16(p, 0x4A));
- }
-}
-
-static int
-atadebug(int cmdport, int ctlport, char* fmt, ...)
-{
- int i, n;
- va_list arg;
- char buf[PRINTSIZE];
-
- if(!(DEBUG & DbgPROBE)){
- USED(cmdport, ctlport, fmt);
- return 0;
- }
-
- va_start(arg, fmt);
- n = vseprint(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 = Features; i < Command; 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 if necessary 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(as & reset){
- /* nothing to do */
- }
- else if(dev){
- outb(cmdport+Dh, dev);
- dev = 0;
- }
- else 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
-atacsf(Drive* drive, vlong csf, int supported)
-{
- ushort *info;
- int cmdset, i, x;
-
- if(supported)
- info = &drive->info[Icsfs];
- else
- info = &drive->info[Icsfe];
-
- for(i = 0; i < 3; i++){
- x = (csf>>(16*i)) & 0xFFFF;
- if(x == 0)
- continue;
- cmdset = info[i];
- if(cmdset == 0 || cmdset == 0xFFFF)
- return 0;
- return cmdset & x;
- }
-
- return 0;
-}
-*/
-
-static int
-atadone(void* arg)
-{
- return ((Ctlr*)arg)->done;
-}
-
-static int
-atarwmmode(Drive* drive, int cmdport, int ctlport, int dev)
-{
- int as, maxrwm, rwm;
-
- maxrwm = (drive->info[Imaxrwm] & 0xFF);
- if(maxrwm == 0)
- return 0;
-
- /*
- * Sometimes drives come up with the current count set
- * to 0; if so, set a suitable value, otherwise believe
- * the value in Irwm if the 0x100 bit is set.
- */
- if(drive->info[Irwm] & 0x100)
- rwm = (drive->info[Irwm] & 0xFF);
- else
- rwm = 0;
- if(rwm == 0)
- rwm = maxrwm;
- if(rwm > 16)
- rwm = 16;
- if(ataready(cmdport, ctlport, dev, Bsy|Drq, Drdy, 102*1000) < 0)
- return 0;
- outb(cmdport+Count, rwm);
- outb(cmdport+Command, Csm);
- microdelay(1);
- as = ataready(cmdport, ctlport, 0, Bsy, Drdy|Df|Err, 1000);
- inb(cmdport+Status);
- if(as < 0 || (as & (Df|Err)))
- return 0;
-
- drive->rwm = rwm;
-
- return rwm;
-}
-
-static int
-atadmamode(Drive* drive)
-{
- int dma;
-
- /*
- * Check if any DMA mode enabled.
- * Assumes the BIOS has picked and enabled the best.
- * This is completely passive at the moment, no attempt is
- * made to ensure the hardware is correctly set up.
- */
- dma = drive->info[Imwdma] & 0x0707;
- drive->dma = (dma>>8) & dma;
- if(drive->dma == 0 && (drive->info[Ivalid] & 0x04)){
- dma = drive->info[Iudma] & 0x7F7F;
- drive->dma = (dma>>8) & dma;
- if(drive->dma)
- drive->dma |= 'U'<<16;
- }
- if(!getconf("*nodma"))
- drive->dmactl = drive->dma;
- return dma;
-}
-
-static int
-ataidentify(int cmdport, int ctlport, int dev, int pkt, void* info)
-{
- int as, command, drdy;
-
- if(pkt){
- command = Cidpkt;
- drdy = 0;
- }
- else{
- command = Cid;
- drdy = Drdy;
- }
- as = ataready(cmdport, ctlport, dev, Bsy|Drq, drdy, 103*1000);
- if(as < 0)
- return as;
- outb(cmdport+Command, command);
- microdelay(1);
-
- as = ataready(cmdport, ctlport, 0, Bsy, Drq|Err, 400*1000);
- if(as < 0)
- return -1;
- if(as & Err)
- return as;
-
- memset(info, 0, 512);
- inss(cmdport+Data, info, 256);
- inb(cmdport+Status);
-
- if(DEBUG & DbgIDENTIFY){
- int i;
- ushort *sp;
-
- sp = (ushort*)info;
- for(i = 0; i < 256; i++){
- if(i && (i%16) == 0)
- print("\n");
- print(" %4.4uX", *sp);
- sp++;
- }
- print("\n");
- }
-
- return 0;
-}
-
-static Drive*
-atadrive(int cmdport, int ctlport, int dev)
-{
- Drive *drive;
- int as, i, pkt;
- uchar buf[512], *p;
- ushort iconfig, *sp;
-
- atadebug(0, 0, "identify: port 0x%uX dev 0x%2.2uX\n", cmdport, dev);
- pkt = 1;
-retry:
- as = ataidentify(cmdport, ctlport, dev, pkt, buf);
- if(as < 0)
- return nil;
- if(as & Err){
- if(pkt == 0)
- return nil;
- pkt = 0;
- goto retry;
- }
-
- 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++;
- }
-
- drive->secsize = 512;
-
- /*
- * Beware the CompactFlash Association feature set.
- * Now, why this value in Iconfig just walks all over the bit
- * definitions used in the other parts of the ATA/ATAPI standards
- * is a mystery and a sign of true stupidity on someone's part.
- * Anyway, the standard says if this value is 0x848A then it's
- * CompactFlash and it's NOT a packet device.
- */
- iconfig = drive->info[Iconfig];
- if(iconfig != 0x848A && (iconfig & 0xC000) == 0x8000){
- if(iconfig & 0x01)
- drive->pkt = 16;
- else
- drive->pkt = 12;
- }
- else{
- if(drive->info[Ivalid] & 0x0001){
- drive->c = drive->info[Iccyl];
- drive->h = drive->info[Ichead];
- drive->s = drive->info[Icsec];
- }
- else{
- drive->c = drive->info[Ilcyl];
- drive->h = drive->info[Ilhead];
- drive->s = drive->info[Ilsec];
- }
- if(drive->info[Icapabilities] & Mlba){
- if(drive->info[Icsfs+1] & Maddr48){
- drive->sectors = drive->info[Ilba48]
- | (drive->info[Ilba48+1]<<16)
- | ((vlong)drive->info[Ilba48+2]<<32);
- drive->flags |= Lba48;
- }
- else{
- drive->sectors = (drive->info[Ilba+1]<<16)
- |drive->info[Ilba];
- }
- drive->dev |= Lba;
- }
- else
- drive->sectors = drive->c*drive->h*drive->s;
- atarwmmode(drive, cmdport, ctlport, dev);
- }
- atadmamode(drive);
-
- if(DEBUG & DbgCONFIG){
- print("dev %2.2uX port %uX config %4.4uX capabilities %4.4uX",
- dev, cmdport, iconfig, drive->info[Icapabilities]);
- print(" mwdma %4.4uX", drive->info[Imwdma]);
- if(drive->info[Ivalid] & 0x04)
- print(" udma %4.4uX", drive->info[Iudma]);
- print(" dma %8.8uX rwm %ud", drive->dma, drive->rwm);
- if(drive->flags&Lba48)
- print("\tLLBA sectors %lld", drive->sectors);
- print("\n");
- }
-
- 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;
- static int nonlegacy = 'C';
-
- if(ioalloc(cmdport, 8, 0, "atacmd") < 0) {
- print("ataprobe: Cannot allocate %X\n", cmdport);
- return nil;
- }
- if(ioalloc(ctlport+As, 1, 0, "atactl") < 0){
- print("ataprobe: Cannot allocate %X\n", ctlport + As);
- 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);
- microdelay(1);
-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, 105*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, 106*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 = atadrive(cmdport, ctlport, dev)) == nil)
- goto release;
- if((ctlr = malloc(sizeof(Ctlr))) == nil){
- free(drive);
- goto release;
- }
- memset(ctlr, 0, sizeof(Ctlr));
- if((sdev = malloc(sizeof(SDev))) == nil){
- free(ctlr);
- free(drive);
- goto release;
- }
- memset(sdev, 0, sizeof(SDev));
- 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 = atadrive(cmdport, ctlport, Dev1);
- if(drive != nil){
- drive->ctlr = ctlr;
- ctlr->drive[1] = drive;
- }
- else{
- outb(cmdport+Dh, Dev0);
- microdelay(1);
- }
- }
- }
- else
- ctlr->drive[1] = drive;
-
- ctlr->cmdport = cmdport;
- ctlr->ctlport = ctlport;
- ctlr->irq = irq;
- ctlr->tbdf = BUSUNKNOWN;
- ctlr->command = Cedd; /* debugging */
-
- switch(cmdport){
- default:
- sdev->idno = nonlegacy;
- break;
- case 0x1F0:
- sdev->idno = 'C';
- nonlegacy = 'E';
- break;
- case 0x170:
- sdev->idno = 'D';
- nonlegacy = 'E';
- break;
- }
- sdev->ifc = &sdataifc;
- sdev->ctlr = ctlr;
- sdev->nunit = 2;
- ctlr->sdev = sdev;
-
- return sdev;
-}
-
-static void
-ataclear(SDev *sdev)
-{
- Ctlr* ctlr;
-
- ctlr = sdev->ctlr;
- iofree(ctlr->cmdport);
- iofree(ctlr->ctlport + As);
-
- if (ctlr->drive[0])
- free(ctlr->drive[0]);
- if (ctlr->drive[1])
- free(ctlr->drive[1]);
- if (sdev->name)
- free(sdev->name);
- if (sdev->unitflg)
- free(sdev->unitflg);
- if (sdev->unit)
- free(sdev->unit);
- free(ctlr);
- free(sdev);
-}
-
-static char *
-atastat(SDev *sdev, char *p, char *e)
-{
- Ctlr *ctlr = sdev->ctlr;
-
- return seprint(p, e, "%s ata port %X ctl %X irq %d\n",
- sdev->name, ctlr->cmdport, ctlr->ctlport, ctlr->irq);
-}
-
-static SDev*
-ataprobew(DevConf *cf)
-{
- char *p;
- ISAConf isa;
-
- if (cf->nports != 2)
- error(Ebadarg);
-
- memset(&isa, 0, sizeof isa);
- isa.port = cf->ports[0].port;
- isa.irq = cf->intnum;
- if((p=strchr(cf->type, '/')) == nil || pcmspecial(p+1, &isa) < 0)
- error("cannot find controller");
-
- return ataprobe(cf->ports[0].port, cf->ports[1].port, cf->intnum);
-}
-
-/*
- * These are duplicated with sdsetsense, etc., in devsd.c, but
- * those assume that the disk is not SCSI while in fact here
- * ata drives are not SCSI but ATAPI ones kind of are.
- */
-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 int
-atastandby(Drive* drive, int period)
-{
- Ctlr* ctlr;
- int cmdport, done;
-
- ctlr = drive->ctlr;
- drive->command = Cstandby;
- qlock(ctlr);
-
- cmdport = ctlr->cmdport;
- ilock(ctlr);
- outb(cmdport+Count, period);
- outb(cmdport+Dh, drive->dev);
- ctlr->done = 0;
- ctlr->curdrive = drive;
- ctlr->command = Cstandby; /* debugging */
- outb(cmdport+Command, Cstandby);
- iunlock(ctlr);
-
- while(waserror())
- ;
- tsleep(ctlr, atadone, ctlr, 60*1000);
- poperror();
-
- done = ctlr->done;
- qunlock(ctlr);
-
- if(!done || (drive->status & Err))
- return atasetsense(drive, SDcheck, 4, 8, drive->error);
- 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);
- ctlr->command = Cnop; /* debugging */
- outb(cmdport+Command, Cnop);
-
- 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, int dolock)
-{
- /*
- * If NOP is available (packet commands) use it otherwise
- * must try a software reset.
- */
- if(dolock)
- ilock(drive->ctlr);
- if(drive->info[Icsfs] & Mnop)
- atanop(drive, 0);
- else{
- atasrst(drive->ctlr->ctlport);
- drive->error |= Abrt;
- }
- if(dolock)
- iunlock(drive->ctlr);
-}
-
-static int
-atadmasetup(Drive* drive, int len)
-{
- Prd *prd;
- ulong pa;
- Ctlr *ctlr;
- int bmiba, bmisx, count, i, span;
-
- ctlr = drive->ctlr;
- pa = PCIWADDR(drive->data);
- if(pa & 0x03)
- return -1;
-
- /*
- * Sometimes drives identify themselves as being DMA capable
- * although they are not on a busmastering controller.
- */
- prd = ctlr->prdt;
- if(prd == nil){
- drive->dmactl = 0;
- print("disabling dma: not on a busmastering controller\n");
- return -1;
- }
-
- for(i = 0; len && i < Nprd; i++){
- prd->pa = pa;
- span = ROUNDUP(pa, ctlr->span);
- if(span == pa)
- span += ctlr->span;
- count = span - pa;
- if(count >= len){
- prd->count = PrdEOT|len;
- break;
- }
- prd->count = count;
- len -= count;
- pa += count;
- prd++;
- }
- if(i == Nprd)
- (prd-1)->count |= PrdEOT;
-
- 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 int
-atadmastop(Ctlr* ctlr)
-{
- int bmiba;
-
- bmiba = ctlr->bmiba;
- outb(bmiba+Bmicx, inb(bmiba+Bmicx) & ~Ssbm);
-
- return inb(bmiba+Bmisx);
-}
-
-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, 0);
- break;
- }
- atadmastop(ctlr);
- ctlr->done = 1;
-}
-
-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);
-
- as = ataready(cmdport, ctlport, drive->dev, Bsy|Drq, Drdy, 107*1000);
- /* used to test as&Chk as failure too, but some CD readers use that for media change */
- if(as < 0){
- qunlock(ctlr);
- return -1;
- }
-
- ilock(ctlr);
- if(drive->dlen && drive->dmactl && !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;
- ctlr->curdrive = drive;
- ctlr->command = Cpkt; /* debugging */
- if(drive->pktdma)
- atadmastart(ctlr, drive->write);
- outb(cmdport+Command, Cpkt);
-
- if((drive->info[Iconfig] & Mdrq) != 0x0020){
- microdelay(1);
- as = ataready(cmdport, ctlport, 0, Bsy, Drq|Chk, 4*1000);
- if(as < 0 || (as & (Bsy|Chk))){
- drive->status = as<0 ? 0 : as;
- ctlr->curdrive = nil;
- ctlr->done = 1;
- r = SDtimeout;
- }else
- atapktinterrupt(drive);
- }
- iunlock(ctlr);
-
- while(waserror())
- ;
- if(!drive->pktdma)
- sleep(ctlr, atadone, ctlr);
- else for(timeo = 0; !ctlr->done; timeo++){
- tsleep(ctlr, atadone, ctlr, 1000);
- if(ctlr->done)
- break;
- ilock(ctlr);
- atadmainterrupt(drive, 0);
- if(!drive->error && timeo > 20){
- ataabort(drive, 0);
- atadmastop(ctlr);
- drive->dmactl = 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 uchar cmd48[256] = {
- [Crs] Crs48,
- [Crd] Crd48,
- [Crdq] Crdq48,
- [Crsm] Crsm48,
- [Cws] Cws48,
- [Cwd] Cwd48,
- [Cwdq] Cwdq48,
- [Cwsm] Cwsm48,
-};
-
-static int
-atageniostart(Drive* drive, uvlong lba)
-{
- Ctlr *ctlr;
- uchar cmd;
- int as, c, cmdport, ctlport, h, len, s, use48;
-
- use48 = 0;
- if((drive->flags&Lba48always) || lba > Last28 || drive->count > 256){
- if(!(drive->flags & Lba48))
- return -1;
- use48 = 1;
- c = h = s = 0;
- }
- else 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, Drdy, 101*1000) < 0)
- return -1;
-
- ilock(ctlr);
- if(drive->dmactl && !atadmasetup(drive, drive->count*drive->secsize)){
- if(drive->write)
- drive->command = Cwd;
- else
- drive->command = Crd;
- }
- else if(drive->rwmctl){
- drive->block = drive->rwm*drive->secsize;
- if(drive->write)
- drive->command = Cwsm;
- else
- drive->command = Crsm;
- }
- else{
- drive->block = drive->secsize;
- if(drive->write)
- drive->command = Cws;
- else
- drive->command = Crs;
- }
- drive->limit = drive->data + drive->count*drive->secsize;
- cmd = drive->command;
- if(use48){
- outb(cmdport+Count, drive->count>>8);
- outb(cmdport+Count, drive->count);
- outb(cmdport+Lbalo, lba>>24);
- outb(cmdport+Lbalo, lba);
- outb(cmdport+Lbamid, lba>>32);
- outb(cmdport+Lbamid, lba>>8);
- outb(cmdport+Lbahi, lba>>40);
- outb(cmdport+Lbahi, lba>>16);
- outb(cmdport+Dh, drive->dev|Lba);
- cmd = cmd48[cmd];
-
- if(DEBUG & Dbg48BIT)
- print("using 48-bit commands\n");
- }
- else{
- outb(cmdport+Count, drive->count);
- outb(cmdport+Sector, s);
- outb(cmdport+Cyllo, c);
- outb(cmdport+Cylhi, c>>8);
- outb(cmdport+Dh, drive->dev|h);
- }
- ctlr->done = 0;
- ctlr->curdrive = drive;
- ctlr->command = drive->command; /* debugging */
- outb(cmdport+Command, cmd);
-
- switch(drive->command){
- case Cws:
- case Cwsm:
- microdelay(1);
- /* 10*1000 for flash ide drives - maybe detect them? */
- as = ataready(cmdport, ctlport, 0, Bsy, Drq|Err, 10*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;
- }
- iunlock(ctlr);
-
- return 0;
-}
-
-static int
-atagenioretry(Drive* drive)
-{
- if(drive->dmactl){
- drive->dmactl = 0;
- print("atagenioretry: disabling dma\n");
- }
- else if(drive->rwmctl)
- drive->rwmctl = 0;
- else
- return atasetsense(drive, SDcheck, 4, 8, drive->error);
-
- return SDretry;
-}
-
-static int
-atagenio(Drive* drive, uchar* cmd, int clen)
-{
- uchar *p;
- Ctlr *ctlr;
- vlong lba, len;
- int count, maxio;
-
- /*
- * 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 0x9E: /* long 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>>56;
- *p++ = len>>48;
- *p++ = len>>40;
- *p++ = len>>32;
- *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 += 12;
- return SDok;
-
- case 0x28: /* read */
- case 0x88:
- case 0x2a: /* write */
- case 0x8a:
- break;
-
- case 0x5A:
- return atamodesense(drive, cmd);
- }
-
- ctlr = drive->ctlr;
- if(clen == 16){
- /* ata commands only go to 48-bit lba */
- if(cmd[2] || cmd[3])
- return atasetsense(drive, SDcheck, 3, 0xc, 2);
- lba = (uvlong)cmd[4]<<40 | (uvlong)cmd[5]<<32;
- lba |= cmd[6]<<24 | cmd[7]<<16 | cmd[8]<<8 | cmd[9];
- count = cmd[10]<<24 | cmd[11]<<16 | cmd[12]<<8 | cmd[13];
- }else{
- 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);
- if(ctlr->maxio)
- maxio = ctlr->maxio;
- else if(drive->flags & Lba48)
- maxio = 65536;
- else
- maxio = 256;
- while(count){
- if(count > maxio)
- drive->count = maxio;
- else
- drive->count = count;
- if(atageniostart(drive, lba)){
- ilock(ctlr);
- atanop(drive, 0);
- iunlock(ctlr);
- qunlock(ctlr);
- return atagenioretry(drive);
- }
-
- while(waserror())
- ;
- tsleep(ctlr, atadone, ctlr, 60*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.
- */
- atadumpstate(drive, cmd, lba, count);
- ataabort(drive, 1);
- qunlock(ctlr);
- return atagenioretry(drive);
- }
-
- if(drive->status & Err){
- qunlock(ctlr);
- return atasetsense(drive, SDcheck, 4, 8, drive->error);
- }
- count -= drive->count;
- lba += 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);
-retry:
- drive->write = r->write;
- drive->data = r->data;
- drive->dlen = r->dlen;
-
- drive->status = 0;
- drive->error = 0;
- if(drive->pkt)
- status = atapktio(drive, cmdp, clen);
- else
- status = atagenio(drive, cmdp, clen);
- if(status == SDretry){
- if(DbgDEBUG)
- print("%s: retry: dma %8.8uX rwm %4.4uX\n",
- unit->name, drive->dmactl, drive->rwmctl);
- goto retry;
- }
- 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->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);
- if(DEBUG & DbgBsy)
- print("IBsy+");
- return;
- }
- cmdport = ctlr->cmdport;
- status = inb(cmdport+Status);
- if((drive = ctlr->curdrive) == nil){
- iunlock(ctlr);
- if((DEBUG & DbgINL) && ctlr->command != Cedd)
- print("Inil%2.2uX+", ctlr->command);
- 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;
-
- case Cstandby:
- ctlr->done = 1;
- 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;
- SDev *legacy[2], *sdev, *head, *tail;
- int channel, ispc87415, maxio, pi, r, span;
-
- 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).
- * Sub-class code 0x04 is 'RAID controller', e.g. VIA VT8237.
- */
- if(p->ccrb != 0x01)
- continue;
- if(p->ccru != 0x01 && p->ccru != 0x04 && p->ccru != 0x80)
- continue;
- pi = p->ccrp;
- ispc87415 = 0;
- maxio = 0;
- span = BMspan;
-
- 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 (0x1000<<16)|0x1042: /* PC-Tech RZ1000 */
- /*
- * Turn off prefetch. Overkill, but cheap.
- */
- r = pcicfgr32(p, 0x40);
- r &= ~0x2000;
- pcicfgw32(p, 0x40, r);
- break;
- case (0x4D38<<16)|0x105A: /* Promise PDC20262 */
- case (0x4D30<<16)|0x105A: /* Promise PDC202xx */
- case (0x4D68<<16)|0x105A: /* Promise PDC20268 */
- case (0x4D69<<16)|0x105A: /* Promise Ultra/133 TX2 */
- case (0x3373<<16)|0x105A: /* Promise 20378 RAID */
- case (0x3149<<16)|0x1106: /* VIA VT8237 SATA/RAID */
- case (0x3112<<16)|0x1095: /* SiI 3112 SATA/RAID */
- maxio = 15;
- span = 8*1024;
- /*FALLTHROUGH*/
- case (0x0680<<16)|0x1095: /* SiI 0680/680A PATA133 ATAPI/RAID */
- case (0x3114<<16)|0x1095: /* SiI 3114 SATA/RAID */
- pi = 0x85;
- break;
- case (0x0004<<16)|0x1103: /* HighPoint HPT366 */
- pi = 0x85;
- /*
- * Turn off fast interrupt prediction.
- */
- if((r = pcicfgr8(p, 0x51)) & 0x80)
- pcicfgw8(p, 0x51, r & ~0x80);
- if((r = pcicfgr8(p, 0x55)) & 0x80)
- pcicfgw8(p, 0x55, r & ~0x80);
- break;
- case (0x0640<<16)|0x1095: /* CMD 640B */
- /*
- * Bugfix code here...
- */
- break;
- case (0x7441<<16)|0x1022: /* AMD 768 */
- /*
- * Set:
- * 0x41 prefetch, postwrite;
- * 0x43 FIFO configuration 1/2 and 1/2;
- * 0x44 status register read retry;
- * 0x46 DMA read and end of sector flush.
- */
- r = pcicfgr8(p, 0x41);
- pcicfgw8(p, 0x41, r|0xF0);
- r = pcicfgr8(p, 0x43);
- pcicfgw8(p, 0x43, (r & 0x90)|0x2A);
- r = pcicfgr8(p, 0x44);
- pcicfgw8(p, 0x44, r|0x08);
- r = pcicfgr8(p, 0x46);
- pcicfgw8(p, 0x46, (r & 0x0C)|0xF0);
- /*FALLTHROUGH*/
- case (0x7401<<16)|0x1022: /* AMD 755 Cobra */
- case (0x7409<<16)|0x1022: /* AMD 756 Viper */
- case (0x7410<<16)|0x1022: /* AMD 766 Viper Plus */
- case (0x7469<<16)|0x1022: /* AMD 3111 */
- /*
- * This can probably be lumped in with the 768 above.
- */
- /*FALLTHROUGH*/
- case (0x209A<<16)|0x1022: /* AMD CS5536 */
- case (0x01BC<<16)|0x10DE: /* nVidia nForce1 */
- case (0x0065<<16)|0x10DE: /* nVidia nForce2 */
- case (0x0085<<16)|0x10DE: /* nVidia nForce2 MCP */
- case (0x00E3<<16)|0x10DE: /* nVidia nForce2 250 SATA */
- case (0x00D5<<16)|0x10DE: /* nVidia nForce3 */
- case (0x00E5<<16)|0x10DE: /* nVidia nForce3 Pro */
- case (0x00EE<<16)|0x10DE: /* nVidia nForce3 250 SATA */
- case (0x0035<<16)|0x10DE: /* nVidia nForce3 MCP */
- case (0x0053<<16)|0x10DE: /* nVidia nForce4 */
- case (0x0054<<16)|0x10DE: /* nVidia nForce4 SATA */
- case (0x0055<<16)|0x10DE: /* nVidia nForce4 SATA */
- case (0x0266<<16)|0x10DE: /* nVidia nForce4 430 SATA */
- case (0x0267<<16)|0x10DE: /* nVidia nForce 55 MCP SATA */
- case (0x03EC<<16)|0x10DE: /* nVidia nForce 61 MCP SATA */
- case (0x0448<<16)|0x10DE: /* nVidia nForce 65 MCP SATA */
- case (0x0560<<16)|0x10DE: /* nVidia nForce 69 MCP SATA */
- /*
- * Ditto, although it may have a different base
- * address for the registers (0x50?).
- */
- /*FALLTHROUGH*/
- case (0x4376<<16)|0x1002: /* ATI SB400 PATA */
- case (0x4379<<16)|0x1002: /* ATI SB400 SATA */
- case (0x437a<<16)|0x1002: /* ATI SB400 SATA */
- case (0x438c<<16)|0x1002: /* ATI SB600 PATA */
- break;
- case (0x0211<<16)|0x1166: /* ServerWorks IB6566 */
- {
- Pcidev *sb;
-
- sb = pcimatch(nil, 0x1166, 0x0200);
- if(sb == nil)
- break;
- r = pcicfgr32(sb, 0x64);
- r &= ~0x2000;
- pcicfgw32(sb, 0x64, r);
- }
- span = 32*1024;
- break;
- case (0x0502<<17)|0x100B: /* NS SC1100/SCx200 */
- case (0x5229<<16)|0x10B9: /* ALi M1543 */
- case (0x5288<<16)|0x10B9: /* ALi M5288 SATA */
- case (0x5513<<16)|0x1039: /* SiS 962 */
- case (0x0646<<16)|0x1095: /* CMD 646 */
- case (0x0571<<16)|0x1106: /* VIA 82C686 */
- case (0x9001<<16)|0x1106: /* VIA chipset in VIA PV530 */
- case (0x2363<<16)|0x197b: /* JMicron SATA */
- case (0x1230<<16)|0x8086: /* 82371FB (PIIX) */
- case (0x7010<<16)|0x8086: /* 82371SB (PIIX3) */
- case (0x7111<<16)|0x8086: /* 82371[AE]B (PIIX4[E]) */
- case (0x2411<<16)|0x8086: /* 82801AA (ICH) */
- case (0x2421<<16)|0x8086: /* 82801AB (ICH0) */
- case (0x244A<<16)|0x8086: /* 82801BA (ICH2, Mobile) */
- case (0x244B<<16)|0x8086: /* 82801BA (ICH2, High-End) */
- case (0x248A<<16)|0x8086: /* 82801CA (ICH3, Mobile) */
- case (0x248B<<16)|0x8086: /* 82801CA (ICH3, High-End) */
- case (0x24CA<<16)|0x8086: /* 82801DBM (ICH4, Mobile) */
- case (0x24CB<<16)|0x8086: /* 82801DB (ICH4, High-End) */
- case (0x24DB<<16)|0x8086: /* 82801EB (ICH5) */
- case (0x25A3<<16)|0x8086: /* 6300ESB (E7210) */
- case (0x2653<<16)|0x8086: /* 82801FBM SATA */
- case (0x266F<<16)|0x8086: /* 82801FB (ICH6) */
- case (0x27DF<<16)|0x8086: /* 82801G SATA (ICH7) */
- case (0x27C0<<16)|0x8086: /* 82801GB SATA AHCI (ICH7) */
-// case (0x27C4<<16)|0x8086: /* 82801GBM SATA (ICH7) */
- case (0x27C5<<16)|0x8086: /* 82801GBM SATA AHCI (ICH7) */
- case (0x2820<<16)|0x8086: /* 82801HB/HR/HH/HO SATA IDE */
- case (0x2850<<16)|0x8086: /* 82801HBM/HEM PATA */
- case (0x2828<<16)|0x8086: /* 82801HBM SATA (ICH8-M) */
- case (0x2829<<16)|0x8086: /* 82801HBM SATA AHCI (ICH8-M) */
- case (0x2920<<16)|0x8086: /* 82801(IB)/IR/IH/IO SATA IDE (ICH9) */
- case (0x3a20<<16)|0x8086: /* 82801JI (ICH10) */
- case (0x3a26<<16)|0x8086: /* 82801JI (ICH10) */
- 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;
- print("pc87415disable: not yet implemented\n");
- }
-
- if(head != nil)
- tail->next = sdev;
- else
- head = sdev;
- tail = sdev;
- ctlr->tbdf = p->tbdf;
- }
- else if((sdev = legacy[channel]) == nil)
- continue;
- else
- ctlr = sdev->ctlr;
-
- ctlr->pcidev = p;
- ctlr->maxio = maxio;
- ctlr->span = span;
- if(!(pi & 0x80))
- continue;
- ctlr->bmiba = (p->mem[4].bar & ~0x01) + channel*8;
- }
- }
-
-if(0){
- int port;
- ISAConf isa;
-
- /*
- * Hack for PCMCIA drives.
- * This will be tidied once we figure out how the whole
- * removeable device thing is going to work.
- */
- memset(&isa, 0, sizeof(isa));
- isa.port = 0x180; /* change this for your machine */
- isa.irq = 11; /* change this for your machine */
-
- port = isa.port+0x0C;
- channel = pcmspecial("MK2001MPL", &isa);
- if(channel == -1)
- channel = pcmspecial("SunDisk", &isa);
- if(channel == -1){
- isa.irq = 10;
- channel = pcmspecial("CF", &isa);
- }
- if(channel == -1){
- isa.irq = 10;
- channel = pcmspecial("OLYMPUS", &isa);
- }
- if(channel == -1){
- port = isa.port+0x204;
- channel = pcmspecial("ATA/ATAPI", &isa);
- }
- if(channel >= 0 && (sdev = ataprobe(isa.port, port, isa.irq)) != nil){
- if(head != nil)
- tail->next = sdev;
- else
- head = sdev;
- }
-}
- return head;
-}
-
-static SDev*
-atalegacy(int port, int irq)
-{
- return ataprobe(port, port+0x204, irq);
-}
-
-static int
-ataenable(SDev* sdev)
-{
- Ctlr *ctlr;
- char name[32];
-
- ctlr = sdev->ctlr;
-
- if(ctlr->bmiba){
-#define ALIGN (4 * 1024)
- if(ctlr->pcidev != nil)
- pcisetbme(ctlr->pcidev);
- ctlr->prdt = mallocalign(Nprd*sizeof(Prd), 4, 0, 4*1024);
- }
- snprint(name, sizeof(name), "%s (%s)", sdev->name, sdev->ifc->name);
- intrenable(ctlr->irq, atainterrupt, ctlr, ctlr->tbdf, name);
- outb(ctlr->ctlport+Dc, 0);
- if(ctlr->ienable)
- ctlr->ienable(ctlr);
-
- return 1;
-}
-
-static int
-atadisable(SDev *sdev)
-{
- Ctlr *ctlr;
- char name[32];
-
- ctlr = sdev->ctlr;
- outb(ctlr->ctlport+Dc, Nien); /* disable interrupts */
- if (ctlr->idisable)
- ctlr->idisable(ctlr);
- snprint(name, sizeof(name), "%s (%s)", sdev->name, sdev->ifc->name);
- intrdisable(ctlr->irq, atainterrupt, ctlr, ctlr->tbdf, name);
- if (ctlr->bmiba) {
- if (ctlr->pcidev)
- pciclrbme(ctlr->pcidev);
- free(ctlr->prdt);
- }
- return 0;
-}
-
-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];
-
- qlock(drive);
- n = snprint(p, l, "config %4.4uX capabilities %4.4uX",
- drive->info[Iconfig], drive->info[Icapabilities]);
- if(drive->dma)
- n += snprint(p+n, l-n, " dma %8.8uX dmactl %8.8uX",
- drive->dma, drive->dmactl);
- if(drive->rwm)
- n += snprint(p+n, l-n, " rwm %ud rwmctl %ud",
- drive->rwm, drive->rwmctl);
- if(drive->flags&Lba48)
- n += snprint(p+n, l-n, " lba48always %s",
- (drive->flags&Lba48always) ? "on" : "off");
- n += snprint(p+n, l-n, "\n");
- if(drive->sectors){
- n += snprint(p+n, l-n, "geometry %lld %d",
- drive->sectors, drive->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");
- }
- qunlock(drive);
-
- return n;
-}
-
-static int
-atawctl(SDunit* unit, Cmdbuf* cb)
-{
- int period;
- Ctlr *ctlr;
- Drive *drive;
-
- if((ctlr = unit->dev->ctlr) == nil || ctlr->drive[unit->subno] == nil)
- return 0;
- drive = ctlr->drive[unit->subno];
-
- qlock(drive);
- if(waserror()){
- qunlock(drive);
- nexterror();
- }
-
- /*
- * Dma and rwm control is passive at the moment,
- * i.e. it is assumed that the hardware is set up
- * correctly already either by the BIOS or when
- * the drive was initially identified.
- */
- if(strcmp(cb->f[0], "dma") == 0){
- if(cb->nf != 2 || drive->dma == 0)
- error(Ebadctl);
- if(strcmp(cb->f[1], "on") == 0)
- drive->dmactl = drive->dma;
- else if(strcmp(cb->f[1], "off") == 0)
- drive->dmactl = 0;
- else
- error(Ebadctl);
- }
- else if(strcmp(cb->f[0], "rwm") == 0){
- if(cb->nf != 2 || drive->rwm == 0)
- error(Ebadctl);
- if(strcmp(cb->f[1], "on") == 0)
- drive->rwmctl = drive->rwm;
- else if(strcmp(cb->f[1], "off") == 0)
- drive->rwmctl = 0;
- else
- error(Ebadctl);
- }
- else if(strcmp(cb->f[0], "standby") == 0){
- switch(cb->nf){
- default:
- error(Ebadctl);
- case 2:
- period = strtol(cb->f[1], 0, 0);
- if(period && (period < 30 || period > 240*5))
- error(Ebadctl);
- period /= 5;
- break;
- }
- if(atastandby(drive, period) != SDok)
- error(Ebadctl);
- }
- else if(strcmp(cb->f[0], "lba48always") == 0){
- if(cb->nf != 2 || !(drive->flags&Lba48))
- error(Ebadctl);
- if(strcmp(cb->f[1], "on") == 0)
- drive->flags |= Lba48always;
- else if(strcmp(cb->f[1], "off") == 0)
- drive->flags &= ~Lba48always;
- else
- error(Ebadctl);
- }
- else
- error(Ebadctl);
- qunlock(drive);
- poperror();
-
- return 0;
-}
-
-SDifc sdataifc = {
- "ata", /* name */
-
- atapnp, /* pnp */
- atalegacy, /* legacy */
- ataenable, /* enable */
- atadisable, /* disable */
-
- scsiverify, /* verify */
- scsionline, /* online */
- atario, /* rio */
- atarctl, /* rctl */
- atawctl, /* wctl */
-
- scsibio, /* bio */
- ataprobew, /* probe */
- ataclear, /* clear */
- atastat, /* rtopctl */
- nil, /* wtopctl */
-};
--- a/sys/src/9/pc/sdiahci.c
+++ b/sys/src/9/pc/sdiahci.c
@@ -1,6 +1,6 @@
/*
* intel/amd ahci sata controller
- * copyright © 2007-8 coraid, inc.
+ * copyright © 2007-10 coraid, inc.
*/
#include "u.h"
@@ -11,12 +11,19 @@
#include "io.h"
#include "../port/error.h"
#include "../port/sd.h"
+#include <fis.h>
#include "ahci.h"
+#include "../port/led.h"
+#pragma varargck type "T" int
#define dprint(...) if(debug) iprint(__VA_ARGS__); else USED(debug)
-#define idprint(...) if(prid) iprint(__VA_ARGS__); else USED(prid)
-#define aprint(...) if(datapi) iprint(__VA_ARGS__); else USED(datapi)
+#define idprint(...) if(prid) print(__VA_ARGS__); else USED(prid)
+#define aprint(...) if(datapi) print(__VA_ARGS__); else USED(datapi)
+#define ledprint(...) if(dled) print(__VA_ARGS__); else USED(dled)
+#define Pciwaddrh(a) 0
#define Tname(c) tname[(c)->type]
+#define Ticks MACHP(0)->ticks
+#define MS2TK(t) (((ulong)(t)*HZ)/1000)
enum {
NCtlr = 4,
@@ -23,8 +30,12 @@
NCtlrdrv= 32,
NDrive = NCtlr*NCtlrdrv,
+ Fahdrs = 4,
+
Read = 0,
Write,
+
+ Eesb = 1<<0, /* must have (Eesb & Emtype) == 0 */
};
/* pci space configuration */
@@ -38,16 +49,16 @@
Tesb,
Tich,
Tsb600,
- Tunk,
+ Tjmicron,
+ Tahci,
};
-#define Intel(x) ((x)->pci->vid == 0x8086)
-
static char *tname[] = {
"63xxesb",
"ich",
"sb600",
- "unk",
+ "jmicron",
+ "ahci",
};
enum {
@@ -80,21 +91,21 @@
DMautoneg,
DMsatai,
DMsataii,
+ DMsataiii,
+ DMlast,
};
-static char *modename[] = {
+static char *modes[DMlast] = {
"auto",
"satai",
"sataii",
+ "sataiii",
};
-static char *flagname[] = {
- "llba",
- "smart",
- "power",
- "nop",
- "atapi",
- "atapi16",
+typedef struct Htab Htab;
+struct Htab {
+ ulong bit;
+ char *name;
};
typedef struct {
@@ -105,29 +116,35 @@
char name[10];
Aport *port;
Aportm portm;
- Aportc portc; /* redundant ptr to port and portm */
+ Aportc portc; /* redundant ptr to port and portm. */
+ Ledport;
- uchar mediachange;
+ uchar drivechange;
uchar state;
- uchar smartrs;
uvlong sectors;
- ulong secsize;
- ulong intick; /* start tick of current transfer */
+ uint secsize;
+ ulong totick;
ulong lastseen;
- int wait;
- uchar mode; /* DMautoneg, satai or sataii */
+ uint wait;
+ uchar mode;
uchar active;
char serial[20+1];
char firmware[8+1];
char model[40+1];
+ uvlong wwn;
ushort info[0x200];
- int driveno; /* ctlr*NCtlrdrv + unit */
- /* controller port # != driveno when not all ports are enabled */
- int portno;
+ /*
+ * ahci allows non-sequential ports.
+ * to avoid this hassle, we let
+ * driveno ctlr*NCtlrdrv + unit
+ * portno nth available port
+ */
+ uint driveno;
+ uint portno;
} Drive;
struct Ctlr {
@@ -141,11 +158,12 @@
uchar *mmio;
ulong *lmmio;
Ahba *hba;
+ Aenc;
+ uint enctype;
Drive rawdrive[NCtlrdrv];
Drive* drive[NCtlrdrv];
int ndrive;
- int mport;
};
static Ctlr iactlr[NCtlr];
@@ -155,10 +173,10 @@
static Drive *iadrive[NDrive];
static int niadrive;
-/* these are fiddled in iawtopctl() */
static int debug;
static int prid = 1;
static int datapi;
+static int dled;
static char stab[] = {
[0] 'i', 'm',
@@ -186,13 +204,13 @@
static void
preg(uchar *reg, int n)
{
- int i;
char buf[25*3+1], *e;
+ int i;
e = buf;
for(i = 0; i < n; i++){
- *e++ = ntab[reg[i]>>4];
- *e++ = ntab[reg[i]&0xf];
+ *e++ = ntab[reg[i] >> 4];
+ *e++ = ntab[reg[i] & 0xf];
*e++ = ' ';
}
*e++ = '\n';
@@ -203,7 +221,7 @@
static void
dreg(char *s, Aport *p)
{
- dprint("ahci: %stask=%lux; cmd=%lux; ci=%lux; is=%lux\n",
+ dprint("%stask=%lux; cmd=%lux; ci=%lux; is=%lux\n",
s, p->task, p->cmd, p->ci, p->isr);
}
@@ -219,7 +237,7 @@
typedef struct {
Aport *p;
int i;
-}Asleep;
+} Asleep;
static int
ahciclear(void *v)
@@ -242,8 +260,8 @@
static int
ahciwait(Aportc *c, int ms)
{
- Asleep as;
Aport *p;
+ Asleep as;
p = c->p;
p->ci = 1;
@@ -250,94 +268,75 @@
as.p = p;
as.i = 1;
aesleep(c->m, &as, ms);
- if((p->task&1) == 0 && p->ci == 0)
+ if((p->task & 1) == 0 && p->ci == 0)
return 0;
- dreg("ahciwait timeout ", c->p);
+ dreg("ahciwait fail/timeout ", c->p);
return -1;
}
-static int
-nop(Aportc *pc)
+static void
+mkalist(Aportm *m, uint flags, uchar *data, int len)
{
- uchar *c;
Actab *t;
Alist *l;
+ Aprdt *p;
- if((pc->m->feat & Dnop) == 0)
- return -1;
-
- t = pc->m->ctab;
- c = t->cfis;
-
- memset(c, 0, 0x20);
- c[0] = 0x27;
- c[1] = 0x80;
- c[2] = 0x00;
- c[7] = 0xa0; /* obsolete device bits */
-
- l = pc->m->list;
- l->flags = Lwrite | 0x5;
+ t = m->ctab;
+ l = m->list;
+ l->flags = flags | 0x5;
l->len = 0;
l->ctab = PCIWADDR(t);
- l->ctabhi = 0;
-
- return ahciwait(pc, 3*1000);
+ l->ctabhi = Pciwaddrh(t);
+ if(data){
+ l->flags |= 1<<16;
+ p = &t->prdt;
+ p->dba = PCIWADDR(data);
+ p->dbahi = Pciwaddrh(data);
+ p->count = 1<<31 | len - 2 | 1;
+ }
}
static int
-setfeatures(Aportc *pc, uchar f)
+nop(Aportc *pc)
{
uchar *c;
- Actab *t;
- Alist *l;
- t = pc->m->ctab;
- c = t->cfis;
-
- memset(c, 0, 0x20);
- c[0] = 0x27;
- c[1] = 0x80;
- c[2] = 0xef;
- c[3] = f;
- c[7] = 0xa0; /* obsolete device bits */
-
- l = pc->m->list;
- l->flags = Lwrite | 0x5;
- l->len = 0;
- l->ctab = PCIWADDR(t);
- l->ctabhi = 0;
-
+ if((pc->m->feat & Dnop) == 0)
+ return -1;
+ c = pc->m->ctab->cfis;
+ nopfis(pc->m, c, 0);
+ mkalist(pc->m, Lwrite, 0, 0);
return ahciwait(pc, 3*1000);
}
static int
-setudmamode(Aportc *pc, uchar f)
+setfeatures(Aportc *pc, uchar f, uint w)
{
uchar *c;
- Actab *t;
- Alist *l;
- /* hack */
- if((pc->p->sig >> 16) == 0xeb14)
- return 0;
+ c = pc->m->ctab->cfis;
+ featfis(pc->m, c, f);
+ mkalist(pc->m, Lwrite, 0, 0);
+ return ahciwait(pc, w);
+}
- t = pc->m->ctab;
- c = t->cfis;
+/*
+ * ata 7, required for sata, requires that all devices "support"
+ * udma mode 5, however sata:pata bridges allow older devices
+ * which may not. the innodisk satadom, for example allows
+ * only udma mode 2. on the assumption that actual udma is
+ * taking place on these bridges, we set the highest udma mode
+ * available, or pio if there is no udma mode available.
+ */
+static int
+settxmode(Aportc *pc, uchar f)
+{
+ uchar *c;
- memset(c, 0, 0x20);
- c[0] = 0x27;
- c[1] = 0x80;
- c[2] = 0xef;
- c[3] = 3; /* set transfer mode */
- c[7] = 0xa0; /* obsolete device bits */
- c[12] = 0x40 | f; /* sector count */
-
- l = pc->m->list;
- l->flags = Lwrite | 0x5;
- l->len = 0;
- l->ctab = PCIWADDR(t);
- l->ctabhi = 0;
-
+ c = pc->m->ctab->cfis;
+ if(txmodefis(pc->m, c, f) == -1)
+ return 0;
+ mkalist(pc->m, Lwrite, 0, 0);
return ahciwait(pc, 3*1000);
}
@@ -351,7 +350,7 @@
}
static int
-ahciportreset(Aportc *c)
+ahciportreset(Aportc *c, uint mode)
{
ulong *cmd, i;
Aport *p;
@@ -360,229 +359,72 @@
cmd = &p->cmd;
*cmd &= ~(Afre|Ast);
for(i = 0; i < 500; i += 25){
- if((*cmd&Acr) == 0)
+ if((*cmd & Acr) == 0)
break;
asleep(25);
}
- p->sctl = 1|(p->sctl&~7);
+ p->sctl = 3*Aipm | 0*Aspd | Adet;
delay(1);
- p->sctl &= ~7;
+ p->sctl = 3*Aipm | mode*Aspd;
return 0;
}
static int
-smart(Aportc *pc, int n)
+ahciflushcache(Aportc *pc)
{
uchar *c;
- Actab *t;
- Alist *l;
- if((pc->m->feat&Dsmart) == 0)
- return -1;
+ c = pc->m->ctab->cfis;
+ flushcachefis(pc->m, c);
+ mkalist(pc->m, Lwrite, 0, 0);
- t = pc->m->ctab;
- c = t->cfis;
-
- memset(c, 0, 0x20);
- c[0] = 0x27;
- c[1] = 0x80;
- c[2] = 0xb0;
- c[3] = 0xd8 + n; /* able smart */
- c[5] = 0x4f;
- c[6] = 0xc2;
- c[7] = 0xa0;
-
- l = pc->m->list;
- l->flags = Lwrite | 0x5;
- l->len = 0;
- l->ctab = PCIWADDR(t);
- l->ctabhi = 0;
-
- if(ahciwait(pc, 1000) == -1 || pc->p->task & (1|32)){
- dprint("ahci: smart fail %lux\n", pc->p->task);
+ if(ahciwait(pc, 60000) == -1 || pc->p->task & (1|32)){
+ dprint("ahciflushcache fail %lux\n", pc->p->task);
// preg(pc->m->fis.r, 20);
return -1;
}
- if(n)
- return 0;
- return 1;
-}
-
-static int
-smartrs(Aportc *pc)
-{
- uchar *c;
- Actab *t;
- Alist *l;
-
- t = pc->m->ctab;
- c = t->cfis;
-
- memset(c, 0, 0x20);
- c[0] = 0x27;
- c[1] = 0x80;
- c[2] = 0xb0;
- c[3] = 0xda; /* return smart status */
- c[5] = 0x4f;
- c[6] = 0xc2;
- c[7] = 0xa0;
-
- l = pc->m->list;
- l->flags = Lwrite | 0x5;
- l->len = 0;
- l->ctab = PCIWADDR(t);
- l->ctabhi = 0;
-
- c = pc->m->fis.r;
- if(ahciwait(pc, 1000) == -1 || pc->p->task & (1|32)){
- dprint("ahci: smart fail %lux\n", pc->p->task);
- preg(c, 20);
- return -1;
- }
- if(c[5] == 0x4f && c[6] == 0xc2)
- return 1;
return 0;
}
static int
-ahciflushcache(Aportc *pc)
+ahciidentify0(Aportc *pc, void *id)
{
- uchar *c, llba;
- Actab *t;
- Alist *l;
- static uchar tab[2] = {0xe7, 0xea};
-
- llba = pc->m->feat&Dllba? 1: 0;
- t = pc->m->ctab;
- c = t->cfis;
-
- memset(c, 0, 0x20);
- c[0] = 0x27;
- c[1] = 0x80;
- c[2] = tab[llba];
- c[7] = 0xa0;
-
- l = pc->m->list;
- l->flags = Lwrite | 0x5;
- l->len = 0;
- l->ctab = PCIWADDR(t);
- l->ctabhi = 0;
-
- if(ahciwait(pc, 60000) == -1 || pc->p->task & (1|32)){
- dprint("ahciflushcache: fail %lux\n", pc->p->task);
-// preg( pc->m->fis.r, 20);
- return -1;
- }
- return 0;
-}
-
-static ushort
-gbit16(void *a)
-{
- uchar *i;
-
- i = a;
- return i[1]<<8 | i[0];
-}
-
-static ulong
-gbit32(void *a)
-{
- ulong j;
- uchar *i;
-
- i = a;
- j = i[3] << 24;
- j |= i[2] << 16;
- j |= i[1] << 8;
- j |= i[0];
- return j;
-}
-
-static uvlong
-gbit64(void *a)
-{
- uchar *i;
-
- i = a;
- return (uvlong)gbit32(i+4) << 32 | gbit32(a);
-}
-
-static int
-ahciidentify0(Aportc *pc, void *id, int atapi)
-{
uchar *c;
Actab *t;
- Alist *l;
- Aprdt *p;
- static uchar tab[] = { 0xec, 0xa1, };
t = pc->m->ctab;
c = t->cfis;
-
- memset(c, 0, 0x20);
- c[0] = 0x27;
- c[1] = 0x80;
- c[2] = tab[atapi];
- c[7] = 0xa0; /* obsolete device bits */
-
- l = pc->m->list;
- l->flags = 1<<16 | 0x5;
- l->len = 0;
- l->ctab = PCIWADDR(t);
- l->ctabhi = 0;
-
- memset(id, 0, 0x100);
- p = &t->prdt;
- p->dba = PCIWADDR(id);
- p->dbahi = 0;
- p->count = 1<<31 | (0x200-2) | 1;
-
+ memset(id, 0, 0x200);
+ identifyfis(pc->m, c);
+ mkalist(pc->m, 0, id, 0x200);
return ahciwait(pc, 3*1000);
}
static vlong
-ahciidentify(Aportc *pc, ushort *id)
+ahciidentify(Aportc *pc, ushort *id, uint *ss, char *d)
{
- int i, sig;
+ int i, n;
vlong s;
Aportm *m;
m = pc->m;
- m->feat = 0;
- m->smart = 0;
- i = 0;
- sig = pc->p->sig >> 16;
- if(sig == 0xeb14){
- m->feat |= Datapi;
- i = 1;
+ for(i = 0;; i++){
+ if(i > 5 || ahciidentify0(pc, id) != 0)
+ return -1;
+ n = idpuis(id);
+ if(n & Pspinup && setfeatures(pc, 7, 20*1000) == -1)
+ print("%s: puis spinup fail\n", d);
+ if(n & Pidready)
+ break;
+ print("%s: puis waiting\n", d);
}
- if(ahciidentify0(pc, id, i) == -1)
+ s = idfeat(m, id);
+ *ss = idss(m, id);
+ if(s == -1 || (m->feat&Dlba) == 0){
+ if((m->feat&Dlba) == 0)
+ dprint("%s: no lba support\n", d);
return -1;
-
- i = gbit16(id+83) | gbit16(id+86);
- if(i & (1<<10)){
- m->feat |= Dllba;
- s = gbit64(id+100);
- }else
- s = gbit32(id+60);
-
- if(m->feat&Datapi){
- i = gbit16(id+0);
- if(i&1)
- m->feat |= Datapi16;
}
-
- i = gbit16(id+83);
- if((i>>14) == 1) {
- if(i & (1<<3))
- m->feat |= Dpower;
- i = gbit16(id+82);
- if(i & 1)
- m->feat |= Dsmart;
- if(i & (1<<14))
- m->feat |= Dnop;
- }
return s;
}
@@ -614,10 +456,10 @@
return -1;
stop1:
/* extra check */
- dprint("ahci: clo clear %lx\n", a->task);
+ dprint("ahci: clo clear %lux\n", a->task);
if(a->task & ASbsy)
return -1;
- *p |= Ast;
+ *p |= Afre | Ast;
return 0;
}
@@ -625,52 +467,28 @@
ahcicomreset(Aportc *pc)
{
uchar *c;
- Actab *t;
- Alist *l;
- dprint("ahcicomreset\n");
- dreg("ahci: comreset ", pc->p);
+ dreg("comreset ", pc->p);
if(ahciquiet(pc->p) == -1){
- dprint("ahciquiet failed\n");
+ dprint("ahci: ahciquiet failed\n");
return -1;
}
dreg("comreset ", pc->p);
- t = pc->m->ctab;
- c = t->cfis;
-
- memset(c, 0, 0x20);
- c[0] = 0x27;
- c[1] = 0x00;
- c[7] = 0xa0; /* obsolete device bits */
- c[15] = 1<<2; /* srst */
-
- l = pc->m->list;
- l->flags = Lclear | Lreset | 0x5;
- l->len = 0;
- l->ctab = PCIWADDR(t);
- l->ctabhi = 0;
-
+ c = pc->m->ctab->cfis;
+ nopfis(pc->m, c, 1);
+ mkalist(pc->m, Lclear | Lreset, 0, 0);
if(ahciwait(pc, 500) == -1){
- dprint("ahcicomreset: first command failed\n");
+ dprint("ahci: comreset1 failed\n");
return -1;
}
microdelay(250);
dreg("comreset ", pc->p);
- memset(c, 0, 0x20);
- c[0] = 0x27;
- c[1] = 0x00;
- c[7] = 0xa0; /* obsolete device bits */
-
- l = pc->m->list;
- l->flags = Lwrite | 0x5;
- l->len = 0;
- l->ctab = PCIWADDR(t);
- l->ctabhi = 0;
-
+ nopfis(pc->m, c, 0);
+ mkalist(pc->m, Lwrite, 0, 0);
if(ahciwait(pc, 150) == -1){
- dprint("ahcicomreset: second command failed\n");
+ dprint("ahci: comreset2 failed\n");
return -1;
}
dreg("comreset ", pc->p);
@@ -706,7 +524,7 @@
}
/*
- * § 6.2.2.1 first part; comreset handled by reset disk.
+ * §6.2.2.1 first part; comreset handled by reset disk.
* - remainder is handled by configdisk.
* - ahcirecover is a quick recovery from a failed command.
*/
@@ -729,7 +547,7 @@
{
ahciswreset(pc);
pc->p->cmd |= Ast;
- if(setudmamode(pc, 5) == -1)
+ if(settxmode(pc, pc->m->udma) == -1)
return -1;
return 0;
}
@@ -756,21 +574,19 @@
}
static void
-ahciwakeup(Aport *p)
+ahciwakeup(Aportc *c, uint mode)
{
ushort s;
- s = p->sstatus;
- if((s & 0xF00) != 0x600)
+ s = c->p->sstatus;
+ if((s & Isleepy) == 0)
return;
- if((s & 7) != 1){ /* not (device, no phy) */
- iprint("ahci: slumbering drive unwakable %ux\n", s);
+ if((s & Smask) != Spresent){
+ print("ahci: slumbering drive missing %.3ux\n", s);
return;
}
- p->sctl = 3*Aipm | 0*Aspd | Adet;
- delay(1);
- p->sctl &= ~7;
-// iprint("ahci: wake %ux -> %ux\n", s, p->sstatus);
+ ahciportreset(c, mode);
+// iprint("ahci: wake %.3ux -> %.3lux\n", s, c->p->sstatus);
}
static int
@@ -788,29 +604,34 @@
m->ctab = malign(sizeof *m->ctab, 128);
}
- if(p->sstatus & 3 && h->cap & Hsss){
- /* device connected & staggered spin-up */
- dprint("ahci: configdrive: spinning up ... [%lux]\n",
- p->sstatus);
- p->cmd |= Apod|Asud;
- asleep(1400);
- }
-
- p->serror = SerrAll;
-
p->list = PCIWADDR(m->list);
- p->listhi = 0;
+ p->listhi = Pciwaddrh(m->list);
p->fis = PCIWADDR(m->fis.base);
- p->fishi = 0;
- p->cmd |= Afre|Ast;
+ p->fishi = Pciwaddrh(m->fis.base);
- if((p->sstatus & 0xF0F) == 0x601) /* drive coming up in slumbering? */
- ahciwakeup(p);
+ p->cmd |= Afre;
+ if((p->sstatus & Sbist) == 0 && (p->cmd & Apwr) != Apwr)
+ if((p->sstatus & Sphylink) == 0 && h->cap & Hss){
+ /* staggered spin-up? */
+ dprint("ahci: spin up ... [%.3lux]\n", p->sstatus);
+ p->cmd |= Apwr;
+ for(int i = 0; i < 1400; i += 50){
+ if(p->sstatus & (Sphylink | Sbist))
+ break;
+ asleep(50);
+ }
+ }
+
+ p->serror = SerrAll;
+
+ if((p->sstatus & SSmask) == (Isleepy | Spresent))
+ ahciwakeup(c, mode);
/* disable power managment sequence from book. */
- p->sctl = (3*Aipm) | (mode*Aspd) | (0*Adet);
+ p->sctl = 3*Aipm | mode*Aspd | 0*Adet;
p->cmd &= ~Aalpe;
+ p->cmd |= Ast;
p->ie = IEM;
return 0;
@@ -843,22 +664,22 @@
}
static int
-ahciconf(Ctlr *ctlr)
+ahciconf(Ctlr *c)
{
+ uint u;
Ahba *h;
- ulong u;
- h = ctlr->hba = (Ahba*)ctlr->mmio;
+ h = c->hba = (Ahba*)c->mmio;
u = h->cap;
- if((u&Hsam) == 0)
+ if((u & Ham) == 0)
h->ghc |= Hae;
- print("#S/sd%c: ahci %s port %#p: sss %ld ncs %ld coal %ld "
- "mports %ld led %ld clo %ld ems %ld\n",
- ctlr->sdev->idno, tname[ctlr->type], h,
- (u>>27) & 1, (u>>8) & 0x1f, (u>>7) & 1, u & 0x1f, (u>>25) & 1,
- (u>>24) & 1, (u>>6) & 1);
+// print("#S/sd%c: ahci %s port %#p: sss %d ncs %d coal %d "
+// "mport %d led %d clo %d ems %d\n",
+// c->sdev->idno, Tname(c), h,
+// (u>>27) & 1, (u>>8) & 0x1f, (u>>7) & 1, u & 0x1f, (u>>25) & 1,
+// (u>>24) & 1, (u>>6) & 1);
return countbits(h->pi);
}
@@ -867,7 +688,7 @@
{
int wait;
- h->ghc |= 1;
+ h->ghc |= Hhr;
for(wait = 0; wait < 1000; wait += 100){
if(h->ghc == 0)
return 0;
@@ -876,36 +697,27 @@
return -1;
}
-static void
-idmove(char *p, ushort *a, int n)
+static char*
+dnam(Drive *d)
{
- int i;
- char *op, *e;
+ char *s;
- op = p;
- for(i = 0; i < n/2; i++){
- *p++ = a[i] >> 8;
- *p++ = a[i];
- }
- *p = 0;
- while(p > op && *--p == ' ')
- *p = 0;
- e = p;
- for (p = op; *p == ' '; p++)
- ;
- memmove(op, p, n - (e - p));
+ s = d->name;
+ if(d->unit && d->unit->name)
+ s = d->unit->name;
+ return s;
}
static int
identify(Drive *d)
{
+ uchar oserial[21];
ushort *id;
vlong osectors, s;
- uchar oserial[21];
SDunit *u;
id = d->info;
- s = ahciidentify(&d->portc, id);
+ s = ahciidentify(&d->portc, id, &d->secsize, dnam(d));
if(s == -1){
d->state = Derror;
return -1;
@@ -913,17 +725,14 @@
osectors = d->sectors;
memmove(oserial, d->serial, sizeof d->serial);
- u = d->unit;
d->sectors = s;
- d->secsize = u->secsize;
- if(d->secsize == 0)
- d->secsize = 512; /* default */
- d->smartrs = 0;
idmove(d->serial, id+10, 20);
idmove(d->firmware, id+23, 8);
idmove(d->model, id+27, 40);
+ d->wwn = idwwn(d->portc.m, id);
+ u = d->unit;
memset(u->inquiry, 0, sizeof u->inquiry);
u->inquiry[2] = 2;
u->inquiry[3] = 2;
@@ -930,8 +739,8 @@
u->inquiry[4] = sizeof u->inquiry - 4;
memmove(u->inquiry+8, d->model, 40);
- if(osectors != s || memcmp(oserial, d->serial, sizeof oserial) != 0){
- d->mediachange = 1;
+ if(osectors != s || memcmp(oserial, d->serial, sizeof oserial)){
+ d->drivechange = 1;
u->sectors = 0;
}
return 0;
@@ -940,88 +749,102 @@
static void
clearci(Aport *p)
{
- if(p->cmd & Ast) {
+ if(p->cmd & Ast){
p->cmd &= ~Ast;
p->cmd |= Ast;
}
}
+static int
+intel(Ctlr *c)
+{
+ return c->pci->vid == 0x8086;
+}
+
+static int
+ignoreahdrs(Drive *d)
+{
+ return d->portm.feat & Datapi && d->ctlr->type == Tsb600;
+}
+
static void
updatedrive(Drive *d)
{
- ulong cause, serr, s0, pr, ewake;
- char *name;
+ ulong f, cause, serr, s0, pr, ewake;
Aport *p;
static ulong last;
pr = 1;
ewake = 0;
+ f = 0;
p = d->port;
cause = p->isr;
+ if(d->ctlr->type == Tjmicron)
+ cause &= ~Aifs;
serr = p->serror;
p->isr = cause;
- name = "??";
- if(d->unit && d->unit->name)
- name = d->unit->name;
if(p->ci == 0){
- d->portm.flag |= Fdone;
- wakeup(&d->portm);
+ f |= Fdone;
pr = 0;
}else if(cause & Adps)
pr = 0;
if(cause & Ifatal){
ewake = 1;
- dprint("ahci: updatedrive: fatal\n");
+ dprint("%s: fatal\n", dnam(d));
}
if(cause & Adhrs){
- if(p->task & (1<<5|1)){
- dprint("ahci: Adhrs cause %lux serr %lux task %lux\n",
- cause, serr, p->task);
- d->portm.flag |= Ferror;
+ if(p->task & 33){
+ if(ignoreahdrs(d) && serr & ErrE)
+ f |= Fahdrs;
+ dprint("%s: Adhrs cause %lux serr %lux task %lux\n",
+ dnam(d), cause, serr, p->task);
+ f |= Ferror;
ewake = 1;
}
pr = 0;
}
if(p->task & 1 && last != cause)
- dprint("%s: err ca %lux serr %lux task %lux sstat %lux\n",
- name, cause, serr, p->task, p->sstatus);
+ dprint("%s: err ca %lux serr %lux task %lux sstat %.3lux\n",
+ dnam(d), cause, serr, p->task, p->sstatus);
if(pr)
- dprint("%s: upd %lux ta %lux\n", name, cause, p->task);
+ dprint("%s: upd %lux ta %lux\n", dnam(d), cause, p->task);
if(cause & (Aprcs|Aifs)){
s0 = d->state;
- switch(p->sstatus & 7){
- case 0: /* no device */
+ switch(p->sstatus & Smask){
+ case Smissing:
d->state = Dmissing;
break;
- case 1: /* device but no phy comm. */
- if((p->sstatus & 0xF00) == 0x600)
- d->state = Dnew; /* slumbering */
+ case Spresent:
+ if((p->sstatus & Imask) == Islumber)
+ d->state = Dnew;
else
d->state = Derror;
break;
- case 3: /* device & phy comm. estab. */
- /* power mgnt crap for surprise removal */
+ case Sphylink:
+ /* power mgnt crap for suprise removal */
p->ie |= Aprcs|Apcs; /* is this required? */
d->state = Dreset;
break;
- case 4: /* phy off-line */
+ case Sbist:
d->state = Doffline;
break;
}
- dprint("%s: %s → %s [Apcrs] %lux\n", name,
- diskstates[s0], diskstates[d->state], p->sstatus);
- /* print pulled message here. */
+ dprint("%s: %s → %s [Apcrs] %.3lux\n", dnam(d), diskstates[s0],
+ diskstates[d->state], p->sstatus);
if(s0 == Dready && d->state != Dready)
- idprint("%s: pulled\n", name);
+ idprint("%s: pulled\n", dnam(d));
if(d->state != Dready)
- d->portm.flag |= Ferror;
- ewake = 1;
+ f |= Ferror;
+ if(d->state != Dready || p->ci)
+ ewake = 1;
}
p->serror = serr;
- if(ewake){
+ if(ewake)
clearci(p);
+ if(f){
+ d->portm.flag = f;
wakeup(&d->portm);
}
last = cause;
@@ -1032,27 +855,23 @@
{
/*
* bogus code because the first interrupt is currently dropped.
- * likely my fault. serror may be cleared at the wrong time.
+ * likely my fault. serror is maybe cleared at the wrong time.
*/
switch(s){
- case 0: /* no device */
+ default:
+ print("%s: pstatus: bad status %.3lux\n", dnam(d), s);
+ case Smissing:
d->state = Dmissing;
break;
- case 1: /* device but no phy. comm. */
+ case Spresent:
break;
- case 2: /* should this be missing? need testcase. */
- dprint("ahci: pstatus 2\n");
- /* fallthrough */
- case 3: /* device & phy. comm. */
+ case Sphylink:
d->wait = 0;
d->state = Dnew;
break;
- case 4: /* offline */
+ case Sbist:
d->state = Doffline;
break;
- case 6: /* ? not sure this makes sense. TODO */
- d->state = Dnew;
- break;
}
}
@@ -1062,7 +881,7 @@
if(ahciconfigdrive(d->ctlr->hba, &d->portc, d->mode) == -1)
return -1;
ilock(d);
- pstatus(d, d->port->sstatus & 7);
+ pstatus(d, d->port->sstatus & Smask);
iunlock(d);
return 0;
}
@@ -1075,9 +894,9 @@
p = d->port;
det = p->sctl & 7;
- stat = p->sstatus & 7;
+ stat = p->sstatus & Smask;
state = (p->cmd>>28) & 0xf;
- dprint("ahci: resetdisk: icc %ux det %d sdet %d\n", state, det, stat);
+ dprint("%s: resetdisk: icc %ux det %.3ux sdet %.3ux\n", dnam(d), state, det, stat);
ilock(d);
state = d->state;
@@ -1085,13 +904,15 @@
d->portm.flag |= Ferror;
clearci(p); /* satisfy sleep condition. */
wakeup(&d->portm);
- if(stat != 3){ /* device absent or phy not communicating? */
+ d->state = Derror;
+ iunlock(d);
+
+ if(stat != Sphylink){
+ ilock(d);
d->state = Dportreset;
iunlock(d);
return;
}
- d->state = Derror;
- iunlock(d);
qlock(&d->portm);
if(p->cmd&Ast && ahciswreset(&d->portc) == -1){
@@ -1098,15 +919,13 @@
ilock(d);
d->state = Dportreset; /* get a bigger stick. */
iunlock(d);
- } else {
+ }else{
ilock(d);
d->state = Dmissing;
iunlock(d);
-
configdrive(d);
}
- dprint("ahci: resetdisk: %s → %s\n",
- diskstates[state], diskstates[d->state]);
+ dprint("%s: resetdisk: %s → %s\n", dnam(d), diskstates[state], diskstates[d->state]);
qunlock(&d->portm);
}
@@ -1113,7 +932,7 @@
static int
newdrive(Drive *d)
{
- char *name;
+ char *s;
Aportc *c;
Aportm *m;
@@ -1120,22 +939,17 @@
c = &d->portc;
m = &d->portm;
- name = d->unit->name;
- if(name == 0)
- name = "??";
-
- if(d->port->task == 0x80)
- return -1;
qlock(c->m);
- if(setudmamode(c, 5) == -1){
- dprint("%s: can't set udma mode\n", name);
+ setfissig(m, c->p->sig);
+ if(identify(d) == -1){
+ dprint("%s: identify failure\n", dnam(d));
goto lose;
}
- if(identify(d) == -1){
- dprint("%s: identify failure\n", name);
+ if(settxmode(c, m->udma) == -1){
+ dprint("%s: can't set udma mode\n", dnam(d));
goto lose;
}
- if(m->feat & Dpower && setfeatures(c, 0x85) == -1){
+ if(m->feat & Dpower && setfeatures(c, 0x85, 3*1000) == -1){
m->feat &= ~Dpower;
if(ahcirecover(c) == -1)
goto lose;
@@ -1147,13 +961,16 @@
qunlock(c->m);
- idprint("%s: %sLBA %,llud sectors: %s %s %s %s\n", d->unit->name,
- (m->feat & Dllba? "L": ""), d->sectors, d->model, d->firmware,
- d->serial, d->mediachange? "[mediachange]": "");
+ s = "";
+ if(m->feat & Dllba)
+ s = "L";
+ idprint("%s: %sLBA %,lld sectors\n", dnam(d), s, d->sectors);
+ idprint(" %s %s %s %s\n", d->model, d->firmware, d->serial,
+ d->drivechange? "[newdrive]": "");
return 0;
lose:
- idprint("%s: can't be initialized\n", d->unit->name);
+ idprint("%s: can't be initialized\n", dnam(d));
ilock(d);
d->state = Dnull;
iunlock(d);
@@ -1169,12 +986,12 @@
};
static void
-westerndigitalhung(Drive *d)
+hangck(Drive *d)
{
- if((d->portm.feat&Datapi) == 0 && d->active &&
- TK2MS(MACHP(0)->ticks - d->intick) > 5000){
- dprint("%s: drive hung; resetting [%lux] ci %lx\n",
- d->unit->name, d->port->task, d->port->ci);
+ if((d->portm.feat & Datapi) == 0 && d->active &&
+ d->totick != 0 && (long)(Ticks - d->totick) > 0){
+ dprint("%s: drive hung; resetting [%lux] ci %lux\n",
+ dnam(d), d->port->task, d->port->ci);
d->state = Dreset;
}
}
@@ -1188,13 +1005,13 @@
i = -1;
qlock(&d->portm);
- if(ahciportreset(&d->portc) == -1)
- dprint("ahci: doportreset: fails\n");
+ if(ahciportreset(&d->portc, d->mode) == -1)
+ dprint("ahci: ahciportreset fails\n");
else
i = 0;
qunlock(&d->portm);
- dprint("ahci: doportreset: portreset → %s [task %lux]\n",
- diskstates[d->state], d->port->task);
+ dprint("ahci: portreset → %s [task %.4lux ss %.3lux]\n",
+ diskstates[d->state], d->port->task, d->port->sstatus);
return i;
}
@@ -1205,46 +1022,38 @@
switch(d->state){
case Dnull:
case Doffline:
+ if(d->unit)
if(d->unit->sectors != 0){
d->sectors = 0;
- d->mediachange = 1;
+ d->drivechange = 1;
}
- /* fallthrough */
case Dready:
d->wait = 0;
- break;
}
}
+static uint
+maxmode(Ctlr *c)
+{
+ return (c->hba->cap & 0xf*Hiss)/Hiss;
+}
+
static void
checkdrive(Drive *d, int i)
{
- ushort s;
- char *name;
+ ushort s, sig;
- if(d == nil) {
- print("checkdrive: nil d\n");
- return;
- }
ilock(d);
- if(d->unit == nil || d->port == nil) {
- if(0)
- print("checkdrive: nil d->%s\n",
- d->unit == nil? "unit": "port");
- iunlock(d);
- return;
- }
- name = d->unit->name;
s = d->port->sstatus;
if(s)
- d->lastseen = MACHP(0)->ticks;
+ d->lastseen = Ticks;
if(s != olds[i]){
- dprint("%s: status: %04ux -> %04ux: %s\n",
- name, olds[i], s, diskstates[d->state]);
+ dprint("%s: status: %.3ux -> %.3ux: %s\n",
+ dnam(d), olds[i], s, diskstates[d->state]);
olds[i] = s;
d->wait = 0;
}
- westerndigitalhung(d);
+ hangck(d);
switch(d->state){
case Dnull:
case Dready:
@@ -1251,40 +1060,44 @@
break;
case Dmissing:
case Dnew:
- switch(s & 0x107){
- case 1: /* no device (pm), device but no phy. comm. */
- ahciwakeup(d->port);
- /* fall through */
- case 0: /* no device */
+ switch(s & (Iactive|Smask)){
+ case Spresent:
+ ahciwakeup(&d->portc, d->mode);
+ case Smissing:
break;
default:
- dprint("%s: unknown status %04ux\n", name, s);
+ dprint("%s: unknown status %.3ux\n", dnam(d), s);
/* fall through */
- case 0x100: /* active, no device */
+ case Iactive: /* active, no device */
if(++d->wait&Mphywait)
break;
reset:
- if(++d->mode > DMsataii)
- d->mode = 0;
- if(d->mode == DMsatai){ /* we tried everything */
+ if(d->mode == 0)
+ d->mode = maxmode(d->ctlr);
+ else
+ d->mode--;
+ if(d->mode == DMautoneg){
d->state = Dportreset;
goto portreset;
}
- dprint("%s: reset; new mode %s\n", name,
- modename[d->mode]);
+ dprint("%s: reset; new mode %s\n", dnam(d),
+ modes[d->mode]);
iunlock(d);
resetdisk(d);
ilock(d);
break;
- case 0x103: /* active, device, phy. comm. */
+ case Iactive | Sphylink:
+ if(d->unit == nil)
+ break;
if((++d->wait&Midwait) == 0){
- dprint("%s: slow reset %04ux task=%lux; %d\n",
- name, s, d->port->task, d->wait);
+ dprint("%s: slow reset %.3ux task=%lux; %d\n",
+ dnam(d), s, d->port->task, d->wait);
goto reset;
}
s = (uchar)d->port->task;
- if(s == 0x7f || ((d->port->sig >> 16) != 0xeb14 &&
- (s & ~0x17) != (1<<6)))
+ sig = d->port->sig >> 16;
+ if(s == 0x7f || s&ASbsy ||
+ (sig != 0xeb14 && (s & ASdrdy) == 0))
break;
iunlock(d);
newdrive(d);
@@ -1298,8 +1111,8 @@
/* fallthrough */
case Derror:
case Dreset:
- dprint("%s: reset [%s]: mode %d; status %04ux\n",
- name, diskstates[d->state], d->mode, s);
+ dprint("%s: reset [%s]: mode %d; status %.3ux\n",
+ dnam(d), diskstates[d->state], d->mode, s);
iunlock(d);
resetdisk(d);
ilock(d);
@@ -1306,15 +1119,14 @@
break;
case Dportreset:
portreset:
- if(d->wait++ & 0xff && (s & 0x100) == 0)
+ if(d->wait++ & 0xff && (s & Iactive) == 0)
break;
- /* device is active */
- dprint("%s: portreset [%s]: mode %d; status %04ux\n",
- name, diskstates[d->state], d->mode, s);
+ dprint("%s: portreset [%s]: mode %d; status %.3ux\n",
+ dnam(d), diskstates[d->state], d->mode, s);
d->portm.flag |= Ferror;
clearci(d->port);
wakeup(&d->portm);
- if((s & 7) == 0){ /* no device */
+ if((s & Smask) == 0){
d->state = Dmissing;
break;
}
@@ -1350,10 +1162,11 @@
c = a;
ilock(c);
cause = c->hba->isr;
- for(i = 0; i < c->mport; i++){
+ for(i = 0; cause; i++){
m = 1 << i;
if((cause & m) == 0)
continue;
+ cause &= ~m;
d = c->rawdrive + i;
ilock(d);
if(d->port->isr && c->hba->pi & m)
@@ -1365,6 +1178,229 @@
}
static int
+ahciencreset(Ctlr *c)
+{
+ Ahba *h;
+
+ if(c->enctype == Eesb)
+ return 0;
+ h = c->hba;
+ h->emctl |= Emrst;
+ while(h->emctl & Emrst)
+ delay(1);
+ return 0;
+}
+
+/*
+ * from the standard: (http://en.wikipedia.org/wiki/IBPI)
+ * rebuild is preferred as locate+fail; alternate 1hz fail
+ * we're going to assume no locate led.
+ */
+
+enum {
+ Ledsleep = 125, /* 8hz */
+
+ N0 = Ledon*Aled,
+ L0 = Ledon*Aled | Ledon*Locled,
+ L1 = Ledon*Aled | Ledoff*Locled,
+ R0 = Ledon*Aled | Ledon*Locled | Ledon*Errled,
+ R1 = Ledon*Aled | Ledoff*Errled,
+ S0 = Ledon*Aled | Ledon*Locled /*| Ledon*Errled*/, /* botch */
+ S1 = Ledon*Aled | Ledoff*Errled,
+ P0 = Ledon*Aled | Ledon*Errled,
+ P1 = Ledon*Aled | Ledoff*Errled,
+ F0 = Ledon*Aled | Ledon*Errled,
+ C0 = Ledon*Aled | Ledon*Locled,
+ C1 = Ledon*Aled | Ledoff*Locled,
+
+};
+
+//static ushort led3[Ibpilast*8] = {
+//[Ibpinone*8] 0, 0, 0, 0, 0, 0, 0, 0,
+//[Ibpinormal*8] N0, N0, N0, N0, N0, N0, N0, N0,
+//[Ibpirebuild*8] R0, R0, R0, R0, R1, R1, R1, R1,
+//[Ibpilocate*8] L0, L1, L0, L1, L0, L1, L0, L1,
+//[Ibpispare*8] S0, S1, S0, S1, S1, S1, S1, S1,
+//[Ibpipfa*8] P0, P1, P0, P1, P1, P1, P1, P1, /* first 1 sec */
+//[Ibpifail*8] F0, F0, F0, F0, F0, F0, F0, F0,
+//[Ibpicritarray*8] C0, C0, C0, C0, C1, C1, C1, C1,
+//[Ibpifailarray*8] C0, C1, C0, C1, C0, C1, C0, C1,
+//};
+
+static ushort led2[Ibpilast*8] = {
+[Ibpinone*8] 0, 0, 0, 0, 0, 0, 0, 0,
+[Ibpinormal*8] N0, N0, N0, N0, N0, N0, N0, N0,
+[Ibpirebuild*8] R0, R0, R0, R0, R1, R1, R1, R1,
+[Ibpilocate*8] L0, L0, L0, L0, L0, L0, L0, L0,
+[Ibpispare*8] S0, S0, S0, S0, S1, S1, S1, S1,
+[Ibpipfa*8] P0, P1, P0, P1, P1, P1, P1, P1, /* first 1 sec */
+[Ibpifail*8] F0, F0, F0, F0, F0, F0, F0, F0,
+[Ibpicritarray*8] C0, C0, C0, C0, C1, C1, C1, C1,
+[Ibpifailarray*8] C0, C1, C0, C1, C0, C1, C0, C1,
+};
+
+static int
+ledstate(Ledport *p, uint seq)
+{
+ ushort i;
+
+ if(p->led == Ibpipfa && seq%32 >= 8)
+ i = P1;
+ else
+ i = led2[8*p->led + seq%8];
+ if(i != p->ledbits){
+ p->ledbits = i;
+ ledprint("ledstate %,.011ub %ud\n", p->ledbits, seq);
+ return 1;
+ }
+ return 0;
+}
+
+static int
+blink(Drive *d, ulong t)
+{
+ Ahba *h;
+ Ctlr *c;
+ Aledmsg msg;
+
+ if(ledstate(d, t) == 0)
+ return 0;
+ c = d->ctlr;
+ h = c->hba;
+ /* ensure last message has been transmitted */
+ while(h->emctl & Tmsg)
+ microdelay(1);
+ switch(c->enctype){
+ default:
+ panic("%s: bad led type %d\n", dnam(d), c->enctype);
+ case Elmt:
+ memset(&msg, 0, sizeof msg);
+ msg.type = Mled;
+ msg.dsize = 0;
+ msg.msize = sizeof msg - 4;
+ msg.led[0] = d->ledbits;
+ msg.led[1] = d->ledbits>>8;
+ msg.pm = 0;
+ msg.hba = d->driveno;
+ memmove(c->enctx, &msg, sizeof msg);
+ break;
+ }
+ h->emctl |= Tmsg;
+ return 1;
+}
+
+enum {
+ Esbdrv0 = 4, /* start pos in bits */
+ Esbiota = 3, /* shift in bits */
+ Esbact = 1,
+ Esbloc = 2,
+ Esberr = 4,
+};
+
+uint
+esbbits(uint s)
+{
+ uint i, e; /* except after c */
+
+ e = 0;
+ for(i = 0; i < 3; i++)
+ e |= ((s>>3*i & 7) != 0)<<i;
+ return e;
+}
+
+static int
+blinkesb(Ctlr *c, ulong t)
+{
+ uint i, s, u[32/4];
+ uvlong v;
+ Drive *d;
+
+ s = 0;
+ for(i = 0; i < c->ndrive; i++){
+ d = c->drive[i];
+ s |= ledstate(d, t); /* no port mapping */
+ }
+ if(s == 0)
+ return 0;
+ memset(u, 0, sizeof u);
+ for(i = 0; i < c->ndrive; i++){
+ d = c->drive[i];
+ s = Esbdrv0 + Esbiota*i;
+ v = esbbits(d->ledbits) * (1ull << s%32);
+ u[s/32 + 0] |= v;
+ u[s/32 + 1] |= v>>32;
+ }
+ for(i = 0; i < c->encsz; i++)
+ c->enctx[i] = u[i];
+ return 1;
+}
+
+static long
+ahciledr(SDunit *u, Chan *ch, void *a, long n, vlong off)
+{
+ Ctlr *c;
+ Drive *d;
+
+ c = u->dev->ctlr;
+ d = c->drive[u->subno];
+ return ledr(d, ch, a, n, off);
+}
+
+static long
+ahciledw(SDunit *u, Chan *ch, void *a, long n, vlong off)
+{
+ Ctlr *c;
+ Drive *d;
+
+ c = u->dev->ctlr;
+ d = c->drive[u->subno];
+ return ledw(d, ch, a, n, off);
+}
+
+static void
+ledkproc(void*)
+{
+ uchar map[NCtlr];
+ uint i, j, t0, t1;
+ Ctlr *c;
+ Drive *d;
+
+ j = 0;
+ memset(map, 0, sizeof map);
+ for(i = 0; i < niactlr; i++)
+ if(iactlr[i].enctype != 0){
+ ahciencreset(iactlr + i);
+ map[i] = 1;
+ j++;
+ }
+ if(j == 0)
+ pexit("no work", 1);
+ for(i = 0; i < niadrive; i++){
+ iadrive[i]->nled = 3; /* hardcoded */
+ if(iadrive[i]->ctlr->enctype == Eesb)
+ iadrive[i]->nled = 3;
+ iadrive[i]->ledbits = -1;
+ }
+ for(i = 0; ; i++){
+ t0 = Ticks;
+ for(j = 0; j < niadrive; ){
+ c = iadrive[j]->ctlr;
+ if(map[j] == 0)
+ j += c->enctype;
+ else if(c->enctype == Eesb){
+ blinkesb(c, i);
+ j += c->ndrive;
+ }else{
+ d = iadrive[j++];
+ blink(d, i);
+ }
+ }
+ t1 = Ticks;
+ esleep(Ledsleep - TK2MS(t1 - t0));
+ }
+}
+
+static int
iaverify(SDunit *u)
{
Ctlr *c;
@@ -1374,7 +1410,11 @@
d = c->drive[u->subno];
ilock(c);
ilock(d);
- d->unit = u;
+ if(d->unit == nil){
+ d->unit = u;
+ if(c->enctype != 0)
+ sdaddfile(u, "led", 0644, eve, ahciledr, ahciledw);
+ }
iunlock(d);
iunlock(c);
checkdrive(d, d->driveno); /* c->d0 + d->driveno */
@@ -1390,11 +1430,9 @@
c = s->ctlr;
ilock(c);
- if(!c->enabled) {
- if(once == 0) {
- once = 1;
+ if(!c->enabled){
+ if(once == 0)
kproc("iasata", satakproc, 0);
- }
if(c->ndrive == 0)
panic("iaenable: zero s->ctlr->ndrive");
pcisetbme(c->pci);
@@ -1403,6 +1441,8 @@
/* supposed to squelch leftover interrupts here. */
ahcienable(c->hba);
c->enabled = 1;
+ if(++once == niactlr)
+ kproc("ialed", ledkproc, 0);
}
iunlock(c);
return 1;
@@ -1435,84 +1475,40 @@
d = c->drive[unit->subno];
r = 0;
- if(d->portm.feat & Datapi && d->mediachange){
+ if(d->portm.feat & Datapi && d->drivechange){
r = scsionline(unit);
if(r > 0)
- d->mediachange = 0;
+ d->drivechange = 0;
return r;
}
ilock(d);
- if(d->mediachange){
+ if(d->drivechange){
r = 2;
- d->mediachange = 0;
+ d->drivechange = 0;
/* devsd resets this after online is called; why? */
unit->sectors = d->sectors;
- unit->secsize = 512; /* default size */
- } else if(d->state == Dready)
+ unit->secsize = d->secsize;
+ }else if(d->state == Dready)
r = 1;
iunlock(d);
return r;
}
-/* returns locked list! */
static Alist*
-ahcibuild(Drive *d, uchar *cmd, void *data, int n, vlong lba)
+ahcibuild(Aportm *m, int rw, void *data, uint n, vlong lba)
{
- uchar *c, acmd, dir, llba;
+ uchar *c;
+ uint flags;
Alist *l;
- Actab *t;
- Aportm *m;
- Aprdt *p;
- static uchar tab[2][2] = { 0xc8, 0x25, 0xca, 0x35, };
- m = &d->portm;
- dir = *cmd != 0x28;
- llba = m->feat&Dllba? 1: 0;
- acmd = tab[dir][llba];
- qlock(m);
l = m->list;
- t = m->ctab;
- c = t->cfis;
-
- c[0] = 0x27;
- c[1] = 0x80;
- c[2] = acmd;
- c[3] = 0;
-
- c[4] = lba; /* sector lba low 7:0 */
- c[5] = lba >> 8; /* cylinder low lba mid 15:8 */
- c[6] = lba >> 16; /* cylinder hi lba hi 23:16 */
- c[7] = 0xa0 | 0x40; /* obsolete device bits + lba */
- if(llba == 0)
- c[7] |= (lba>>24) & 7;
-
- c[8] = lba >> 24; /* sector (exp) lba 31:24 */
- c[9] = lba >> 32; /* cylinder low (exp) lba 39:32 */
- c[10] = lba >> 48; /* cylinder hi (exp) lba 48:40 */
- c[11] = 0; /* features (exp); */
-
- c[12] = n; /* sector count */
- c[13] = n >> 8; /* sector count (exp) */
- c[14] = 0; /* r */
- c[15] = 0; /* control */
-
- *(ulong*)(c + 16) = 0;
-
- l->flags = 1<<16 | Lpref | 0x5; /* Lpref ?? */
- if(dir == Write)
- l->flags |= Lwrite;
- l->len = 0;
- l->ctab = PCIWADDR(t);
- l->ctabhi = 0;
-
- p = &t->prdt;
- p->dba = PCIWADDR(data);
- p->dbahi = 0;
- if(d->unit == nil)
- panic("ahcibuild: nil d->unit");
- p->count = 1<<31 | (d->unit->secsize*n - 2) | 1;
-
+ c = m->ctab->cfis;
+ rwfis(m, c, rw, n, lba);
+ flags = Lpref;
+ if(rw == SDwrite)
+ flags |= Lwrite;
+ mkalist(m, flags, data, 512*n);
return l;
}
@@ -1519,55 +1515,44 @@
static Alist*
ahcibuildpkt(Aportm *m, SDreq *r, void *data, int n)
{
- int fill, len;
+ uint flags;
uchar *c;
- Alist *l;
Actab *t;
- Aprdt *p;
+ Alist *l;
- qlock(m);
l = m->list;
t = m->ctab;
c = t->cfis;
-
- fill = m->feat&Datapi16? 16: 12;
- if((len = r->clen) > fill)
- len = fill;
- memmove(t->atapi, r->cmd, len);
- memset(t->atapi+len, 0, fill-len);
-
- c[0] = 0x27;
- c[1] = 0x80;
- c[2] = 0xa0;
- if(n != 0)
- c[3] = 1; /* dma */
- else
- c[3] = 0; /* features (exp); */
-
- c[4] = 0; /* sector lba low 7:0 */
- c[5] = n; /* cylinder low lba mid 15:8 */
- c[6] = n >> 8; /* cylinder hi lba hi 23:16 */
- c[7] = 0xa0; /* obsolete device bits */
-
- *(ulong*)(c + 8) = 0;
- *(ulong*)(c + 12) = 0;
- *(ulong*)(c + 16) = 0;
-
- l->flags = 1<<16 | Lpref | Latapi | 0x5;
+ atapirwfis(m, c, r->cmd, r->clen, n);
+ flags = 1<<16 | Lpref | Latapi;
if(r->write != 0 && data)
- l->flags |= Lwrite;
- l->len = 0;
- l->ctab = PCIWADDR(t);
- l->ctabhi = 0;
+ flags |= Lwrite;
+ mkalist(m, flags, data, n);
+ return l;
+}
- if(data == 0)
- return l;
+static Alist*
+ahcibuildfis(Aportm *m, SDreq *r, void *data, uint n)
+{
+ uchar *c;
+ uint flags;
+ Alist *l;
- p = &t->prdt;
- p->dba = PCIWADDR(data);
- p->dbahi = 0;
- p->count = 1<<31 | (n - 2) | 1;
-
+ l = m->list;
+ c = m->ctab->cfis;
+ if((r->ataproto & Pprotom) != Ppkt){
+ memmove(c, r->cmd, r->clen);
+ flags = Lpref;
+ if(r->write || n == 0)
+ flags |= Lwrite;
+ mkalist(m, flags, data, n);
+ }else{
+ atapirwfis(m, c, r->cmd, r->clen, n);
+ flags = 1<<16 | Lpref | Latapi;
+ if(r->write && data)
+ flags |= Lwrite;
+ mkalist(m, flags, data, n);
+ }
return l;
}
@@ -1580,19 +1565,19 @@
if(d->state == Dreset || d->state == Dportreset ||
d->state == Dnew)
return 1;
- δ = MACHP(0)->ticks - d->lastseen;
+ δ = Ticks - d->lastseen;
if(d->state == Dnull || δ > 10*1000)
return -1;
ilock(d);
s = d->port->sstatus;
iunlock(d);
- if((s & 0x700) == 0 && δ > 1500)
- return -1; /* no detect */
- if(d->state == Dready && (s & 7) == 3)
- return 0; /* ready, present & phy. comm. */
+ if((s & Imask) == 0 && δ > 1500)
+ return -1;
+ if(d->state == Dready && (s & Smask) == Sphylink)
+ return 0;
esleep(250);
}
- print("%s: not responding; offline\n", d->unit->name);
+ print("%s: not responding; offline\n", dnam(d));
ilock(d);
d->state = Doffline;
iunlock(d);
@@ -1626,55 +1611,43 @@
}
static int
-iariopkt(SDreq *r, Drive *d)
+io(Drive *d, uint proto, int to, int interrupt)
{
- int n, count, try, max, flag, task;
- char *name;
- uchar *cmd, *data;
+ uint task, flag, rv;
Aport *p;
Asleep as;
- cmd = r->cmd;
- name = d->unit->name;
- p = d->port;
-
- aprint("ahci: iariopkt: %02ux %02ux %c %d %p\n",
- cmd[0], cmd[2], "rw"[r->write], r->dlen, r->data);
- if(cmd[0] == 0x5a && (cmd[2] & 0x3f) == 0x3f)
- return sdmodesense(r, cmd, d->info, sizeof d->info);
- r->rlen = 0;
- count = r->dlen;
- max = 65536;
-
- try = 0;
-retry:
- data = r->data;
- n = count;
- if(n > max)
- n = max;
- ahcibuildpkt(&d->portm, r, data, n);
switch(waitready(d)){
case -1:
- qunlock(&d->portm);
return SDeio;
case 1:
- qunlock(&d->portm);
- esleep(1);
- goto retry;
+ return SDretry;
}
ilock(d);
d->portm.flag = 0;
iunlock(d);
+ p = d->port;
p->ci = 1;
as.p = p;
as.i = 1;
- d->intick = MACHP(0)->ticks;
+ d->totick = 0;
+ if(to > 0)
+ d->totick = Ticks + MS2TK(to) | 1; /* fix fencepost */
d->active++;
while(waserror())
- ;
+ if(interrupt){
+ d->active--;
+ d->port->ci = 0;
+ if(ahcicomreset(&d->portc) == -1){
+ ilock(d);
+ d->state = Dreset;
+ iunlock(d);
+ }
+ return SDtimeout;
+ }
sleep(&d->portm, ahciclear, &as);
poperror();
@@ -1684,45 +1657,117 @@
task = d->port->task;
iunlock(d);
- if(task & (Efatal<<8) || task & (ASbsy|ASdrq) && d->state == Dready){
+ rv = SDok;
+ if(proto & Ppkt){
+ rv = task >> 8 + 4 & 0xf;
+ flag &= ~Fahdrs;
+ flag |= Fdone;
+ }else if(task & (Efatal<<8) || task & (ASbsy|ASdrq) && d->state == Dready){
d->port->ci = 0;
ahcirecover(&d->portc);
task = d->port->task;
flag &= ~Fdone; /* either an error or do-over */
}
- qunlock(&d->portm);
if(flag == 0){
- if(++try == 10){
- print("%s: bad disk\n", name);
- r->status = SDcheck;
- return SDcheck;
- }
- print("%s: retry\n", name);
- goto retry;
+ print("%s: retry\n", dnam(d));
+ return SDretry;
}
- if(flag & Ferror){
- if((task&Eidnf) == 0)
- print("%s: i/o error %ux\n", name, task);
- r->status = SDcheck;
+ if(flag & (Fahdrs | Ferror)){
+ if((task & Eidnf) == 0)
+ print("%s: i/o error %ux\n", dnam(d), task);
return SDcheck;
}
+ return rv;
+}
- data += n;
+static int
+iariopkt(SDreq *r, Drive *d)
+{
+ int n, count, try, max;
+ uchar *cmd;
- r->rlen = data - (uchar*)r->data;
- r->status = SDok;
- return SDok;
+ cmd = r->cmd;
+ aprint("%s: %.2ux %.2ux %c %d %p\n", dnam(d), cmd[0], cmd[2],
+ "rw"[r->write], r->dlen, r->data);
+ r->rlen = 0;
+ count = r->dlen;
+ max = 65536;
+
+ for(try = 0; try < 10; try++){
+ n = count;
+ if(n > max)
+ n = max;
+ qlock(&d->portm);
+ ahcibuildpkt(&d->portm, r, r->data, n);
+ r->status = io(d, Ppkt, 5000, 0);
+ qunlock(&d->portm);
+ switch(r->status){
+ case SDeio:
+ return SDeio;
+ case SDretry:
+ continue;
+ }
+// print("%.2ux :: %.2ux :: %.4ux\n", r->cmd[0], r->status, d->port->task);
+ r->rlen = d->portm.list->len;
+ return SDok;
+ }
+ print("%s: bad disk\n", dnam(d));
+ return r->status = SDcheck;
}
+static long
+ahcibio(SDunit *u, int lun, int write, void *a, long count, uvlong lba)
+{
+ int n, rw, try, status, max;
+ uchar *data;
+ Ctlr *c;
+ Drive *d;
+
+ c = u->dev->ctlr;
+ d = c->drive[u->subno];
+ if(d->portm.feat & Datapi)
+ return scsibio(u, lun, write, a, count, lba);
+
+ max = 128;
+ if(d->portm.feat & Dllba){
+ max = 8192; /* ahci maximum */
+ if(c->type == Tsb600)
+ max = 255; /* errata */
+ }
+ rw = write? SDwrite: SDread;
+ data = a;
+ for(try = 0; try < 10;){
+ n = count;
+ if(n > max)
+ n = max;
+ qlock(&d->portm);
+ ahcibuild(&d->portm, rw, data, n, lba);
+ status = io(d, Pdma, 5000, 0);
+ qunlock(&d->portm);
+ switch(status){
+ case SDeio:
+ return -1;
+ case SDretry:
+ try++;
+ continue;
+ }
+ try = 0;
+ count -= n;
+ lba += n;
+ data += n * u->secsize;
+ if(count == 0)
+ return data - (uchar*)a;
+ }
+ print("%s: bad disk\n", dnam(d));
+ return -1;
+}
+
static int
iario(SDreq *r)
{
- int i, n, count, try, max, flag, task;
- vlong lba;
- char *name;
- uchar *cmd, *data;
- Aport *p;
- Asleep as;
+ int i, n, count, rw;
+ uchar *cmd;
+ uvlong lba;
Ctlr *c;
Drive *d;
SDunit *unit;
@@ -1733,101 +1778,123 @@
if(d->portm.feat & Datapi)
return iariopkt(r, d);
cmd = r->cmd;
- name = d->unit->name;
- p = d->port;
- if(r->cmd[0] == 0x35 || r->cmd[0] == 0x91){
+ if(cmd[0] == 0x35 || cmd[0] == 0x91){
if(flushcache(d) == 0)
return sdsetsense(r, SDok, 0, 0, 0);
return sdsetsense(r, SDcheck, 3, 0xc, 2);
}
- if((i = sdfakescsi(r, d->info, sizeof d->info)) != SDnostatus){
+ if((i = sdfakescsi(r)) != SDnostatus){
r->status = i;
return i;
}
- if(*cmd != 0x28 && *cmd != 0x2a){
- print("%s: bad cmd 0x%.2ux\n", name, cmd[0]);
- r->status = SDcheck;
- return SDcheck;
- }
+ if((i = sdfakescsirw(r, &lba, &count, &rw)) != SDnostatus)
+ return i;
+ n = ahcibio(unit, r->lun, r->write, r->data, count, lba);
+ if(n == -1)
+ return SDeio;
+ r->rlen = n;
+ return SDok;
+}
- lba = cmd[2]<<24 | cmd[3]<<16 | cmd[4]<<8 | cmd[5];
- count = cmd[7]<<8 | cmd[8];
- if(r->data == nil)
- return SDok;
- if(r->dlen < count * unit->secsize)
- count = r->dlen / unit->secsize;
- max = 128;
+static uchar bogusrfis[16] = {
+[Ftype] 0x34,
+[Fioport] 0x40,
+[Fstatus] 0x50,
- try = 0;
-retry:
- data = r->data;
- while(count > 0){
- n = count;
- if(n > max)
- n = max;
- ahcibuild(d, cmd, data, n, lba);
- switch(waitready(d)){
- case -1:
- qunlock(&d->portm);
- return SDeio;
- case 1:
- qunlock(&d->portm);
- esleep(1);
- goto retry;
- }
- ilock(d);
- d->portm.flag = 0;
- iunlock(d);
- p->ci = 1;
+[Fdev] 0xa0,
+};
- as.p = p;
- as.i = 1;
- d->intick = MACHP(0)->ticks;
- d->active++;
+static void
+sdr0(Drive *d)
+{
+ uchar *c;
- while(waserror())
- ;
- sleep(&d->portm, ahciclear, &as);
- poperror();
+ c = d->portm.fis.r;
+ memmove(c, bogusrfis, sizeof bogusrfis);
+ coherence();
+}
- d->active--;
- ilock(d);
- flag = d->portm.flag;
- task = d->port->task;
- iunlock(d);
+static int
+sdr(SDreq *r, Drive *d, int st)
+{
+ uchar *c;
+ uint t;
- if(task & (Efatal<<8) ||
- task & (ASbsy|ASdrq) && d->state == Dready){
- d->port->ci = 0;
- ahcirecover(&d->portc);
- task = d->port->task;
- }
+ if((r->ataproto & Pprotom) == Ppkt){
+ t = d->port->task;
+ if(t & ASerr)
+ st = t >> 8 + 4 & 0xf;
+ }
+ c = d->portm.fis.r;
+ memmove(r->cmd, c, 16);
+ r->status = st;
+ if(st == SDcheck)
+ st = SDok;
+ return st;
+}
+
+static int
+fisreqchk(Sfis *f, SDreq *r)
+{
+ if((r->ataproto & Pprotom) == Ppkt)
+ return SDnostatus;
+ /*
+ * handle oob requests;
+ * restrict & sanitize commands
+ */
+ if(r->clen != 16)
+ error(Eio);
+ if(r->cmd[0] == 0xf0){
+ sigtofis(f, r->cmd);
+ r->status = SDok;
+ return SDok;
+ }
+ r->cmd[0] = 0x27;
+ r->cmd[1] = 0x80;
+ r->cmd[7] |= 0xa0;
+ return SDnostatus;
+}
+
+static int
+iaataio(SDreq *r)
+{
+ int try;
+ Ctlr *c;
+ Drive *d;
+ SDunit *u;
+
+ u = r->unit;
+ c = u->dev->ctlr;
+ d = c->drive[u->subno];
+
+ if((r->status = fisreqchk(&d->portm, r)) != SDnostatus)
+ return r->status;
+ r->rlen = 0;
+ sdr0(d);
+ for(try = 0; try < 10; try++){
+ qlock(&d->portm);
+ ahcibuildfis(&d->portm, r, r->data, r->dlen);
+ r->status = io(d, r->ataproto & Pprotom, -1, 1);
qunlock(&d->portm);
- if(flag == 0){
- if(++try == 10){
- print("%s: bad disk\n", name);
- r->status = SDeio;
- return SDeio;
- }
- iprint("%s: retry %lld\n", name, lba);
- goto retry;
- }
- if(flag & Ferror){
- iprint("%s: i/o error %ux @%,lld\n", name, task, lba);
- r->status = SDeio;
+ switch(r->status){
+ case SDtimeout:
+ return sdsetsense(r, SDcheck, 11, 0, 6);
+ case SDeio:
return SDeio;
+ case SDretry:
+ continue;
}
-
- count -= n;
- lba += n;
- data += n * unit->secsize;
+ r->rlen = r->dlen;
+ if((r->ataproto & Pprotom) == Ppkt)
+ r->rlen = d->portm.list->len;
+ return sdr(r, d, r->status);
}
- r->rlen = data - (uchar*)r->data;
- r->status = SDok;
- return SDok;
+ print("%s: bad disk\n", dnam(d));
+ r->status = SDeio;
+ return SDeio;
}
/*
@@ -1836,54 +1903,156 @@
static int
iaahcimode(Pcidev *p)
{
- dprint("iaahcimode: %ux %ux %ux\n", pcicfgr8(p, 0x91), pcicfgr8(p, 92),
- pcicfgr8(p, 93));
- pcicfgw16(p, 0x92, pcicfgr32(p, 0x92) | 0xf); /* ports 0-3 */
-// pcicfgw8(p, 0x93, pcicfgr32(p, 9x93) | 3); /* ports 4-5 */
+ uint u;
+
+ u = pcicfgr16(p, 0x92);
+ dprint("ahci: %T: iaahcimode %.2ux %.4ux\n", p->tbdf, pcicfgr8(p, 0x91), u);
+ pcicfgw16(p, 0x92, u | 0xf); /* ports 0-15 */
return 0;
}
+enum{
+ Ghc = 0x04/4, /* global host control */
+ Pi = 0x0c/4, /* ports implemented */
+ Cmddec = 1<<15, /* enable command block decode */
+
+ /* Ghc bits */
+ Ahcien = 1<<31, /* ahci enable */
+};
+
static void
iasetupahci(Ctlr *c)
{
- /* disable cmd block decoding. */
- pcicfgw16(c->pci, 0x40, pcicfgr16(c->pci, 0x40) & ~(1<<15));
- pcicfgw16(c->pci, 0x42, pcicfgr16(c->pci, 0x42) & ~(1<<15));
+ pcicfgw16(c->pci, 0x40, pcicfgr16(c->pci, 0x40) & ~Cmddec);
+ pcicfgw16(c->pci, 0x42, pcicfgr16(c->pci, 0x42) & ~Cmddec);
- c->lmmio[0x4/4] |= 1 << 31; /* enable ahci mode (ghc register) */
- c->lmmio[0xc/4] = (1 << 6) - 1; /* 5 ports. (supposedly ro pi reg.) */
+ c->lmmio[Ghc] |= Ahcien;
+ c->lmmio[Pi] = (1 << 6) - 1; /* 5 ports (supposedly ro pi reg) */
/* enable ahci mode; from ich9 datasheet */
pcicfgw16(c->pci, 0x90, 1<<6 | 1<<5);
}
+static void
+sbsetupahci(Pcidev *p)
+{
+ print("sbsetupahci: tweaking %.4ux ccru %.2ux ccrp %.2ux\n",
+ p->did, p->ccru, p->ccrp);
+ pcicfgw8(p, 0x40, pcicfgr8(p, 0x40) | 1);
+ pcicfgw8(p, PciCCRu, 6);
+ pcicfgw8(p, PciCCRp, 1);
+ p->ccru = 6;
+ p->ccrp = 1;
+}
+
static int
+esbenc(Ctlr *c)
+{
+ c->encsz = 1;
+ c->enctx = (ulong*)(c->mmio + 0xa0);
+ c->enctype = Eesb;
+ c->enctx[0] = 0;
+ return 0;
+}
+
+static int
+ahciencinit(Ctlr *c)
+{
+ ulong type, sz, o, *bar;
+ Ahba *h;
+
+ h = c->hba;
+ if(c->type == Tesb)
+ return esbenc(c);
+ if((h->cap & Hems) == 0)
+ return -1;
+ type = h->emctl & Emtype;
+ switch(type){
+ case Esgpio:
+ case Eses2:
+ case Esafte:
+ return -1;
+ case Elmt:
+ break;
+ default:
+ return -1;
+ }
+
+ sz = h->emloc & 0xffff;
+ o = h->emloc>>16;
+ if(sz == 0 || o == 0)
+ return -1;
+ bar = c->lmmio;
+ dprint("size = %.4lux; loc = %.4lux*4\n", sz, o);
+ c->encsz = sz;
+ c->enctx = bar + o;
+ if((h->emctl & Xonly) == 0){
+ if(h->emctl & Smb)
+ c->encrx = bar + o;
+ else
+ c->encrx = bar + o*2;
+ }
+ c->enctype = type;
+ return 0;
+}
+
+static int
didtype(Pcidev *p)
{
+ int type;
+
+ type = Tahci;
switch(p->vid){
+ default:
+ return -1;
case 0x8086:
if((p->did & 0xfffc) == 0x2680)
return Tesb;
+ if((p->did & 0xfffb) == 0x27c1)
+ return Tich; /* 82801g[bh]m */
+ if((p->did & 0xffff) == 0x2821)
+ return Tich; /* 82801h[roh] */
+ if((p->did & 0xfffe) == 0x2824)
+ return Tich; /* 82801h[b] */
+ if((p->did & 0xfeff) == 0x2829)
+ return Tich; /* ich8 */
+ if((p->did & 0xfffe) == 0x2922)
+ return Tich; /* ich9 */
+ if((p->did & 0xffff) == 0x3a02)
+ return Tich; /* 82801jd/do */
+ if((p->did & 0xfefe) == 0x3a22)
+ return Tich; /* ich10, pch */
+ if((p->did & 0xfff7) == 0x3b28)
+ return Tich; /* pchm */
+ if((p->did & 0xfffe) == 0x3b22)
+ return Tich; /* pch */
+ break;
+ case 0x1002:
+ if(p->ccru == 1 || p->ccrp != 1)
+ if(p->did == 0x4380 || p->did == 0x4390)
+ sbsetupahci(p);
+ type = Tsb600;
+ break;
+ case 0x1106:
/*
- * 0x27c4 is the intel 82801 in compatibility (not sata) mode.
+ * unconfirmed report that the programming
+ * interface is set incorrectly.
*/
- if ((p->did & 0xfffb) == 0x27c1 || /* 82801g[bh]m ich7 */
- p->did == 0x2821 || /* 82801h[roh] */
- (p->did & 0xfffe) == 0x2824 || /* 82801h[b] */
- (p->did & 0xfeff) == 0x2829 || /* ich8/9m */
- (p->did & 0xfffe) == 0x2922 || /* ich9 */
- p->did == 0x3a02 || /* 82801jd/do */
- (p->did & 0xfefe) == 0x3a22 || /* ich10, pch */
- (p->did & 0xfff7) == 0x3b28) /* pchm */
- return Tich;
+ if(p->did == 0x3349)
+ return Tahci;
break;
- case 0x1002:
- if(p->did == 0x4380)
- return Tsb600;
+ case 0x10de:
+ case 0x1039:
+ case 0x1b4b:
+ case 0x11ab:
break;
+ case 0x197b:
+ case 0x10b9:
+ type = Tjmicron;
+ break;
}
if(p->ccrb == Pcibcstore && p->ccru == 6 && p->ccrp == 1)
- return Tunk;
+ return type;
return -1;
}
@@ -1895,33 +2064,32 @@
Ctlr *c;
Drive *d;
Pcidev *p;
- SDev *head, *tail, *s;
+ SDev *s;
static int done;
- if(done++)
+ if(done)
return nil;
-
+ done = 1;
memset(olds, 0xff, sizeof olds);
p = nil;
- head = tail = nil;
loop:
while((p = pcimatch(p, 0, 0)) != nil){
- type = didtype(p);
- if (type == -1 || p->mem[Abar].bar == 0)
+ if((type = didtype(p)) == -1)
continue;
+ if(p->mem[Abar].bar == 0)
+ continue;
if(niactlr == NCtlr){
- print("ahci: iapnp: %s: too many controllers\n",
- tname[type]);
+ print("iapnp: %s: too many controllers\n", tname[type]);
break;
}
c = iactlr + niactlr;
- s = sdevs + niactlr;
+ s = sdevs + niactlr;
memset(c, 0, sizeof *c);
memset(s, 0, sizeof *s);
io = p->mem[Abar].bar & ~0xf;
c->mmio = vmap(io, p->mem[Abar].size);
if(c->mmio == 0){
- print("ahci: %s: address 0x%luX in use did=%x\n",
+ print("%s: address %#p in use did %.4ux\n",
Tname(c), io, p->did);
continue;
}
@@ -1930,31 +2098,24 @@
c->type = type;
s->ifc = &sdiahciifc;
- s->idno = 'E' + niactlr;
+ s->idno = 'E';
s->ctlr = c;
c->sdev = s;
- if(Intel(c) && p->did != 0x2681)
+ if(intel(c) && p->did != 0x2681)
iasetupahci(c);
- nunit = ahciconf(c);
// ahcihbareset((Ahba*)c->mmio);
- if(Intel(c) && iaahcimode(p) == -1)
- break;
- if(nunit < 1){
+ nunit = ahciconf(c);
+ if(intel(c) && iaahcimode(p) == -1 || nunit < 1){
vunmap(c->mmio, p->mem[Abar].size);
continue;
}
c->ndrive = s->nunit = nunit;
- c->mport = c->hba->cap & ((1<<5)-1);
- i = (c->hba->cap >> 21) & 1;
- print("#S/sd%c: %s: sata-%s with %d ports\n", s->idno,
- Tname(c), "I\0II" + i*2, nunit);
-
/* map the drives -- they don't all need to be enabled. */
memset(c->rawdrive, 0, sizeof c->rawdrive);
n = 0;
- for(i = 0; i < NCtlrdrv; i++) {
+ for(i = 0; i < NCtlrdrv; i++){
d = c->rawdrive + i;
d->portno = i;
d->driveno = -1;
@@ -1961,8 +2122,9 @@
d->sectors = 0;
d->serial[0] = ' ';
d->ctlr = c;
- if((c->hba->pi & (1<<i)) == 0)
+ if((c->hba->pi & 1<<i) == 0)
continue;
+ snprint(d->name, sizeof d->name, "iahci%d.%d", niactlr, i);
d->port = (Aport*)(c->mmio + 0x80*i + 0x100);
d->portc.p = d->port;
d->portc.m = &d->portm;
@@ -1972,58 +2134,69 @@
}
for(i = 0; i < n; i++)
if(ahciidle(c->drive[i]->port) == -1){
- dprint("ahci: %s: port %d wedged; abort\n",
+ print("%s: port %d wedged; abort\n",
Tname(c), i);
goto loop;
}
for(i = 0; i < n; i++){
- c->drive[i]->mode = DMsatai;
+ c->drive[i]->mode = DMautoneg;
configdrive(c->drive[i]);
}
+ ahciencinit(c);
niadrive += n;
niactlr++;
- if(head)
- tail->next = s;
- else
- head = s;
- tail = s;
+ sdadddevs(s);
+ i = (c->hba->cap >> 21) & 1;
+ print("#S/%s: %s: sata-%s with %d ports\n", s->name,
+ Tname(c), "I\0II" + i*2, nunit);
}
- return head;
+ return nil;
}
-static char* smarttab[] = {
- "unset",
- "error",
- "threshold exceeded",
- "normal"
+static Htab ctab[] = {
+ Aasp, "asp",
+ Aalpe , "alpe ",
+ Adlae, "dlae",
+ Aatapi, "atapi",
+ Apste, "pste",
+ Afbsc, "fbsc",
+ Aesp, "esp",
+ Acpd, "cpd",
+ Ampsp, "mpsp",
+ Ahpcp, "hpcp",
+ Apma, "pma",
+ Acps, "cps",
+ Acr, "cr",
+ Afr, "fr",
+ Ampss, "mpss",
+ Apod, "pod",
+ Asud, "sud",
+ Ast, "st",
};
-static char *
-pflag(char *s, char *e, uchar f)
+static char*
+capfmt(char *p, char *e, Htab *t, int n, ulong cap)
{
- uchar i;
+ uint i;
- for(i = 0; i < 8; i++)
- if(f & (1 << i))
- s = seprint(s, e, "%s ", flagname[i]);
- return seprint(s, e, "\n");
+ *p = 0;
+ for(i = 0; i < n; i++)
+ if(cap & t[i].bit)
+ p = seprint(p, e, "%s ", t[i].name);
+ return p;
}
static int
iarctl(SDunit *u, char *p, int l)
{
- char buf[32];
- char *e, *op;
+ char buf[32], *e, *op;
Aport *o;
Ctlr *c;
Drive *d;
- c = u->dev->ctlr;
- if(c == nil) {
-print("iarctl: nil u->dev->ctlr\n");
+ if((c = u->dev->ctlr) == nil)
return 0;
- }
d = c->drive[u->subno];
o = d->port;
@@ -2033,47 +2206,34 @@
p = seprint(p, e, "model\t%s\n", d->model);
p = seprint(p, e, "serial\t%s\n", d->serial);
p = seprint(p, e, "firm\t%s\n", d->firmware);
- if(d->smartrs == 0xff)
- p = seprint(p, e, "smart\tenable error\n");
- else if(d->smartrs == 0)
- p = seprint(p, e, "smart\tdisabled\n");
- else
- p = seprint(p, e, "smart\t%s\n",
- smarttab[d->portm.smart]);
+ if(d->wwn != 0)
+ p = seprint(p, e, "wwn\t%ullx\n", d->wwn);
p = seprint(p, e, "flag\t");
- p = pflag(p, e, d->portm.feat);
+ p = pflag(p, e, &d->portm);
+ p = seprint(p, e, "udma\t%d\n", d->portm.udma);
}else
p = seprint(p, e, "no disk present [%s]\n", diskstates[d->state]);
serrstr(o->serror, buf, buf + sizeof buf - 1);
- p = seprint(p, e, "reg\ttask %lux cmd %lux serr %lux %s ci %lux is %lux; "
- "sig %lux sstatus %04lux\n", o->task, o->cmd, o->serror, buf,
+ p = seprint(p, e, "reg\ttask %lux cmd %lux serr %lux %s ci %lux is %lux "
+ "sig %lux sstatus %.3lux\n", o->task, o->cmd, o->serror, buf,
o->ci, o->isr, o->sig, o->sstatus);
- if(d->unit == nil)
- panic("iarctl: nil d->unit");
- p = seprint(p, e, "geometry %llud %lud\n", d->sectors, d->unit->secsize);
+ p = seprint(p, e, "cmd\t");
+ p = capfmt(p, e, ctab, nelem(ctab), o->cmd);
+ p = seprint(p, e, "\n");
+ p = seprint(p, e, "mode\t%s %s\n", modes[d->mode], modes[maxmode(c)]);
+ p = seprint(p, e, "geometry %llud %lud\n", d->sectors, u->secsize);
return p - op;
}
static void
-runflushcache(Drive *d)
-{
- long t0;
-
- t0 = MACHP(0)->ticks;
- if(flushcache(d) != 0)
- error(Eio);
- dprint("ahci: flush in %ld ms\n", MACHP(0)->ticks - t0);
-}
-
-static void
forcemode(Drive *d, char *mode)
{
int i;
- for(i = 0; i < nelem(modename); i++)
- if(strcmp(mode, modename[i]) == 0)
+ for(i = 0; i < nelem(modes); i++)
+ if(strcmp(mode, modes[i]) == 0)
break;
- if(i == nelem(modename))
+ if(i == nelem(modes))
i = 0;
ilock(d);
d->mode = i;
@@ -2081,22 +2241,6 @@
}
static void
-runsmartable(Drive *d, int i)
-{
- if(waserror()){
- qunlock(&d->portm);
- d->smartrs = 0;
- nexterror();
- }
- if(lockready(d) == -1)
- error(Eio);
- d->smartrs = smart(&d->portc, i);
- d->portm.smart = 0;
- qunlock(&d->portm);
- poperror();
-}
-
-static void
forcestate(Drive *d, char *state)
{
int i;
@@ -2108,10 +2252,31 @@
error(Ebadctl);
ilock(d);
d->state = i;
+// statechange(d);
iunlock(d);
}
+static int
+runsettxmode(Drive *d, char *s)
+{
+ int i;
+ Aportc *c;
+ Aportm *m;
+ c = &d->portc;
+ m = &d->portm;
+
+ i = 1;
+ if(lockready(d) == 0){
+ m->udma = atoi(s);
+ if(settxmode(c, m->udma) == 0)
+ i = 0;
+ }
+ qunlock(m);
+ return i;
+}
+
+
static int
iawctl(SDunit *u, Cmdbuf *cmd)
{
@@ -2118,62 +2283,20 @@
char **f;
Ctlr *c;
Drive *d;
- uint i;
c = u->dev->ctlr;
d = c->drive[u->subno];
f = cmd->f;
- if(strcmp(f[0], "flushcache") == 0)
- runflushcache(d);
- else if(strcmp(f[0], "identify") == 0){
- i = strtoul(f[1]? f[1]: "0", 0, 0);
- if(i > 0xff)
- i = 0;
- dprint("ahci: %04d %ux\n", i, d->info[i]);
- }else if(strcmp(f[0], "mode") == 0)
+ if(strcmp(f[0], "mode") == 0)
forcemode(d, f[1]? f[1]: "satai");
- else if(strcmp(f[0], "nop") == 0){
- if((d->portm.feat & Dnop) == 0){
- cmderror(cmd, "no drive support");
- return -1;
- }
- if(waserror()){
- qunlock(&d->portm);
- nexterror();
- }
- if(lockready(d) == -1)
- error(Eio);
- nop(&d->portc);
- qunlock(&d->portm);
- poperror();
- }else if(strcmp(f[0], "reset") == 0)
- forcestate(d, "reset");
- else if(strcmp(f[0], "smart") == 0){
- if(d->smartrs == 0){
- cmderror(cmd, "smart not enabled");
- return -1;
- }
- if(waserror()){
- qunlock(&d->portm);
- d->smartrs = 0;
- nexterror();
- }
- if(lockready(d) == -1)
- error(Eio);
- d->portm.smart = 2 + smartrs(&d->portc);
- qunlock(&d->portm);
- poperror();
- }else if(strcmp(f[0], "smartdisable") == 0)
- runsmartable(d, 1);
- else if(strcmp(f[0], "smartenable") == 0)
- runsmartable(d, 0);
else if(strcmp(f[0], "state") == 0)
forcestate(d, f[1]? f[1]: "null");
- else{
+ else if(strcmp(f[0], "txmode") == 0){
+ if(runsettxmode(d, f[1]? f[1]: "0"))
+ cmderror(cmd, "bad txmode / stuck port");
+ }else
cmderror(cmd, Ebadctl);
- return -1;
- }
return 0;
}
@@ -2202,42 +2325,63 @@
return p;
}
-/* must emit exactly one line per controller (sd(3)) */
+static Htab htab[] = {
+ H64a, "64a",
+ Hncq, "ncq",
+ Hsntf, "ntf",
+ Hmps, "mps",
+ Hss, "ss",
+ Halp, "alp",
+ Hal, "led",
+ Hclo, "clo",
+ Ham, "am",
+ Hpm, "pm",
+ Hfbs, "fbs",
+ Hpmb, "pmb",
+ Hssc, "slum",
+ Hpsc, "pslum",
+ Hcccs, "coal",
+ Hems, "ems",
+ Hxs, "xs",
+};
+
+static Htab htab2[] = {
+ Apts, "apts",
+ Nvmp, "nvmp",
+ Boh, "boh",
+};
+
+static Htab emtab[] = {
+ Pm, "pm",
+ Alhd, "alhd",
+ Xonly, "xonly",
+ Smb, "smb",
+ Esgpio, "esgpio",
+ Eses2, "eses2",
+ Esafte, "esafte",
+ Elmt, "elmt",
+};
+
static char*
-iartopctl(SDev *sdev, char *p, char *e)
+iartopctl(SDev *s, char *p, char *e)
{
- ulong cap;
char pr[25];
- Ahba *hba;
- Ctlr *ctlr;
+ ulong cap;
+ Ahba *h;
+ Ctlr *c;
-#define has(x, str) if(cap & (x)) p = seprint(p, e, "%s ", (str))
-
- ctlr = sdev->ctlr;
- hba = ctlr->hba;
- p = seprint(p, e, "sd%c ahci port %#p: ", sdev->idno, hba);
- cap = hba->cap;
- has(Hs64a, "64a");
- has(Hsalp, "alp");
- has(Hsam, "am");
- has(Hsclo, "clo");
- has(Hcccs, "coal");
- has(Hems, "ems");
- has(Hsal, "led");
- has(Hsmps, "mps");
- has(Hsncq, "ncq");
- has(Hssntf, "ntf");
- has(Hspm, "pm");
- has(Hpsc, "pslum");
- has(Hssc, "slum");
- has(Hsss, "ss");
- has(Hsxs, "sxs");
- portr(pr, pr + sizeof pr, hba->pi);
+ c = s->ctlr;
+ h = c->hba;
+ cap = h->cap;
+ p = seprint(p, e, "sd%c ahci %s port %#p: ", s->idno, Tname(c), h);
+ p = capfmt(p, e, htab, nelem(htab), cap);
+ p = capfmt(p, e, htab2, nelem(htab2), h->cap2);
+ p = capfmt(p, e, emtab, nelem(emtab), h->emctl);
+ portr(pr, pr + sizeof pr, h->pi);
return seprint(p, e,
- "iss %ld ncs %ld np %ld; ghc %lux isr %lux pi %lux %s ver %lux\n",
+ "iss %ld ncs %ld np %ld ghc %lux isr %lux pi %lux %s ver %lux\n",
(cap>>20) & 0xf, (cap>>8) & 0x1f, 1 + (cap & 0x1f),
- hba->ghc, hba->isr, hba->pi, pr, hba->ver);
-#undef has
+ h->ghc, h->isr, h->pi, pr, h->ver);
}
static int
@@ -2249,8 +2393,6 @@
f = cmd->f;
v = 0;
- if (f[0] == nil)
- return 0;
if(strcmp(f[0], "debug") == 0)
v = &debug;
else if(strcmp(f[0], "idprint") == 0)
@@ -2257,6 +2399,8 @@
v = &prid;
else if(strcmp(f[0], "aprint") == 0)
v = &datapi;
+ else if(strcmp(f[0], "ledprint") == 0)
+ v = &dled;
else
cmderror(cmd, Ebadctl);
@@ -2277,7 +2421,7 @@
}
SDifc sdiahciifc = {
- "iahci",
+ "ahci",
iapnp,
nil, /* legacy */
@@ -2290,9 +2434,10 @@
iarctl,
iawctl,
- scsibio,
+ ahcibio,
nil, /* probe */
nil, /* clear */
iartopctl,
iawtopctl,
+ iaataio,
};
--- /dev/null
+++ b/sys/src/9/pc/sdide.c
@@ -1,0 +1,2524 @@
+#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 "../port/sd.h"
+#include <fis.h>
+
+#define HOWMANY(x, y) (((x)+((y)-1))/(y))
+#define ROUNDUP(x, y) (HOWMANY((x), (y))*(y))
+#define uprint(...) snprint(up->genbuf, sizeof up->genbuf, __VA_ARGS__);
+#pragma varargck argpos atadebug 3
+
+extern SDifc sdideifc;
+
+enum {
+ DbgCONFIG = 0x0001, /* detected drive config info */
+ DbgIDENTIFY = 0x0002, /* detected drive identify info */
+ DbgSTATE = 0x0004, /* dump state on panic */
+ DbgPROBE = 0x0008, /* trace device probing */
+ DbgDEBUG = 0x0080, /* the current problem... */
+ DbgINL = 0x0100, /* That Inil20+ message we hate */
+ Dbg48BIT = 0x0200, /* 48-bit LBA */
+ DbgBsy = 0x0400, /* interrupt but Bsy (shared IRQ) */
+ DbgAtazz = 0x0800, /* debug raw ata io */
+};
+#define DEBUG (DbgDEBUG|DbgSTATE)
+
+enum { /* I/O ports */
+ Data = 0,
+ Error = 1, /* (read) */
+ Features = 1, /* (write) */
+ Count = 2, /* sector count<7-0>, sector count<15-8> */
+ Ir = 2, /* interrupt reason (PACKET) */
+ Sector = 3, /* sector number */
+ Lbalo = 3, /* LBA<7-0>, LBA<31-24> */
+ Cyllo = 4, /* cylinder low */
+ Bytelo = 4, /* byte count low (PACKET) */
+ Lbamid = 4, /* LBA<15-8>, LBA<39-32> */
+ Cylhi = 5, /* cylinder high */
+ Bytehi = 5, /* byte count hi (PACKET) */
+ Lbahi = 5, /* LBA<23-16>, LBA<47-40> */
+ Dh = 6, /* Device/Head, LBA<27-24> */
+ 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 */
+ Devs = Dev0 | Dev1,
+ 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 */
+ Crs = 0x20, /* Read Sectors */
+ Crs48 = 0x24, /* Read Sectors Ext */
+ Crd48 = 0x25, /* Read w/ DMA Ext */
+ Crsm48 = 0x29, /* Read Multiple Ext */
+ Cws = 0x30, /* Write Sectors */
+ Cws48 = 0x34, /* Write Sectors Ext */
+ Cwd48 = 0x35, /* Write w/ DMA Ext */
+ Cwsm48 = 0x39, /* Write Multiple Ext */
+ Cedd = 0x90, /* Execute Device Diagnostics */
+ Cpkt = 0xA0, /* Packet */
+ Cidpkt = 0xA1, /* Identify Packet Device */
+ Crsm = 0xC4, /* Read Multiple */
+ Cwsm = 0xC5, /* Write Multiple */
+ Csm = 0xC6, /* Set Multiple */
+ Crd = 0xC8, /* Read DMA */
+ Cwd = 0xCA, /* Write DMA */
+ Cid = 0xEC, /* Identify Device */
+};
+
+enum { /* Device Control */
+ Nien = 0x02, /* (not) Interrupt Enable */
+ Srst = 0x04, /* Software Reset */
+ Hob = 0x80, /* High Order Bit [sic] */
+};
+
+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, /* End of Transfer */
+};
+
+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 */
+ Ilba = 60, /* 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 */
+ Icsfs = 82, /* command set/feature supported */
+ Icsfe = 85, /* command set/feature enabled */
+ Iudma = 88, /* ultra DMA mode */
+ Ierase = 89, /* time for security erase */
+ Ieerase = 90, /* time for enhanced security erase */
+ Ipower = 91, /* current advanced power management */
+ Ilba48 = 100, /* 48-bit LBA size (64 bits in 100-103) */
+ Irmsn = 127, /* removable status notification */
+ Isecstat = 128, /* security status */
+ Icfapwr = 160, /* CFA power mode */
+ Imediaserial = 176, /* current media serial number */
+ Icksum = 255, /* checksum */
+};
+
+enum { /* bit masks for config identify info */
+ Mpktsz = 0x0003, /* packet command size */
+ Mincomplete = 0x0004, /* incomplete information */
+ Mdrq = 0x0060, /* DRQ type */
+ Mrmdev = 0x0080, /* device is removable */
+ Mtype = 0x1F00, /* device type */
+ Mproto = 0x8000, /* command protocol */
+};
+
+enum { /* bit masks for capabilities identify info */
+ Mdma = 0x0100, /* DMA supported */
+ Mlba = 0x0200, /* LBA supported */
+ Mnoiordy = 0x0400, /* IORDY may be disabled */
+ Miordy = 0x0800, /* IORDY supported */
+ Msoftrst = 0x1000, /* needs soft reset when Bsy */
+ Mqueueing = 0x4000, /* queueing overlap supported */
+ Midma = 0x8000, /* interleaved DMA supported */
+};
+
+enum { /* bit masks for supported/enabled features */
+ Msmart = 0x0001,
+ Msecurity = 0x0002,
+ Mrmmedia = 0x0004,
+ Mpwrmgmt = 0x0008,
+ Mpkt = 0x0010,
+ Mwcache = 0x0020,
+ Mlookahead = 0x0040,
+ Mrelirq = 0x0080,
+ Msvcirq = 0x0100,
+ Mreset = 0x0200,
+ Mprotected = 0x0400,
+ Mwbuf = 0x1000,
+ Mrbuf = 0x2000,
+ Mnop = 0x4000,
+ Mmicrocode = 0x0001,
+ Mqueued = 0x0002,
+ Mcfa = 0x0004,
+ Mapm = 0x0008,
+ Mnotify = 0x0010,
+ Mspinup = 0x0040,
+ Mmaxsec = 0x0100,
+ Mautoacoustic = 0x0200,
+ Maddr48 = 0x0400,
+ Mdevconfov = 0x0800,
+ Mflush = 0x1000,
+ Mflush48 = 0x2000,
+ Msmarterror = 0x0001,
+ Msmartselftest = 0x0002,
+ Mmserial = 0x0004,
+ Mmpassthru = 0x0008,
+ Mlogging = 0x0020,
+};
+
+typedef struct Ctlr Ctlr;
+typedef struct Drive Drive;
+
+typedef struct Prd { /* Physical Region Descriptor */
+ ulong pa; /* Physical Base Address */
+ int count;
+} Prd;
+
+enum {
+ BMspan = 64*1024, /* must be power of 2 <= 64*1024 */
+
+ Nprd = SDmaxio/BMspan+2,
+};
+
+typedef struct Ctlr {
+ int cmdport;
+ int ctlport;
+ int irq;
+ int tbdf;
+ int bmiba; /* bus master interface base address */
+ int maxio; /* sector count transfer maximum */
+ int span; /* don't span this boundary with dma */
+
+ Pcidev* pcidev;
+ void (*ienable)(Ctlr*);
+ void (*idisable)(Ctlr*);
+ SDev* sdev;
+
+ Drive* drive[2];
+
+ Prd* prdt; /* physical region descriptor table */
+ void (*irqack)(Ctlr*);
+
+ QLock; /* current command */
+ Drive* curdrive;
+ int command; /* last command issued (debugging) */
+ Rendez;
+ int done;
+ uint nrq;
+ uint nildrive;
+ uint bsy;
+
+ Lock; /* register access */
+} Ctlr;
+
+typedef struct Drive {
+ Ctlr* ctlr;
+ SDunit *unit;
+
+ int dev;
+ ushort info[256];
+ Sfis;
+
+ int dma; /* DMA R/W possible */
+ int dmactl;
+ int rwm; /* read/write multiple possible */
+ int rwmctl;
+
+ int pkt; /* PACKET device, length of pktcmd */
+ uchar pktcmd[16];
+ int pktdma; /* this PACKET command using dma */
+
+ uvlong sectors;
+ uint secsize;
+ char serial[20+1];
+ char firmware[8+1];
+ char model[40+1];
+
+ QLock; /* drive access */
+ int command; /* current command */
+ int write;
+ uchar* data;
+ int dlen;
+ uchar* limit;
+ int count; /* sectors */
+ int block; /* R/W bytes per block */
+ int status;
+ int error;
+ int flags; /* internal flags */
+ uint missirq;
+ uint spurloop;
+ uint irq;
+ uint bsy;
+} Drive;
+
+enum { /* internal flags */
+ Lba48always = 0x2, /* ... */
+ Online = 0x4, /* drive onlined */
+};
+
+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 void
+atadumpstate(Drive* drive, SDreq *r, uvlong lba, int count)
+{
+ Prd *prd;
+ Pcidev *p;
+ Ctlr *ctlr;
+ int i, bmiba, ccnt;
+ uvlong clba;
+
+ if(!(DEBUG & DbgSTATE))
+ return;
+
+ ctlr = drive->ctlr;
+ print("command %2.2uX\n", ctlr->command);
+ print("data %8.8p limit %8.8p dlen %d status %uX error %uX\n",
+ drive->data, drive->limit, drive->dlen,
+ drive->status, drive->error);
+ if(r->clen == -16)
+ clba = fisrw(nil, r->cmd, &ccnt);
+ else
+ sdfakescsirw(r, &clba, &ccnt, 0);
+ print("lba %llud -> %llud, count %d -> %d (%d)\n",
+ clba, lba, ccnt, count, drive->count);
+ if(!(inb(ctlr->ctlport+As) & Bsy)){
+ for(i = 1; i < 7; i++)
+ print(" 0x%2.2uX", inb(ctlr->cmdport+i));
+ print(" 0x%2.2uX\n", inb(ctlr->ctlport+As));
+ }
+ if(drive->command == Cwd || drive->command == Crd
+ || drive->command == (Pdma|Pin) || drive->command == (Pdma|Pout)){
+ bmiba = ctlr->bmiba;
+ prd = ctlr->prdt;
+ print("bmicx %2.2uX bmisx %2.2uX prdt %8.8p\n",
+ inb(bmiba+Bmicx), inb(bmiba+Bmisx), prd);
+ for(;;){
+ print("pa 0x%8.8luX count %8.8uX\n",
+ prd->pa, prd->count);
+ if(prd->count & PrdEOT)
+ break;
+ prd++;
+ }
+ }
+ if(ctlr->pcidev && ctlr->pcidev->vid == 0x8086){
+ p = ctlr->pcidev;
+ print("0x40: %4.4uX 0x42: %4.4uX ",
+ pcicfgr16(p, 0x40), pcicfgr16(p, 0x42));
+ print("0x48: %2.2uX\n", pcicfgr8(p, 0x48));
+ print("0x4A: %4.4uX\n", pcicfgr16(p, 0x4A));
+ }
+}
+
+static void
+atadebug(int cmdport, int ctlport, char* fmt, ...)
+{
+ char *p, *e, buf[PRINTSIZE];
+ int i;
+ va_list arg;
+
+ if(!(DEBUG & DbgPROBE))
+ return;
+
+ p = buf;
+ e = buf + sizeof buf;
+ va_start(arg, fmt);
+ p = vseprint(p, e, fmt, arg);
+ va_end(arg);
+
+ if(cmdport){
+ if(p > buf && p[-1] == '\n')
+ p--;
+ p = seprint(p, e, " ataregs 0x%uX:", cmdport);
+ for(i = Features; i < Command; i++)
+ p = seprint(p, e, " 0x%2.2uX", inb(cmdport+i));
+ if(ctlport)
+ p = seprint(p, e, " 0x%2.2uX", inb(ctlport+As));
+ p = seprint(p, e, "\n");
+ }
+ putstrn(buf, p - buf);
+}
+
+static int
+ataready(int cmdport, int ctlport, int dev, int reset, int ready, int m)
+{
+ int as, m0;
+
+ atadebug(cmdport, ctlport, "ataready: dev %ux:%ux reset %ux ready %ux",
+ cmdport, dev, reset, ready);
+ m0 = m;
+ do{
+ /*
+ * 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 if necessary 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(as & reset){
+ /* nothing to do */
+ }
+ else if(dev){
+ outb(cmdport+Dh, dev);
+ dev = 0;
+ }
+ else if(ready == 0 || (as & ready)){
+ atadebug(0, 0, "ataready: %d:%d %#.2ux\n", m, m0, as);
+ return as;
+ }
+ microdelay(1);
+ }while(m-- > 0);
+ atadebug(0, 0, "ataready: timeout %d %#.2ux\n", m0, as);
+ return -1;
+}
+
+static int
+atadone(void* arg)
+{
+ return ((Ctlr*)arg)->done;
+}
+
+static int
+atarwmmode(Drive* drive, int cmdport, int ctlport, int dev)
+{
+ int as, maxrwm, rwm;
+
+ maxrwm = drive->info[Imaxrwm] & 0xFF;
+ if(maxrwm == 0)
+ return 0;
+
+ /*
+ * Sometimes drives come up with the current count set
+ * to 0; if so, set a suitable value, otherwise believe
+ * the value in Irwm if the 0x100 bit is set.
+ */
+ if(drive->info[Irwm] & 0x100)
+ rwm = drive->info[Irwm] & 0xFF;
+ else
+ rwm = 0;
+ if(rwm == 0)
+ rwm = maxrwm;
+ if(rwm > 16)
+ rwm = 16;
+ if(ataready(cmdport, ctlport, dev, Bsy|Drq, Drdy, 102*1000) < 0)
+ return 0;
+ outb(cmdport+Count, rwm);
+ outb(cmdport+Command, Csm);
+ microdelay(1);
+ as = ataready(cmdport, ctlport, 0, Bsy, Drdy|Df|Err, 1000);
+ inb(cmdport+Status);
+ if(as < 0 || (as & (Df|Err)))
+ return 0;
+
+ drive->rwm = rwm;
+
+ return rwm;
+}
+
+static int
+atadmamode(SDunit *unit, Drive* drive)
+{
+ char buf[32], *s;
+ int dma;
+
+ /*
+ * Check if any DMA mode enabled.
+ * Assumes the BIOS has picked and enabled the best.
+ * This is completely passive at the moment, no attempt is
+ * made to ensure the hardware is correctly set up.
+ */
+ dma = drive->info[Imwdma] & 0x0707;
+ drive->dma = (dma>>8) & dma;
+ if(drive->dma == 0 && (drive->info[Ivalid] & 0x04)){
+ dma = drive->info[Iudma] & 0x7F7F;
+ drive->dma = (dma>>8) & dma;
+ if(drive->dma)
+ drive->dma |= 'U'<<16;
+ }
+ if(unit != nil){
+ snprint(buf, sizeof buf, "*%sdma", unit->name);
+ if((s = getconf(buf)) && strcmp(s, "on") == 0){
+ print("set %s dma\n", unit->name);
+ drive->dmactl = drive->dma;
+ }
+ }
+ return dma;
+}
+
+static int
+ataidentify(Ctlr*, int cmdport, int ctlport, int dev, int pkt, void* info)
+{
+ int as, command, drdy;
+
+ if(pkt){
+ command = Cidpkt;
+ drdy = 0;
+ }
+ else{
+ command = Cid;
+ drdy = Drdy;
+ }
+ dev &= ~Lba;
+ as = ataready(cmdport, ctlport, dev, Bsy|Drq, drdy, 103*1000);
+ if(as < 0)
+ return as;
+ outb(cmdport+Command, command);
+ microdelay(1);
+
+ as = ataready(cmdport, ctlport, 0, Bsy, Drq|Err, 400*1000);
+ if(as < 0)
+ return -1;
+ if(as & Err)
+ return as;
+
+ memset(info, 0, 512);
+ inss(cmdport+Data, info, 256);
+ ataready(cmdport, ctlport, dev, Bsy|Drq, Drdy, 3*1000);
+ inb(cmdport+Status);
+
+ return 0;
+}
+
+static Drive*
+atadrive(SDunit *unit, Drive *drive, int cmdport, int ctlport, int dev)
+{
+ int as, pkt;
+ uchar buf[512], oserial[21];
+ uvlong osectors;
+ Ctlr *ctlr;
+
+ if(DEBUG & DbgIDENTIFY)
+ print("identify: port %ux dev %.2ux\n", cmdport, dev & ~Lba);
+ atadebug(0, 0, "identify: port 0x%uX dev 0x%2.2uX\n", cmdport, dev);
+ pkt = 1;
+ if(drive != nil){
+ osectors = drive->sectors;
+ memmove(oserial, drive->serial, sizeof drive->serial);
+ ctlr = drive->ctlr;
+ }else{
+ osectors = 0;
+ memset(oserial, 0, sizeof drive->serial);
+ ctlr = nil;
+ }
+retry:
+ as = ataidentify(ctlr, cmdport, ctlport, dev, pkt, buf);
+ if(as < 0)
+ return nil;
+ if(as & Err){
+ if(pkt == 0)
+ return nil;
+ pkt = 0;
+ goto retry;
+ }
+
+ if(drive == 0){
+ if((drive = malloc(sizeof(Drive))) == nil)
+ return nil;
+ drive->serial[0] = ' ';
+ drive->dev = dev;
+ }
+
+ memmove(drive->info, buf, sizeof(drive->info));
+
+ setfissig(drive, pkt? 0xeb140000: 0x0101);
+ drive->sectors = idfeat(drive, drive->info);
+ drive->secsize = idss(drive, drive->info);
+
+ idmove(drive->serial, drive->info+10, 20);
+ idmove(drive->firmware, drive->info+23, 8);
+ idmove(drive->model, drive->info+27, 40);
+ if(unit != nil){
+ memset(unit->inquiry, 0, sizeof unit->inquiry);
+ unit->inquiry[2] = 2;
+ unit->inquiry[3] = 2;
+ unit->inquiry[4] = sizeof unit->inquiry - 4;
+ memmove(unit->inquiry+8, drive->model, 40);
+ }
+
+ if(pkt){
+ drive->pkt = 12;
+ if(drive->feat & Datapi16)
+ drive->pkt = 16;
+ }else{
+ if(drive->feat & Dlba)
+ drive->dev |= Lba;
+ atarwmmode(drive, cmdport, ctlport, dev);
+ }
+ atadmamode(unit, drive);
+
+ if(osectors != 0 && memcmp(oserial, drive->serial, sizeof oserial) != 0)
+ if(unit)
+ unit->sectors = 0;
+ drive->unit = unit;
+ if(DEBUG & DbgCONFIG){
+ print("dev %2.2uX port %uX config %4.4uX capabilities %4.4uX",
+ dev, cmdport, drive->info[Iconfig], drive->info[Icapabilities]);
+ print(" mwdma %4.4uX", drive->info[Imwdma]);
+ if(drive->info[Ivalid] & 0x04)
+ print(" udma %4.4uX", drive->info[Iudma]);
+ print(" dma %8.8uX rwm %ud", drive->dma, drive->rwm);
+ if(drive->feat&Dllba)
+ print("\tLLBA sectors %llud", drive->sectors);
+ print("\n");
+ }
+
+ return drive;
+}
+
+static void
+atasrst(int ctlport)
+{
+ int dc0;
+
+ /*
+ * 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.
+ */
+ dc0 = inb(ctlport+Dc);
+ microdelay(5);
+ outb(ctlport+Dc, Srst|dc0);
+ microdelay(5);
+ outb(ctlport+Dc, dc0);
+ microdelay(2*1000);
+}
+
+static int
+seldev(int dev, int map)
+{
+ if((dev & Devs) == Dev0 && map&1)
+ return dev;
+ if((dev & Devs) == Dev1 && map&2)
+ return dev;
+ return -1;
+}
+
+static SDev*
+ataprobe(int cmdport, int ctlport, int irq, int map)
+{
+ Ctlr* ctlr;
+ SDev *sdev;
+ Drive *drive;
+ int dev, error, rhi, rlo;
+ static int nonlegacy = 'C';
+
+ if(ioalloc(cmdport, 8, 0, "atacmd") < 0) {
+ print("ataprobe: Cannot allocate %X\n", cmdport);
+ return nil;
+ }
+ if(ioalloc(ctlport+As, 1, 0, "atactl") < 0){
+ print("ataprobe: Cannot allocate %X\n", ctlport + As);
+ 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.
+ */
+ if((dev = seldev(Dev0, map)) == -1)
+ if((dev = seldev(Dev1, map)) == -1)
+ goto release;
+ if(inb(ctlport+As) & Bsy){
+ outb(cmdport+Dh, dev);
+ microdelay(1);
+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 || (dev = seldev(Dev1, map)) == -1){
+release:
+ outb(cmdport+Dc, Nien);
+ inb(cmdport+Status);
+ /* further measures to prevent irqs? */
+ iofree(cmdport);
+ iofree(ctlport+As);
+ return nil;
+ }
+ 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, 105*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, 106*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;
+ if((dev = seldev(Dev1, map)) == -1)
+ goto release;
+ 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 = atadrive(0, 0, 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 = atadrive(0, 0, cmdport, ctlport, Dev1);
+ if(drive != nil){
+ drive->ctlr = ctlr;
+ ctlr->drive[1] = drive;
+ }
+ else{
+ outb(cmdport+Dh, Dev0);
+ microdelay(1);
+ }
+ }
+ }
+ else
+ ctlr->drive[1] = drive;
+
+ ctlr->cmdport = cmdport;
+ ctlr->ctlport = ctlport;
+ ctlr->irq = irq;
+ ctlr->tbdf = BUSUNKNOWN;
+ ctlr->command = Cedd; /* debugging */
+
+ switch(cmdport){
+ default:
+ sdev->idno = nonlegacy;
+ break;
+ case 0x1F0:
+ sdev->idno = 'C';
+ nonlegacy = 'E';
+ break;
+ case 0x170:
+ sdev->idno = 'D';
+ nonlegacy = 'E';
+ break;
+ }
+ sdev->ifc = &sdideifc;
+ sdev->ctlr = ctlr;
+ sdev->nunit = 2;
+ ctlr->sdev = sdev;
+
+ return sdev;
+}
+
+static void
+ataclear(SDev *sdev)
+{
+ Ctlr* ctlr;
+
+ ctlr = sdev->ctlr;
+ iofree(ctlr->cmdport);
+ iofree(ctlr->ctlport + As);
+
+ if (ctlr->drive[0])
+ free(ctlr->drive[0]);
+ if (ctlr->drive[1])
+ free(ctlr->drive[1]);
+ if (sdev->name)
+ free(sdev->name);
+ if (sdev->unitflg)
+ free(sdev->unitflg);
+ if (sdev->unit)
+ free(sdev->unit);
+ free(ctlr);
+ free(sdev);
+}
+
+static char *
+atastat(SDev *sdev, char *p, char *e)
+{
+ Ctlr *ctlr;
+
+ ctlr = sdev->ctlr;
+// return seprint(p, e, "%s ata port %X ctl %X irq %d %T\n",
+// sdev->name, ctlr->cmdport, ctlr->ctlport, ctlr->irq, ctlr->tbdf);
+ return seprint(p, e, "%s ata port %X ctl %X irq %d\n",
+ sdev->name, ctlr->cmdport, ctlr->ctlport, ctlr->irq);
+}
+
+static SDev*
+ataprobew(DevConf *cf)
+{
+ char *p;
+ ISAConf isa;
+
+ if (cf->nports != 2)
+ error(Ebadarg);
+
+ memset(&isa, 0, sizeof isa);
+ isa.port = cf->ports[0].port;
+ isa.irq = cf->intnum;
+ if((p=strchr(cf->type, '/')) == nil || pcmspecial(p+1, &isa) < 0)
+ error("cannot find controller");
+
+ return ataprobe(cf->ports[0].port, cf->ports[1].port, cf->intnum, 3);
+}
+
+static void atainterrupt(Ureg*, void*);
+
+static int
+iowait(Drive *drive, int ms, int interrupt)
+{
+ int msec, step;
+ Ctlr *ctlr;
+
+ step = 1000;
+ if(drive->missirq > 10)
+ step = 50;
+ ctlr = drive->ctlr;
+ for(msec = 0; msec < ms; msec += step){
+ while(waserror())
+ if(interrupt)
+ return -1;
+ tsleep(ctlr, atadone, ctlr, step);
+ poperror();
+ if(ctlr->done)
+ break;
+ atainterrupt(nil, ctlr);
+ if(ctlr->done){
+ if(drive->missirq++ < 3)
+ print("ide: caught missed irq\n");
+ break;
+ }else
+ drive->spurloop++;
+ }
+ return ctlr->done;
+}
+
+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);
+ ctlr->command = Cnop; /* debugging */
+ outb(cmdport+Command, Cnop);
+
+ 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, int dolock)
+{
+ /*
+ * If NOP is available use it otherwise
+ * must try a software reset.
+ */
+ if(dolock)
+ ilock(drive->ctlr);
+ if(drive->feat & Dnop)
+ atanop(drive, 0);
+ else{
+ atasrst(drive->ctlr->ctlport);
+ drive->error |= Abrt;
+ }
+ if(dolock)
+ iunlock(drive->ctlr);
+}
+
+static int
+atadmasetup(Drive* drive, int len)
+{
+ Prd *prd;
+ ulong pa;
+ Ctlr *ctlr;
+ int bmiba, bmisx, count, i, span;
+
+ ctlr = drive->ctlr;
+ pa = PCIWADDR(drive->data);
+ if(pa & 0x03)
+ return -1;
+
+ /*
+ * Sometimes drives identify themselves as being DMA capable
+ * although they are not on a busmastering controller.
+ */
+ prd = ctlr->prdt;
+ if(prd == nil){
+ drive->dmactl = 0;
+ print("disabling dma: not on a busmastering controller\n");
+ return -1;
+ }
+
+ for(i = 0; len && i < Nprd; i++){
+ prd->pa = pa;
+ span = ROUNDUP(pa, ctlr->span);
+ if(span == pa)
+ span += ctlr->span;
+ count = span - pa;
+ if(count >= len){
+ prd->count = PrdEOT|len;
+ break;
+ }
+ prd->count = count;
+ len -= count;
+ pa += count;
+ prd++;
+ }
+ if(i == Nprd)
+ (prd-1)->count |= PrdEOT;
+
+ bmiba = ctlr->bmiba;
+ outl(bmiba+Bmidtpx, PCIWADDR(ctlr->prdt));
+ if(drive->write)
+ outb(bmiba+Bmicx, 0);
+ else
+ outb(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 int
+atadmastop(Ctlr* ctlr)
+{
+ int bmiba;
+
+ bmiba = ctlr->bmiba;
+ outb(bmiba+Bmicx, inb(bmiba+Bmicx) & ~Ssbm);
+
+ return inb(bmiba+Bmisx);
+}
+
+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, 0);
+ break;
+ }
+ atadmastop(ctlr);
+ ctlr->done = 1;
+}
+
+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
+atapktio0(Drive *drive, SDreq *r)
+{
+ uchar *cmd;
+ int as, cmdport, ctlport, len, rv, timeo;
+ Ctlr *ctlr;
+
+ rv = SDok;
+ cmd = r->cmd;
+ drive->command = Cpkt;
+ memmove(drive->pktcmd, cmd, r->clen);
+ memset(drive->pktcmd+r->clen, 0, drive->pkt-r->clen);
+ drive->limit = drive->data+drive->dlen;
+
+ ctlr = drive->ctlr;
+ cmdport = ctlr->cmdport;
+ ctlport = ctlr->ctlport;
+
+ as = ataready(cmdport, ctlport, drive->dev, Bsy|Drq, Drdy, 107*1000);
+ /* used to test as&Chk as failure too, but some CD readers use that for media change */
+ if(as < 0)
+ return SDnostatus;
+
+ ilock(ctlr);
+ if(drive->dlen && drive->dmactl && !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;
+ ctlr->curdrive = drive;
+ ctlr->command = Cpkt; /* debugging */
+ if(drive->pktdma)
+ atadmastart(ctlr, drive->write);
+ outb(cmdport+Command, Cpkt);
+
+ if((drive->info[Iconfig] & Mdrq) != 0x0020){
+ microdelay(1);
+ as = ataready(cmdport, ctlport, 0, Bsy, Drq|Chk, 4*1000);
+ if(as < 0 || (as & (Bsy|Chk))){
+ drive->status = as<0 ? 0 : as;
+ ctlr->curdrive = nil;
+ ctlr->done = 1;
+ rv = SDtimeout;
+ }else
+ atapktinterrupt(drive);
+ }
+ iunlock(ctlr);
+
+ while(waserror())
+ ;
+ if(!drive->pktdma)
+ sleep(ctlr, atadone, ctlr);
+ else for(timeo = 0; !ctlr->done; timeo++){
+ tsleep(ctlr, atadone, ctlr, 1000);
+ if(ctlr->done)
+ break;
+ ilock(ctlr);
+ atadmainterrupt(drive, 0);
+ if(!drive->error && timeo > 20){
+ ataabort(drive, 0);
+ atadmastop(ctlr);
+ drive->dmactl = 0;
+ drive->error |= Abrt;
+ }
+ if(drive->error){
+ drive->status |= Chk;
+ ctlr->curdrive = nil;
+ }
+ iunlock(ctlr);
+ }
+ poperror();
+
+ if(drive->status & Chk)
+ rv = SDcheck;
+ return rv;
+}
+
+static int
+atapktio(Drive* drive, SDreq *r)
+{
+ int n;
+ Ctlr *ctlr;
+
+ ctlr = drive->ctlr;
+ qlock(ctlr);
+ n = atapktio0(drive, r);
+ qunlock(ctlr);
+ return n;
+}
+
+static uchar cmd48[256] = {
+ [Crs] Crs48,
+ [Crd] Crd48,
+ [Crsm] Crsm48,
+ [Cws] Cws48,
+ [Cwd] Cwd48,
+ [Cwsm] Cwsm48,
+};
+
+enum{
+ Last28 = (1<<28) - 1 - 1,
+};
+
+static int
+atageniostart(Drive* drive, uvlong lba)
+{
+ Ctlr *ctlr;
+ uchar cmd;
+ int as, c, cmdport, ctlport, h, len, s, use48;
+
+ use48 = 0;
+ if((drive->flags&Lba48always) || lba > Last28 || drive->count > 256){
+ if((drive->feat & Dllba) == 0)
+ return -1;
+ use48 = 1;
+ c = h = s = 0;
+ }else if(drive->dev & Lba){
+ c = (lba>>8) & 0xFFFF;
+ h = (lba>>24) & 0x0F;
+ s = lba & 0xFF;
+ }else{
+ if (drive->s == 0 || drive->h == 0){
+ print("sdide: chs address botch");
+ return -1;
+ }
+ 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, Drdy, 101*1000) < 0)
+ return -1;
+
+ ilock(ctlr);
+ if(drive->dmactl && !atadmasetup(drive, drive->count*drive->secsize)){
+ if(drive->write)
+ drive->command = Cwd;
+ else
+ drive->command = Crd;
+ }
+ else if(drive->rwmctl){
+ drive->block = drive->rwm*drive->secsize;
+ if(drive->write)
+ drive->command = Cwsm;
+ else
+ drive->command = Crsm;
+ }
+ else{
+ drive->block = drive->secsize;
+ if(drive->write)
+ drive->command = Cws;
+ else
+ drive->command = Crs;
+ }
+ drive->limit = drive->data + drive->count*drive->secsize;
+ cmd = drive->command;
+ if(use48){
+ outb(cmdport+Count, drive->count>>8);
+ outb(cmdport+Count, drive->count);
+ outb(cmdport+Lbalo, lba>>24);
+ outb(cmdport+Lbalo, lba);
+ outb(cmdport+Lbamid, lba>>32);
+ outb(cmdport+Lbamid, lba>>8);
+ outb(cmdport+Lbahi, lba>>40);
+ outb(cmdport+Lbahi, lba>>16);
+ outb(cmdport+Dh, drive->dev|Lba);
+ cmd = cmd48[cmd];
+
+ if(DEBUG & Dbg48BIT)
+ print("using 48-bit commands\n");
+ }else{
+ outb(cmdport+Count, drive->count);
+ outb(cmdport+Sector, s);
+ outb(cmdport+Cyllo, c);
+ outb(cmdport+Cylhi, c>>8);
+ outb(cmdport+Dh, drive->dev|h);
+ }
+ ctlr->done = 0;
+ ctlr->curdrive = drive;
+ ctlr->command = drive->command; /* debugging */
+ outb(cmdport+Command, cmd);
+
+ switch(drive->command){
+ case Cws:
+ case Cwsm:
+ microdelay(1);
+ /* 10*1000 for flash ide drives - maybe detect them? */
+ as = ataready(cmdport, ctlport, 0, Bsy, Drq|Err, 10*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;
+ }
+ iunlock(ctlr);
+
+ return 0;
+}
+
+static int
+atagenioretry(Drive* drive, SDreq *r, uvlong lba, int count)
+{
+ char *s;
+ int rv, count0, rw;
+ uvlong lba0;
+
+ if(drive->dmactl){
+ drive->dmactl = 0;
+ s = "disabling dma";
+ rv = SDretry;
+ }else if(drive->rwmctl){
+ drive->rwmctl = 0;
+ s = "disabling rwm";
+ rv = SDretry;
+ }else{
+ s = "nondma";
+ rv = sdsetsense(r, SDcheck, 4, 8, drive->error);
+ }
+ sdfakescsirw(r, &lba0, &count0, &rw);
+ print("atagenioretry: %s %c:%llud:%d @%llud:%d\n",
+ s, "rw"[rw], lba0, count0, lba, count);
+ return rv;
+}
+
+static int
+atagenio(Drive* drive, SDreq *r)
+{
+ Ctlr *ctlr;
+ uvlong lba;
+ int i, rw, count, maxio;
+
+ if((i = sdfakescsi(r)) != SDnostatus)
+ return i;
+ if((i = sdfakescsirw(r, &lba, &count, &rw)) != SDnostatus)
+ return i;
+ ctlr = drive->ctlr;
+ if(drive->data == nil)
+ return SDok;
+ if(drive->dlen < count*drive->secsize)
+ count = drive->dlen/drive->secsize;
+ qlock(ctlr);
+ if(ctlr->maxio)
+ maxio = ctlr->maxio;
+ else if(drive->feat & Dllba)
+ maxio = 65536;
+ else
+ maxio = 256;
+ while(count){
+ if(count > maxio)
+ drive->count = maxio;
+ else
+ drive->count = count;
+ if(atageniostart(drive, lba)){
+ ilock(ctlr);
+ atanop(drive, 0);
+ iunlock(ctlr);
+ qunlock(ctlr);
+ return atagenioretry(drive, r, lba, count);
+ }
+ iowait(drive, 60*1000, 0);
+ 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.
+ */
+ atadumpstate(drive, r, lba, count);
+ ataabort(drive, 1);
+ qunlock(ctlr);
+ return atagenioretry(drive, r, lba, count);
+ }
+
+ if(drive->status & Err){
+ qunlock(ctlr);
+print("atagenio: %llud:%d\n", lba, drive->count);
+ return sdsetsense(r, SDcheck, 4, 8, drive->error);
+ }
+ count -= drive->count;
+ lba += drive->count;
+ }
+ qunlock(ctlr);
+
+ return SDok;
+}
+
+static int
+atario(SDreq* r)
+{
+ uchar *p;
+ int status;
+ Ctlr *ctlr;
+ Drive *drive;
+ SDunit *unit;
+
+ unit = r->unit;
+ if((ctlr = unit->dev->ctlr) == nil || ctlr->drive[unit->subno] == nil){
+ r->status = SDtimeout;
+ return SDtimeout;
+ }
+ drive = ctlr->drive[unit->subno];
+ qlock(drive);
+ for(;;){
+ drive->write = r->write;
+ drive->data = r->data;
+ drive->dlen = r->dlen;
+ drive->status = 0;
+ drive->error = 0;
+ if(drive->pkt)
+ status = atapktio(drive, r);
+ else
+ status = atagenio(drive, r);
+ if(status != SDretry)
+ break;
+ if(DbgDEBUG)
+ print("%s: retry: dma %8.8uX rwm %4.4uX\n",
+ unit->name, drive->dmactl, drive->rwmctl);
+ }
+ if(status == SDok && r->rlen == 0 && (r->flags & SDvalidsense) == 0){
+ sdsetsense(r, SDok, 0, 0, 0);
+ if(drive->data){
+ p = r->data;
+ r->rlen = drive->data - p;
+ }
+ else
+ r->rlen = 0;
+ }
+ qunlock(drive);
+ return status;
+}
+
+/**/
+static int
+isdmacmd(Drive *d, SDreq *r)
+{
+ switch(r->ataproto & Pprotom){
+ default:
+ return 0;
+ case Pdmq:
+ error("no queued support");
+ case Pdma:
+ if(!(d->dmactl || d->rwmctl))
+ error("dma in non dma mode\n");
+ return 1;
+ }
+}
+
+static int
+atagenatastart(Drive* d, SDreq *r)
+{
+ uchar u;
+ int as, cmdport, ctlport, len, pr, isdma;
+ Ctlr *ctlr;
+
+ isdma = isdmacmd(d, r);
+ ctlr = d->ctlr;
+ cmdport = ctlr->cmdport;
+ ctlport = ctlr->ctlport;
+ if(ataready(cmdport, ctlport, d->dev, Bsy|Drq, d->pkt? 0: Drdy, 101*1000) < 0)
+ return -1;
+
+ ilock(ctlr);
+ if(isdma && atadmasetup(d, d->block)){
+ iunlock(ctlr);
+ return -1;
+
+ }
+ if(d->feat & Dllba && (r->ataproto & P28) == 0){
+ outb(cmdport+Features, r->cmd[Ffeat8]);
+ outb(cmdport+Features, r->cmd[Ffeat]);
+ outb(cmdport+Count, r->cmd[Fsc8]);
+ outb(cmdport+Count, r->cmd[Fsc]);
+ outb(cmdport+Lbalo, r->cmd[Flba24]);
+ outb(cmdport+Lbalo, r->cmd[Flba0]);
+ outb(cmdport+Lbamid, r->cmd[Flba32]);
+ outb(cmdport+Lbamid, r->cmd[Flba8]);
+ outb(cmdport+Lbahi, r->cmd[Flba40]);
+ outb(cmdport+Lbahi, r->cmd[Flba16]);
+ u = r->cmd[Fdev] & ~0xb0;
+ outb(cmdport+Dh, d->dev|u);
+ }else{
+ outb(cmdport+Features, r->cmd[Ffeat]);
+ outb(cmdport+Count, r->cmd[Fsc]);
+ outb(cmdport+Lbalo, r->cmd[Flba0]);
+ outb(cmdport+Lbamid, r->cmd[Flba8]);
+ outb(cmdport+Lbahi, r->cmd[Flba16]);
+ u = r->cmd[Fdev] & ~0xb0;
+ outb(cmdport+Dh, d->dev|u);
+ }
+ ctlr->done = 0;
+ ctlr->curdrive = d;
+ d->command = r->ataproto & (Pprotom|Pdatam);
+ ctlr->command = r->cmd[Fcmd];
+ outb(cmdport+Command, r->cmd[Fcmd]);
+
+ pr = r->ataproto & Pprotom;
+ if(pr == Pnd || pr == Preset)
+ USED(d);
+ else if(!isdma){
+ microdelay(1);
+ /* 10*1000 for flash ide drives - maybe detect them? */
+ as = ataready(cmdport, ctlport, 0, Bsy, Drq|Err, 10*1000);
+ if(as < 0 || (as & Err)){
+ iunlock(ctlr);
+ return -1;
+ }
+ len = d->block;
+ if(r->write && len > 0)
+ outss(cmdport+Data, d->data, len/2);
+ }else
+ atadmastart(ctlr, d->write);
+ iunlock(ctlr);
+ return 0;
+}
+
+static void
+mkrfis(Drive *d, SDreq *r)
+{
+ uchar *u;
+ int cmdport;
+ Ctlr *ctlr;
+
+ ctlr = d->ctlr;
+ cmdport = ctlr->cmdport;
+ u = r->cmd;
+
+ ilock(ctlr);
+ u[Ftype] = 0x34;
+ u[Fioport] = 0;
+ if((d->feat & Dllba) && (r->ataproto & P28) == 0){
+ u[Frerror] = inb(cmdport+Error);
+ u[Fsc8] = inb(cmdport+Count);
+ u[Fsc] = inb(cmdport+Count);
+ u[Flba24] = inb(cmdport+Lbalo);
+ u[Flba0] = inb(cmdport+Lbalo);
+ u[Flba32] = inb(cmdport+Lbamid);
+ u[Flba8] = inb(cmdport+Lbamid);
+ u[Flba40] = inb(cmdport+Lbahi);
+ u[Flba16] = inb(cmdport+Lbahi);
+ u[Fdev] = inb(cmdport+Dh);
+ u[Fstatus] = inb(cmdport+Status);
+ }else{
+ u[Frerror] = inb(cmdport+Error);
+ u[Fsc] = inb(cmdport+Count);
+ u[Flba0] = inb(cmdport+Lbalo);
+ u[Flba8] = inb(cmdport+Lbamid);
+ u[Flba16] = inb(cmdport+Lbahi);
+ u[Fdev] = inb(cmdport+Dh);
+ u[Fstatus] = inb(cmdport+Status);
+ }
+ iunlock(ctlr);
+}
+
+static int
+atarstdone(Drive *d)
+{
+ int as;
+ Ctlr *c;
+
+ c = d->ctlr;
+ as = ataready(c->cmdport, c->ctlport, 0, Bsy|Drq, 0, 5*1000);
+ c->done = as >= 0;
+ return c->done;
+}
+
+static uint
+cmdss(Drive *d, SDreq *r)
+{
+ switch(r->cmd[Fcmd]){
+ case Cid:
+ case Cidpkt:
+ return 512;
+ default:
+ return d->secsize;
+ }
+}
+
+/*
+ * various checks. we should be craftier and
+ * avoid figuring out how big stuff is supposed to be.
+ */
+static uint
+patasizeck(Drive *d, SDreq *r)
+{
+ uint count, maxio, secsize;
+ Ctlr *ctlr;
+
+ secsize = cmdss(d, r); /* BOTCH */
+ if(secsize == 0)
+ error(Eio);
+ count = r->dlen / secsize;
+ ctlr = d->ctlr;
+ if(ctlr->maxio)
+ maxio = ctlr->maxio;
+ else if((d->feat & Dllba) && (r->ataproto & P28) == 0)
+ maxio = 65536;
+ else
+ maxio = 256;
+ if(count > maxio){
+ uprint("i/o too large, lim %d", maxio);
+ error(up->genbuf);
+ }
+ if(r->ataproto&Ppio && count > 1)
+ error("invalid # of sectors");
+ return count;
+}
+
+static int
+atapataio(Drive *d, SDreq *r)
+{
+ int rv;
+ Ctlr *ctlr;
+
+ d->count = 0;
+ if(r->ataproto & Pdatam)
+ d->count = patasizeck(d, r);
+ d->block = r->dlen;
+ d->limit = d->data + r->dlen;
+
+ ctlr = d->ctlr;
+ qlock(ctlr);
+ if(waserror()){
+ qunlock(ctlr);
+ nexterror();
+ }
+ rv = atagenatastart(d, r);
+ poperror();
+ if(rv){
+ if(DEBUG & DbgAtazz)
+ print("sdide: !atageatastart\n");
+ ilock(ctlr);
+ atanop(d, 0);
+ iunlock(ctlr);
+ qunlock(ctlr);
+ return sdsetsense(r, SDcheck, 4, 8, d->error);
+ }
+
+ if((r->ataproto & Pprotom) == Preset)
+ atarstdone(d);
+ else
+ while(iowait(d, 30*1000, 1) == 0)
+ ;
+ if(!ctlr->done){
+ if(DEBUG & DbgAtazz){
+ print("sdide: !done\n");
+ atadumpstate(d, r, 0, d->count);
+ }
+ ataabort(d, 1);
+ qunlock(ctlr);
+ return sdsetsense(r, SDcheck, 11, 0, 6); /* aborted; i/o process terminated */
+ }
+ mkrfis(d, r);
+ if(d->status & Err){
+ if(DEBUG & DbgAtazz)
+ print("sdide: status&Err\n");
+ qunlock(ctlr);
+ return sdsetsense(r, SDcheck, 4, 8, d->error);
+ }
+ qunlock(ctlr);
+ return SDok;
+}
+
+static int
+ataataio0(Drive *d, SDreq *r)
+{
+ int i;
+
+ if((r->ataproto & Pprotom) == Ppkt){
+ if(r->clen > d->pkt)
+ error(Eio);
+ qlock(d->ctlr);
+ i = atapktio0(d, r);
+ d->block = d->data - (uchar*)r->data;
+ mkrfis(d, r);
+ qunlock(d->ctlr);
+ return i;
+ }else
+ return atapataio(d, r);
+}
+
+/*
+ * hack to allow udma mode to be set or unset
+ * via direct ata command. it would be better
+ * to move the assumptions about dma mode out
+ * of some of the helper functions.
+ */
+static int
+isudm(SDreq *r)
+{
+ uchar *c;
+
+ c = r->cmd;
+ if(c[Fcmd] == 0xef && c[Ffeat] == 0x03){
+ if(c[Fsc]&0x40)
+ return 1;
+ return -1;
+ }
+ return 0;
+}
+
+static int
+fisreqchk(Sfis *f, SDreq *r)
+{
+ if((r->ataproto & Pprotom) == Ppkt)
+ return SDnostatus;
+ /*
+ * handle oob requests;
+ * restrict & sanitize commands
+ */
+ if(r->clen != 16)
+ error(Eio);
+ if(r->cmd[0] == 0xf0){
+ sigtofis(f, r->cmd);
+ r->status = SDok;
+ return SDok;
+ }
+ r->cmd[0] = 0x27;
+ r->cmd[1] = 0x80;
+ r->cmd[7] |= 0xa0;
+ return SDnostatus;
+}
+
+static int
+ataataio(SDreq *r)
+{
+ int status, udm;
+ Ctlr *c;
+ Drive *d;
+ SDunit *u;
+
+ u = r->unit;
+ if((c = u->dev->ctlr) == nil || (d = c->drive[u->subno]) == nil){
+ r->status = SDtimeout;
+ return SDtimeout;
+ }
+ if((status = fisreqchk(d, r)) != SDnostatus)
+ return status;
+ udm = isudm(r);
+
+ qlock(d);
+ if(waserror()){
+ qunlock(d);
+ nexterror();
+ }
+retry:
+ d->write = r->write;
+ d->data = r->data;
+ d->dlen = r->dlen;
+ d->status = 0;
+ d->error = 0;
+
+ switch(status = ataataio0(d, r)){
+ case SDretry:
+ if(DbgDEBUG)
+ print("%s: retry: dma %.8ux rwm %.4ux\n",
+ u->name, d->dmactl, d->rwmctl);
+ goto retry;
+ case SDok:
+ if(udm == 1)
+ d->dmactl = d->dma;
+ else if(udm == -1)
+ d->dmactl = 0;
+ sdsetsense(r, SDok, 0, 0, 0);
+ r->rlen = d->block;
+ break;
+ }
+ poperror();
+ qunlock(d);
+ r->status = status;
+ return status;
+}
+/**/
+
+static void
+ichirqack(Ctlr *ctlr)
+{
+ int bmiba;
+
+ if(bmiba = ctlr->bmiba)
+ outb(bmiba+Bmisx, inb(bmiba+Bmisx));
+}
+
+static void
+atainterrupt(Ureg*, void* arg)
+{
+ Ctlr *ctlr;
+ Drive *drive;
+ int cmdport, len, status;
+
+ ctlr = arg;
+
+ ilock(ctlr);
+ ctlr->nrq++;
+ if(ctlr->curdrive)
+ ctlr->curdrive->irq++;
+ if(inb(ctlr->ctlport+As) & Bsy){
+ ctlr->bsy++;
+ if(ctlr->curdrive)
+ ctlr->curdrive->bsy++;
+ iunlock(ctlr);
+ if(DEBUG & DbgBsy)
+ print("IBsy+");
+ return;
+ }
+ cmdport = ctlr->cmdport;
+ status = inb(cmdport+Status);
+ if((drive = ctlr->curdrive) == nil){
+ ctlr->nildrive++;
+ if(ctlr->irqack != nil)
+ ctlr->irqack(ctlr);
+ iunlock(ctlr);
+ if((DEBUG & DbgINL) && ctlr->command != Cedd)
+ print("Inil%2.2uX+", ctlr->command);
+ return;
+ }
+
+ if(status & Err)
+ drive->error = inb(cmdport+Error);
+ else switch(drive->command){
+ default:
+ drive->error = Abrt;
+ break;
+
+ case Crs:
+ case Crsm:
+ case Ppio|Pin:
+ 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:
+ case Ppio|Pout:
+ 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:
+ case Ppkt|Pin:
+ case Ppkt|Pout:
+ atapktinterrupt(drive);
+ break;
+
+ case Crd:
+ case Cwd:
+ case Pdma|Pin:
+ case Pdma|Pout:
+ atadmainterrupt(drive, drive->count*drive->secsize);
+ break;
+
+ case Pnd:
+ case Preset:
+ ctlr->done = 1;
+ break;
+ }
+ if(ctlr->irqack != nil)
+ ctlr->irqack(ctlr);
+ iunlock(ctlr);
+
+ if(drive->error){
+ status |= Err;
+ ctlr->done = 1;
+ }
+
+ if(ctlr->done){
+ ctlr->curdrive = nil;
+ drive->status = status;
+ wakeup(ctlr);
+ }
+}
+
+typedef struct Lchan Lchan;
+struct Lchan {
+ int cmdport;
+ int ctlport;
+ int irq;
+ int probed;
+};
+static Lchan lchan[2] = {
+ 0x1f0, 0x3f4, IrqATA0, 0,
+ 0x170, 0x374, IrqATA1, 0,
+};
+
+static int
+badccru(Pcidev *p)
+{
+ switch(p->did<<16 | p->did){
+ case 0x439c<<16 | 0x1002:
+ case 0x438c<<16 | 0x1002:
+print("hi, anothy\n");
+print("%T: allowing bad ccru %.2ux for suspected ide controller\n", p->tbdf, p->ccru);
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static SDev*
+atapnp(void)
+{
+ char *s;
+ int channel, map, ispc87415, maxio, pi, r, span, tbdf;
+ Ctlr *ctlr;
+ Pcidev *p;
+ SDev *sdev, *head, *tail;
+ void (*irqack)(Ctlr*);
+
+ head = tail = nil;
+ for(p = nil; 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).
+ * Sub-class code 0x04 is 'RAID controller', e.g. VIA VT8237.
+ */
+ if(p->ccrb != 0x01)
+ continue;
+ if(!badccru(p))
+ if(p->ccru != 0x01 && p->ccru != 0x04 && p->ccru != 0x80)
+ continue;
+ pi = p->ccrp;
+ map = 3;
+ ispc87415 = 0;
+ maxio = 0;
+ if(s = getconf("*idemaxio"))
+ maxio = atoi(s);
+ span = BMspan;
+ irqack = nil;
+
+ 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 (0x1000<<16)|0x1042: /* PC-Tech RZ1000 */
+ /*
+ * Turn off prefetch. Overkill, but cheap.
+ */
+ r = pcicfgr32(p, 0x40);
+ r &= ~0x2000;
+ pcicfgw32(p, 0x40, r);
+ break;
+ case (0x4D38<<16)|0x105A: /* Promise PDC20262 */
+ case (0x4D30<<16)|0x105A: /* Promise PDC202xx */
+ case (0x4D68<<16)|0x105A: /* Promise PDC20268 */
+ case (0x4D69<<16)|0x105A: /* Promise Ultra/133 TX2 */
+ case (0x3373<<16)|0x105A: /* Promise 20378 RAID */
+ case (0x3149<<16)|0x1106: /* VIA VT8237 SATA/RAID */
+ case (0x3112<<16)|0x1095: /* SiL 3112 SATA/RAID */
+ maxio = 15;
+ span = 8*1024;
+ /*FALLTHROUGH*/
+ case (0x3114<<16)|0x1095: /* SiL 3114 SATA/RAID */
+ case (0x0680<<16)|0x1095: /* SiI 0680/680A PATA133 ATAPI/RAID */
+ pi = 0x85;
+ break;
+ case (0x0004<<16)|0x1103: /* HighPoint HPT366 */
+ pi = 0x85;
+ /*
+ * Turn off fast interrupt prediction.
+ */
+ if((r = pcicfgr8(p, 0x51)) & 0x80)
+ pcicfgw8(p, 0x51, r & ~0x80);
+ if((r = pcicfgr8(p, 0x55)) & 0x80)
+ pcicfgw8(p, 0x55, r & ~0x80);
+ break;
+ case (0x0640<<16)|0x1095: /* CMD 640B */
+ /*
+ * Bugfix code here...
+ */
+ break;
+ case (0x7441<<16)|0x1022: /* AMD 768 */
+ /*
+ * Set:
+ * 0x41 prefetch, postwrite;
+ * 0x43 FIFO configuration 1/2 and 1/2;
+ * 0x44 status register read retry;
+ * 0x46 DMA read and end of sector flush.
+ */
+ r = pcicfgr8(p, 0x41);
+ pcicfgw8(p, 0x41, r|0xF0);
+ r = pcicfgr8(p, 0x43);
+ pcicfgw8(p, 0x43, (r & 0x90)|0x2A);
+ r = pcicfgr8(p, 0x44);
+ pcicfgw8(p, 0x44, r|0x08);
+ r = pcicfgr8(p, 0x46);
+ pcicfgw8(p, 0x46, (r & 0x0C)|0xF0);
+ /*FALLTHROUGH*/
+ case (0x01BC<<16)|0x10DE: /* nVidia nForce1 */
+ case (0x0065<<16)|0x10DE: /* nVidia nForce2 */
+ case (0x0085<<16)|0x10DE: /* nVidia nForce2 MCP */
+ case (0x00E3<<16)|0x10DE: /* nVidia nForce2 250 SATA */
+ case (0x00D5<<16)|0x10DE: /* nVidia nForce3 */
+ case (0x00E5<<16)|0x10DE: /* nVidia nForce3 Pro */
+ case (0x00EE<<16)|0x10DE: /* nVidia nForce3 250 SATA */
+ case (0x0035<<16)|0x10DE: /* nVidia nForce3 MCP */
+ case (0x0053<<16)|0x10DE: /* nVidia nForce4 */
+ case (0x0054<<16)|0x10DE: /* nVidia nForce4 SATA */
+ case (0x0055<<16)|0x10DE: /* nVidia nForce4 SATA */
+ case (0x0266<<16)|0x10DE: /* nVidia nForce4 430 SATA */
+ case (0x0265<<16)|0x10DE: /* nVidia nForce 51 MCP */
+ case (0x0267<<16)|0x10DE: /* nVidia nForce 55 MCP SATA */
+ case (0x03ec<<16)|0x10DE: /* nVidia nForce 61 MCP SATA */
+ case (0x03f6<<16)|0x10DE: /* nVidia nForce 61 MCP PATA */
+ case (0x0448<<16)|0x10DE: /* nVidia nForce 65 MCP SATA */
+ case (0x0560<<16)|0x10DE: /* nVidia nForce 69 MCP SATA */
+ /*
+ * Ditto, although it may have a different base
+ * address for the registers (0x50?).
+ */
+ /*FALLTHROUGH*/
+ case (0x209A<<16)|0x1022: /* AMD CS5536 */
+ case (0x7401<<16)|0x1022: /* AMD 755 Cobra */
+ case (0x7409<<16)|0x1022: /* AMD 756 Viper */
+ case (0x7410<<16)|0x1022: /* AMD 766 Viper Plus */
+ case (0x7469<<16)|0x1022: /* AMD 3111 */
+ case (0x4376<<16)|0x1002: /* SB4xx pata */
+ case (0x4379<<16)|0x1002: /* SB4xx sata */
+ case (0x437a<<16)|0x1002: /* SB4xx sata ctlr #2 */
+ case (0x437c<<16)|0x1002: /* Rx6xx pata */
+ case (0x439c<<16)|0x1002: /* SB7xx pata */
+ break;
+ case (0x0211<<16)|0x1166: /* ServerWorks IB6566 */
+ {
+ Pcidev *sb;
+
+ sb = pcimatch(nil, 0x1166, 0x0200);
+ if(sb == nil)
+ break;
+ r = pcicfgr32(sb, 0x64);
+ r &= ~0x2000;
+ pcicfgw32(sb, 0x64, r);
+ }
+ span = 32*1024;
+ break;
+ case (0x5229<<16)|0x10B9: /* ALi M1543 */
+ case (0x5288<<16)|0x10B9: /* ALi M5288 SATA */
+ /*FALLTHROUGH*/
+ case (0x5513<<16)|0x1039: /* SiS 962 */
+ case (0x0646<<16)|0x1095: /* CMD 646 */
+ case (0x0571<<16)|0x1106: /* VIA 82C686 */
+ case (0x0502<<16)|0x100b: /* National Semiconductor SC1100/SCx200 */
+ break;
+ case (0x2360<<16)|0x197b: /* jmicron jmb360 */
+ case (0x2361<<16)|0x197b: /* jmicron jmb361 */
+ case (0x2363<<16)|0x197b: /* jmicron jmb363 */
+ case (0x2365<<16)|0x197b: /* jmicron jmb365 */
+ case (0x2366<<16)|0x197b: /* jmicron jmb366 */
+ case (0x2368<<16)|0x197b: /* jmicron jmb368 */
+ break;
+ case (0x1230<<16)|0x8086: /* 82371FB (PIIX) */
+ case (0x7010<<16)|0x8086: /* 82371SB (PIIX3) */
+ case (0x7111<<16)|0x8086: /* 82371[AE]B (PIIX4[E]) */
+ break;
+ case (0x2411<<16)|0x8086: /* 82801AA (ICH) */
+ case (0x2421<<16)|0x8086: /* 82801AB (ICH0) */
+ case (0x244A<<16)|0x8086: /* 82801BA (ICH2, Mobile) */
+ case (0x244B<<16)|0x8086: /* 82801BA (ICH2, High-End) */
+ case (0x248A<<16)|0x8086: /* 82801CA (ICH3, Mobile) */
+ case (0x248B<<16)|0x8086: /* 82801CA (ICH3, High-End) */
+ case (0x24CA<<16)|0x8086: /* 82801DBM (ICH4, Mobile) */
+ case (0x24CB<<16)|0x8086: /* 82801DB (ICH4, High-End) */
+ case (0x24D1<<16)|0x8086: /* 82801er (ich5) */
+ case (0x24DB<<16)|0x8086: /* 82801EB (ICH5) */
+ case (0x25A2<<16)|0x8086: /* 6300ESB pata */
+ case (0x25A3<<16)|0x8086: /* 6300ESB (E7210) */
+ case (0x266F<<16)|0x8086: /* 82801FB (ICH6) */
+ case (0x2653<<16)|0x8086: /* 82801FBM (ICH6, Mobile) */
+ case (0x269e<<16)|0x8086: /* 63xxESB (intel 5000) */
+ case (0x27DF<<16)|0x8086: /* 82801G PATA (ICH7) */
+ case (0x27C0<<16)|0x8086: /* 82801GB SATA (ICH7) */
+ case (0x27C4<<16)|0x8086: /* 82801GBM SATA (ICH7) */
+ case (0x27C5<<16)|0x8086: /* 82801GBM SATA AHCI (ICH7) */
+ case (0x2820<<16)|0x8086: /* 82801HB/HR/HH/HO SATA IDE */
+ case (0x2828<<16)|0x8086: /* 82801HBM SATA (ICH8-M) */
+ case (0x2920<<16)|0x8086: /* 82801(IB)/IR/IH/IO SATA (ICH9) port 0-3 */
+ case (0x2921<<16)|0x8086: /* 82801(IB)/IR/IH/IO SATA (ICH9) port 0-1 */
+ case (0x2926<<16)|0x8086: /* 82801(IB)/IR/IH/IO SATA (ICH9) port 4-5 */
+ case (0x2928<<16)|0x8086: /* 82801(IB)/IR/IH/IO SATA (ICH9m) port 0-1 */
+ case (0x2929<<16)|0x8086: /* 82801(IB)/IR/IH/IO SATA (ICH9m) port 0-1, 4-5 */
+ case (0x292d<<16)|0x8086: /* 82801(IB)/IR/IH/IO SATA (ICH9m) port 4-5*/
+ case (0x3a20<<16)|0x8086: /* 82801ji (ich10) */
+ case (0x3a26<<16)|0x8086: /* 82801ji (ich10) */
+ case (0x3b20<<16)|0x8086: /* 34x0 (pch) port 0-3 */
+ case (0x3b21<<16)|0x8086: /* 34x0 (pch) port 4-5 */
+ case (0x3b28<<16)|0x8086: /* 34x0pm (pch) port 0-1, 4-5 */
+ case (0x3b2e<<16)|0x8086: /* 34x0pm (pch) port 0-3 */
+ map = 0;
+ if(pcicfgr16(p, 0x40) & 0x8000)
+ map |= 1;
+ if(pcicfgr16(p, 0x42) & 0x8000)
+ map |= 2;
+ irqack = ichirqack;
+ break;
+ }
+ for(channel = 0; channel < 2; channel++){
+ if((map & 1<<channel) == 0)
+ continue;
+ if(pi & 1<<2*channel){
+ sdev = ataprobe(p->mem[0+2*channel].bar & ~0x01,
+ p->mem[1+2*channel].bar & ~0x01,
+ p->intl, 3);
+ tbdf = p->tbdf;
+ }
+ else if(lchan[channel].probed == 0){
+ sdev = ataprobe(lchan[channel].cmdport,
+ lchan[channel].ctlport, lchan[channel].irq, 3);
+ lchan[channel].probed = 1;
+ tbdf = BUSUNKNOWN;
+ }
+ else
+ continue;
+ if(sdev == nil)
+ continue;
+ ctlr = sdev->ctlr;
+ if(ispc87415) {
+ ctlr->ienable = pc87415ienable;
+ print("pc87415disable: not yet implemented\n");
+ }
+ ctlr->tbdf = tbdf;
+ ctlr->pcidev = p;
+ ctlr->maxio = maxio;
+ ctlr->span = span;
+ ctlr->irqack = irqack;
+ if(pi & 0x80)
+ ctlr->bmiba = (p->mem[4].bar & ~0x01) + channel*8;
+ if(head != nil)
+ tail->next = sdev;
+ else
+ head = sdev;
+ tail = sdev;
+ }
+ }
+
+ if(lchan[0].probed + lchan[1].probed == 0)
+ for(channel = 0; channel < 2; channel++){
+ sdev = nil;
+ if(lchan[channel].probed == 0){
+ // print("sdide: blind probe %.3ux\n", lchan[channel].cmdport);
+ sdev = ataprobe(lchan[channel].cmdport,
+ lchan[channel].ctlport, lchan[channel].irq, 3);
+ lchan[channel].probed = 1;
+ }
+ if(sdev == nil)
+ continue;
+ if(head != nil)
+ tail->next = sdev;
+ else
+ head = sdev;
+ tail = sdev;
+ }
+
+if(0){
+ int port;
+ ISAConf isa;
+
+ /*
+ * Hack for PCMCIA drives.
+ * This will be tidied once we figure out how the whole
+ * removeable device thing is going to work.
+ */
+ memset(&isa, 0, sizeof(isa));
+ isa.port = 0x180; /* change this for your machine */
+ isa.irq = 11; /* change this for your machine */
+
+ port = isa.port+0x0C;
+ channel = pcmspecial("MK2001MPL", &isa);
+ if(channel == -1)
+ channel = pcmspecial("SunDisk", &isa);
+ if(channel == -1){
+ isa.irq = 10;
+ channel = pcmspecial("CF", &isa);
+ }
+ if(channel == -1){
+ isa.irq = 10;
+ channel = pcmspecial("OLYMPUS", &isa);
+ }
+ if(channel == -1){
+ port = isa.port+0x204;
+ channel = pcmspecial("ATA/ATAPI", &isa);
+ }
+ if(channel >= 0 && (sdev = ataprobe(isa.port, port, isa.irq, 3)) != nil){
+ if(head != nil)
+ tail->next = sdev;
+ else
+ head = sdev;
+ }
+}
+ return head;
+}
+
+static void
+atadmaclr(Ctlr *ctlr)
+{
+ int bmiba, bmisx;
+
+ if(ctlr->curdrive)
+ ataabort(ctlr->curdrive, 1);
+ bmiba = ctlr->bmiba;
+ if(bmiba == 0)
+ return;
+ atadmastop(ctlr);
+ outl(bmiba+Bmidtpx, 0);
+ bmisx = inb(bmiba+Bmisx) & ~Bmidea;
+ outb(bmiba+Bmisx, bmisx|Ideints|Idedmae);
+// pciintst(ctlr->pcidev);
+}
+
+static int
+ataenable(SDev* sdev)
+{
+ Ctlr *ctlr;
+ char name[32];
+
+ ctlr = sdev->ctlr;
+ if(ctlr->bmiba){
+ atadmaclr(ctlr);
+ if(ctlr->pcidev != nil)
+ pcisetbme(ctlr->pcidev);
+ ctlr->prdt = mallocalign(Nprd*sizeof(Prd), 4, 0, 64*1024);
+ }
+ snprint(name, sizeof(name), "%s (%s)", sdev->name, sdev->ifc->name);
+ intrenable(ctlr->irq, atainterrupt, ctlr, ctlr->tbdf, name);
+ outb(ctlr->ctlport+Dc, 0);
+ if(ctlr->ienable)
+ ctlr->ienable(ctlr);
+ return 1;
+}
+
+static int
+atadisable(SDev *sdev)
+{
+ Ctlr *ctlr;
+ char name[32];
+
+ ctlr = sdev->ctlr;
+ outb(ctlr->ctlport+Dc, Nien); /* disable interrupts */
+ if (ctlr->idisable)
+ ctlr->idisable(ctlr);
+ snprint(name, sizeof(name), "%s (%s)", sdev->name, sdev->ifc->name);
+ intrdisable(ctlr->irq, atainterrupt, ctlr, ctlr->tbdf, name);
+ if(ctlr->bmiba) {
+// atadmaclr(ctlr);
+ if (ctlr->pcidev)
+ pciclrbme(ctlr->pcidev);
+ free(ctlr->prdt);
+ }
+ return 0;
+}
+
+static int
+ataonline(SDunit *unit)
+{
+ Ctlr *ctlr;
+ Drive *drive;
+
+ if((ctlr = unit->dev->ctlr) == nil || ctlr->drive[unit->subno] == nil)
+ return 0;
+ drive = ctlr->drive[unit->subno];
+ if((drive->flags & Online) == 0){
+ drive->flags |= Online;
+ atadrive(unit, drive, ctlr->cmdport, ctlr->ctlport, drive->dev);
+ }
+ unit->sectors = drive->sectors;
+ unit->secsize = drive->secsize;
+ if(drive->feat & Datapi)
+ return scsionline(unit);
+ return 1;
+}
+
+static int
+atarctl(SDunit* unit, char* p, int l)
+{
+ Ctlr *ctlr;
+ Drive *drive;
+ char *e, *op;
+
+ if((ctlr = unit->dev->ctlr) == nil || ctlr->drive[unit->subno] == nil)
+ return 0;
+ drive = ctlr->drive[unit->subno];
+
+ e = p+l;
+ op = p;
+ qlock(drive);
+ p = seprint(p, e, "config %4.4uX capabilities %4.4uX", drive->info[Iconfig], drive->info[Icapabilities]);
+ if(drive->dma)
+ p = seprint(p, e, " dma %8.8uX dmactl %8.8uX", drive->dma, drive->dmactl);
+ if(drive->rwm)
+ p = seprint(p, e, " rwm %ud rwmctl %ud", drive->rwm, drive->rwmctl);
+ if(drive->feat & Dllba)
+ p = seprint(p, e, " lba48always %s", (drive->flags&Lba48always) ? "on" : "off");
+ p = seprint(p, e, "\n");
+ p = seprint(p, e, "model %s\n", drive->model);
+ p = seprint(p, e, "serial %s\n", drive->serial);
+ p = seprint(p, e, "firm %s\n", drive->firmware);
+ p = seprint(p, e, "feat ");
+ p = pflag(p, e, drive);
+ if(drive->sectors){
+ p = seprint(p, e, "geometry %llud %d", drive->sectors, drive->secsize);
+ if(drive->pkt == 0 && (drive->feat & Dlba) == 0)
+ p = seprint(p, e, " %d %d %d", drive->c, drive->h, drive->s);
+ p = seprint(p, e, "\n");
+ }
+ p = seprint(p, e, "missirq %ud\n", drive->missirq);
+ p = seprint(p, e, "sloop %ud\n", drive->spurloop);
+ p = seprint(p, e, "irq %ud %ud\n", ctlr->nrq, drive->irq);
+ p = seprint(p, e, "bsy %ud %ud\n", ctlr->bsy, drive->bsy);
+ p = seprint(p, e, "nildrive %ud\n", ctlr->nildrive);
+ qunlock(drive);
+
+ return p - op;
+}
+
+static int
+atawctl(SDunit* unit, Cmdbuf* cb)
+{
+ Ctlr *ctlr;
+ Drive *drive;
+
+ if((ctlr = unit->dev->ctlr) == nil || ctlr->drive[unit->subno] == nil)
+ return 0;
+ drive = ctlr->drive[unit->subno];
+
+ qlock(drive);
+ if(waserror()){
+ qunlock(drive);
+ nexterror();
+ }
+
+ /*
+ * Dma and rwm control is passive at the moment,
+ * i.e. it is assumed that the hardware is set up
+ * correctly already either by the BIOS or when
+ * the drive was initially identified.
+ */
+ if(strcmp(cb->f[0], "dma") == 0){
+ if(cb->nf != 2 || drive->dma == 0)
+ error(Ebadctl);
+ if(strcmp(cb->f[1], "on") == 0)
+ drive->dmactl = drive->dma;
+ else if(strcmp(cb->f[1], "off") == 0)
+ drive->dmactl = 0;
+ else
+ error(Ebadctl);
+ }
+ else if(strcmp(cb->f[0], "rwm") == 0){
+ if(cb->nf != 2 || drive->rwm == 0)
+ error(Ebadctl);
+ if(strcmp(cb->f[1], "on") == 0)
+ drive->rwmctl = drive->rwm;
+ else if(strcmp(cb->f[1], "off") == 0)
+ drive->rwmctl = 0;
+ else
+ error(Ebadctl);
+ }
+ else if(strcmp(cb->f[0], "lba48always") == 0){
+ if(cb->nf != 2 || !(drive->feat & Dllba))
+ error(Ebadctl);
+ if(strcmp(cb->f[1], "on") == 0)
+ drive->flags |= Lba48always;
+ else if(strcmp(cb->f[1], "off") == 0)
+ drive->flags &= ~Lba48always;
+ else
+ error(Ebadctl);
+ }
+ else if(strcmp(cb->f[0], "identify") == 0){
+ atadrive(unit, drive, ctlr->cmdport, ctlr->ctlport, drive->dev);
+ }
+ else
+ error(Ebadctl);
+ qunlock(drive);
+ poperror();
+
+ return 0;
+}
+
+SDifc sdideifc = {
+ "ide", /* name */
+
+ atapnp, /* pnp */
+ nil, /* legacy */
+ ataenable, /* enable */
+ atadisable, /* disable */
+
+ scsiverify, /* verify */
+ ataonline, /* online */
+ atario, /* rio */
+ atarctl, /* rctl */
+ atawctl, /* wctl */
+
+ scsibio, /* bio */
+ ataprobew, /* probe */
+ ataclear, /* clear */
+ atastat, /* rtopctl */
+ nil, /* wtopctl */
+ ataataio,
+};
--- a/sys/src/9/pc/sdmv50xx.c
+++ b/sys/src/9/pc/sdmv50xx.c
@@ -1,5 +1,5 @@
/*
- * Marvell 88SX[56]0[48][01] fileserver Serial ATA (SATA) driver
+ * Marvell 88SX[56]0[48][01] Serial ATA (SATA) driver
*
* See MV-S101357-00 Rev B Marvell PCI/PCI-X to 8-Port/4-Port
* SATA Host Controller, ATA-5 ANSI NCITS 340-2000.
@@ -16,25 +16,18 @@
#include "fns.h"
#include "io.h"
#include "../port/error.h"
+#include "../port/sd.h"
+#include <fis.h>
-#include "../port/sd.h"
+#define dprint(...) // print(__VA_ARGS__)
+#define idprint(...) print(__VA_ARGS__)
+#define Ticks MACHP(0)->ticks
-#define dprint if(!0){}else iprint
-#define idprint if(!0){}else iprint
-#define ioprint if(!0){}else iprint
-
enum {
NCtlr = 4,
- NCtlrdrv = 8,
+ NCtlrdrv = 8,
NDrive = NCtlr*NCtlrdrv,
- Read = 0,
- Write,
-
- Coraiddebug = 0,
-};
-
-enum {
SrbRing = 32,
/* Addresses of ATA register */
@@ -48,63 +41,58 @@
ARseccnt = 022,
ARstat = 027,
- ATAerr = (1<<0),
- ATAdrq = (1<<3),
- ATAdf = (1<<5),
- ATAdrdy = (1<<6),
- ATAbusy = (1<<7),
- ATAabort = (1<<2),
- ATAobs = (1<<1 | 1<<2 | 1<<4),
- ATAeIEN = (1<<1),
- ATAsrst = (1<<2),
- ATAhob = (1<<7),
- ATAbad = (ATAbusy|ATAdf|ATAdrq|ATAerr),
+ ATAerr = 1<<0,
+ ATAdrq = 1<<3,
+ ATAdf = 1<<5,
+ ATAdrdy = 1<<6,
+ ATAbusy = 1<<7,
+ ATAabort = 1<<2,
+ ATAobs = 1<<1 | 1<<2 | 1<<4,
+ ATAeIEN = 1<<1,
+ ATAbad = ATAbusy|ATAdf|ATAdrq|ATAerr,
- SFdone = (1<<0),
- SFerror = (1<<1),
+ SFdone = 1<<0,
+ SFerror = 1<<1,
- SRBident = 0,
- SRBread,
- SRBwrite,
- SRBsmart,
+ PRDeot = 1<<15,
- SRBnodata = 0,
- SRBdatain,
- SRBdataout,
-
- RQread = 1, /* data coming IN from device */
-
- PRDeot = (1<<15),
-
/* EDMA interrupt error cause register */
+ ePrtDataErr = 1<<0,
+ ePrtPRDErr = 1<<1,
+ eDevErr = 1<<2,
+ eDevDis = 1<<3,
+ eDevCon = 1<<4,
+ eOverrun = 1<<5,
+ eUnderrun = 1<<6,
+ eSelfDis = 1<<8,
+ ePrtCRQBErr = 1<<9,
+ ePrtCRPBErr = 1<<10,
+ ePrtIntErr = 1<<11,
+ eIORdyErr = 1<<12,
- ePrtDataErr = (1<<0),
- ePrtPRDErr = (1<<1),
- eDevErr = (1<<2),
- eDevDis = (1<<3),
- eDevCon = (1<<4),
- eOverrun = (1<<5),
- eUnderrun = (1<<6),
- eSelfDis = (1<<8),
- ePrtCRQBErr = (1<<9),
- ePrtCRPBErr = (1<<10),
- ePrtIntErr = (1<<11),
- eIORdyErr = (1<<12),
-
/* flags for sata 2 version */
- eSelfDis2 = (1<<7),
- SerrInt = (1<<5),
+ eSelfDis2 = 1<<7,
+ SerrInt = 1<<5,
/* EDMA Command Register */
+ eEnEDMA = 1<<0,
+ eDsEDMA = 1<<1,
+ eAtaRst = 1<<2,
- eEnEDMA = (1<<0),
- eDsEDMA = (1<<1),
- eAtaRst = (1<<2),
-
/* Interrupt mask for errors we care about */
- IEM = (eDevDis | eDevCon | eSelfDis),
- IEM2 = (eDevDis | eDevCon | eSelfDis2),
+ IEM = eDevDis | eDevCon | eSelfDis,
+ IEM2 = eDevDis | eDevCon | eSelfDis2,
+ /* phyerrata magic */
+ Mpreamp = 0x7e0,
+ Dpreamp = 0x720,
+
+ REV60X1B2 = 0x7,
+ REV60X1C0 = 0x9,
+
+ /* general mmio registers */
+ Portswtch = 0x1d64/4,
+
/* drive states */
Dnull = 0,
Dnew,
@@ -114,33 +102,12 @@
Dreset,
Dlast,
- /* drive flags */
- Dext = (1<<0), /* use ext commands */
- Dpio = (1<<1), /* doing pio */
- Dwanted = (1<<2), /* someone wants an srb entry */
- Dedma = (1<<3), /* device in edma mode */
- Dpiowant = (1<<4), /* some wants to use the pio mode */
-
- /* phyerrata magic crap */
- Mpreamp = 0x7e0,
- Dpreamp = 0x720,
-
- REV60X1B2 = 0x7,
- REV60X1C0 = 0x9,
-
+ /* sata mode */
+ DMautoneg = 0,
+ DMsatai,
+ DMsataii,
};
-static char* diskstates[Dlast] = {
- "null",
- "new",
- "ready",
- "error",
- "missing",
- "reset",
-};
-
-extern SDifc sdmv50xxifc;
-
typedef struct Arb Arb;
typedef struct Bridge Bridge;
typedef struct Chip Chip;
@@ -162,12 +129,6 @@
Edma *edma;
};
-enum {
- DMautoneg,
- DMsatai,
- DMsataii,
-};
-
struct Drive
{
Lock;
@@ -175,7 +136,7 @@
Ctlr *ctlr;
SDunit *unit;
char name[10];
- ulong magic;
+ Sfis;
Bridge *bridge;
Edma *edma;
@@ -182,12 +143,12 @@
Chip *chip;
int chipx;
- int mediachange;
+ int drivechange;
int state;
- int flag;
uvlong sectors;
+ uint secsize;
ulong pm2; /* phymode 2 init state */
- ulong intick; /* check for hung western digital drives. */
+ ulong intick; /* check for hung drives. */
int wait;
int mode; /* DMautoneg, satai or sataii. */
@@ -194,6 +155,7 @@
char serial[20+1];
char firmware[8+1];
char model[40+1];
+ uvlong wwn;
ushort info[256];
@@ -205,7 +167,7 @@
Srb *srbhead;
Srb *srbtail;
- int driveno; /* ctlr*NCtlrdrv + unit */
+ int driveno; /* ctlr*NCtlrdrv + unit */
};
struct Ctlr
@@ -252,7 +214,7 @@
/*
* Memory-mapped I/O registers in many forms.
*/
-struct Bridge /* memory-mapped per-Drive registers */
+struct Bridge /* memory-mapped per-drive registers */
{
ulong status;
ulong serror;
@@ -268,9 +230,9 @@
char fill2[0x34];
ulong phymode;
char fill3[0x88];
-}; /* length must be 0x100 */
+}; /* must be 0x100 hex in length */
-struct Arb /* memory-mapped per-Chip registers */
+struct Arb /* memory-mapped per-chip registers */
{
ulong config; /* satahc configuration register (sata2 only) */
ulong rqop; /* request queue out-pointer */
@@ -285,7 +247,7 @@
Bridge bridge[4];
};
-struct Edma /* memory-mapped per-Drive DMA-related registers */
+struct Edma /* memory-mapped per-drive DMA-related registers */
{
ulong config; /* configuration register */
ulong timer;
@@ -357,56 +319,28 @@
ulong ts; /* time stamp */
};
-static Drive *mvsatadrive[NDrive];
-static int nmvsatadrive;
+static Ctlr *mvsatactlr[NCtlr];
+static Drive *mvsatadrive[NDrive];
+static int nmvsatadrive;
+static char *diskstates[Dlast] = {
+ "null",
+ "new",
+ "ready",
+ "error",
+ "missing",
+ "reset",
+};
-/*
- * Little-endian parsing for drive data.
- */
-static ushort
-lhgets(void *p)
-{
- uchar *a = p;
- return ((ushort) a[1] << 8) | a[0];
-}
+extern SDifc sdmv50xxifc;
-static ulong
-lhgetl(void *p)
-{
- uchar *a = p;
- return ((ulong) lhgets(a+2) << 16) | lhgets(a);
-}
-
-static uvlong
-lhgetv(void *p)
-{
- uchar *a = p;
- return ((uvlong) lhgetl(a+4) << 32) | lhgetl(a);
-}
-
-static void
-idmove(char *p, ushort *a, int n)
-{
- char *op;
- int i;
-
- op = p;
- for(i=0; i<n/2; i++){
- *p++ = a[i]>>8;
- *p++ = a[i];
- }
- while(p>op && *--p == ' ')
- *p = 0;
-}
-
/*
* Request buffers.
*/
-struct
+static struct
{
Lock;
- Srb *freechain;
- int nalloc;
+ Srb *freechain;
+ int nalloc;
} srblist;
static Srb*
@@ -435,9 +369,6 @@
iunlock(&srblist);
}
-/*
- * Wait for a byte to be a particular value.
- */
static int
satawait(uchar *p, uchar mask, uchar v, int ms)
{
@@ -448,32 +379,27 @@
return (*p & mask) == v;
}
-/*
- * Drive initialization
- */
/* unmask in the pci registers err done */
static void
-unmask(ulong *mmio, int port, int coal)
+portswitch(ulong *mmio, int port, uint coal, uint on)
{
- port &= 7;
- if(coal)
- coal = 1;
- if (port < 4)
- mmio[0x1d64/4] |= (3 << (((port&3)*2)) | (coal<<8));
+ ulong m;
+
+ m = 3<<(port&3)*2 | coal<<8;
+ if((port&7) >= 4)
+ m <<= 9;
+ if(on)
+ mmio[Portswtch] |= m;
else
- mmio[0x1d64/4] |= (3 << (((port&3)*2+9)) | (coal<<17));
+ mmio[Portswtch] &= m;
}
-static void
-mask(ulong *mmio, int port, int coal)
+static char*
+dnam(Drive *d)
{
- port &= 7;
- if(coal)
- coal = 1;
- if (port < 4)
- mmio[0x1d64/4] &= ~(3 << (((port&3)*2)) | (coal<<8));
- else
- mmio[0x1d64/4] &= ~(3 << (((port&3)*2+9)) | (coal<<17));
+ if(d->unit)
+ return d->unit->name;
+ return d->name;
}
/* I give up, marvell. You win. */
@@ -483,17 +409,22 @@
ulong n, m;
enum { BadAutoCal = 0xf << 26, };
- if (d->ctlr->type == 1)
+ if(d->ctlr->type == 1){
+ /* set phyctrl bits [0:1] to 01 per MV-S102013-00 Rev C. */
+ n = d->bridge->phyctrl;
+ n &= ~3;
+ d->bridge->phyctrl = n | 1;
return;
+ }
microdelay(200);
n = d->bridge->phymode2;
while ((n & BadAutoCal) == BadAutoCal) {
- dprint("%s: badautocal\n", d->unit->name);
+ dprint("%s: badautocal\n", dnam(d));
n &= ~(1<<16);
- n |= (1<<31);
+ n |= 1<<31;
d->bridge->phymode2 = n;
microdelay(200);
- d->bridge->phymode2 &= ~((1<<16) | (1<<31));
+ d->bridge->phymode2 &= ~(1<<16 | 1<<31);
microdelay(200);
n = d->bridge->phymode2;
}
@@ -552,6 +483,28 @@
}
}
+static int
+edmadisable(Drive *d, int reset)
+{
+ Edma *e;
+
+ e = d->edma;
+ if(!reset && (e->ctl & eEnEDMA) == 0)
+ return 0;
+ e->ctl = eDsEDMA;
+ microdelay(1);
+ if(reset)
+ e->ctl = eAtaRst;
+ microdelay(25);
+ e->ctl = 0;
+ if (satawait((uchar *)&e->ctl, eEnEDMA, 0, 3*1000) == 0){
+ print("%s: eEnEDMA never cleared on reset\n", dnam(d));
+ return -1;
+ }
+ edmacleanout(d);
+ return 0;
+}
+
static void
resetdisk(Drive *d)
{
@@ -571,16 +524,10 @@
n = d->edma->sataconfig; /* flush */
USED(n);
}
- d->edma->ctl = eDsEDMA;
- microdelay(1);
- d->edma->ctl = eAtaRst;
- microdelay(25);
- d->edma->ctl = 0;
- if (satawait((uchar *)&d->edma->ctl, eEnEDMA, 0, 3*1000) == 0)
- print("%s: eEnEDMA never cleared on reset\n", d->unit->name);
- edmacleanout(d);
+ if(edmadisable(d, 1) == -1){
+ }
phyerrata(d);
- d->bridge->sctrl = 0x301 | (d->mode << 4);
+ d->bridge->sctrl = 0x301 | d->mode<<4;
d->state = Dmissing;
}
@@ -596,7 +543,7 @@
d->rx = xspanalloc(32*sizeof(Rx), 256, 0);
d->prd = xspanalloc(32*sizeof(Prd), 32, 0);
for(i = 0; i < 32; i++)
- d->tx[i].prdpa = PADDR(&d->prd[i]);
+ d->tx[i].prdpa = PCIWADDR(&d->prd[i]);
coherence();
}
@@ -604,27 +551,12 @@
configdrive(Ctlr *ctlr, Drive *d, SDunit *unit)
{
dprint("%s: configdrive\n", unit->name);
- if(d->driveno < 0)
- panic("mv50xx: configdrive: unset driveno\n");
d->unit = unit;
- edmainit(d);
- d->mode = DMsatai;
- if(d->ctlr->type == 1){
- d->edma->iem = IEM;
- d->bridge = &d->chip->arb->bridge[d->chipx];
- }else{
- d->edma->iem = IEM2;
- d->bridge = &d->chip->edma[d->chipx].port;
- d->edma->iem = ~(1<<6);
- d->pm2 = Dpreamp;
- if(d->ctlr->lmmio[0x180d8/4] & 1)
- d->pm2 = d->bridge->phymode2 & Mpreamp;
- }
resetdisk(d);
- unmask(ctlr->lmmio, d->driveno, 0);
+ portswitch(ctlr->lmmio, d->driveno, 0, 1);
delay(100);
if(d->bridge->status){
- dprint("%s: configdrive: found drive %lx\n", unit->name, d->bridge->status);
+ dprint("%s: configdrive: found drive %lux\n", unit->name, d->bridge->status);
return 0;
}
return -1;
@@ -631,21 +563,19 @@
}
static int
-enabledrive(Drive *d)
+edmaenable(Drive *d)
{
Edma *edma;
- dprint("%s: enabledrive..", d->unit->name);
+ dprint("%s: enabledrive..", dnam(d));
if((d->bridge->status & 0xf) != 3){
- dprint("%s: not present\n", d->unit->name);
- d->state = Dmissing;
+ dprint("%s: not present\n", dnam(d));
return -1;
}
edma = d->edma;
if(satawait(&edma->cmdstat, ATAbusy, 0, 5*1000) == 0){
- dprint("%s: busy timeout\n", d->unit->name);
- d->state = Dmissing;
+ dprint("%s: busy timeout\n", dnam(d));
return -1;
}
edma->iec = 0;
@@ -653,40 +583,40 @@
edma->config = 0x51f;
if (d->ctlr->type == 2)
edma->config |= 7<<11;
- edma->txi = PADDR(d->tx);
+ edma->txi = PCIWADDR(d->tx);
edma->txo = (ulong)d->tx & 0x3e0;
edma->rxi = (ulong)d->rx & 0xf8;
- edma->rxo = PADDR(d->rx);
+ edma->rxo = PCIWADDR(d->rx);
edma->ctl |= 1; /* enable dma */
-
- if(d->bridge->status = 0x113){
- dprint("%s: new\n", d->unit->name);
- d->state = Dnew;
- }else
- print("%s: status not forced (should be okay)\n", d->unit->name);
return 0;
}
+static int
+enabledrive(Drive *d)
+{
+ dprint("%s: enabledrive..", dnam(d));
+ if(edmaenable(d) == 0){
+ switch(d->bridge->status){
+ case 0x113:
+ case 0x123:
+ d->state = Dnew;
+ break;
+ }
+ return 0;
+ }
+ print("mv50: enable reset\n");
+ d->state = Dreset;
+ return -1;
+}
+
static void
disabledrive(Drive *d)
{
- int i;
- ulong *r;
-
- dprint("%s: disabledrive\n", d->unit->name);
-
if(d->tx == nil) /* never enabled */
return;
-
d->edma->ctl = 0;
d->edma->iem = 0;
-
- r = (ulong*)(d->ctlr->mmio + 0x1d64);
- i = d->chipx;
- if(d->chipx < 4)
- *r &= ~(3 << (i*2));
- else
- *r |= ~(3 << (i*2+9));
+ portswitch(d->ctlr->lmmio, d->driveno, 0, 0);
}
static int
@@ -694,15 +624,14 @@
{
Edma *edma;
- dprint("%s: setudmamode %d\n", d->unit->name, mode);
-
+ dprint("%s: setudmamode %d\n", dnam(d), mode);
edma = d->edma;
- if (edma == nil) {
+ if(edma == nil) {
iprint("setudamode(m%d): zero d->edma\m", d->driveno);
return 0;
}
- if(satawait(&edma->cmdstat, ~ATAobs, ATAdrdy, 9*1000) == 0){
- iprint("%s: cmdstat 0x%.2ux ready timeout\n", d->unit->name, edma->cmdstat);
+ if(satawait(&edma->cmdstat, ~ATAobs, ATAdrdy, 250) == 0){
+ iprint("%s: cmdstat 0x%.2ux ready timeout\n", dnam(d), edma->cmdstat);
return 0;
}
edma->altstat = ATAeIEN;
@@ -711,7 +640,7 @@
edma->cmdstat = 0xef;
microdelay(1);
if(satawait(&edma->cmdstat, ATAbusy, 0, 5*1000) == 0){
- iprint("%s: cmdstat 0x%.2ux busy timeout\n", d->unit->name, edma->cmdstat);
+ iprint("%s: cmdstat 0x%.2ux busy timeout\n", dnam(d), edma->cmdstat);
return 0;
}
return 1;
@@ -720,14 +649,15 @@
static int
identifydrive(Drive *d)
{
+ char *s;
int i;
ushort *id;
Edma *edma;
- SDunit *unit;
+ SDunit *u;
- dprint("%s: identifydrive\n", d->unit->name);
-
- if(setudmamode(d, 5) == 0) /* do all SATA support 5? */
+ dprint("%s: identifydrive\n", dnam(d));
+ setfissig(d, 0); /* BOTCH; need to find and set signature */
+ if(setudmamode(d, 5) == 0) /* BOTCH; run after identify */
goto Error;
id = d->info;
@@ -745,29 +675,27 @@
id[i] = edma->pio;
if(edma->cmdstat & ATAbad)
goto Error;
- i = lhgets(id+83) | lhgets(id+86);
- if(i & (1<<10)){
- d->flag |= Dext;
- d->sectors = lhgetv(id+100);
- }else{
- d->flag &= ~Dext;
- d->sectors = lhgetl(id+60);
- }
+ d->sectors = idfeat(d, id);
+ d->secsize = idss(d, id);
idmove(d->serial, id+10, 20);
idmove(d->firmware, id+23, 8);
idmove(d->model, id+27, 40);
+ d->wwn = idwwn(d, id);
- unit = d->unit;
- memset(unit->inquiry, 0, sizeof unit->inquiry);
- unit->inquiry[2] = 2;
- unit->inquiry[3] = 2;
- unit->inquiry[4] = sizeof(unit->inquiry)-4;
- idmove((char*)unit->inquiry+8, id+27, 40);
+ u = d->unit;
+ memset(u->inquiry, 0, sizeof u->inquiry);
+ u->inquiry[2] = 2;
+ u->inquiry[3] = 2;
+ u->inquiry[4] = sizeof u->inquiry - 4;
+ idmove((char*)u->inquiry+8, id+27, 40);
if(enabledrive(d) == 0) {
d->state = Dready;
- d->mediachange = 1;
- idprint("%s: LLBA %lld sectors\n", d->unit->name, d->sectors);
+ d->drivechange = 1;
+ s = nil;
+ if(d->feat & Dllba)
+ s = "L";
+ idprint("%s: %sLBA %llud sectors\n", dnam(d), s, d->sectors);
} else
d->state = Derror;
if(d->state == Dready)
@@ -779,20 +707,21 @@
return -1;
}
-/* p. 163:
- M recovered error
- P protocol error
- N PhyRdy change
- W CommWake
- B 8-to-10 encoding error
- D disparity error
- C crc error
- H handshake error
- S link sequence error
- T transport state transition error
- F unrecognized fis type
- X device changed
-*/
+/*
+ * p. 163:
+ * M recovered error
+ * P protocol error
+ * N PhyRdy change
+ * W CommWake
+ * B 8-to-10 encoding error
+ * D disparity error
+ * C crc error
+ * H handshake error
+ * S link sequence error
+ * T transport state transition error
+ * F unrecognized fis type
+ * X device changed
+ */
static char stab[] = {
[1] 'M',
@@ -800,7 +729,7 @@
[16] 'N',
[18] 'W', 'B', 'D', 'C', 'H', 'S', 'T', 'F', 'X'
};
-static ulong sbad = (7<<20)|(3<<23);
+static ulong sbad = 7<<20 | 3<<23;
static void
serrdecode(ulong r, char *s, char *e)
@@ -808,17 +737,16 @@
int i;
e -= 3;
- for(i = 0; i < nelem(stab) && s < e; i++){
- if((r&(1<<i)) && stab[i]){
+ for(i = 0; i < nelem(stab) && s < e; i++)
+ if(r & 1<<i && stab[i]){
*s++ = stab[i];
- if(sbad&(1<<i))
+ if(sbad & 1<<i)
*s++ = '*';
}
- }
*s = 0;
}
-char *iectab[] = {
+static char *iectab[] = {
"ePrtDataErr",
"ePrtPRDErr",
"eDevErr",
@@ -846,7 +774,9 @@
}
enum{
- Cerror = ePrtDataErr|ePrtPRDErr|eDevErr|eSelfDis2|ePrtCRPBErr|ePrtIntErr,
+ Cerror1 = ePrtDataErr|ePrtPRDErr|eOverrun|ePrtCRQBErr|ePrtCRPBErr|ePrtIntErr,
+ Cerror2 = ePrtDataErr|ePrtPRDErr|ePrtCRQBErr|
+ ePrtCRPBErr|ePrtIntErr|eDevErr|eSelfDis2,
};
static void
@@ -858,7 +788,7 @@
char buf[32+4+1];
edma = d->edma;
- if((edma->ctl&eEnEDMA) == 0){
+ if((edma->ctl & eEnEDMA) == 0){
/* FEr SATA#4 40xx */
x = d->edma->cmdstat;
USED(x);
@@ -866,24 +796,46 @@
cause = edma->iec;
if(cause == 0)
return;
- dprint("%s: cause %08ulx [%s]\n", d->unit->name, cause, iecdecode(cause));
+ dprint("%s: cause %.8lux [%s]\n", dnam(d), cause, iecdecode(cause));
if(cause & eDevCon)
d->state = Dnew;
- if(cause&eDevDis && d->state == Dready)
- iprint("%s: pulled: st=%08ulx\n", d->unit->name, cause);
+ if(cause & eDevDis && d->state == Dready)
+ iprint("%s: pulled: st=%.8lux\n", dnam(d), cause);
switch(d->ctlr->type){
case 1:
- if(cause&eSelfDis)
- d->state = Derror;
+ if(cause & eUnderrun){
+ /* FEr SATA#5 50xx for revs A0, B0 */
+ if(d->ctlr->rid < 2)
+ d->state = Dreset;
+ else{
+ d->state = Derror;
+ dprint("%s: underrun\n", dnam(d));
+ }
+ }
+ if(cause & (eDevErr | eSelfDis)){
+ /*
+ * FEr SATA#7 60xx for refs A0, B0
+ * check for IRC error. we only check the
+ * ABORT flag as we don't get the upper nibble
+ */
+ if(d->ctlr->rid < 2)
+ if(edma->altstat & ATAerr && edma->err & ATAabort)
+ d->state = Dreset;
+ else
+ d->state = Derror;
+ }
+ if(cause & Cerror1)
+ d->state = Dreset;
break;
case 2:
- if(cause&Cerror)
- d->state = Derror;
- if(cause&SerrInt){
+ if(cause & Cerror2)
+ d->state = Dreset;
+ if(cause & SerrInt){
serrdecode(d->bridge->serror, buf, buf+sizeof buf);
- dprint("%s: serror %08ulx [%s]\n", d->unit->name, (ulong)d->bridge->serror, buf);
- d->bridge->serror = d->bridge->serror;
+ dprint("%s: serror %.8lux [%s]\n", dnam(d), d->bridge->serror, buf);
+ d->bridge->serror = ~0; /*d->bridge->serror;*/
}
+ break;
}
edma->iec = ~cause;
}
@@ -892,7 +844,7 @@
* Requests
*/
static Srb*
-srbrw(int req, Drive *d, uchar *data, uint sectors, uvlong lba)
+srbrw(int rw, Drive *d, uchar *data, uint sectors, uvlong lba)
{
int i;
Srb *srb;
@@ -899,38 +851,28 @@
static uchar cmd[2][2] = { 0xC8, 0x25, 0xCA, 0x35 };
srb = allocsrb();
- srb->req = req;
+ srb->req = rw;
srb->drive = d;
srb->blockno = lba;
srb->sectors = sectors;
- srb->count = sectors*512;
+ srb->count = sectors * d->secsize;
srb->flag = 0;
srb->data = data;
for(i=0; i<6; i++)
- srb->lba[i] = lba >> (8*i);
- srb->cmd = cmd[srb->req!=SRBread][(d->flag&Dext)!=0];
+ srb->lba[i] = lba >> 8*i;
+ srb->cmd = cmd[srb->req!=SDread][(d->feat&Dllba)!=0];
return srb;
}
-static uintptr
-advance(uintptr pa, int shift)
-{
- int n, mask;
-
- mask = 0x1F<<shift;
- n = (pa & mask) + (1<<shift);
- return (pa & ~mask) | (n & mask);
-}
-
-#define CMD(r, v) (((r)<<8) | ((v)&0xFF))
+#define CMD(r, v) (((r)<<8) | (v))
static void
-mvsatarequest(ushort *cmd, Srb *srb, int ext)
+mvsatarequest(ushort *cmd, Srb *srb, int llba)
{
*cmd++ = CMD(ARseccnt, 0);
*cmd++ = CMD(ARseccnt, srb->sectors);
*cmd++ = CMD(ARfea, 0);
- if(ext){
+ if(llba){
*cmd++ = CMD(ARlba0, srb->lba[3]);
*cmd++ = CMD(ARlba0, srb->lba[0]);
*cmd++ = CMD(ARlba1, srb->lba[4]);
@@ -944,9 +886,19 @@
*cmd++ = CMD(ARlba2, srb->lba[2]);
*cmd++ = CMD(ARdev, srb->lba[3] | 0xe0);
}
- *cmd = CMD(ARcmd, srb->cmd) | (1<<15);
+ *cmd = CMD(ARcmd, srb->cmd) | 1<<15;
}
+static uintptr
+advance(uintptr pa, int shift)
+{
+ int n, mask;
+
+ mask = 0x1F<<shift;
+ n = (pa & mask) + (1<<shift);
+ return (pa & ~mask) | (n & mask);
+}
+
static void
startsrb(Drive *d, Srb *srb)
{
@@ -971,19 +923,19 @@
break;
if(i == nelem(d->srb))
panic("sdmv50xx: no free srbs");
- d->intick = MACHP(0)->ticks;
+ d->intick = Ticks;
d->srb[i] = srb;
edma = d->edma;
tx = (Tx*)KADDR(edma->txi);
- tx->flag = (i<<1) | (srb->req == SRBread);
+ tx->flag = i<<1 | (srb->req == SDread);
prd = KADDR(tx->prdpa);
- prd->pa = PADDR(srb->data);
+ prd->pa = PCIWADDR(srb->data);
prd->count = srb->count;
prd->flag = PRDeot;
- mvsatarequest(tx->regs, srb, d->flag&Dext);
+ mvsatarequest(tx->regs, srb, d->feat&Dllba);
coherence();
edma->txi = advance(edma->txi, 5);
- d->intick = MACHP(0)->ticks;
+ d->intick = Ticks;
}
enum{
@@ -1036,18 +988,20 @@
* Interrupts
*/
static void
-mv50interrupt(Ureg*, void *a)
+mv50interrupt(Ureg*, void *v)
{
int i;
- ulong cause;
+ ulong cause, tk0, m;
+ Arb *a;
Ctlr *ctlr;
Drive *drive;
+ static uint st;
- ctlr = a;
+ ctlr = v;
ilock(ctlr);
cause = ctlr->lmmio[0x1d60/4];
-// dprint("sd%c: mv50interrupt: 0x%lux\n", ctlr->sdev->idno, cause);
- for(i=0; i<ctlr->ndrive; i++)
+// dprint("sd%c: mv50interrupt: %.8lux\n", ctlr->sdev->idno, cause);
+ for(i=0; cause && i<ctlr->ndrive; i++)
if(cause & (3<<(i*2+i/4))){
drive = &ctlr->drive[i];
if(drive->edma == 0)
@@ -1054,9 +1008,17 @@
continue; /* not ready yet. */
ilock(drive);
updatedrive(drive);
- while(ctlr->chip[i/4].arb->ic & (0x0101 << (i%4))){
- ctlr->chip[i/4].arb->ic = ~(0x101 << (i%4));
+ tk0 = Ticks;
+ a = ctlr->chip[i/4].arb;
+ m = 0x0101 << i%4;
+ while(a->ic & m){
+ a->ic = ~m;
completesrb(drive);
+ if(TK2MS(Ticks - tk0) > 3000){
+ print("%s: irq wedge\n", dnam(drive));
+ drive->state = Dreset;
+ break;
+ }
}
iunlock(drive);
}
@@ -1065,20 +1027,20 @@
enum{
Nms = 256,
- Midwait = 16*1024/Nms-1,
- Mphywait = 512/Nms-1,
+ Midwait = 16*1024/Nms - 1,
+ Mphywait = 512/Nms - 1,
};
static void
-westerndigitalhung(Drive *d)
+hangck(Drive *d)
{
Edma *e;
e = d->edma;
- if(d->srb
- && TK2MS(MACHP(0)->ticks-d->intick) > 5*1000
+ if(d->nsrb > 0
+ && TK2MS(Ticks - d->intick) > 5*1000
&& (e->rxo&Rpidx) == (e->rxi&Rpidx)){
- dprint("westerndigital drive hung; resetting\n");
+ print("%s: drive hung; resetting\n", dnam(d));
d->state = Dreset;
}
}
@@ -1087,16 +1049,14 @@
checkdrive(Drive *d, int i)
{
static ulong s, olds[NCtlr*NCtlrdrv];
- char *name;
ilock(d);
- name = d->unit->name;
s = d->bridge->status;
if(s != olds[i]){
- dprint("%s: status: %08lx -> %08lx: %s\n", name, olds[i], s, diskstates[d->state]);
+ dprint("%s: status: %.8lux -> %.8lux: %s\n", dnam(d), olds[i], s, diskstates[d->state]);
olds[i] = s;
}
- /* westerndigitalhung(d); */
+ hangck(d);
switch(d->state){
case Dnew:
case Dmissing:
@@ -1104,12 +1064,12 @@
case 0x000:
break;
default:
- dprint("%s: unknown state %8lx\n", name, s);
+ dprint("%s: unknown state %.8lux\n", dnam(d), s);
case 0x100:
if(++d->wait&Mphywait)
break;
reset: d->mode ^= 1;
- dprint("%s: reset; new mode %d\n", name, d->mode);
+ dprint("%s: reset; new mode %d\n", dnam(d), d->mode);
resetdisk(d);
break;
case 0x123:
@@ -1125,10 +1085,10 @@
case Dready:
if(s != 0)
break;
- iprint("%s: pulled: st=%08ulx\n", name, s); /* never happens */
+ iprint("%s: pulled: st=%.8lux\n", dnam(d), s); /* never happens */
case Dreset:
case Derror:
- dprint("%s reset: mode %d\n", name, d->mode);
+ dprint("%s reset: mode %d\n", dnam(d), d->mode);
resetdisk(d);
break;
}
@@ -1140,9 +1100,6 @@
{
int i;
- while(waserror())
- ;
-
for(;;){
tsleep(&up->sleep, return0, 0, Nms);
for(i = 0; i < nmvsatadrive; i++)
@@ -1150,9 +1107,24 @@
}
}
-/*
- * Device discovery
- */
+static void
+initdrive(Drive *d)
+{
+ edmainit(d);
+ d->mode = DMsatai;
+ if(d->ctlr->type == 1){
+ d->edma->iem = IEM;
+ d->bridge = &d->chip->arb->bridge[d->chipx];
+ }else{
+ d->edma->iem = IEM2;
+ d->bridge = &d->chip->edma[d->chipx].port;
+// d->edma->iem = ~(1<<6);
+ d->pm2 = Dpreamp;
+ if(d->ctlr->lmmio[0x180d8/4] & 1)
+ d->pm2 = d->bridge->phymode2 & Mpreamp;
+ }
+}
+
static SDev*
mv50pnp(void)
{
@@ -1160,12 +1132,11 @@
uchar *base;
ulong io, n, *mem;
Ctlr *ctlr;
+ Drive *d;
Pcidev *p;
SDev *head, *tail, *sdev;
- Drive *d;
static int ctlrno, done;
- dprint("mv50pnp\n");
if(done++)
return nil;
@@ -1173,6 +1144,8 @@
head = nil;
tail = nil;
while((p = pcimatch(p, 0x11ab, 0)) != nil){
+ if(p->ccrb != Pcibcstore || p->ccru + p->ccrp || p->did&0x0f00)
+ continue;
switch(p->did){
case 0x5040:
case 0x5041:
@@ -1190,19 +1163,16 @@
break;
}
nunit = (p->did&0xf0) >> 4;
- print("Marvell 88SX%ux: %d SATA-%s ports with%s flash\n",
- (ushort)p->did, nunit,
+ print("#S/sd%c: Marvell 88sx%ux: %d sata-%s ports with%s flash\n",
+ 'E' + ctlrno, (ushort)p->did, nunit,
((p->did&0xf000)==0x6000? "II": "I"),
(p->did&1? "": "out"));
- if((sdev = malloc(sizeof(SDev))) == nil)
+ if((sdev = malloc(sizeof *sdev)) == nil)
continue;
- if((ctlr = malloc(sizeof(Ctlr))) == nil){
+ if((ctlr = malloc(sizeof *ctlr)) == nil){
free(sdev);
continue;
}
- memset(sdev, 0, sizeof *sdev);
- memset(ctlr, 0, sizeof *ctlr);
-
io = p->mem[0].bar & ~0x0F;
mem = (ulong*)vmap(io, p->mem[0].size);
if(mem == 0){
@@ -1242,6 +1212,7 @@
}
for (i = 0; i < nunit; i++) {
d = &ctlr->drive[i];
+ snprint(d->name, sizeof d->name, "mv50%d.%d", ctlrno, i);
d->sectors = 0;
d->ctlr = ctlr;
d->driveno = ctlrno*NCtlrdrv + i;
@@ -1249,7 +1220,9 @@
d->chip = &ctlr->chip[i/4];
d->edma = &d->chip->edma[d->chipx];
mvsatadrive[d->driveno] = d;
+ initdrive(d);
}
+ mvsatactlr[ctlrno] = ctlr;
nmvsatadrive += nunit;
ctlrno++;
if(head)
@@ -1261,10 +1234,6 @@
return head;
}
-/*
- * Enable the controller. Each disk has its own interrupt mask,
- * and those get enabled as the disks are brought online.
- */
static int
mv50enable(SDev *sdev)
{
@@ -1276,15 +1245,13 @@
ctlr = sdev->ctlr;
if (ctlr->enabled)
return 1;
+ ctlr->enabled = 1;
+ kproc("mvsata", satakproc, 0);
snprint(name, sizeof name, "%s (%s)", sdev->name, sdev->ifc->name);
intrenable(ctlr->irq, mv50interrupt, ctlr, ctlr->tbdf, name);
- ctlr->enabled = 1;
return 1;
}
-/*
- * Disable the controller.
- */
static int
mv50disable(SDev *sdev)
{
@@ -1310,30 +1277,6 @@
}
/*
- * Clean up all disk structures. Already disabled.
- * Could keep count of number of allocated controllers
- * and free the srblist when it drops to zero.
- */
-static void
-mv50clear(SDev *sdev)
-{
- int i;
- Ctlr *ctlr;
- Drive *d;
-
- dprint("sd%c: clear\n", sdev->idno);
-
- ctlr = sdev->ctlr;
- for(i=0; i<ctlr->ndrive; i++){
- d = &ctlr->drive[i];
- free(d->tx);
- free(d->rx);
- free(d->prd);
- }
- free(ctlr);
-}
-
-/*
* Check that there is a disk or at least a hot swap bay in the drive.
*/
static int
@@ -1354,20 +1297,19 @@
/*
* If ctlr->type == 1, then the drives spin up whenever
- * the controller feels like it; if ctlr->type != 1, then
+ * the controller feels like it; if ctlr->type == 2, then
* they spin up as a result of configdrive.
*
- * If there is a drive in the slot, give it 1.5s to spin up
+ * If there is a drive in the slot, give it 1.4s to spin up
* before returning. There is a noticeable drag on the
* power supply when spinning up fifteen drives
* all at once (like in the Coraid enclosures).
*/
- if(ctlr->type != 1 && i == 0){
+ if(ctlr->type == 2 && i == 0)
if(!waserror()){
- tsleep(&up->sleep, return0, 0, 1500);
+ tsleep(&up->sleep, return0, 0, 1400);
poperror();
}
- }
return 1;
}
@@ -1382,9 +1324,6 @@
int r, s0;
static int once;
- if(once++ == 0)
- kproc("mvsata", satakproc, 0);
-
ctlr = unit->dev->ctlr;
d = &ctlr->drive[unit->subno];
r = 0;
@@ -1393,12 +1332,12 @@
USED(s0);
if(d->state == Dnew)
identifydrive(d);
- if(d->mediachange){
+ if(d->drivechange){
idprint("%s: online: %s -> %s\n", unit->name, diskstates[s0], diskstates[d->state]);
r = 2;
unit->sectors = d->sectors;
- unit->secsize = 512;
- d->mediachange = 0;
+ unit->secsize = d->secsize;
+ d->drivechange = 0;
} else if(d->state == Dready)
r = 1;
iunlock(d);
@@ -1483,25 +1422,13 @@
{
int i;
- for(i = 0; i < n; i++)
- p = seprint(p, e, "%s%s%-19s %.8lux\n",
- prefix? prefix: "", prefix? ": ": "",
- r[i].name, *(ulong *)((uchar*)base + r[i].offset));
+ for(i=0; i<n; i++)
+ p = seprint(p, e, "%s%s%-19s %.8ux\n",
+ prefix ? prefix : "", prefix ? ": " : "",
+ r[i].name, *(u32int*)((uchar*)base+r[i].offset));
return p;
}
-static char*
-rdinfo(char *p, char *e, ushort *info)
-{
- int i;
-
- p = seprint(p, e, "info");
- for(i = 0; i < 256; i++)
- p = seprint(p, e, "%s%.4ux%s", i%8 == 0? "\t": "", info[i],
- i%8 == 7? "\n": "");
- return p;
-}
-
static int
mv50rctl(SDunit *unit, char *p, int l)
{
@@ -1519,15 +1446,18 @@
p = seprint(p, e, "model %s\n", drive->model);
p = seprint(p, e, "serial %s\n", drive->serial);
p = seprint(p, e, "firmware %s\n", drive->firmware);
+ p = seprint(p, e, "wwn\t%llux\n", drive->wwn);
+ p = seprint(p, e, "flag\t");
+ p = pflag(p, e, drive);
}else
p = seprint(p, e, "no disk present\n");
- p = seprint(p, e, "geometry %llud 512\n", drive->sectors);
- p = rdinfo(p, e, drive->info);
-
- p = rdregs(p, e, drive->chip->arb, regsarb, nelem(regsarb), nil);
+ p = seprint(p, e, "geometry %llud %ud\n", drive->sectors, drive->secsize);
p = rdregs(p, e, drive->bridge, regsbridge, nelem(regsbridge), nil);
- p = rdregs(p, e, drive->edma, regsedma, nelem(regsedma), nil);
-
+ if(0){
+ p = rdregs(p, e, drive->chip->arb, regsarb, nelem(regsarb), nil);
+ p = rdregs(p, e, drive->bridge, regsbridge, nelem(regsbridge), nil);
+ p = rdregs(p, e, drive->edma, regsedma, nelem(regsedma), nil);
+ }
return p-op;
}
@@ -1537,7 +1467,6 @@
Ctlr *ctlr;
Drive *drive;
- USED(unit);
if(strcmp(cb->f[0], "reset") == 0){
ctlr = unit->dev->ctlr;
drive = &ctlr->drive[unit->subno];
@@ -1550,34 +1479,6 @@
return -1;
}
-/*
- * sd(3): ``Reading /dev/sdctl yields information about each controller,
- * one line per controller.''
- */
-static char*
-mv50rtopctl(SDev *sdev, char *p, char *e)
-{
- char name[10];
- Ctlr *ctlr;
-
- ctlr = sdev->ctlr;
- if(ctlr == nil)
- return p;
-
- snprint(name, sizeof name, "sd%c", sdev->idno);
- p = rdregs(p, e, ctlr->mmio, regsctlr, nelem(regsctlr), name);
- if (Coraiddebug) {
- /* info for first disk. BUG: this shouldn't be here. */
- p = rdregs(p, e, ctlr->chip[0].arb,
- regsarb, nelem(regsarb), name);
- p = rdregs(p, e, &ctlr->chip[0].arb->bridge[0],
- regsbridge, nelem(regsbridge), name);
- p = rdregs(p, e, &ctlr->chip[0].edma[0],
- regsedma, nelem(regsedma), name);
- }
- return p;
-}
-
static int
waitready(Drive *d)
{
@@ -1589,9 +1490,9 @@
iunlock(d);
if(s == 0)
return SDeio;
- if (d->state == Dready)
+ if(d->state == Dready)
return SDok;
- if ((i+1)%60 == 0){
+ if((i+1)%60 == 0){
ilock(d);
resetdisk(d);
iunlock(d);
@@ -1601,87 +1502,42 @@
poperror();
}
}
- print("%s: not responding after 2 minutes\n", d->unit->name);
+ print("%s: not responding; error\n", dnam(d));
return SDeio;
}
-static int
-mv50rio(SDreq *r)
+static long
+mv50bio(SDunit *u, int /*lun*/, int write, void *a, long count, uvlong lba)
{
- int count, max, n, status, try, flag;
- uchar *cmd, *data;
- uvlong lba;
+ int n, try, flag;
+ uchar *data;
Ctlr *ctlr;
- Drive *drive;
- SDunit *unit;
+ Drive *d;
Srb *srb;
- unit = r->unit;
- ctlr = unit->dev->ctlr;
- drive = &ctlr->drive[unit->subno];
- cmd = r->cmd;
-
- if((status = sdfakescsi(r, drive->info, sizeof drive->info)) != SDnostatus){
- /* XXX check for SDcheck here */
- r->status = status;
- return status;
- }
-
- switch(cmd[0]){
- case 0x28: /* read */
- case 0x2A: /* write */
- break;
- default:
- iprint("%s: bad cmd 0x%.2ux\n", drive->unit->name, cmd[0]);
- r->status = SDcheck;
- return SDcheck;
- }
-
- lba = (cmd[2]<<24)|(cmd[3]<<16)|(cmd[4]<<8)|cmd[5];
- count = (cmd[7]<<8)|cmd[8];
- if(r->data == nil)
- return SDok;
- if(r->dlen < count*unit->secsize)
- count = r->dlen/unit->secsize;
-
+ ctlr = u->dev->ctlr;
+ d = ctlr->drive + u->subno;
try = 0;
+ data = a;
retry:
- if(waitready(drive) != SDok)
- return SDeio;
- /*
- * Could arrange here to have an Srb always outstanding:
- *
- * lsrb = nil;
- * while(count > 0 || lsrb != nil){
- * srb = nil;
- * if(count > 0){
- * srb = issue next srb;
- * }
- * if(lsrb){
- * sleep on lsrb and handle it
- * }
- * }
- *
- * On the disks I tried, this didn't help. If anything,
- * it's a little slower. -rsc
- */
- data = r->data;
+ if(waitready(d) != SDok)
+ return -1;
while(count > 0){
/*
* Max is 128 sectors (64kB) because prd->count is 16 bits.
*/
- max = 128;
n = count;
- if(n > max)
- n = max;
- if((drive->edma->ctl&eEnEDMA) == 0)
+ if(n > 128)
+ n = 128;
+ ilock(d);
+ if((d->edma->ctl&eEnEDMA) == 0 && edmaenable(d) == -1){
+ iunlock(d);
goto tryagain;
- srb = srbrw(cmd[0]==0x28 ? SRBread : SRBwrite, drive, data, n, lba);
- ilock(drive);
- startsrb(drive, srb);
- iunlock(drive);
+ }
+ srb = srbrw(write, d, data, n, lba);
+ startsrb(d, srb);
+ iunlock(d);
- /* Don't let user interrupt DMA. */
while(waserror())
;
sleep(srb, srbdone, srb);
@@ -1690,30 +1546,279 @@
flag = srb->flag;
freesrb(srb);
if(flag == 0){
-tryagain:
- if(++try == 10){
- print("%s: bad disk\n", drive->unit->name);
- return SDeio;
+ tryagain: if(++try == 10){
+ print("%s: bad disk\n", dnam(d));
+ return -1;
}
- dprint("%s: retry\n", drive->unit->name);
- if(!waserror()){
- tsleep(&up->sleep, return0, 0, 1000);
- poperror();
- }
+ dprint("%s: retry\n", dnam(d));
goto retry;
}
if(flag & SFerror){
- print("%s: i/o error\n", drive->unit->name);
- return SDeio;
+ print("%s: i/o error\n", dnam(d));
+ return -1;
}
count -= n;
lba += n;
- data += n*unit->secsize;
+ data += n*u->secsize;
}
- r->rlen = data - (uchar*)r->data;
+ return data - (uchar*)a;
+}
+
+static int
+mv50rio(SDreq *r)
+{
+ int count, n, status, rw;
+ uvlong lba;
+ Ctlr *ctlr;
+ Drive *d;
+ SDunit *unit;
+
+ unit = r->unit;
+ ctlr = unit->dev->ctlr;
+ d = &ctlr->drive[unit->subno];
+
+ if((status = sdfakescsi(r)) != SDnostatus)
+ return r->status = status;
+ if((status = sdfakescsirw(r, &lba, &count, &rw)) == SDcheck)
+ return status;
+ n = mv50bio(r->unit, r->lun, rw, r->data, count, lba);
+ if(n == -1)
+ return SDeio;
+ r->rlen = n;
return SDok;
}
+static void
+mkrfis(SDreq *r, Drive *d, Edma *e)
+{
+ uchar *u;
+
+ u = r->cmd;
+ u[Ftype] = 0x34;
+ u[Fioport] = 0;
+ if((d->feat & Dllba) && (r->ataproto & P28) == 0){
+ u[Frerror] = e->err;
+ u[Fsc8] = e->seccnt;
+ u[Fsc] = e->seccnt;
+ u[Flba24] = e->lba0;
+ u[Flba0] = e->lba0;
+ u[Flba32] = e->lba1;
+ u[Flba8] = e->lba1;
+ u[Flba40] = e->lba2;
+ u[Flba16] = e->lba2;
+ u[Fdev] = e->lba3;
+ u[Fstatus] = e->cmdstat;
+ }else{
+ u[Frerror] = e->err;
+ u[Fsc] = e->seccnt;
+ u[Flba0] = e->lba0;
+ u[Flba8] = e->lba1;
+ u[Flba16] = e->lba2;
+ u[Fdev] = e->lba3;
+ u[Fstatus] = e->cmdstat;
+ }
+}
+
+static int
+piocmd(SDreq *r, Drive *d)
+{
+ uchar *p, *c;
+ int n, nsec, i, err;
+ Edma *e;
+ SDunit *u;
+
+ u = r->unit;
+
+ if(waitready(d) != SDok)
+ return SDeio;
+ nsec = 0;
+ if(u->secsize != 0)
+ nsec = r->dlen / u->secsize;
+ if(r->dlen < nsec*u->secsize)
+ nsec = r->dlen/u->secsize;
+ if(nsec > 256)
+ error("can't do more than 256 sectors");
+
+ ilock(d);
+ e = d->edma;
+ if(edmadisable(d, 0) == -1) {
+ iunlock(d);
+ error("can't disable edma");
+ }
+ n = satawait(&e->cmdstat, ATAdrdy|ATAbusy, ATAdrdy, 3*1000);
+ if(n == 0) {
+print("piocmd: notready %.2ux\n", e->cmdstat);
+ iunlock(d);
+ return sdsetsense(r, SDcheck, 4, 8, 0);
+ }
+ c = r->cmd;
+ if(r->ataproto & P28){
+ e->altstat = ATAeIEN;
+ e->seccnt = c[Fsc];
+ e->err = c[Ffeat];
+ e->lba0 = c[Flba0];
+ e->lba1 = c[Flba8];
+ e->lba2 = c[Flba16];
+ e->lba3 = c[Fdev];
+ e->cmdstat = c[Fcmd];
+ }else{
+ e->altstat = ATAeIEN;
+ e->seccnt = c[Fsc8];
+ e->seccnt = c[Fsc];
+ e->err = c[Ffeat];
+ e->lba0 = c[Flba24];
+ e->lba0 = c[Flba0];
+ e->lba1 = c[Flba32];
+ e->lba1 = c[Flba8];
+ e->lba1 = c[Flba40];
+ e->lba2 = c[Flba16];
+ e->lba3 = c[Fdev];
+ e->cmdstat = c[Fcmd];
+ }
+ err = 0;
+
+ if((r->ataproto & Pdatam) == Pnd)
+ n = satawait(&e->cmdstat, ATAbusy, 0, 3*1000);
+ else
+ n = satawait(&e->cmdstat, ATAbusy|ATAdrq, ATAdrq, 3*1000);
+ if(n == 0 || e->cmdstat & ATAerr){
+ err = 1;
+ goto lose;
+ }
+ p = r->data;
+ for(; nsec > 0; nsec--)
+ for (i = 0; i < u->secsize; i += 2) {
+ n = satawait(&e->cmdstat, ATAbusy|ATAdrq, ATAdrq, 300);
+ if (n == 0) {
+ d->state = Dreset;
+ err = 1;
+ goto lose;
+ }
+ if(r->ataproto & Pout){
+ n = (ushort)p[i + 1] << 8;
+ e->pio = n | p[i];
+ } else {
+ n = e->pio;
+ p[i] = n;
+ p[i + 1] = n >> 8;
+ }
+ microdelay(1);
+ }
+lose:
+ if(nsec == 0)
+ r->rlen = r->dlen;
+ mkrfis(r, d, e);
+ iunlock(d);
+ if(err)
+ return sdsetsense(r, SDcheck, 4, 8, 0);
+ else
+ return sdsetsense(r, SDok, 0, 0, 0);
+}
+
+/*
+ * hack to allow udma mode to be set or unset
+ * via direct ata command. it would be better
+ * to move the assumptions about dma mode out
+ * of some of the helper functions.
+ */
+static int
+isudm(SDreq *r)
+{
+ uchar *c;
+
+ c = r->cmd;
+ if(c[Fcmd] == 0xef && c[Ffeat] == 0x03){
+ if(c[Fsc]&0x40)
+ return 1;
+ return -1;
+ }
+ return 0;
+}
+static int
+fisreqchk(Sfis *f, SDreq *r)
+{
+ if((r->ataproto & Pprotom) == Ppkt)
+ return SDnostatus;
+ /*
+ * handle oob requests;
+ * restrict & sanitize commands
+ */
+ if(r->clen != 16)
+ error(Eio);
+ if(r->cmd[0] == 0xf0){
+ sigtofis(f, r->cmd);
+ r->status = SDok;
+ return SDok;
+ }
+ r->cmd[0] = 0x27;
+ r->cmd[1] = 0x80;
+ r->cmd[7] |= 0xa0;
+ return SDnostatus;
+}
+
+static int
+badf(SDreq *r, Drive*)
+{
+print("badf %.2ux %2ux\n", r->cmd[2], r->ataproto);
+ return sdsetsense(r, SDcheck, 2, 24, 0);
+}
+
+static int
+ataio0(SDreq *r, Drive *d)
+{
+ int (*f)(SDreq*, Drive*);
+
+ f = badf;
+ switch(r->ataproto & Pprotom){
+ default:
+ break;
+ case Ppio:
+ case Pnd:
+ f = piocmd;
+ break;
+ }
+ return f(r, d);
+}
+
+static int
+mv50ata(SDreq *r)
+{
+ int status, udm;
+ Ctlr *c;
+ Drive *d;
+ SDunit *u;
+
+ u = r->unit;
+ c = u->dev->ctlr;
+ d = c->drive + u->subno;
+ if((status = fisreqchk(d, r)) != SDnostatus)
+ return status;
+ udm = isudm(r);
+ USED(udm); /* botch */
+
+// qlock(d);
+ if(waserror()){
+// qunlock(d);
+ nexterror();
+ }
+retry:
+ switch(status = ataio0(r, d)){
+ default:
+ dprint("%s: status %d\n", dnam(d), status);
+ break;
+ case SDretry:
+ dprint("%s: retry\n", dnam(d));
+ goto retry;
+ case SDok:
+ sdsetsense(r, SDok, 0, 0, 0);
+ break;
+ }
+ poperror();
+// qunlock(d);
+ return r->status = status;
+}
+
+
SDifc sdmv50xxifc = {
"mv50xx", /* name */
@@ -1725,13 +1830,15 @@
mv50verify, /* verify */
mv50online, /* online */
mv50rio, /* rio */
- mv50rctl, /* rctl */
+ mv50rctl, /* rctl */
mv50wctl, /* wctl */
- scsibio, /* bio */
+ mv50bio, /* bio */
nil, /* probe */
- mv50clear, /* clear */
- mv50rtopctl, /* rtopctl */
+ nil, /* clear */
+ nil, /* rtopctl */
+ nil,
+ mv50ata,
};
/*
--- a/sys/src/9/pc/sdmylex.c
+++ b/sys/src/9/pc/sdmylex.c
@@ -840,7 +840,6 @@
Ctlr *ctlr;
uchar cmd[6], data[256];
int clen, dlen, timeo;
- static int count;
if(ioalloc(port, 0x3, 0, "mylex") < 0)
return nil;
@@ -894,18 +893,13 @@
if(issue(ctlr, cmd, clen, data, dlen)){
if(data[0] == 'E')
ctlr->bus = 32;
+ print("mylex ctlr @ port 0x%ux: 32-bit ", ctlr->port);
ctlr->wide = data[0x0D] & 0x01;
- /*
- * devsd doesn't pass us the `spec' argument, so
- * we'll assume that sd0 goes to the first scsi host
- * adapter found, etc.
- */
- print("#S/sd%d: mylex SCSI: port 0x%ux: %d-bit, ",
- count++, ctlr->port, ctlr->bus);
if (ctlr->wide)
- print("wide\n");
+ print("wide ");
else
- print("narrow\n");
+ print("narrow ");
+ print("SCSI host adapter\n");
}
else{
/*
@@ -1187,8 +1181,9 @@
cmd[1] = 1;
if(!issue(ctlr, cmd, 2, 0, 0)) {
ctlr->wide = 0;
- print("mylex32enable: port 0x%ux: scsi wide-mode setup "
- "failed on wide host adapter", ctlr->port);
+ print(
+"mylex32enable: ctlr @ port 0x%ux: scsi wide-mode setup failed on wide host adapter",
+ ctlr->port);
}
}
--- /dev/null
+++ b/sys/src/9/pc/sdodin.c
@@ -1,0 +1,2841 @@
+/*
+ * marvell odin ii 88se64xx sata/sas controller
+ * copyright © 2009 erik quanstrom
+ * coraid, inc.
+ */
+
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+#include "../port/sd.h"
+#include <fis.h>
+#include "../port/led.h"
+
+#define dprint(...) if(debug) print(__VA_ARGS__); else USED(debug)
+#define idprint(...) if(idebug) print(__VA_ARGS__); else USED(idebug)
+#define aprint(...) if(adebug) print(__VA_ARGS__); else USED(adebug)
+#define Pciwaddrh(a) 0
+#define Pciw64(x) (uvlong)PCIWADDR(x)
+#define Ticks MACHP(0)->ticks
+
+/* copied from sdiahci */
+enum {
+ Dnull = 0,
+ Dmissing = 1<<0,
+ Dnopower = 1<<1,
+ Dnew = 1<<2,
+ Dready = 1<<3,
+ Derror = 1<<4,
+ Dreset = 1<<5,
+ Doffline = 1<<6,
+ Dportreset = 1<<7,
+ Dlast = 9,
+};
+
+static char *diskstates[Dlast] = {
+ "null",
+ "missing",
+ "nopower",
+ "new",
+ "ready",
+ "error",
+ "reset",
+ "offline",
+ "portreset",
+};
+
+static char *type[] = {
+ "offline",
+ "sas",
+ "sata",
+};
+
+enum{
+ Nctlr = 4,
+ Nctlrdrv = 8,
+ Ndrive = Nctlr*Nctlrdrv,
+ Mbar = 2,
+ Mebar = 4,
+ Nqueue = 32, /* cmd queue size */
+ Qmask = Nqueue - 1,
+ Nregset = 8,
+ Rmask = 0xffff,
+ Nms = 256, /* drive check rate */
+
+ Sas = 1,
+ Sata,
+
+ /* cmd bits */
+ Error = 1<<31,
+ Done = 1<<30,
+ Noverdict = 1<<29,
+ Creset = 1<<28,
+ Atareset = 1<<27,
+ Sense = 1<<26,
+ Timeout = 1<<25,
+ Response = 1<<24,
+ Active = 1<<23,
+
+ /* pci registers */
+ Phy0 = 0x40,
+ Gpio = 0x44,
+ Phy1 = 0x90,
+ Gpio1 = 0x94,
+ Dctl = 0xe8,
+
+ /* phy offests */
+ Phydisable = 1<<12,
+ Phyrst = 1<<16,
+ Phypdwn = 1<<20,
+ Phyen = 1<<24,
+
+ /* bar4 registers */
+ Gctl = 0x004/4,
+ Gis = 0x008/4, /* global interrupt status */
+ Pi = 0x00c/4, /* ports implemented */
+ Flashctl = 0x030/4, /* spi flash control */
+ Flashcmd = 0x034/4, /* flash wormhole */
+ Flashdata = 0x038/4,
+ I²cctl = 0x040/4, /* i²c control */
+ I²ccmd = 0x044/4,
+ I²cdata = 0x048/4,
+ Ptype = 0x0a0/4, /* 15:8 auto detect enable; 7:0 sas=1. sata=0 */
+ Portcfg0 = 0x100/4, /* 31:16 register sets 31:16 */
+ Portcfg1 = 0x104/4, /* 31:16 register sets 15:8 tx enable; 7 rx enable */
+ Clbase = 0x108/4, /* cmd list base; 64 bits */
+ Fisbase = 0x110/4, /* 64 bits */
+ Dqcfg = 0x120/4, /* bits 11:0 specify size */
+ Dqbase = 0x124/4,
+ Dqwp = 0x12c/4, /* delivery queue write pointer */
+ Dqrp = 0x130/4,
+ Cqcfg = 0x134/4,
+ Cqbase = 0x138/4,
+ Cqwp = 0x140/4, /* hw */
+ Coal = 0x148/4,
+ Coalto = 0x14c/4, /* coal timeout µs */
+ Cis = 0x150/4, /* centeral irq status */
+ Cie = 0x154/4, /* centeral irq enable */
+ Csis = 0x158/4, /* cmd set irq status */
+ Csie = 0x15c/4,
+ Cmda = 0x1b8/4,
+ Cmdd = 0x1bc/4,
+ Gpioa = 0x270/4,
+ Gpiod = 0x274/4,
+ Gpiooff = 0x100, /* second gpio offset */
+
+ /* port conf registers; mapped through wormhole */
+ Pinfo = 0x000,
+ Paddr = 0x004,
+ Painfo = 0x00c, /* attached device info */
+ Pawwn = 0x010,
+ Psatactl = 0x018,
+ Pphysts = 0x01c,
+ Psig = 0x020, /* 16 bytes */
+ Perr = 0x030,
+ Pcrcerr = 0x034,
+ Pwidecfg = 0x038,
+ Pwwn = 0x080, /* 12 wwn + ict */
+
+ /* port cmd registers; mapped through “cmd” wormhole */
+ Ci = 0x040, /* cmd active (16) */
+ Task = 0x080,
+ Rassoc = 0x0c0,
+ Pfifo0 = 0x1a8,
+ Pfifo1 = 0x1c4,
+ Pwdtimer = 0x13c,
+
+ /* “vendor specific” wormhole */
+ Phymode = 0x001,
+
+ /* gpio wormhole */
+ Sgconf0 = 0x000,
+ Sgconf1 = 0x004,
+ Sgclk = 0x008,
+ Sgconf3 = 0x00c,
+ Sgis = 0x010, /* interrupt set */
+ Sgie = 0x014, /* interrupt enable */
+ Drivesrc = 0x020, /* 4 drives/register; 4 bits/drive */
+ Drivectl = 0x038, /* same deal */
+
+ /* Gctl bits */
+ Reset = 1<<0,
+ Intenable = 1<<1,
+
+ /* Portcfg0/1 bits */
+ Regen = 1<<16, /* enable sata regsets 31:16 or 15:0 */
+ Xmten = 1<<8, /* enable port n transmission */
+ Dataunke = 1<<3,
+ Rsple = 1<<2, /* response frames in le format */
+ Oabe = 1<<1, /* oa frame in big endian format */
+ Framele = 1<<0, /* frame contents in le format */
+
+ Allresrx = 1<<7, /* receive all responses */
+ Stpretry = 1<<6,
+ Cmdirq = 1<<5, /* 1 == self clearing */
+ Fisen = 1<<4, /* enable fis rx */
+ Errstop = 1<<3, /* set -> stop on ssp/smp error */
+ Resetiss = 1<<1, /* reset cmd issue; self clearing */
+ Issueen = 1<<0,
+
+ /* Dqcfg bits */
+ Dqen = 1<<16,
+
+ /* Cqcfg bits */
+ Noattn = 1<<17, /* don't post entries with attn bit */
+ Cqen = 1<<16,
+
+ /* Cis bits */
+ I2cirq = 1<<31,
+ Swirq1 = 1<<30,
+ Swirq0 = 1<<29,
+ Prderr = 1<<28,
+ Dmato = 1<<27,
+ Parity = 1<<28, /* parity error; fatal */
+ Slavei2c = 1<<25,
+ Portstop = 1<<16, /* bitmapped */
+ Portirq = 1<<8, /* bitmapped */
+ Srsirq = 1<<3,
+ Issstop = 1<<1,
+ Cdone = 1<<0,
+ Iclr = Swirq1 | Swirq0,
+
+ /* Pis bits */
+ Caf = 1<<29, /* clear affiliation fail */
+ Sync = 1<<25, /* sync during fis rx */
+ Phyerr = 1<<24,
+ Stperr = 1<<23,
+ Crcerr = 1<<22,
+ Linktx = 1<<21,
+ Linkrx = 1<<20,
+ Martianfis = 1<<19,
+ Anot = 1<<18, /* async notification */
+ Bist = 1<<17,
+ Sigrx = 1<<16, /* native sata signature rx */
+ Phyunrdy = 1<<12, /* phy went offline*/
+ Uilong = 1<<11,
+ Uishort = 1<<10,
+ Martiantag = 1<<9,
+ Bnot = 1<<8, /* broadcast noticication */
+ Comw = 1<<7,
+ Portsel = 1<<6,
+ Hreset = 1<<5,
+ Phyidto = 1<<4,
+ Phyidfail = 1<<3,
+ Phyidok = 1<<2,
+ Hresetok = 1<<1,
+ Phyrdy = 1<<0,
+
+ Pisataup = Phyrdy | Comw | Sigrx,
+ Pisasup = Phyrdy | Comw | Phyidok,
+ Piburp = Sync | Phyerr | Stperr | Crcerr | Linktx |
+ Linkrx | Martiantag,
+ Pireset = Phyidfail | Bnot | Phyunrdy | Bist |
+ Anot | Martianfis | Bist | Phyidto |
+ Hreset,
+ Piunsupp = Portsel,
+
+ /* Psc bits */
+ Sphyrdy = 1<<20,
+ Linkrate = 1<<18, /* 4 bits */
+ Maxrate = 1<<12,
+ Minrate = 1<<8,
+ Sreset = 1<<3,
+ Sbnote = 1<<2,
+ Shreset = 1<<1,
+ Sphyrst = 1<<0,
+
+ /* Painfo bits */
+ Issp = 1<<19,
+ Ismp = 1<<18,
+ Istp = 1<<17,
+ Itype = 1<<0, /* two bits */
+
+ /* Psatactl bits */
+ Powerctl = 1<<30, /* 00 wake; 10 partial 01 slumb */
+ Srst = 1<<29, /* soft reset */
+ Power = 1<<28,
+ Sportsel = 1<<24,
+ Dmahon = 1<<22,
+ Srsten = 1<<20,
+ Dmaxfr = 1<<18,
+
+ /* phy status bits */
+ Phylock = 1<<9,
+ Nspeed = 1<<4,
+ Psphyrdy = 1<<2,
+
+ /* Task bits; modeled after ahci */
+ Eestatus = 0xff<<24,
+ Asdbs = 1<<18,
+ Apio = 1<<17,
+ Adhrs = 1<<16,
+ Eerror = 0xff<<8,
+ Estatus = 0xff,
+
+ /* Phymode bits */
+ Pmnotify = 1<<24,
+ Pmnotifyen = 1<<23,
+
+ /* Sgconf0 bits */
+ Autolen = 1<<24, /* 8 bits */
+ Manlen = 1<<16, /* 8 bits */
+ Sdincapt = 1<<8, /* capture sdatain on + edge */
+ Sdoutch = 1<<7, /* change sdataout on - edge
+ Sldch = 1<<6, /* change sload on - edge
+ Sdoutivt = 1<<5, /* invert sdataout polarity */
+ Ldivt = 1<<4,
+ Sclkivt = 1<<3,
+ Blinkben = 1<<2, /* enable blink b */
+ Blinkaen = 1<<1, /* enable blink a */
+ Sgpioen = 1<<0,
+
+ /* Sgconf1 bits; 4 bits each */
+ Sactoff = 1<<28, /* stretch activity off; 0/64 - 15/64 */
+ Sacton = 1<<24, /* 1/64th - 16/64 */
+ Factoff = 1<<20, /* 0/8 - 15/8; default 1 */
+ Facton = 1<<16, /* 0/4 - 15/4; default 2 */
+ Bhi = 1<<12, /* 1/8 - 16/8 */
+ Blo = 1<<8, /* 1/8 - 16/8 */
+ Ahi = 1<<4, /* 1/8 - 16/8 */
+ Alo = 1<<0, /* 1/8 - 16/8 */
+
+ /* Sgconf3 bits */
+ Autopat = 1<<20, /* 4 bits of start pattern */
+ Manpat = 1<<16,
+ Manrep = 1<<4, /* repeats; 7ff ≡ ∞ */
+ Sdouthalt = 0<<2,
+ Sdoutman = 1<<2,
+ Sdoutauto = 2<<2,
+ Sdoutma = 3<<2,
+ Sdincapoff = 0<<0,
+ Sdinoneshot = 1<<0,
+ Sdinrep = 2<<0,
+
+ /* Sgie Sgis bits */
+ Sgreprem = 1<<8, /* 12 bits; not irq related */
+ Manrep0 = 1<<1, /* write 1 to clear */
+ Capdone = 1<<0, /* capture done */
+
+ /* drive control bits (repeated 4x per drive) */
+ Aled = 1<<5, /* 3 bits */
+ Locled = 1<<3, /* 2 bits */
+ Errled = 1<<0, /* 3 bits */
+ Llow = 0,
+ Lhigh = 1,
+ Lblinka = 2,
+ Lblinkaneg = 3,
+ Lsof = 4,
+ Leof = 5,
+ Lblinkb = 6,
+ Lblinkbneg = 7,
+
+ /* cmd queue bits */
+ Dssp = 1<<29,
+ Dsmp = 2<<29,
+ Dsata = 3<<29, /* also stp */
+ Ditor = 1<<28, /* initiator */
+ Dsatareg = 1<<20,
+ Dphyno = 1<<12,
+ Dcslot = 1,
+
+ /* completion queue bits */
+ Cgood = 1<<23, /* ssp only */
+ Cresetdn = 1<<21,
+ Crx = 1<<20, /* target mode */
+ Cattn = 1<<19,
+ Crxfr = 1<<18,
+ Cerr = 1<<17,
+ Cqdone = 1<<16,
+ Cslot = 1<<0, /* 12 bits */
+
+ /* error bits — first word */
+ Eissuestp = 1<<31, /* cmd issue stopped */
+ Epi = 1<<30, /* protection info error */
+ Eoflow = 1<<29, /* buffer overflow */
+ Eretry = 1<<28, /* retry limit exceeded */
+ Eufis = 1<<27,
+ Edmat = 1<<26, /* dma terminate */
+ Esync = 1<<25, /* sync rx during tx */
+ Etask = 1<<24,
+ Ererr = 1<<23, /* r error received */
+
+ Eroff = 1<<20, /* read data offset error */
+ Exoff = 1<<19, /* xfer rdy offset error */
+ Euxr = 1<<18, /* unexpected xfer rdy */
+ Exflow = 1<<16, /* buffer over/underflow */
+ Elock = 1<<15, /* interlock error */
+ Enak = 1<<14,
+ Enakto = 1<<13,
+ Enoak = 1<<12, /* conn closed wo nak */
+ Eopento = 1<<11, /* open conn timeout */
+ Epath = 1<<10, /* open reject - path blocked */
+ Enodst = 1<<9, /* open reject - no dest */
+ Estpbsy = 1<<8, /* stp resource busy */
+ Ebreak = 1<<7, /* break while sending */
+ Ebaddst = 1<<6, /* open reject - bad dest */
+ Ebadprot = 1<<5, /* open reject - proto not supp */
+ Erate = 1<<4, /* open reject - rate not supp */
+ Ewdest = 1<<3, /* open reject - wrong dest */
+ Ecreditto = 1<<2, /* credit timeout */
+ Edog = 1<<1, /* watchdog timeout */
+ Eparity = 1<<0, /* buffer parity error */
+
+ /* sas ctl cmd header bits */
+ Ssptype = 1<<5, /* 3 bits */
+ Ssppt = 1<<4, /* build your own header *.
+ Firstburst = 1<<3, /* first burst */
+ Vrfylen = 1<<2, /* verify length */
+ Tlretry = 1<<1, /* transport layer retry */
+ Piren = 1<<0, /* pir present */
+
+ /* sata ctl cmd header bits */
+ Lreset = 1<<7,
+ Lfpdma = 1<<6, /* first-party dma. (what's that?) */
+ Latapi = 1<<5,
+ Lpm = 1<<0, /* 4 bits */
+
+ Sspcmd = 0*Ssptype,
+ Ssptask = 1*Ssptype,
+ Sspxfrdy = 4*Ssptype,
+ Ssprsp = 5*Ssptype,
+ Sspread = 6*Ssptype,
+ Sspwrite = 7*Ssptype,
+};
+
+/* following ahci */
+typedef struct {
+ ulong dba;
+ ulong dbahi;
+ ulong pad;
+ ulong count;
+} Aprdt;
+
+typedef struct {
+ union{
+ struct{
+ uchar cfis[0x40];
+ uchar atapi[0x20];
+ };
+ struct{
+ uchar mfis[0x40];
+ };
+ struct{
+ uchar sspfh[0x18];
+ uchar sasiu[0x40];
+ };
+ };
+} Ctab;
+
+/* protection information record */
+typedef struct {
+ uchar ctl;
+ uchar pad;
+ uchar size[2];
+ uchar rtag[4];
+ uchar atag[2];
+ uchar mask[2];
+} Pir;
+
+/* open address frame */
+typedef struct {
+ uchar oaf[0x28];
+ uchar fb[4];
+} Oaf;
+
+/* status buffer */
+typedef struct {
+ uchar error[8];
+ uchar rsp[0x400];
+} Statb;
+
+typedef struct {
+ uchar satactl;
+ uchar sasctl;
+ uchar len[2];
+
+ uchar fislen[2];
+ uchar maxrsp;
+ uchar d0;
+
+ uchar tag[2];
+ uchar ttag[2];
+
+ uchar dlen[4];
+ uchar ctab[8];
+ uchar oaf[8];
+ uchar statb[8];
+ uchar prd[8];
+
+ uchar d3[16];
+} Cmdh;
+
+typedef struct Cmd Cmd;
+struct Cmd {
+ Rendez;
+ uint cflag;
+
+ Cmdh *cmdh;
+ Ctab;
+ Oaf;
+ Statb;
+ Aprdt;
+};
+
+typedef struct Drive Drive;
+typedef struct Ctlr Ctlr;
+
+struct Drive {
+ Lock;
+ QLock;
+ Ctlr *ctlr;
+ SDunit *unit;
+ char name[16];
+
+ Cmd *cmd;
+
+ /* sdscsi doesn't differentiate drivechange/mediachange */
+ uchar drivechange;
+ uchar state;
+ uchar type;
+ ushort info[0x100];
+
+ Sfis; /* sata and media info*/
+ Cfis; /* sas and media info */
+ Ledport; /* led */
+
+ /* hotplug info */
+ uint lastseen;
+ uint intick;
+ uint wait;
+
+ char serial[20+1];
+ char firmware[8+1];
+ char model[40+1];
+ uvlong wwn;
+ uvlong sectors;
+ uint secsize;
+
+ uint driveno;
+};
+
+struct Ctlr {
+ Lock;
+ uchar enabled;
+ SDev *sdev;
+ Pcidev *pci;
+ uint *reg;
+
+ uint dq[Nqueue];
+ uint dqwp;
+ uint cq[Nqueue + 1];
+ uint cqrp;
+ Cmdh *cl;
+ uchar *fis;
+ Cmd *cmdtab;
+
+ Drive drive[Nctlrdrv];
+ uint ndrive;
+};
+
+static Ctlr msctlr[Nctlr];
+static SDev sdevs[Nctlr];
+static uint nmsctlr;
+static Drive *msdrive[Ndrive];
+static uint nmsdrive;
+static int debug=0;
+static int idebug=1;
+static int adebug;
+static uint olds[Nctlr*Nctlrdrv];
+ SDifc sdodinifc;
+
+/* a good register is hard to find */
+static int pis[] = {
+ 0x160/4, 0x168/4, 0x170/4, 0x178/4,
+ 0x200/4, 0x208/4, 0x210/4, 0x218/4,
+};
+static int pcfg[] = {
+ 0x1c0/4, 0x1c8/4, 0x1d0/4, 0x1d8/4,
+ 0x230/4, 0x238/4, 0x240/4, 0x248/4,
+};
+static int psc[] = {
+ 0x180/4, 0x184/4, 0x188/4, 0x18c/4,
+ 0x220/4, 0x224/4, 0x228/4, 0x22c/4,
+};
+static int vscfg[] = {
+ 0x1e0/4, 0x1e8/4, 0x1f0/4, 0x1f8/4,
+ 0x250/4, 0x258/4, 0x260/4, 0x268/4,
+};
+#define sstatus(d) (d)->ctlr->reg[psc[(d)->driveno]]
+
+static char*
+dstate(uint s)
+{
+ int i;
+
+ for(i = 0; s; i++)
+ s >>= 1;
+ return diskstates[i];
+}
+
+static char*
+dnam(Drive *d)
+{
+ if(d->unit)
+ return d->unit->name;
+ return d->name;
+}
+
+static uvlong border = 0x0001020304050607ull;
+static uvlong lorder = 0x0706050403020100ull;
+
+static uvlong
+getle(uchar *t, int w)
+{
+ uint i;
+ uvlong r;
+
+ r = 0;
+ for(i = w; i != 0; )
+ r = r<<8 | t[--i];
+ return r;
+}
+
+static void
+putle(uchar *t, uvlong r, int w)
+{
+ uchar *o, *f;
+ uint i;
+
+ f = (uchar*)&r;
+ o = (uchar*)&lorder;
+ for(i = 0; i < w; i++)
+ t[o[i]] = f[i];
+}
+
+static uvlong
+getbe(uchar *t, int w)
+{
+ uint i;
+ uvlong r;
+
+ r = 0;
+ for(i = 0; i < w; i++)
+ r = r<<8 | t[i];
+ return r;
+}
+
+static void
+putbe(uchar *t, uvlong r, int w)
+{
+ uchar *o, *f;
+ uint i;
+
+ f = (uchar*)&r;
+ o = (uchar*)&border + (sizeof border-w);
+ for(i = 0; i < w; i++)
+ t[i] = f[o[i]];
+}
+
+static int phyrtab[] = {Phy0, Phy1};
+static void
+phyenable(Ctlr *c, Drive *d)
+{
+ uint i, u, reg, m;
+
+ i = d->driveno;
+ reg = phyrtab[i > 3];
+ i &= 3;
+ i = 1<<i;
+ u = pcicfgr32(c->pci, reg);
+ m = i*(Phypdwn | Phydisable | Phyen);
+ if((u & m) == Phyen)
+ return;
+ m = i*(Phypdwn | Phydisable);
+ u &= ~m;
+ u |= i*Phyen;
+ pcicfgw32(c->pci, reg, u);
+}
+
+static void
+regtxreset(Drive *d)
+{
+ uint i, u, m;
+ Ctlr *c = d->ctlr;
+
+ i = d->driveno;
+ u = c->reg[Portcfg1];
+ m = (Regen|Xmten)<<i;
+ u &= ~m;
+ c->reg[Portcfg1] = u;
+ delay(1);
+ c->reg[Portcfg1] = u | m;
+}
+
+/* aka comreset? */
+static void
+phyreset(Drive *d)
+{
+ uint i, u, reg;
+ Ctlr *c;
+
+ c = d->ctlr;
+ phyenable(c, d);
+
+ i = d->driveno;
+ reg = phyrtab[i > 3];
+ i &= 3;
+ i = 1<<i;
+ u = pcicfgr32(c->pci, reg);
+ pcicfgw32(c->pci, reg, u | i*Phyrst);
+ delay(5);
+ pcicfgw32(c->pci, reg, u);
+
+ sstatus(d) |= Shreset;
+ while(sstatus(d) & Shreset);
+ ;
+}
+
+static void
+reset(Drive *d)
+{
+ regtxreset(d);
+ phyreset(d);
+}
+
+/*
+ * sata/sas register reads through wormhole
+ */
+static uint
+ssread(Ctlr *c, int port, uint r)
+{
+ c->reg[Cmda] = r + 4*port;
+ return c->reg[Cmdd];
+}
+
+static void
+sswrite(Ctlr *c, int port, int r, uint u)
+{
+ c->reg[Cmda] = r + 4*port;
+ c->reg[Cmdd] = u;
+}
+
+/*
+ * port configuration r/w through wormhole
+ */
+static uint
+pcread(Ctlr *c, uint port, uint r)
+{
+ c->reg[pcfg[port]] = r;
+ return c->reg[pcfg[port] + 1];
+}
+
+static void
+pcwrite(Ctlr *c, uint port, uint r, uint u)
+{
+ c->reg[pcfg[port] + 0] = r;
+ c->reg[pcfg[port] + 1] = u;
+}
+
+/*
+ * vendor specific r/w through wormhole
+ */
+static uint
+vsread(Ctlr *c, uint port, uint r)
+{
+ c->reg[vscfg[port]] = r;
+ return c->reg[vscfg[port] + 1];
+}
+
+static void
+vswrite(Ctlr *c, uint port, uint r, uint u)
+{
+ c->reg[vscfg[port] + 0] = r;
+ c->reg[vscfg[port] + 1] = u;
+}
+
+/*
+ * gpio wormhole
+ */
+static uint
+gpread(Ctlr *c, uint r)
+{
+ c->reg[Gpioa] = r;
+ return c->reg[Gpiod];
+}
+
+static void
+gpwrite(Ctlr *c, uint r, uint u)
+{
+ c->reg[Gpioa] = r;
+ c->reg[Gpiod] = u;
+}
+
+static uint*
+getsigfis(Drive *d, uint *fis)
+{
+ uint i;
+
+ for(i = 0; i < 4; i++)
+ fis[i] = pcread(d->ctlr, d->driveno, Psig + 4*i);
+ return fis;
+}
+
+static uint
+getsig(Drive *d)
+{
+ uint fis[4];
+
+ return fistosig((uchar*)getsigfis(d, fis));
+}
+
+static uint
+ci(Drive *d)
+{
+ return ssread(d->ctlr, d->driveno, Ci);
+}
+
+static void
+unsetci(Drive *d)
+{
+ uint i;
+
+ i = 1<<d->driveno;
+ sswrite(d->ctlr, d->driveno, Ci, i);
+ while(ci(d) & i)
+ microdelay(1);
+}
+
+static uint
+gettask(Drive *d)
+{
+ return ssread(d->ctlr, d->driveno, Task);
+}
+
+static void
+tprint(Drive *d, uint t)
+{
+ uint s;
+
+ s = sstatus(d);
+ dprint("%s: err task %ux sstat %ux\n", dnam(d), t, s);
+}
+
+static int
+cmdactive(void *v)
+{
+ Cmd *x;
+
+ x = v;
+ return (x->cflag & Done) != 0;
+}
+
+static int
+mswait(Cmd *x, int ms)
+{
+ uint u, tk0;
+
+ if(up){
+ tk0 = Ticks;
+ while(waserror())
+ ;
+ tsleep(x, cmdactive, x, ms);
+ poperror();
+ ms -= TK2MS(Ticks - tk0);
+ }else
+ while(ms-- && cmdactive(x))
+ delay(1);
+// ilock(cmd->d);
+ u = x->cflag;
+ x->cflag = 0;
+// iunlock(cmd->d)
+
+ if(u == (Done | Active))
+ return 0;
+ if((u & Done) == 0){
+ u |= Noverdict | Creset | Timeout;
+ print("cmd timeout ms:%d %ux\n", ms, u);
+ }
+ return u;
+}
+
+static void
+setstate(Drive *d, int state)
+{
+ ilock(d);
+ d->state = state;
+ iunlock(d);
+}
+
+static void
+esleep(int ms)
+{
+ if(waserror())
+ return;
+ tsleep(&up->sleep, return0, 0, ms);
+ poperror();
+}
+
+static int
+waitready(Drive *d)
+{
+ ulong s, i, δ;
+
+ for(i = 0; i < 15000; i += 250){
+ if(d->state & (Dreset | Dportreset | Dnew))
+ return 1;
+ δ = Ticks - d->lastseen;
+ if(d->state == Dnull || δ > 10*1000)
+ return -1;
+ ilock(d);
+ s = sstatus(d);
+ iunlock(d);
+ if((s & Sphyrdy) == 0 && δ > 1500)
+ return -1;
+ if(d->state == Dready && (s & Sphyrdy))
+ return 0;
+ esleep(250);
+ }
+ print("%s: not responding; offline: %.8ux\n", dnam(d), sstatus(d));
+ setstate(d, Doffline);
+ return -1;
+}
+
+static int
+lockready(Drive *d)
+{
+ int i, r;
+
+ for(i = 0; ; i++){
+ qlock(d);
+ if((r = waitready(d)) != 1)
+ return r;
+ qunlock(d);
+ if(i == Nms*10)
+ break;
+ esleep(1);
+ }
+ return -1;
+}
+
+static int
+command(Drive *d, uint cmd, int ms)
+{
+ uint s, n, m, i;
+ Ctlr *c;
+
+ c = d->ctlr;
+ i = d->driveno;
+ m = 1<<i;
+ n = cmd | Ditor | i*Dsatareg | m*Dphyno | i*Dcslot;
+// print("cqwp\t%.8ux : n %ux : d%d; \n", c->cq[0], n, i);
+ /*
+ * xinc doesn't return the previous value and i can't
+ * figure out how to do this without a lock
+ * s = _xinc(&c->dqwp);
+ */
+ d->cmd->cflag = Active;
+ ilock(c);
+ s = c->dqwp++;
+ c->dq[s&Qmask] = n;
+ c->reg[Dqwp] = s&Qmask;
+ iunlock(c);
+// print(" dq slot %d\n", s);
+ d->intick = Ticks; /* move to mswait? */
+ return mswait(d->cmd, ms);
+}
+
+static int
+buildfis(Drive *d, SDreq *r, void *data, int n)
+{
+ Aprdt *p;
+ Cmd *x;
+ Cmdh *h;
+
+ x = d->cmd;
+ memmove(x->cfis, r->cmd, r->clen);
+
+ h = x->cmdh;
+ memset(h, 0, 16);
+ h->fislen[0] = 5;
+ h->len[0] = 0;
+
+ if(data != nil){
+ h->len[0] = 1;
+ p = x;
+ p->dba = PCIWADDR(data);
+ p->dbahi = Pciwaddrh(data);
+ p->count = n;
+ }
+ return command(d, Dsata, 10*1000);
+}
+
+static int
+build(Drive *d, int rw, void *data, int n, vlong lba)
+{
+ Aprdt *p;
+ Cmd *x;
+ Cmdh *h;
+
+ x = d->cmd;
+ rwfis(d, x->cfis, rw, n, lba);
+
+ h = x->cmdh;
+ memset(h, 0, 16);
+ h->fislen[0] = 5;
+ h->len[0] = 1; /* one prdt entry */
+
+ p = x;
+ p->dba = PCIWADDR(data);
+ p->dbahi = Pciwaddrh(data);
+ p->count = d->secsize*n;
+
+ return command(d, Dsata, 10*1000);
+}
+
+enum{
+ Rnone = 1,
+ Rdma = 0x00, /* dma setup; length 0x1b */
+ Rpio = 0x20, /* pio setup; length 0x13 */
+ Rd2h = 0x40, /* d2h register;length 0x13 */
+ Rsdb = 0x58, /* set device bits; length 0x08 */
+};
+
+static uint fisotab[8] = {
+[0] Rnone,
+[1] Rd2h,
+[2] Rpio,
+[3] Rnone,
+[4] Rsdb,
+[5] Rnone,
+[6] Rnone,
+[7] Rnone,
+};
+
+static uint
+fisoffset(Drive *d, int mustbe)
+{
+ uint t, r;
+
+ t = gettask(d) & 0x70000;
+ r = fisotab[t >> 16];
+ if(r == Rnone || (mustbe != 0 && r != mustbe))
+ return 0;
+ return 0x800 + 0x100*d->driveno + r;
+}
+
+/* need to find a non-atapi-specific way of doing this */
+static uint
+atapixfer(Drive *d, uint n)
+{
+ uchar *u;
+ uint i, x;
+
+ if((i = fisoffset(d, Rd2h)) == 0)
+ return 0;
+ u = d->ctlr->fis + i;
+ x = u[Flba16]<<8 | u[Flba8];
+ if(x > n){
+ x = n;
+ print("%s: atapixfer %ux %ux\n", dnam(d), x, n);
+ }
+ return x;
+}
+
+static int
+buildpkt(Drive *d, SDreq *r, void *data, int n)
+{
+ int rv;
+ Aprdt *p;
+ Cmd *x;
+ Cmdh *h;
+
+ x = d->cmd;
+ atapirwfis(d, x->cfis, r->cmd, r->clen, n);
+
+ h = x->cmdh;
+ memset(h, 0, 16);
+ h->satactl = Latapi;
+ h->fislen[0] = 5;
+ h->len[0] = 1; /* one prdt entry */
+
+ if(data != nil){
+ p = x;
+ p->dba = PCIWADDR(data);
+ p->dbahi = Pciwaddrh(data);
+ p->count = n;
+ }
+ rv = command(d, Dsata, 10*1000);
+ if(rv == 0)
+ r->rlen = atapixfer(d, n);
+ return rv;
+}
+
+/*
+ * ata 7, required for sata, requires that all devices "support"
+ * udma mode 5, however sata:pata bridges allow older devices
+ * which may not. the innodisk satadom, for example allows
+ * only udma mode 2. on the assumption that actual udma is
+ * taking place on these bridges, we set the highest udma mode
+ * available, or pio if there is no udma mode available.
+ */
+static int
+settxmode(Drive *d, uchar f)
+{
+ Cmd *x;
+ Cmdh *h;
+
+ x = d->cmd;
+ if(txmodefis(d, x->cfis, f) == -1)
+ return 0;
+
+ h = x->cmdh;
+ memset(h, 0, 16);
+ h->fislen[0] = 5;
+
+ return command(d, Dsata, 3*1000);
+}
+
+static int
+setfeatures(Drive *d, uchar f, uint w)
+{
+ Cmd *x;
+ Cmdh *h;
+
+ x = d->cmd;
+ featfis(d, x->cfis, f);
+
+ h = x->cmdh;
+ memset(h, 0, 16);
+ h->fislen[0] = 5;
+
+ return command(d, Dsata, w);
+}
+
+static int
+mvflushcache(Drive *d)
+{
+ Cmd *x;
+ Cmdh *h;
+
+ x = d->cmd;
+ flushcachefis(d, x->cfis);
+
+ h = x->cmdh;
+ memset(h, 0, 16);
+ h->fislen[0] = 5;
+
+ return command(d, Dsata, 60*1000);
+}
+
+static int
+identify0(Drive *d, void *id)
+{
+ Aprdt *p;
+ Cmd *x;
+ Cmdh *h;
+
+ x = d->cmd;
+ identifyfis(d, x->cfis);
+
+ h = x->cmdh;
+ memset(h, 0, 16);
+ h->fislen[0] = 5;
+ h->len[0] = 1; /* one prdt entry */
+
+ memset(id, 0, 0x200);
+ p = x;
+ p->dba = PCIWADDR(id);
+ p->dbahi = Pciwaddrh(id);
+ p->count = 0x200;
+
+ return command(d, Dsata, 3*1000);
+}
+
+static int
+identify(Drive *d)
+{
+ int i, n;
+ vlong osectors, s;
+ uchar oserial[21];
+ ushort *id;
+ SDunit *u;
+
+ id = d->info;
+ for(i = 0;; i++){
+ if(i > 5 || identify0(d, id) != 0)
+ return -1;
+ n = idpuis(id);
+ if(n & Pspinup && setfeatures(d, 7, 20*1000) == -1)
+ dprint("%s: puis spinup fail\n", dnam(d));
+ if(n & Pidready)
+ break;
+ }
+
+ s = idfeat(d, id);
+ if(s == -1)
+ return -1;
+ if((d->feat&Dlba) == 0){
+ dprint("%s: no lba support\n", dnam(d));
+ return -1;
+ }
+ osectors = d->sectors;
+ memmove(oserial, d->serial, sizeof d->serial);
+
+ d->sectors = s;
+ d->secsize = idss(d, id);
+
+ idmove(d->serial, id+10, 20);
+ idmove(d->firmware, id+23, 8);
+ idmove(d->model, id+27, 40);
+ d->wwn = idwwn(d, id);
+
+ u = d->unit;
+ memset(u->inquiry, 0, sizeof u->inquiry);
+ u->inquiry[2] = 2;
+ u->inquiry[3] = 2;
+ u->inquiry[4] = sizeof u->inquiry - 4;
+ memmove(u->inquiry+8, d->model, 40);
+
+ if(osectors != s || memcmp(oserial, d->serial, sizeof oserial)){
+ d->drivechange = 1;
+ u->sectors = 0;
+ }
+ return 0;
+}
+
+/* open address fises */
+enum{
+ Initiator = 0x80,
+ Openaddr = 1,
+ Awms = 0x8000,
+ Smp = 0,
+ Ssp = 1,
+ Stp = 2,
+ Spd15 = 8,
+ Spd30 = 9,
+};
+
+static void
+oafis(Cfis *f, uchar *c, int type)
+{
+ c[0] = Initiator | type<<4 | Openaddr;
+ c[1] = Spd30; /* botch; just try 3gbps */
+ if(type == Smp)
+ memset(c + 2, 0xff, 2);
+ else
+ memmove(c + 2, f->ict, 2);
+ memmove(c + 4, f->tsasaddr, 8); /* dest "port identifier" §4.2.6 */
+ memmove(c + 12, f->ssasaddr, 8);
+}
+
+/* sas fises */
+static int
+sasfis(Cfis*, uchar *c, SDreq *r)
+{
+ memmove(c, r->cmd, r->clen);
+ if(r->clen < 16)
+ memset(c + r->clen, 0, 16 - r->clen);
+ return 0;
+}
+
+/* sam3 §4.9.4 single-level lun structure */
+static void
+scsilun8(uchar *c, int l)
+{
+ memset(c, 0, 8);
+ if(l < 255)
+ c[1] = l;
+ else if(l < 16384){
+ c[0] = 1<<6 | l>>8;
+ c[1] = l;
+ }else
+ print("bad lun %d\n", l);
+}
+
+static void
+iuhdr(SDreq *r, uchar *c, int fburst)
+{
+ scsilun8(c, r->lun);
+ c[8] = 0;
+ c[9] = 0;
+ if(fburst)
+ c[9] = 0x80;
+}
+
+static void
+ssphdr(Cfis *x, uchar *c, int ftype)
+{
+ memset(c, 0, 0x18);
+ c[0] = ftype;
+ sasbhash(c + 1, x->tsasaddr);
+ sasbhash(c + 5, x->ssasaddr);
+}
+
+/* debugging */
+static void
+dump(uchar *u, uint n)
+{
+ uint i;
+
+ if(n > 100)
+ n = 100;
+ for(i = 0; i < n; i += 4){
+ print("%.2d %.2ux%.2ux%.2ux%.2ux", i, u[i], u[i + 1], u[i + 2], u[i + 3]);
+ print("\n");
+ }
+}
+
+static void
+prsense(uchar *u, uint n)
+{
+ print("sense data %d: \n", n);
+ dump(u, n);
+}
+
+static void
+priu(uchar *u, uint n)
+{
+ print("iu %d: \n", n);
+ dump(u, n);
+}
+
+/*
+ * other suspects:
+ * key asc/q
+ * 02 0401 becoming ready
+ * 040b target port in standby state
+ * 0b01 overtemp
+ * 0b0[345] background *
+ * 0c01 write error - recovered with auto reallocation
+ * 0c02 write error - auto reallocation failed
+ * 0c03 write error - recommend reassignment
+ * 17* recovered data
+ * 18* recovered data
+ * 5d* smart-style reporting (disk/smart handles)
+ * 5e* power state change
+ */
+
+static int
+classifykey(int asckey)
+{
+ if(asckey == 0x062901 || asckey == 0x062900){
+ /* power on */
+ dprint("power on sense\n");
+ return SDretry;
+ }
+ return SDcheck;
+}
+
+/* spc3 §4.5 */
+static int
+sasrspck(Drive *d, SDreq *r, int min)
+{
+ char *p;
+ int rv;
+ uchar *u, *s;
+ uint l, fmt, n, keyasc;
+
+ u = d->cmd->rsp;
+ s = u + 24;
+ dprint("status %d datapres %d\n", u[11], u[10]);
+ switch(u[10]){
+ case 1:
+ l = getbe(u + 20, 4);
+ /*
+ * this is always a bug because we don't do
+ * task mgmt
+ */
+ print("%s: bug: task data %d min %d\n", dnam(d), l, min);
+ return SDcheck;
+ case 2:
+ l = getbe(u + 16, 4);
+ n = sizeof r->sense;
+ if(l < n)
+ n = l;
+ memmove(r->sense, s, n);
+ fmt = s[0] & 0x7f;
+ keyasc = (s[2] & 0xf)<<16 | s[12]<<8 | s[13];
+ rv = SDcheck;
+ /* spc3 §4.5.3; 0x71 is deferred. */
+ if(n >= 18 && (fmt == 0x70 || fmt == 0x71)){
+ rv = classifykey(keyasc);
+ p = "";
+ if(rv == SDcheck){
+ r->flags |= SDvalidsense;
+ p = "valid";
+ }
+ dprint("sense %.6ux %s\n", keyasc, p);
+ }else
+ prsense(s, l);
+ return rv;
+ default:
+ print("%s: sasrspck: spurious\n", dnam(d));
+ priu(u, 24);
+ prsense(s, 0x30);
+ return SDcheck;
+ }
+}
+
+static int
+buildsas(Drive *d, SDreq *r, uchar *data, int n)
+{
+ int w, try, fburst;
+ Aprdt *p;
+ Cmd *x;
+ Cmdh *h;
+
+ try = 0;
+top:
+ fburst = 0; /* Firstburst? */
+ x = d->cmd;
+ /* ssphdr(d, x->sspfh, 6); */
+ iuhdr(r, x->sasiu, fburst);
+ w = 0;
+ if(r->clen > 16)
+ w = r->clen - 16 + 3>> 2;
+ x->sasiu[11] = w;
+ sasfis(d, x->sasiu + 12, r);
+
+ h = x->cmdh;
+ memset(h, 0, 16);
+ h->sasctl = Tlretry | /*Vrfylen |*/ Sspcmd | fburst;
+ h->fislen[0] = sizeof x->sspfh + 12 + 16 + 4*w >> 2;
+ h->maxrsp = 0xff;
+ if(n)
+ h->len[0] = 1;
+ h->ttag[0] = 1;
+ *(uint*)h->dlen = n;
+
+ if(n){
+ p = x;
+ p->dba = PCIWADDR(data);
+ p->dbahi = Pciwaddrh(data);
+ p->count = n;
+ }
+ switch(w = command(d, Dssp, 10*1000)){
+ case 0:
+ r->status = sdsetsense(r, SDok, 0, 0, 0);
+ return 0;
+ case Response | Done | Active:
+ r->status = sasrspck(d, r, 0);
+ if(r->status == SDok)
+ return 0;
+ if(r->status == SDretry){
+ if(try++ < 2)
+ goto top;
+ r->status |= SDvalidsense;
+ }
+ return w | Sense;
+ default:
+ r->status = sdsetsense(r, SDcheck, 4, 24, 0);
+ return w;
+ }
+}
+
+static uint
+analyze(Drive *d, Statb *b)
+{
+ uint u, r, t;
+
+ r = 0;
+ u = *(uint*)b->error;
+ if(u & Eissuestp){
+ r |= Error;
+ unsetci(d);
+ }
+ if(u & Etask && (d->feat & Datapi) == 0){
+ t = gettask(d);
+ if(t & 1)
+ tprint(d, t);
+ if(t & Efatal<<8 || t & (ASbsy|ASdrq))
+ r |= Noverdict|Atareset;
+ if(t&Adhrs && t&33)
+ r |= Noverdict|Atareset;
+ else
+ r |= Error;
+ }
+ if(u & (Ererr | Ebadprot)){
+ /* sas thing */
+ print("%s: sas error %.8ux\n", dnam(d), u);
+ r |= Error;
+ }
+ if(u & ~(Ebadprot | Ererr | Etask | Eissuestp))
+ print("%s: analyze %.8ux\n", dnam(d), u);
+
+ return r;
+}
+
+static void
+updatedone(Ctlr *c)
+{
+ uint a, e, i, u, slot;
+ Cmd *x;
+ Drive *d;
+
+ e = c->cq[0];
+ if(e == 0xfff)
+ return;
+ if(e > Qmask)
+ print("sdodin: bug: bad cqrp %ux\n", e);
+ e = e+1 & Qmask;
+ for(i = c->cqrp; i != e; i = i+1 & Qmask){
+ u = c->cq[1 + i];
+ c->cq[1 + i] = 0;
+ slot = u & 0xfff;
+ u &= ~slot;
+ d = c->drive + slot;
+ x = d->cmd;
+ if(u & Cqdone){
+ x->cflag |= Done;
+ u &= ~Cqdone;
+ }
+ if(u & (Crxfr | Cgood)){
+ if((u & Cgood) == 0)
+ x->cflag |= Response;
+ u &= ~(Crxfr | Cgood);
+ }
+ if(u & Cerr){
+ dprint("%s: Cerr ..\n", dnam(d));
+ a = analyze(d, x);
+ x->cflag |= Done | a;
+ u &= ~Cerr;
+ }
+ if(x->cflag & Done)
+ wakeup(x);
+ if(u)
+ print("%s: odd bits %.8ux\n", dnam(d), u);
+ }
+if(i == c->cqrp)print("odin: spur done\n");
+ c->cqrp = i;
+}
+
+static void
+updatedrive(Drive *d)
+{
+ uint cause, s0, ewake;
+ char *name;
+ Cmd *x;
+ static uint last, tk;
+
+ ewake = 0;
+ cause = d->ctlr->reg[pis[d->driveno]];
+ d->ctlr->reg[pis[d->driveno]] = cause;
+ x = d->cmd;
+ name = dnam(d);
+
+ if(last != cause || Ticks - tk > 5*1000){
+ dprint("%s: ca %ux ta %ux\n", name, cause, gettask(d));
+ tk = Ticks;
+ }
+ if(cause & (Phyunrdy | Phyidto | Pisataup | Pisasup)){
+ s0 = d->state;
+ if(cause == (Phyrdy | Comw)){
+ d->type = 0;
+ d->state = Dnopower;
+ }
+ switch(cause & (Phyunrdy | Phyidto | Phyidok | Sigrx)){
+ case Phyunrdy:
+ d->state = Dmissing;
+ if(sstatus(d) & Sphyrdy){
+ if(d->type != 0)
+ d->state = Dnew;
+ else
+ d->state = Dreset;
+ }
+ break;
+ case Phyidto:
+ d->type = 0;
+ d->state = Dmissing;
+ break;
+ case Phyidok:
+ d->type = Sas;
+ d->state = Dnew;
+ break;
+ case Sigrx:
+ d->type = Sata;
+ d->state = Dnew;
+ break;
+ }
+ dprint("%s: %s → %s [Apcrs] %s %ux\n", name, dstate(s0),
+ dstate(d->state), type[d->type], sstatus(d));
+ if(s0 == Dready && d->state != Dready)
+ idprint("%s: pulled\n", name);
+ if(d->state != Dready || ci(d))
+ ewake |= Done | Noverdict;
+ }else if(cause & Piburp)
+ ewake |= Done | Noverdict;
+ else if(cause & Pireset)
+ ewake |= Done | Noverdict | Creset;
+ else if(cause & Piunsupp){
+ print("%s: unsupported h/w: %.8ux\n", name, cause);
+ ewake |= Done | Error;
+ d->type = 0;
+ d->state = Doffline;
+ }
+ if(ewake){
+ dprint("%s: ewake %.8ux\n", name, ewake);
+ unsetci(d);
+ x->cflag |= ewake;
+ wakeup(x);
+ }
+ last = cause;
+}
+
+static int
+satareset(Drive *d)
+{
+ ilock(d->ctlr);
+ unsetci(d);
+ iunlock(d->ctlr);
+ if(gettask(d) & (ASdrq|ASbsy))
+ return -1;
+ if(settxmode(d, d->udma) != 0)
+ return -1;
+ return 0;
+}
+
+static int
+msriopkt(SDreq *r, Drive *d)
+{
+ int n, count, try, max, flag, task;
+ uchar *cmd;
+
+ cmd = r->cmd;
+ aprint("%02ux %02ux %c %d %p\n", cmd[0], cmd[2], "rw"[r->write],
+ r->dlen, r->data);
+ r->rlen = 0;
+ count = r->dlen;
+ max = 65536;
+
+ for(try = 0; try < 10; try++){
+ n = count;
+ if(n > max)
+ n = max;
+ if(lockready(d) == -1)
+ return SDeio;
+ flag = buildpkt(d, r, r->data, n);
+ task = gettask(d);
+ if(flag & Atareset && satareset(d) == -1)
+ setstate(d, Dreset);
+ qunlock(d);
+ if(flag & Noverdict){
+ if(flag & Creset)
+ setstate(d, Dreset);
+ print("%s: retry\n", dnam(d));
+ continue;
+ }
+ if(flag & Error){
+ if((task & Eidnf) == 0)
+ print("%s: i/o error %ux\n", dnam(d), task);
+ return r->status = SDcheck;
+ }
+ return r->status = SDok;
+ }
+ print("%s: bad disk\n", dnam(d));
+ return r->status = SDcheck;
+}
+
+static int
+msriosas(SDreq *r, Drive *d)
+{
+ int try, flag;
+
+ for(try = 0; try < 10; try++){
+ if(lockready(d) == -1)
+ return SDeio;
+ flag = buildsas(d, r, r->data, r->dlen);
+ qunlock(d);
+ if(flag & Noverdict){
+ if(flag & Creset)
+ setstate(d, Dreset);
+ print("%s: retry\n", dnam(d));
+ continue;
+ }
+ if(flag & Error){
+ print("%s: i/o error\n", dnam(d));
+ return r->status = SDcheck;
+ }
+ r->rlen = r->dlen; /* fishy */
+ return r->status; /* set in sasrspck */
+
+ }
+ print("%s: bad disk\n", dnam(d));
+ sdsetsense(r, SDcheck, 3, r->write? 0xc00: 0x11, 0);
+ return r->status = SDcheck;
+}
+
+static int
+flushcache(Drive *d)
+{
+ int i;
+
+ i = -1;
+ if(lockready(d) == 0)
+ i = mvflushcache(d);
+ qunlock(d);
+ return i;
+}
+
+static int
+msriosata(SDreq *r, Drive *d)
+{
+ char *name;
+ int i, n, count, try, max, flag, task;
+ uvlong lba;
+ uchar *cmd, *data;
+ SDunit *unit;
+
+ unit = r->unit;
+ cmd = r->cmd;
+ name = dnam(d);
+
+ if(cmd[0] == 0x35 || cmd[0] == 0x91){
+ if(flushcache(d) == 0)
+ return sdsetsense(r, SDok, 0, 0, 0);
+ return sdsetsense(r, SDcheck, 3, 0xc, 2);
+ }
+ if((i = sdfakescsi(r)) != SDnostatus){
+ r->status = i;
+ return i;
+ }
+ if((i = sdfakescsirw(r, &lba, &count, nil)) != SDnostatus)
+ return i;
+ max = 128;
+ if(d->feat & Dllba)
+ max = 65536;
+ try = 0;
+ data = r->data;
+ while(count > 0){
+ n = count;
+ if(n > max)
+ n = max;
+ if(lockready(d) == -1)
+ return SDeio;
+ flag = build(d, r->write, data, n, lba);
+ task = gettask(d);
+ if(flag & Atareset && satareset(d) == -1)
+ setstate(d, Dreset);
+ qunlock(d);
+ if(flag & Noverdict){
+ if(flag & Creset)
+ setstate(d, Dreset);
+ if(++try == 2){
+ print("%s: bad disk\n", name);
+ return r->status = SDeio;
+ }
+ iprint("%s: retry %lld [%.8ux]\n", name, lba, task);
+ continue;
+ }
+ if(flag & Error){
+ iprint("%s: i/o error %ux @%,lld\n", name, task, lba);
+ return r->status = SDeio;
+ }
+ count -= n;
+ lba += n;
+ data += n*unit->secsize;
+ }
+ r->rlen = data - (uchar*)r->data;
+ r->status = SDok;
+ return SDok;
+}
+
+static int
+msrio(SDreq *r)
+{
+ Ctlr *c;
+ Drive *d;
+ SDunit *u;
+
+ u = r->unit;
+ c = u->dev->ctlr;
+ d = c->drive + u->subno;
+ if(d->feat & Datapi)
+ return msriopkt(r, d);
+ if(d->type == Sas)
+ return msriosas(r, d);
+ if(d->type == Sata)
+ return msriosata(r, d);
+ return sdsetsense(r, SDcheck, 3, 0x04, 0x24);
+}
+
+/*
+ * §6.1.9.5
+ * not clear that this is necessary
+ * we should know that it's a d2h from the status.
+ * pio returns pio setup fises. hw bug?
+ */
+static int
+sdr(SDreq *r, Drive *d, int st)
+{
+ uint i;
+
+ if(i = fisoffset(d, 0/*Rd2h*/))
+ memmove(r->cmd, d->ctlr->fis + i, 16);
+ else
+ memset(r->cmd, 0xff, 16);
+ r->status = st;
+ return st;
+}
+
+/*
+ * handle oob requests;
+ * restrict & sanitize commands
+ */
+static int
+fisreqchk(Sfis *f, SDreq *r)
+{
+ uchar *c;
+
+ if((r->ataproto & Pprotom) == Ppkt)
+ return SDnostatus;
+ if(r->clen != 16)
+ error("bad command length"); //error(Eio);
+ c = r->cmd;
+ if(c[0] == 0xf0){
+ sigtofis(f, r->cmd);
+ return r->status = SDok;
+ }
+ c[0] = H2dev;
+ c[1] = Fiscmd;
+ c[7] |= Ataobs;
+ return SDnostatus;
+}
+
+static int
+msataio(SDreq *r)
+{
+ char *name;
+ int try, flag, task;
+ Ctlr *c;
+ Drive *d;
+ SDunit *u;
+ int (*build)(Drive*, SDreq*, void*, int);
+
+ u = r->unit;
+ c = u->dev->ctlr;
+ d = c->drive + u->subno;
+ name = dnam(d);
+
+ if(d->type != Sata)
+ error("not sata");
+ if(r->cmd[0] == 0xf1){
+ d->state = Dreset;
+ return r->status = SDok;
+ }
+ if((r->status = fisreqchk(d, r)) != SDnostatus)
+ return r->status;
+ build = buildfis;
+ if((r->ataproto & Pprotom) == Ppkt)
+ build = buildpkt;
+
+ for(try = 0; try < 10; try++){
+ if(lockready(d) == -1)
+ return SDeio;
+ flag = build(d, r, r->data, r->dlen);
+ task = gettask(d);
+ if(flag & Atareset && satareset(d) == -1)
+ setstate(d, Dreset);
+ qunlock(d);
+ if(flag & Noverdict){
+ if(flag & (Timeout | Creset))
+ setstate(d, Dreset);
+ else if(task & Eabrt<<8){
+ /* assume bad cmd */
+ r->status = SDeio;
+ return SDeio;
+ }
+ print("%s: retry\n", name);
+ continue;
+ }
+ if(flag & Error){
+ print("%s: i/o error %.8ux\n", name, task);
+ r->status = SDeio;
+ return SDeio;
+ }
+ if(build != buildpkt)
+ r->rlen = r->dlen;
+ return sdr(r, d, SDok);
+ }
+ print("%s: bad disk\n", name);
+ return sdr(r, d, SDeio);
+}
+
+static void
+msinterrupt(Ureg *, void *a)
+{
+ Ctlr *c;
+ uint u, i;
+ static uint cnt;
+
+ c = a;
+ ilock(c);
+ u = c->reg[Cis];
+ if(u == 0){
+ iunlock(c);
+ return;
+ }
+ c->reg[Cis] = u & ~Iclr;
+ if(u != Cdone && cnt++ < 15)
+ print("sdodin: irq %s %.8ux\n", c->sdev->ifc->name, u);
+ for(i = 0; i < 8; i++)
+ if(u & (1<<i)*(Portirq|Portstop))
+ updatedrive(c->drive + i);
+ if(u & Srsirq){
+ u = c->reg[Csis];
+ c->reg[Csis] = u;
+ for(i = 0; i < 8; i++)
+ if(u & 1<<i)
+ updatedrive(c->drive + i);
+ }
+ if(u & Cdone){
+ updatedone(c);
+ c->reg[Cis] = Cdone;
+ }
+ iunlock(c);
+}
+
+static char*
+mc(Drive *d)
+{
+ char *s;
+
+ s = "";
+ if(d->drivechange)
+ s = "[newdrive]";
+ return s;
+}
+
+static int
+newsatadrive(Drive *d)
+{
+ uint task;
+
+ task = gettask(d);
+ if((task & 0xffff) == 0x80)
+ return SDretry;
+ setfissig(d, getsig(d));
+ if(identify(d) != 0){
+ dprint("%s: identify failure\n", dnam(d));
+ return SDeio;
+ }
+ if(d->feat & Dpower && setfeatures(d, 0x85, 3*1000) != 0){
+ d->feat &= ~Dpower;
+ if(satareset(d) == -1)
+ return SDeio;
+ }
+ if(settxmode(d, d->udma) != 0){
+ dprint("%s: can't set tx mode\n", dnam(d));
+ return SDeio;
+ }
+ return SDok;
+}
+
+static void
+newoaf(Drive *d, int type)
+{
+ uint ict, i;
+ uvlong sa;
+ Ctlr *c;
+
+ i = d->driveno;
+ c = d->ctlr;
+
+ sa = pcread(c, i, Pawwn + 0);
+ sa |= (uvlong)pcread(c, i, Pawwn + 4)<<32;
+ putbe(d->tsasaddr, sa, 8);
+ memmove(d->ssasaddr, d->ssasaddr, 8);
+ ict = pcread(c, i, Pwwn + 8);
+ putbe(d->ict, ict, 2);
+ oafis(d, d->cmd->oaf, type);
+}
+
+static int
+sasinquiry(Drive *d)
+{
+ SDreq r;
+ SDunit *u;
+
+ u = d->unit;
+ memset(&r, 0, sizeof r);
+ r.cmd[0] = 0x12;
+ r.cmd[4] = 0xff;
+ r.clen = 6;
+ r.unit = u;
+
+ return buildsas(d, &r, u->inquiry, sizeof u->inquiry);
+}
+
+static int
+sastur(Drive *d)
+{
+ SDreq r;
+ SDunit *u;
+
+ u = d->unit;
+ memset(&r, 0, sizeof r);
+ r.clen = 6;
+ r.unit = u;
+ return buildsas(d, &r, 0, 0);
+}
+
+static int
+sasvpd(Drive *d, uchar *buf, int l)
+{
+ SDreq r;
+ SDunit *u;
+
+ u = d->unit;
+ memset(&r, 0, sizeof r);
+ r.cmd[0] = 0x12;
+ r.cmd[1] = 1;
+ r.cmd[2] = 0x80;
+ r.cmd[4] = l;
+ r.clen = 6;
+ r.unit = u;
+ return buildsas(d, &r, buf, l);
+}
+
+static int
+sascapacity10(Drive *d, uchar *buf, int l)
+{
+ SDreq r;
+ SDunit *u;
+
+ u = d->unit;
+ memset(&r, 0, sizeof r);
+ r.cmd[0] = 0x25;
+ r.clen = 10;
+ r.unit = u;
+ return buildsas(d, &r, buf, l);
+}
+
+static int
+sascapacity16(Drive *d, uchar *buf, int l)
+{
+ SDreq r;
+ SDunit *u;
+
+ u = d->unit;
+ memset(&r, 0, sizeof r);
+ r.cmd[0] = 0x9e;
+ r.cmd[1] = 0x10;
+ r.cmd[13] = l;
+ r.clen = 16;
+ r.unit = u;
+ return buildsas(d, &r, buf, l);
+}
+
+static void
+frmove(char *p, uchar *c, int n)
+{
+ char *op, *e;
+
+ memmove(p, c, n);
+ op = p;
+ p[n] = 0;
+ for(p = p + n - 1; p > op && *p == ' '; p--)
+ *p = 0;
+ e = p;
+ p = op;
+ while(*p == ' ')
+ p++;
+ memmove(op, p, n - (e - p));
+}
+
+static void
+chkinquiry(Drive *d, uchar *c)
+{
+ char buf[32], buf2[32], omod[sizeof d->model];
+
+ memmove(omod, d->model, sizeof d->model);
+ frmove(buf, c + 8, 8);
+ frmove(buf2, c + 16, 16);
+ snprint(d->model, sizeof d->model, "%s %s", buf, buf2);
+ frmove(d->firmware, c + 23, 4);
+ if(memcmp(omod, d->model, sizeof omod) != 0)
+ d->drivechange = 1;
+}
+
+static void
+chkvpd(Drive *d, uchar *c, int n)
+{
+ char buf[sizeof d->serial];
+ int l;
+
+ l = c[3];
+ if(l > n)
+ l = n;
+ frmove(buf, c + 4, l);
+ if(strcmp(buf, d->serial) != 0)
+ d->drivechange = 1;
+ memmove(d->serial, buf, sizeof buf);
+}
+
+static int
+adjcapacity(Drive *d, uvlong ns, uint nss)
+{
+ if(ns != 0)
+ ns++;
+ if(nss == 2352)
+ nss = 2048;
+ if(d->sectors != ns || d->secsize != nss){
+ d->drivechange = 1;
+ d->sectors = ns;
+ d->secsize = nss;
+ }
+ return 0;
+}
+
+static int
+chkcapacity10(uchar *p, uvlong *ns, uint *nss)
+{
+ *ns = getbe(p, 4);
+ *nss = getbe(p + 4, 4);
+ return 0;
+}
+
+static int
+chkcapacity16(uchar *p, uvlong *ns, uint *nss)
+{
+ *ns = getbe(p, 8);
+ *nss = getbe(p + 8, 4);
+ return 0;
+}
+
+static int
+sasprobe(Drive *d)
+{
+ uchar buf[0x40];
+ int r;
+ uint nss;
+ uvlong ns;
+
+ if((r = sastur(d)) != 0)
+ return r;
+ if((r = sasinquiry(d)) != 0)
+ return r;
+ chkinquiry(d, d->unit->inquiry);
+ /* vpd 0x80 (unit serial) is not mandatory */
+ if((r = sasvpd(d, buf, sizeof buf)) == 0)
+ chkvpd(d, buf, sizeof buf);
+ else if(r & (Error | Timeout))
+ return r;
+ else{
+ if(d->serial[0])
+ d->drivechange = 1;
+ d->serial[0] = 0;
+ }
+ if((r = sascapacity10(d, buf, sizeof buf)) != 0)
+ return r;
+ chkcapacity10(buf, &ns, &nss);
+ if(ns == 0xffffffff){
+ if((r = sascapacity16(d, buf, sizeof buf)) != 0)
+ return r;
+ chkcapacity16(buf, &ns, &nss);
+ }
+ adjcapacity(d, ns, nss);
+
+ return 0;
+}
+
+static int
+newsasdrive(Drive *d)
+{
+ memset(d->cmd->rsp, 0, sizeof d->cmd->rsp);
+ newoaf(d, Ssp);
+ switch(sasprobe(d) & (Error | Noverdict | Timeout | Sense)){
+ case Error:
+ case Timeout:
+ return SDeio;
+ case Sense:
+ case Noverdict:
+ return SDretry;
+ }
+ return SDok;
+}
+
+static int
+newdrive(Drive *d)
+{
+ char *t;
+ int r;
+
+ memset(&d->Sfis, 0, sizeof d->Sfis);
+ memset(&d->Cfis, 0, sizeof d->Cfis);
+ qlock(d);
+ switch(d->type){
+ case Sata:
+ r = newsatadrive(d);
+ break;
+ case Sas:
+ r = newsasdrive(d);
+ break;
+ default:
+ print("%s: bug: martian drive %d\n", dnam(d), d->type);
+ qunlock(d);
+ return -1;
+ }
+ t = type[d->type];
+ switch(r){
+ case SDok:
+ idprint("%s: %s %,lld sectors\n", dnam(d), t, d->sectors);
+ idprint(" %s %s %s %s\n", d->model, d->firmware, d->serial, mc(d));
+ setstate(d, Dready);
+ break;
+ case SDeio:
+ idprint("%s: %s can't be initialized\n", dnam(d), t);
+ setstate(d, Derror);
+ case SDretry:
+ break;
+ }
+ qunlock(d);
+ return r;
+}
+
+static void
+statechange(Drive *d)
+{
+ switch(d->state){
+ case Dmissing:
+ case Dnull:
+ case Doffline:
+ d->drivechange = 1;
+ d->unit->sectors = 0;
+ break;
+ case Dready:
+ d->wait = 0;
+ break;
+ }
+}
+
+/*
+ * we don't respect running commands. botch?
+ */
+static void
+checkdrive(Drive *d, int i)
+{
+ uint s;
+
+ if(d->unit == nil)
+ return;
+ ilock(d);
+ s = sstatus(d);
+ d->wait++;
+ if(s & Sphyrdy)
+ d->lastseen = Ticks;
+ if(s != olds[i]){
+ dprint("%s: status: %.6ux -> %.6ux: %s\n",
+ dnam(d), olds[i], s, dstate(d->state));
+ olds[i] = s;
+ statechange(d);
+ }
+ switch(d->state){
+ case Dnull:
+ case Dmissing:
+ if(d->type != 0 && s & Sphyrdy)
+ d->state = Dnew;
+ break;
+ case Dnopower:
+ phyreset(d); /* spinup */
+ break;
+ case Dnew:
+ if(d->wait % 6 != 0)
+ break;
+ iunlock(d);
+ newdrive(d);
+ ilock(d);
+ break;
+ case Dready:
+ d->wait = 0;
+ break;
+ case Derror:
+ d->wait = 0;
+ d->state = Dreset;
+ case Dreset:
+ if(d->wait % 40 != 0)
+ break;
+ reset(d);
+ break;
+ case Doffline:
+ case Dportreset:
+ break;
+ }
+ iunlock(d);
+}
+
+static void
+mskproc(void*)
+{
+ int i;
+
+ for(;;){
+ tsleep(&up->sleep, return0, 0, Nms);
+ for(i = 0; i < nmsdrive; i++)
+ checkdrive(msdrive[i], i);
+ }
+}
+
+static void
+ledcfg(Ctlr *c, int port, uint cfg)
+{
+ uint u, r, s;
+
+ r = Drivectl + (port>>2)*Gpiooff;
+ s = 15 - port & 3;
+ s *= 8;
+ u = gpread(c, r);
+ u &= ~(0xff << s);
+ u |= cfg<<s;
+ gpwrite(c, r, u);
+}
+
+static uchar ses2ledstd[Ibpilast] = {
+[Ibpinone] Lhigh*Aled,
+[Ibpinormal] Lsof*Aled | Llow*Locled | Llow*Errled,
+[Ibpirebuild] Lsof*Aled | Llow*Locled | Llow*Errled,
+[Ibpilocate] Lsof*Aled | Lblinka*Locled | Llow*Errled,
+[Ibpispare] Lsof*Aled | Llow*Locled| Lblinka*Errled,
+[Ibpipfa] Lsof*Aled | Lblinkb*Locled | Llow*Errled,
+[Ibpifail] Lsof*Aled | Llow*Locled | Lhigh*Errled,
+[Ibpicritarray] Lsof*Aled,
+[Ibpifailarray] Lsof*Aled,
+};
+
+static uchar ses2led[Ibpilast] = {
+[Ibpinone] Lhigh*Aled,
+[Ibpinormal] Lsof*Aled | Llow*Locled | Llow*Errled,
+[Ibpirebuild] Lsof*Aled | Lblinkaneg*Locled | Llow*Errled,
+[Ibpilocate] Lsof*Aled | Lhigh*Locled | Llow*Errled,
+[Ibpispare] Lsof*Aled | Lblinka*Locled| Llow*Errled,
+[Ibpipfa] Lsof*Aled | Lblinkb*Locled | Llow*Errled,
+[Ibpifail] Lsof*Aled | Llow*Locled | Lhigh*Errled,
+[Ibpicritarray] Lsof*Aled,
+[Ibpifailarray] Lsof*Aled,
+};
+
+static void
+setupled(Ctlr *c)
+{
+ int i, l, blen;
+ pcicfgw32(c->pci, Gpio, pcicfgr32(c->pci, Gpio) | 1<<7);
+
+ /*
+ * configure a for 4hz (1/8s on and 1/8s off)
+ * configure b for 1hz (2/8s on and 6/8s off)
+ */
+ l = 3 + c->ndrive >> 2;
+ blen = 3*24 - 1;
+ for(i = 0; i < l*Gpiooff; i += Gpiooff){
+ gpwrite(c, Sgconf0 + i, blen*Autolen | Blinkben | Blinkaen | Sgpioen);
+ gpwrite(c, Sgconf1 + i, 1*Bhi | 1*Blo | 1*Ahi | 7*Alo);
+ gpwrite(c, Sgconf3 + i, 7<<20 | Sdoutauto);
+ }
+}
+
+static void
+trebuild(Ctlr *c, Drive *d, int dno, uint i)
+{
+ uchar bits;
+
+ if(0 && d->led == Ibpirebuild){
+ switch(i%19){
+ case 0:
+ bits = 0;
+ break;
+ case 1:
+ bits = ses2led[Ibpirebuild] | Lblinka*Locled;
+ break;
+ case 3:
+ bits = ses2led[Ibpirebuild] | Lblinkb*Locled;
+ break;
+ }
+ }else
+ bits = ses2led[d->led];
+ if(d->ledbits != bits)
+ ledcfg(c, dno, bits);
+}
+
+static long
+odinledr(SDunit *u, Chan *ch, void *a, long n, vlong off)
+{
+ Ctlr *c;
+ Drive *d;
+
+ c = u->dev->ctlr;
+ d = c->drive + u->subno;
+ return ledr(d, ch, a, n, off);
+}
+
+static long
+odinledw(SDunit *u, Chan *ch, void *a, long n, vlong off)
+{
+ Ctlr *c;
+ Drive *d;
+
+ c = u->dev->ctlr;
+ d = c->drive + u->subno;
+ return ledw(d, ch, a, n, off);
+}
+
+/*
+ * this kproc can probablly go when i figure out
+ * how to program the manual blinker
+ */
+static void
+ledkproc(void*)
+{
+ uint i, j;
+ Drive *d;
+
+ for(i = 0; i < nmsdrive; i++){
+ d = msdrive[i];
+ d->nled = 2; /* how to know? */
+ }
+ for(i = 0; i < nmsctlr; i++)
+ pcicfgw32(msctlr[i].pci, Gpio, pcicfgr32(msctlr[i].pci, Gpio) | 1<<7);
+ for(i = 0; i < nmsctlr; i++)
+ setupled(msctlr + i);
+ for(i = 0; ; i++){
+ esleep(Nms);
+ for(j = 0; j < nmsdrive; j++){
+ d = msdrive[j];
+ trebuild(d->ctlr, d, j, i);
+ }
+ }
+}
+
+static int
+msenable(SDev *s)
+{
+ char buf[32];
+ Ctlr *c;
+ static int once;
+
+ c = s->ctlr;
+ ilock(c);
+ if(!c->enabled){
+ if(once++ == 0)
+ kproc("odin", mskproc, 0);
+ pcisetbme(c->pci);
+ snprint(buf, sizeof buf, "%s (%s)", s->name, s->ifc->name);
+ intrenable(c->pci->intl, msinterrupt, c, c->pci->tbdf, buf);
+// c->reg[Cis] |= Swirq1; /* force initial interrupt. */
+ c->enabled = 1;
+ }
+ iunlock(c);
+ return 1;
+}
+
+static int
+msdisable(SDev *s)
+{
+ char buf[32];
+ Ctlr *c;
+
+ c = s->ctlr;
+ ilock(c);
+// disable(c->hba);
+ snprint(buf, sizeof buf, "%s (%s)", s->name, s->ifc->name);
+ intrdisable(c->pci->intl, msinterrupt, c, c->pci->tbdf, buf);
+ c->enabled = 0;
+ iunlock(c);
+ return 1;
+}
+
+static int
+scsiish(Drive *d)
+{
+ return d->type == Sas || d->feat & Datapi;
+}
+
+static int
+msonline(SDunit *u)
+{
+ int r;
+ Ctlr *c;
+ Drive *d;
+
+ c = u->dev->ctlr;
+ d = c->drive + u->subno;
+ r = 0;
+
+ if(scsiish(d)){
+ if(!d->drivechange)
+ return r;
+ r = scsionline(u);
+ if(r > 0)
+ d->drivechange = 0;
+ return r;
+ }
+ ilock(d);
+ if(d->drivechange){
+ r = 2;
+ d->drivechange = 0;
+ u->sectors = d->sectors;
+ u->secsize = d->secsize;
+ } else if(d->state == Dready)
+ r = 1;
+ iunlock(d);
+ return r;
+}
+
+static void
+verifychk(Drive *d)
+{
+ int w;
+
+ if(!up)
+ checkdrive(d, d->driveno);
+ for(w = 0; w < 12000; w += 210){
+ if(d->state == Dready)
+ break;
+ if(w > 2000 && d->state != Dnew)
+ break;
+ if((sstatus(d) & Sphyrdy) == 0)
+ break;
+ if(!up)
+ checkdrive(d, d->driveno);
+ esleep(210);
+ }
+}
+
+static int
+msverify(SDunit *u)
+{
+ int chk;
+ Ctlr *c;
+ Drive *d;
+ static int once;
+
+ c = u->dev->ctlr;
+ d = c->drive + u->subno;
+ ilock(c);
+ ilock(d);
+ chk = 0;
+ if(d->unit == nil){
+ d->unit = u;
+ sdaddfile(u, "led", 0644, eve, odinledr, odinledw);
+ once++;
+ if(once == nmsctlr)
+ kproc("mvled", ledkproc, 0);
+ chk = 1;
+ }
+ iunlock(d);
+ iunlock(c);
+
+ /*
+ * since devsd doesn't know much about hot-plug drives,
+ * we need to give detected drives a chance.
+ */
+ if(chk){
+ reset(d);
+ verifychk(d);
+ }
+ return 1;
+}
+
+static uint*
+map(Pcidev *p, int bar)
+{
+ uintptr io;
+
+ io = p->mem[bar].bar & ~0xf;
+ return (uint*)vmap(io, p->mem[bar].size);
+}
+
+/* §5.1.3 */
+static void
+initmem(Ctlr *c)
+{
+ c->fis = smalloc(0x800 + 0x100*16); /* §6.1.9.3 */
+ c->reg[Fisbase + 0] = PCIWADDR(c->fis);
+ c->reg[Fisbase + 1] = Pciwaddrh(c->fis);
+ c->reg[Cqbase + 0] = PCIWADDR(c->cq);
+ c->reg[Cqbase + 1] = Pciwaddrh(c->cq);
+ c->reg[Cqcfg] = Cqen | Noattn | nelem(c->cq) - 1;
+ c->reg[Dqbase + 0] = PCIWADDR(c->dq);
+ c->reg[Dqbase + 1] = Pciwaddrh(c->dq);
+ c->reg[Dqcfg] = Dqen | nelem(c->dq);
+ c->cl = smalloc(nelem(c->cq)*sizeof *c->cl);
+ c->reg[Clbase + 0] = PCIWADDR(c->cl);
+ c->reg[Clbase + 1] = Pciwaddrh(c->cl);
+ c->cmdtab = smalloc(Nctlrdrv*sizeof *c->cmdtab);
+}
+
+/* §5.1.2 */
+static void
+startup(Ctlr *c)
+{
+ c->reg[Gctl] |= Reset;
+ while(c->reg[Gctl] & Reset)
+ ;
+ initmem(c);
+ c->reg[Cie] = Swirq1 | 0xff*Portstop | 0xff*Portirq | Srsirq | Issstop | Cdone;
+ c->reg[Gctl] |= Intenable;
+ c->reg[Portcfg0] = Rmask*Regen | Dataunke | Rsple | Framele;
+ c->reg[Portcfg1] = Rmask*Regen | 0xff*Xmten | /*Cmdirq |*/ Fisen | Resetiss | Issueen;
+ c->reg[Csie] = ~0;
+ sswrite(c, 0, Pwdtimer, 0x7fffff);
+}
+
+static void
+forcetype(Ctlr*)
+{
+ /*
+ * if we want to force sas/sata, here's where to do it.
+ */
+}
+
+static void
+setupcmd(Drive *d)
+{
+ int i;
+ Ctlr *c;
+ Cmd *cmd;
+ Cmdh *h;
+
+ i = d->driveno;
+ c = d->ctlr;
+ d->cmd = c->cmdtab + i;
+ d->cmd->cmdh = c->cl + i;
+ cmd = d->cmd;
+ h = cmd->cmdh;
+
+ /* prep the precomputable bits in the cmd hdr §6.1.4 */
+ putle(h->ctab, Pciw64(&cmd->Ctab), sizeof h->ctab);
+ putle(h->oaf, Pciw64(&cmd->Oaf), sizeof h->oaf);
+ putle(h->statb, Pciw64(&cmd->Statb), sizeof h->statb);
+ putle(h->prd, Pciw64(&cmd->Aprdt), sizeof h->prd);
+
+ /* finally, set up the wide-port participating bit */
+ pcwrite(c, i, Pwidecfg, 1<<i);
+}
+
+static void
+phychk(Ctlr *c, Drive *d)
+{
+ int i;
+ uvlong u;
+ static uchar src[8] = {0x50, 0x03, 0x04, 0x80};
+
+ i = d->driveno;
+ memmove(d->ssasaddr, src, 8);
+ u = getbe(d->ssasaddr, 8);
+ pcwrite(c, i, Paddr + 0, u);
+ pcwrite(c, i, Paddr + 4, u>>32);
+}
+
+static SDev*
+mspnp(void)
+{
+ int i, nunit;
+ Ctlr *c;
+ Drive *d;
+ Pcidev *p;
+ SDev **ll, *s, *s0;
+ static int done;
+
+ if(done++)
+ return nil;
+ s0 = nil;
+ ll = &s0;
+ for(p = nil; (p = pcimatch(p, 0x11ab, 0x6485)) != nil; ){
+ if(nmsctlr == Nctlr){
+ print("sdodin: too many controllers\n");
+ break;
+ }
+ c = msctlr + nmsctlr;
+ s = sdevs + nmsctlr;
+ memset(c, 0, sizeof *c);
+ memset(s, 0, sizeof *s);
+ if((c->reg = map(p, Mebar)) == 0){
+ print("sdodin: bar %#p in use\n", c->reg);
+ continue;
+ }
+ nunit = p->did>>4 & 0xf;
+ s->ifc = &sdodinifc;
+ s->idno = 'a' + nmsctlr;
+ s->ctlr = c;
+ c->sdev = s;
+ c->pci = p;
+ c->ndrive = s->nunit = nunit;
+ i = pcicfgr32(p, Dctl) & ~(7<<12);
+ pcicfgw32(p, Dctl, i | 4<<12);
+
+ print("#S/sd%c: odin ii sata/sas with %d ports\n", s->idno, nunit);
+ startup(c);
+ forcetype(c);
+ for(i = 0; i < nunit; i++){
+ d = c->drive + i;
+ d->driveno = i;
+ d->sectors = 0;
+ d->ctlr = c;
+ setupcmd(d);
+ snprint(d->name, sizeof d->name, "odin%d.%d", nmsctlr, i);
+ msdrive[nmsdrive + i] = d;
+// phychk(c, d);
+ c->reg[pis[i] + 1] =
+ Sync | Phyerr | Stperr | Crcerr |
+ Linkrx | Martianfis | Anot | Bist | Sigrx |
+ Phyunrdy | Martiantag | Bnot | Comw |
+ Portsel | Hreset | Phyidto | Phyidok |
+ Hresetok | Phyrdy;
+ }
+ nmsdrive += nunit;
+ nmsctlr++;
+ *ll = s;
+ ll = &s->next;
+ }
+ return s0;
+}
+
+static char*
+msrctlsata(Drive *d, char *p, char *e)
+{
+ p = seprint(p, e, "flag\t");
+ p = pflag(p, e, d);
+ p = seprint(p, e, "udma\t%d\n", d->udma);
+ return p;
+}
+
+static char*
+rctldebug(char *p, char *e, Ctlr *c, Drive *d)
+{
+ int i;
+ uvlong sasid;
+
+ i = d->driveno;
+ p = seprint(p, e, "sstatus\t%.8ux\n", sstatus(d));
+// p = seprint(p, e, "cis\t%.8ux %.8ux\n", c->reg[Cis], c->reg[Cie]);
+// p = seprint(p, e, "gis\t%.8ux\n", c->reg[Gis]);
+ p = seprint(p, e, "pis\t%.8ux %.8ux\n", c->reg[pis[i]], c->reg[pis[i] + 1]);
+ p = seprint(p, e, "sis\t%.8ux\n", c->reg[Csis]);
+ p = seprint(p, e, "cqwp\t%.8ux\n", c->cq[0]);
+ p = seprint(p, e, "cerror\t%.8ux %.8ux\n", *(uint*)d->cmd->error, *(uint*)(d->cmd->error+4));
+ p = seprint(p, e, "task\t%.8ux\n", gettask(d));
+ p = seprint(p, e, "ptype\t%.8ux\n", c->reg[Ptype]);
+ p = seprint(p, e, "satactl\t%.8ux\n", pcread(c, i, Psatactl)); /* appears worthless */
+ p = seprint(p, e, "info %.8ux %.8ux\n", pcread(c, i, Pinfo), pcread(c, i, Painfo));
+ p = seprint(p, e, "physts %.8ux\n", pcread(c, i, Pphysts));
+ p = seprint(p, e, "widecfg %.8ux\n", pcread(c, i, Pwidecfg));
+ sasid = pcread(c, i, Pwwn + 0);
+ sasid |= (uvlong)pcread(c, i, Pwwn + 4)<<32;
+ p = seprint(p, e, "wwn %.16llux %.8ux\n", sasid, pcread(c, i, Pwwn + 8));
+ sasid = pcread(c, i, Pawwn + 0);
+ sasid |= (uvlong)pcread(c, i, Pawwn + 4)<<32;
+ p = seprint(p, e, "awwn %.16llux\n", sasid);
+ sasid = pcread(c, i, Paddr + 0);
+ sasid |= (uvlong)pcread(c, i, Paddr + 4)<<32;
+ p = seprint(p, e, "sasid %.16llux\n", sasid);
+ return p;
+}
+
+static int
+msrctl(SDunit *u, char *p, int l)
+{
+ char *e, *op;
+ Ctlr *c;
+ Drive *d;
+
+ if((c = u->dev->ctlr) == nil)
+ return 0;
+ d = c->drive + u->subno;
+ e = p + l;
+ op = p;
+ p = seprint(p, e, "state\t%s\n", dstate(d->state));
+ p = seprint(p, e, "type\t%s", type[d->type]);
+ if(d->type == Sata)
+ p = seprint(p, e, " sig %.8ux", getsig(d));
+ p = seprint(p, e, "\n");
+ if(d->state == Dready){
+ p = seprint(p, e, "model\t%s\n", d->model);
+ p = seprint(p, e, "serial\t%s\n", d->serial);
+ p = seprint(p, e, "firm\t%s\n", d->firmware);
+ p = seprint(p, e, "wwn\t%llux\n", d->wwn);
+ p = msrctlsata(d, p, e);
+ }
+ p = rctldebug(p, e, c, d);
+ p = seprint(p, e, "geometry %llud %lud\n", d->sectors, u->secsize);
+ return p - op;
+}
+
+static void
+forcestate(Drive *d, char *state)
+{
+ int i;
+
+ for(i = 1; i < nelem(diskstates); i++)
+ if(strcmp(state, diskstates[i]) == 0)
+ break;
+ if(i == nelem(diskstates))
+ error(Ebadctl);
+ ilock(d);
+ d->state = 1 << i - 1;
+ statechange(d);
+ iunlock(d);
+}
+
+static int
+mswctl(SDunit *u, Cmdbuf *cmd)
+{
+ char **f;
+ Ctlr *c;
+ Drive *d;
+
+ c = u->dev->ctlr;
+ d = c->drive + u->subno;
+ f = cmd->f;
+ if(strcmp(f[0], "state") == 0)
+ forcestate(d, f[1]? f[1]: "null");
+ else
+ cmderror(cmd, Ebadctl);
+ return 0;
+}
+
+static int
+mswtopctl(SDev*, Cmdbuf *cmd)
+{
+ char **f;
+ int *v;
+
+ f = cmd->f;
+ v = 0;
+ if(strcmp(f[0], "debug") == 0)
+ v = &debug;
+ else if(strcmp(f[0], "idprint") == 0)
+ v = &idebug;
+ else if(strcmp(f[0], "aprint") == 0)
+ v = &adebug;
+ else
+ cmderror(cmd, Ebadctl);
+ if(cmd->nf == 1)
+ *v ^= 1;
+ else if(cmd->nf == 2)
+ *v = strcmp(f[1], "on") == 0;
+ else
+ cmderror(cmd, Ebadarg);
+ return 0;
+}
+
+SDifc sdodinifc = {
+ "odin",
+ mspnp,
+ nil,
+ msenable,
+ msdisable,
+ msverify,
+ msonline,
+ msrio,
+ msrctl,
+ mswctl,
+ scsibio,
+ nil, /* probe */
+ nil, /* clear */
+ nil,
+ mswtopctl,
+ msataio,
+};
--- a/sys/src/9/port/aoe.h
+++ b/sys/src/9/port/aoe.h
@@ -1,9 +1,8 @@
-/*
- * ATA-over-Ethernet (AoE) protocol
- */
enum {
ACata,
ACconfig,
+ ACmask,
+ ACres,
};
enum {
@@ -15,25 +14,57 @@
};
enum {
- AEcmd = 1,
- AEarg,
- AEdev,
- AEcfg,
- AEver,
+ AEunk,
+ AEcmd, /* bad command */
+ AEarg, /* bad argument */
+ AEoff, /* device offline */
+ AEcfg, /* config string already set */
+ AEver, /* unsupported version */
+ AEres, /* target reserved */
};
enum {
- Aoetype = 0x88a2,
- Aoesectsz = 512, /* standard sector size */
- Aoever = 1,
+ /* mask commands */
+ Mread = 0,
+ Medit,
- AFerr = 1<<2,
- AFrsp = 1<<3,
+ /* mask directives */
+ MDnop = 0,
+ MDadd,
+ MDdel,
- AAFwrite= 1,
- AAFext = 1<<6,
+ /* mask errors */
+ MEunk = 1,
+ MEbad,
+ MEfull,
+
+ /* reserve / release */
+ Rrread = 0,
+ Rrset,
+ Rrforce,
};
+enum {
+ Aoetype = 0x88a2,
+ Aoesectsz = 512,
+ Aoemaxcfg = 1024,
+
+ Aoehsz = 24,
+ Aoeatasz = 12,
+ Aoecfgsz = 8,
+ Aoerrsz = 2,
+ Aoemsz = 4,
+ Aoemdsz = 8,
+
+ Aoever = 1,
+
+ AFerr = 1<<2,
+ AFrsp = 1<<3,
+
+ AAFwrite = 1,
+ AAFext = 1<<6,
+};
+
typedef struct {
uchar dst[Eaddrlen];
uchar src[Eaddrlen];
@@ -44,13 +75,9 @@
uchar minor;
uchar cmd;
uchar tag[4];
- uchar payload[];
} Aoehdr;
-#define AOEHDRSZ offsetof(Aoehdr, payload[0])
-
typedef struct {
- Aoehdr;
uchar aflag;
uchar errfeat;
uchar scnt;
@@ -57,22 +84,34 @@
uchar cmdstat;
uchar lba[6];
uchar res[2];
- uchar payload[];
} Aoeata;
-#define AOEATASZ offsetof(Aoeata, payload[0])
-
typedef struct {
- Aoehdr;
uchar bufcnt[2];
uchar fwver[2];
uchar scnt;
uchar verccmd;
uchar cslen[2];
- uchar payload[];
-} Aoeqc;
+} Aoecfg;
-#define AOEQCSZ offsetof(Aoeqc, payload[0])
+typedef struct {
+ uchar dres;
+ uchar dcmd;
+ uchar ea[Eaddrlen];
+} Aoemd;
+
+typedef struct {
+ uchar mres;
+ uchar mcmd;
+ uchar merr;
+ uchar mcnt;
+} Aoem;
+
+typedef struct {
+ uchar rcmd;
+ uchar nea;
+ uchar ea0[];
+} Aoerr;
extern char Echange[];
extern char Enotup[];
--- a/sys/src/9/port/devaoe.c
+++ b/sys/src/9/port/devaoe.c
@@ -1,5 +1,5 @@
/*
- * © 2005-2010 coraid
+ * © 2005-10 coraid
* aoe storage initiator
*/
@@ -15,6 +15,7 @@
#include "etherif.h"
#include "../ip/ip.h"
#include "../port/aoe.h"
+#include <fis.h>
#pragma varargck argpos eventlog 1
@@ -24,6 +25,7 @@
enum {
Maxunits = 0xff,
Maxframes = 128,
+ Maxmtu = 100000,
Ndevlink = 6,
Nea = 6,
Nnetlink = 6,
@@ -35,13 +37,10 @@
#define QID(u, t) ((u)<<4 | (t))
#define Q3(l, u, t) ((l)<<8 | QID(u, t))
#define UP(d) ((d)->flag & Dup)
-/*
- * would like this to depend on the chan (srb).
- * not possible in the current structure.
- */
-#define Nofail(d, s) ((d)->flag & Dnofail)
-#define MS2TK(t) ((t)/MS2HZ)
+#define Ticks MACHP(0)->ticks
+#define Ms2tk(t) (((t)*HZ)/1000)
+#define Tk2ms(t) (((t)*1000)/HZ)
enum {
Qzero,
@@ -67,7 +66,7 @@
Qdevlinkfiles = Qdevlinkend-Qdevlinkbase,
Eventlen = 256,
- Nevents = 64, /* must be power of 2 */
+ Nevents = 64,
Fread = 0,
Fwrite,
@@ -74,15 +73,10 @@
Tfree = -1,
Tmgmt,
- /*
- * round trip bounds, timeouts, in ticks.
- * timeouts should be long enough that rebooting
- * the coraid (which usually takes under two minutes)
- * doesn't trigger a timeout.
- */
- Rtmax = MS2TK(320),
- Rtmin = MS2TK(20),
- Maxreqticks = 4*60*HZ, /* was 45*HZ */
+ /* round trip bounds, timeouts, in ticks */
+ Rtmax = Ms2tk(320),
+ Rtmin = Ms2tk(20),
+ Srbtimeout = 45*HZ,
Dbcnt = 1024,
@@ -91,6 +85,9 @@
Cwr = 0x30,
Cwrext = 0x34,
Cid = 0xec,
+
+ Alloc = 0x01234567,
+ Free = 0x89abcdef,
};
enum {
@@ -104,28 +101,12 @@
* to send jumbograms to that interface.
*/
enum {
- /* sync with ahci.h */
- Dllba = 1<<0,
- Dsmart = 1<<1,
- Dpower = 1<<2,
- Dnop = 1<<3,
- Datapi = 1<<4,
- Datapi16= 1<<5,
-
- /* aoe specific */
- Dup = 1<<6,
- Djumbo = 1<<7,
- Dnofail = 1<<8,
+ Dup = 1<<0,
+ Djumbo = 1<<1,
+ Dnofail = 1<<2,
};
static char *flagname[] = {
- "llba",
- "smart",
- "power",
- "nop",
- "atapi",
- "atapi16",
-
"up",
"jumbo",
"nofail",
@@ -132,9 +113,8 @@
};
typedef struct {
- ushort flag;
+ uchar flag;
uint lostjumbo;
- int datamtu;
Chan *cc;
Chan *dc;
@@ -148,9 +128,10 @@
int nea;
ulong eaidx;
uchar eatab[Nea][Eaddrlen];
+ int datamtu;
ulong npkt;
ulong resent;
- ushort flag;
+ uchar flag;
ulong rttavg;
ulong mintimer;
@@ -159,6 +140,7 @@
typedef struct Srb Srb;
struct Srb {
Rendez;
+ uint state;
Srb *next;
ulong ticksent;
ulong len;
@@ -197,11 +179,11 @@
Devlink *dl;
Devlink dltab[Ndevlink];
+ uchar flag;
ushort fwver;
- ushort flag;
int nopen;
- int major;
- int minor;
+ uint major;
+ uint minor;
int unit;
int lasttag;
int nframes;
@@ -210,6 +192,8 @@
vlong realbsize;
uint maxbcnt;
+ uint maxmtu;
+ ulong lostjumbo;
ushort nout;
ushort maxout;
ulong lastwadj;
@@ -217,7 +201,7 @@
Srb *tail;
Srb *inprocess;
- /* magic numbers 'R' us */
+ Sfis;
char serial[20+1];
char firmware[8+1];
char model[40+1];
@@ -250,16 +234,15 @@
Netlink nl[Nnetlink];
} netlinks;
-extern Dev aoedevtab;
-static Ref units;
-static Ref drivevers;
-static int debug;
-static int autodiscover = 1;
-static int rediscover;
+extern Dev aoedevtab;
+static Ref units;
+static Ref drivevers;
+static int debug;
+static int autodiscover = 1;
+static int rediscover;
+extern char Enotup[] = "aoe device is down";
+extern char Echange[] = "media or partition has changed";
-char Enotup[] = "aoe device is down";
-char Echange[] = "media or partition has changed";
-
static Srb*
srballoc(ulong sz)
{
@@ -266,8 +249,9 @@
Srb *srb;
srb = malloc(sizeof *srb+sz);
+ srb->state = Alloc;
srb->dp = srb->data = srb+1;
- srb->ticksent = MACHP(0)->ticks;
+ srb->ticksent = Ticks;
return srb;
}
@@ -277,33 +261,79 @@
Srb *srb;
srb = malloc(sizeof *srb);
+ srb->state = Alloc;
srb->dp = srb->data = db;
- srb->ticksent = MACHP(0)->ticks;
+ srb->ticksent = Ticks;
return srb;
}
-#define srbfree(srb) free(srb)
+static int
+srbready(void *v)
+{
+ Srb *s;
+ s = v;
+ return s->nout == 0 && (s->len == 0 || s->error != nil);
+}
+
static void
-srberror(Srb *srb, char *s)
+srbfree(Srb *srb)
{
- srb->error = s;
- srb->nout--;
- if (srb->nout == 0)
+ int n;
+
+ for(n = 0; srb->state != Free; n++)
+ sched();
+ free(srb);
+}
+
+/* under Aoedev qlock() so setting of srb->state is safe */
+static void
+srbwakeup(Srb *srb)
+{
+ if(srbready(srb)){
+ assert(srb->state == Alloc);
wakeup(srb);
+ srb->state = Free;
+ }
}
static void
+srbcleanout(Aoedev *d, Srb *srb)
+{
+ Srb *x, **ll;
+
+ if(srb == d->inprocess)
+ d->inprocess = nil;
+ else
+ for(ll = &d->head; x = *ll; ll = &x->next){
+ d->tail = x;
+ if(x == srb)
+ *ll = x->next;
+ }
+}
+
+static void
+srberror(Aoedev *d, Srb *srb, char *s)
+{
+ srbcleanout(d, srb);
+ srb->error = s;
+ srbwakeup(srb);
+}
+
+static void
frameerror(Aoedev *d, Frame *f, char *s)
{
Srb *srb;
- srb = f->srb;
- if(f->tag == Tfree || !srb)
+ if(f->tag == Tfree)
return;
+ srb = f->srb;
f->srb = nil;
f->tag = Tfree; /* don't get fooled by way-slow responses */
- srberror(srb, s);
+ if(!srb)
+ return;
+ srb->nout--;
+ srberror(d, srb, s);
d->nout--;
}
@@ -310,16 +340,10 @@
static char*
unitname(Aoedev *d)
{
- uprint("%d.%d", d->major, d->minor);
+ uprint("%ud.%ud", d->major, d->minor);
return up->genbuf;
}
-static int
-eventlogready(void*)
-{
- return *events.rp;
-}
-
static long
eventlogread(void *a, long n)
{
@@ -385,13 +409,13 @@
static int
eventcount(void)
{
- int n;
+ uint n;
lock(&events);
if(*events.rp == 0)
n = 0;
else
- n = (events.wp - events.rp) & (Nevents - 1);
+ n = events.wp - events.rp & Nevents - 1;
unlock(&events);
return n/Eventlen;
}
@@ -401,7 +425,7 @@
{
int n;
- n = MACHP(0)->ticks & 0xffff;
+ n = Ticks & 0xffff;
n -= tag & 0xffff;
if(n < 0)
n += 1<<16;
@@ -415,7 +439,7 @@
do {
t = ++d->lasttag << 16;
- t |= MACHP(0)->ticks & 0xffff;
+ t |= Ticks & 0xffff;
} while (t == Tfree || t == Tmgmt);
return t;
}
@@ -428,7 +452,7 @@
d->flag &= ~Dup;
f = d->frames;
e = f + d->nframes;
- for(; f < e; f->tag = Tfree, f->srb = nil, f++)
+ for(; f < e; f++)
frameerror(d, f, Enotup);
d->inprocess = nil;
eventlog("%æ: removed; %s\n", d, err);
@@ -490,6 +514,12 @@
return l->eaidx++ % l->nea;
}
+/*
+ * would like this to depend on the chan (srb).
+ * not possible in the current structure.
+ */
+#define Nofail(d, s) (((d)->flag&Dnofail) == Dnofail)
+
static int
hset(Aoedev *d, Frame *f, Aoehdr *h, int cmd)
{
@@ -496,10 +526,11 @@
int i;
Devlink *l;
- if(f->srb && MACHP(0)->ticks - f->srb->ticksent > Maxreqticks){
+ if(f->srb)
+ if((long)(Ticks-f->srb->ticksent) > Srbtimeout){
eventlog("%æ: srb timeout\n", d);
- if(cmd == ACata && f->srb && Nofail(d, s))
- f->srb->ticksent = MACHP(0)->ticks;
+ if(cmd == ACata && Nofail(d, s))
+ f->srb->ticksent = Ticks;
else
frameerror(d, f, Etimedout);
return -1;
@@ -507,7 +538,7 @@
l = pickdevlink(d);
i = pickea(l);
if(i == -1){
- if(cmd != ACata || f->srb == nil || !Nofail(d, s))
+ if(!(cmd == ACata && f->srb && Nofail(d, s)))
downdev(d, "resend fails; no netlink/ea");
return -1;
}
@@ -524,7 +555,7 @@
f->dl = l;
f->nl = l->nl;
f->eaidx = i;
- f->ticksent = MACHP(0)->ticks;
+ f->ticksent = Ticks;
return f->tag;
}
@@ -534,10 +565,12 @@
{
ulong n;
Aoeata *a;
+ Aoehdr *h;
- a = (Aoeata*)f->hdr;
- if(hset(d, f, a, a->cmd) == -1)
+ h = (Aoehdr*)f->hdr;
+ if(hset(d, f, h, h->cmd) == -1)
return -1;
+ a = (Aoeata*)(f->hdr + Aoehsz);
n = f->bcnt;
if(n > d->maxbcnt){
n = d->maxbcnt; /* mtu mismatch (jumbo fail?) */
@@ -548,6 +581,7 @@
f->dl->resent++;
f->dl->npkt++;
if(waserror())
+ /* should remove the netlink */
return -1;
devtab[f->nl->dc->type]->bwrite(f->nl->dc, allocfb(f), 0);
poperror();
@@ -555,7 +589,7 @@
}
static void
-discover(int major, int minor)
+discover(uint major, uint minor)
{
Aoehdr *h;
Block *b;
@@ -582,7 +616,6 @@
h->minor = minor;
h->cmd = ACconfig;
poperror();
- /* send b down the queue */
devtab[nl->dc->type]->bwrite(nl->dc, b, 0);
}
}
@@ -596,7 +629,7 @@
{
ulong i, tx, timeout, nbc;
vlong starttick;
- enum { Nms = 100, Nbcms = 30*1000, }; /* magic */
+ enum { Nms = 100, Nbcms = 30*1000, };
uchar *ea;
Aoeata *a;
Aoedev *d;
@@ -612,7 +645,7 @@
}
nbc = Nbcms/Nms;
}
- starttick = MACHP(0)->ticks;
+ starttick = Ticks;
rlock(&devs);
for(d = devs.d; d; d = d->next){
if(!canqlock(d))
@@ -635,13 +668,13 @@
if(d->nout == d->maxout){
if(d->maxout > 1)
d->maxout--;
- d->lastwadj = MACHP(0)->ticks;
+ d->lastwadj = Ticks;
}
- a = (Aoeata*)f->hdr;
+ a = (Aoeata*)(f->hdr + Aoehsz);
if(a->scnt > Dbcnt / Aoesectsz &&
++f->nl->lostjumbo > (d->nframes << 1)){
ea = f->dl->eatab[f->eaidx];
- eventlog("%æ: jumbo failure on %s:%E; lba%lld\n",
+ eventlog("%æ: jumbo failure on %s:%E; %llud\n",
d, f->nl->path, ea, f->lba);
d->maxbcnt = Dbcnt;
d->flag &= ~Djumbo;
@@ -650,18 +683,18 @@
if(tx++ == 0){
if((l->rttavg <<= 1) > Rtmax)
l->rttavg = Rtmax;
- eventlog("%æ: rtt %ldms\n", d, TK2MS(l->rttavg));
+ eventlog("%æ: rtt %ldms\n", d, Tk2ms(l->rttavg));
}
}
if(d->nout == d->maxout && d->maxout < d->nframes &&
- TK2MS(MACHP(0)->ticks - d->lastwadj) > 10*1000){ /* more magic */
+ TK2MS(Ticks-d->lastwadj) > 10*1000){
d->maxout++;
- d->lastwadj = MACHP(0)->ticks;
+ d->lastwadj = Ticks;
}
qunlock(d);
}
runlock(&devs);
- i = Nms - TK2MS(MACHP(0)->ticks - starttick);
+ i = Nms - TK2MS(Ticks - starttick);
if(i > 0)
tsleep(&up->sleep, return0, 0, i);
goto loop;
@@ -674,7 +707,7 @@
Aoedev *d;
d = va_arg(f->args, Aoedev*);
- snprint(buf, sizeof buf, "aoe%d.%d", d->major, d->minor);
+ snprint(buf, sizeof buf, "aoe%ud.%ud", d->major, d->minor);
return fmtstrcpy(f, buf);
}
@@ -683,11 +716,14 @@
static void
aoecfg(void)
{
+ char *p, *f[32], buf[24], ifbuf[64];
int n, i;
- char *p, *f[32], buf[24];
- if((p = getconf("aoeif")) == nil || (n = tokenize(p, f, nelem(f))) < 1)
+ if((p = getconf("aoeif")) == nil)
return;
+ strncpy(ifbuf, p, sizeof buf);
+ if((n = tokenize(ifbuf, f, nelem(f))) < 1)
+ return;
/* goo! */
for(i = 0; i < n; i++){
p = f[i];
@@ -736,21 +772,33 @@
}
static Aoedev*
-unit2dev(ulong unit)
+unitseq(ulong unit)
{
int i;
Aoedev *d;
- rlock(&devs);
i = 0;
+ rlock(&devs);
for(d = devs.d; d; d = d->next)
- if(i++ == unit){
+ if(i++ == unit)
+ break;
+ runlock(&devs);
+ return d;
+}
+
+static Aoedev*
+unit2dev(ulong unit)
+{
+ Aoedev *d;
+
+ rlock(&devs);
+ for(d = devs.d; d; d = d->next)
+ if(d->unit == unit){
runlock(&devs);
return d;
}
runlock(&devs);
- uprint("unit lookup failure: %lux pc %#p", unit, getcallerpc(&unit));
- error(up->genbuf);
+ error("unit lookup failure");
return nil;
}
@@ -867,11 +915,9 @@
if(s < Qtopfiles)
return topgen(c, Qtopbase + s, dp);
s -= Qtopfiles;
- if(s >= units.ref)
+ if((d = unitseq(s)) == 0)
return -1;
- mkqid(&q, QID(s, Qunitdir), 0, QTDIR);
- d = unit2dev(s);
- assert(d != nil);
+ mkqid(&q, QID(d->unit, Qunitdir), 0, QTDIR);
devdir(c, q, unitname(d), 0, eve, 0555, dp);
return 1;
case Qtopctl:
@@ -972,6 +1018,7 @@
ulong bcnt;
char extbit, writebit;
Aoeata *ah;
+ Aoehdr *h;
Srb *srb;
extbit = 0x4;
@@ -981,13 +1028,14 @@
bcnt = d->maxbcnt;
if(bcnt > srb->len)
bcnt = srb->len;
- f->nhdr = AOEATASZ;
+ f->nhdr = Aoehsz + Aoeatasz;
memset(f->hdr, 0, f->nhdr);
- ah = (Aoeata*)f->hdr;
- if(hset(d, f, ah, ACata) == -1) {
+ h = (Aoehdr*)f->hdr;
+ if(hset(d, f, h, ACata) == -1){
d->inprocess = nil;
return;
}
+ ah = (Aoeata*)(f->hdr + Aoehsz);
f->dp = srb->dp;
f->bcnt = bcnt;
f->lba = srb->sector;
@@ -995,7 +1043,7 @@
ah->scnt = bcnt / Aoesectsz;
putlba(ah, f->lba);
- if(d->flag & Dllba)
+ if(d->feat & Dllba)
ah->aflag |= AAFext;
else {
extbit = 0;
@@ -1020,13 +1068,12 @@
d->inprocess = nil;
d->nout++;
f->dl->npkt++;
- if(waserror()){
- f->tag = Tfree;
- d->inprocess = nil;
- nexterror();
+ if(waserror())
+ frameerror(d, f, "write error");
+ else{
+ devtab[f->nl->dc->type]->bwrite(f->nl->dc, allocfb(f), 0);
+ poperror();
}
- devtab[f->nl->dc->type]->bwrite(f->nl->dc, allocfb(f), 0);
- poperror();
}
static char*
@@ -1074,15 +1121,6 @@
l->rttavg += n >> 2;
}
-static int
-srbready(void *v)
-{
- Srb *s;
-
- s = v;
- return s->error || (!s->nout && !s->len);
-}
-
static Frame*
getframe(Aoedev *d, int tag)
{
@@ -1109,7 +1147,7 @@
{
Frame *f;
- while ((f = freeframe(d)) != nil) {
+ while(f = freeframe(d)) {
if(d->inprocess == nil){
if(d->head == nil)
return;
@@ -1130,6 +1168,8 @@
qunlock(d);
nexterror();
}
+ if(!UP(d))
+ error(Eio);
srb->next = nil;
if(d->tail)
d->tail->next = srb;
@@ -1140,8 +1180,11 @@
poperror();
qunlock(d);
- while(waserror())
- ;
+ while(waserror()){
+ qlock(d);
+ srberror(d, srb, "interrupted");
+ qunlock(d);
+ }
sleep(srb, srbready, srb);
poperror();
}
@@ -1152,12 +1195,14 @@
rw(Aoedev *d, int write, uchar *db, long len, uvlong off)
{
long n, nlen, copy;
- enum { Srbsz = 1<<19, }; /* magic allocation */
+ enum { Srbsz = 1<<19, };
Srb *srb;
if((off|len) & (Aoesectsz-1))
error("offset and length must be sector multiple.\n");
- if(off > d->bsize || len == 0)
+ if(!UP(d))
+ error(Eio);
+ if(off >= d->bsize)
return 0;
if(off + len > d->bsize)
len = d->bsize - off;
@@ -1171,11 +1216,8 @@
srbfree(srb);
nexterror();
}
- nlen = len;
srb->write = write;
- do {
- if(!UP(d))
- error(Eio);
+ for(nlen = len; nlen; nlen -= n){
srb->sector = off / Aoesectsz;
srb->dp = srb->data;
n = nlen;
@@ -1189,10 +1231,9 @@
error(srb->error);
if(!write && !copy)
memmove(db, srb->data, n);
- nlen -= n;
db += n;
off += n;
- } while (nlen > 0);
+ }
poperror();
srbfree(srb);
return len;
@@ -1209,16 +1250,14 @@
return n;
}
-static char *
-pflag(char *s, char *e, uchar f)
+static char*
+aoeflag(char *s, char *e, uchar f)
{
- uchar i, m;
+ uchar i;
- for(i = 0; i < 8; i++){
- m = 1 << i;
- if(f & m)
- s = seprint(s, e, "%s ", flagname[i]? flagname[i]: "oops");
- }
+ for(i = 0; i < nelem(flagname); i++)
+ if(f & 1 << i)
+ s = seprint(s, e, "%s ", flagname[i]);
return seprint(s, e, "\n");
}
@@ -1237,15 +1276,17 @@
p = seprint(p, e,
"state: %s\n" "nopen: %d\n" "nout: %d\n"
- "nmaxout: %d\n" "nframes: %d\n" "maxbcnt: %d\n"
+ "nmaxout: %d\n" "nframes: %d\n" "maxbcnt: %d [maxmtu %d]\n"
"fw: %.4ux\n"
"model: %s\n" "serial: %s\n" "firmware: %s\n",
state, d->nopen, d->nout,
- d->maxout, d->nframes, d->maxbcnt,
+ d->maxout, d->nframes, d->maxbcnt, d->maxmtu,
d->fwver,
d->model, d->serial, d->firmware);
p = seprint(p, e, "flag: ");
- p = pflag(p, e, d->flag);
+ p = pflag(p, e, d);
+ p[-1] = ' '; /* horrid */
+ p = aoeflag(p, e, d->flag);
if(p - s < len)
len = p - s;
@@ -1270,11 +1311,11 @@
case Qdata:
return rw(d, Read, db, len, off);
case Qconfig:
- if (!UP(d))
+ if(!UP(d))
error(Enotup);
return readmem(off, db, len, d->config, d->nconfig);
case Qident:
- if (!UP(d))
+ if(!UP(d))
error(Enotup);
return readmem(off, db, len, d->ident, sizeof d->ident);
}
@@ -1281,6 +1322,24 @@
}
static int
+getmtu(Chan *m)
+{
+ int n, mtu;
+ char buf[36];
+
+ mtu = 1514;
+ if(m == nil || waserror())
+ return mtu;
+ n = devtab[m->type]->read(m, buf, sizeof buf - 1, 0);
+ poperror();
+ if(n > 12){
+ buf[n] = 0;
+ mtu = strtoul(buf + 12, 0, 0);
+ }
+ return mtu;
+}
+
+static int
devlinkread(Chan *c, void *db, int len, int off)
{
int i;
@@ -1303,15 +1362,18 @@
p = seprint(p, e, "\n");
p = seprint(p, e, "npkt: %uld\n", l->npkt);
p = seprint(p, e, "resent: %uld\n", l->resent);
- p = seprint(p, e, "flag: "); p = pflag(p, e, l->flag);
- p = seprint(p, e, "rttavg: %uld\n", TK2MS(l->rttavg));
- p = seprint(p, e, "mintimer: %uld\n", TK2MS(l->mintimer));
+ p = seprint(p, e, "flag: ");
+ p = aoeflag(p, e, l->flag);
+ p = seprint(p, e, "rttavg: %uld\n", Tk2ms(l->rttavg));
+ p = seprint(p, e, "mintimer: %uld\n", Tk2ms(l->mintimer));
+ p = seprint(p, e, "datamtu: %d\n", l->datamtu);
p = seprint(p, e, "nl path: %s\n", l->nl->path);
p = seprint(p, e, "nl ea: %E\n", l->nl->ea);
- p = seprint(p, e, "nl flag: "); p = pflag(p, e, l->flag);
+ p = seprint(p, e, "nl flag: ");
+ p = aoeflag(p, e, l->flag);
p = seprint(p, e, "nl lostjumbo: %d\n", l->nl->lostjumbo);
- p = seprint(p, e, "nl datamtu: %d\n", l->nl->datamtu);
+ p = seprint(p, e, "nl datamtu: %d\n", getmtu(l->nl->mtu));
if(p - s < len)
len = p - s;
@@ -1340,9 +1402,10 @@
continue;
p = seprint(p, e, "if%d path: %s\n", i, n->path);
p = seprint(p, e, "if%d ea: %E\n", i, n->ea);
- p = seprint(p, e, "if%d flag: ", i); p = pflag(p, e, n->flag);
+ p = seprint(p, e, "if%d flag: ", i);
+ p = aoeflag(p, e, n->flag);
p = seprint(p, e, "if%d lostjumbo: %d\n", i, n->lostjumbo);
- p = seprint(p, e, "if%d datamtu: %d\n", i, n->datamtu);
+ p = seprint(p, e, "if%d datamtu: %d\n", i, getmtu(n->mtu));
}
if(p - s < len)
@@ -1381,13 +1444,14 @@
configwrite(Aoedev *d, void *db, long len)
{
char *s;
- Aoeqc *ch;
+ Aoehdr *h;
+ Aoecfg *ch;
Frame *f;
Srb *srb;
if(!UP(d))
error(Enotup);
- if(len > ETHERMAXTU - AOEQCSZ)
+ if(len > sizeof d->config)
error(Etoobig);
srb = srballoc(len);
s = malloc(len);
@@ -1413,11 +1477,12 @@
tsleep(&up->sleep, return0, 0, 100);
poperror();
}
- f->nhdr = AOEQCSZ;
+ f->nhdr = Aoehsz + Aoecfgsz;
memset(f->hdr, 0, f->nhdr);
- ch = (Aoeqc*)f->hdr;
- if(hset(d, f, ch, ACconfig) == -1)
+ h = (Aoehdr*)f->hdr;
+ if(hset(d, f, h, ACconfig) == -1)
return 0;
+ ch = (Aoecfg*)(f->hdr + Aoehsz);
f->srb = srb;
f->dp = s;
ch->verccmd = AQCfset;
@@ -1456,16 +1521,15 @@
return len;
}
-static int getmtu(Chan*);
-
static int
devmaxdata(Aoedev *d)
{
- int i, m, mtu;
+ int i, m, mtu, datamtu;
Devlink *l;
Netlink *n;
mtu = 100000;
+ datamtu = 100000;
for(i = 0; i < d->ndl; i++){
l = d->dl + i;
n = l->nl;
@@ -1472,21 +1536,30 @@
if((l->flag & Dup) == 0 || (n->flag & Dup) == 0)
continue;
m = getmtu(n->mtu);
+ if(l->datamtu < datamtu)
+ datamtu = l->datamtu;
if(m < mtu)
mtu = m;
}
if(mtu == 100000)
- mtu = 0;
- mtu -= AOEATASZ;
+ mtu = 1514;
+ mtu -= Aoehsz + Aoeatasz;
+ mtu -= mtu % Aoesectsz;
+ if(mtu > datamtu)
+ mtu = datamtu;
return mtu;
}
static int
-toggle(char *s, int init)
+toggle(char *s, uint f, uint bit)
{
if(s == nil)
- return init ^ 1;
- return strcmp(s, "on") == 0;
+ f = f^bit;
+ else if(strcmp(s, "on") == 0)
+ f |= bit;
+ else
+ f &= ~bit;
+ return f;
}
static void ataident(Aoedev*);
@@ -1513,7 +1586,7 @@
{Jumbo, "jumbo", 0 },
{Maxbno, "maxbno", 0 },
{Mtu, "mtu", 0 },
- {Nofailf, "nofail", 0 },
+ {Nofailf, "nofail", 0 },
{Setsize, "setsize", 0 },
};
@@ -1533,14 +1606,7 @@
ataident(d);
break;
case Jumbo:
- m = 0;
- if(d->flag & Djumbo)
- m = 1;
- toggle(cb->f[1], m);
- if(m)
- d->flag |= Djumbo;
- else
- d->flag &= ~Djumbo;
+ d->flag = toggle(cb->f[1], d->flag, Djumbo);
break;
case Maxbno:
case Mtu:
@@ -1552,20 +1618,19 @@
if(ct->index == Maxbno)
m *= Aoesectsz;
else{
- m -= AOEATASZ;
+ m -= Aoehsz + Aoeatasz;
m &= ~(Aoesectsz-1);
}
if(m == 0 || m > maxbcnt)
- cmderror(cb, "mtu out of legal range");
+ cmderror(cb, "invalid mtu");
maxbcnt = m;
- }
+ d->maxmtu = m;
+ } else
+ d->maxmtu = Maxmtu;
d->maxbcnt = maxbcnt;
break;
case Nofailf:
- if (toggle(cb->f[1], (d->flag & Dnofail) != 0))
- d->flag |= Dnofail;
- else
- d->flag &= ~Dnofail;
+ d->flag = toggle(cb->f[1], d->flag, Dnofail);
break;
case Setsize:
bsize = d->realbsize;
@@ -1578,8 +1643,6 @@
}
d->bsize = bsize;
break;
- default:
- cmderror(cb, "unknown aoe control message");
}
poperror();
qunlock(d);
@@ -1635,7 +1698,7 @@
e = nl + nelem(netlinks.nl);
for(; nl < e && nl->cc; nl++)
continue;
- if (nl >= e)
+ if(nl == e)
error("out of netlink structures");
nl->cc = cc;
nl->dc = dc;
@@ -1677,14 +1740,14 @@
* always allocate max frames. maxout may change.
*/
static Aoedev*
-newdev(long major, long minor, int n)
+newdev(uint major, uint minor, int n)
{
Aoedev *d;
Frame *f, *e;
- d = mallocz(sizeof *d, 1);
- f = mallocz(sizeof *f * Maxframes, 1);
- if (!d || !f) {
+ d = malloc(sizeof *d);
+ f = malloc(sizeof *f*Maxframes);
+ if(!d || !f) {
free(d);
free(f);
error("aoe device allocation failure");
@@ -1691,7 +1754,7 @@
}
d->nframes = n;
d->frames = f;
- for (e = f + n; f < e; f++)
+ for (e = f + Maxframes; f < e; f++)
f->tag = Tfree;
d->maxout = n;
d->major = major;
@@ -1698,6 +1761,7 @@
d->minor = minor;
d->maxbcnt = Dbcnt;
d->flag = Djumbo;
+ d->maxmtu = Maxmtu;
d->unit = newunit(); /* bzzt. inaccurate if units removed */
if(d->unit == -1){
free(d);
@@ -1709,7 +1773,7 @@
}
static Aoedev*
-mm2dev(int major, int minor)
+mm2dev(uint major, uint minor)
{
Aoedev *d;
@@ -1720,13 +1784,13 @@
return d;
}
runlock(&devs);
- eventlog("mm2dev: %d.%d not found\n", major, minor);
+ eventlog("mm2dev: %ud.%ud not found\n", major, minor);
return nil;
}
/* Find the device in our list. If not known, add it */
static Aoedev*
-getdev(long major, long minor, int n)
+getdev(uint major, uint minor, int n)
{
Aoedev *d;
@@ -1740,7 +1804,7 @@
for(d = devs.d; d; d = d->next)
if(d->major == major && d->minor == minor)
break;
- if (d == nil) {
+ if(d == nil) {
d = newdev(major, minor, n);
d->next = devs.d;
devs.d = d;
@@ -1750,53 +1814,23 @@
return d;
}
-static ushort
-gbit16(void *a)
-{
- uchar *i;
-
- i = a;
- return i[1] << 8 | i[0];
-}
-
-static ulong
-gbit32(void *a)
-{
- ulong j;
- uchar *i;
-
- i = a;
- j = i[3] << 24;
- j |= i[2] << 16;
- j |= i[1] << 8;
- j |= i[0];
- return j;
-}
-
-static uvlong
-gbit64(void *a)
-{
- uchar *i;
-
- i = a;
- return (uvlong)gbit32(i+4) << 32 | gbit32(a);
-}
-
static void
ataident(Aoedev *d)
{
Aoeata *a;
- Block *b;
+ Aoehdr *h;
Frame *f;
f = freeframe(d);
if(f == nil)
return;
- f->nhdr = AOEATASZ;
+ f->nhdr = Aoehsz + Aoeatasz;
memset(f->hdr, 0, f->nhdr);
- a = (Aoeata*)f->hdr;
- if(hset(d, f, a, ACata) == -1)
+ h = (Aoehdr*)f->hdr;
+ if(hset(d, f, h, ACata) == -1)
return;
+ a = (Aoeata*)(f->hdr + Aoehsz);
+ f->srb = srbkalloc(0, 0);
a->cmdstat = Cid; /* ata 6, page 110 */
a->scnt = 1;
a->lba[3] = 0xa0;
@@ -1804,26 +1838,14 @@
f->dl->npkt++;
f->bcnt = 512;
f->dlen = 0;
- b = allocfb(f);
- devtab[f->nl->dc->type]->bwrite(f->nl->dc, b, 0);
-}
-
-static int
-getmtu(Chan *m)
-{
- int n, mtu;
- char buf[36];
-
- mtu = 1514;
- if(m == nil || waserror())
- return mtu;
- n = devtab[m->type]->read(m, buf, sizeof buf - 1, 0);
- if(n > 12){
- buf[n] = 0;
- mtu = strtoul(buf + 12, 0, 0);
+ if(waserror()){
+ srbfree(f->srb);
+ d->nout--;
+ f->tag = Tfree;
+ }else{
+ devtab[f->nl->dc->type]->bwrite(f->nl->dc, allocfb(f), 0);
+ poperror();
}
- poperror();
- return mtu;
}
static int
@@ -1845,16 +1867,19 @@
}
static Devlink*
-newdevlink(Aoedev *d, Netlink *n, Aoeqc *c)
+newdevlink(Aoedev *d, Netlink *n, Aoehdr *h)
{
int i;
+ Aoecfg *c;
Devlink *l;
+ c = (Aoecfg*)((uchar*)h + Aoehsz);
for(i = 0; i < Ndevlink; i++){
l = d->dl + i;
if(i == d->ndl){
d->ndl++;
- newdlea(l, c->src);
+ newdlea(l, h->src);
+ l->datamtu = c->scnt*Aoesectsz;
l->nl = n;
l->flag |= Dup;
l->mintimer = Rtmin;
@@ -1861,13 +1886,14 @@
l->rttavg = Rtmax;
return l;
}
- if(l->nl == n) {
- newdlea(l, c->src);
+ if(l->nl == n){
+ newdlea(l, h->src);
+ l->datamtu = c->scnt*Aoesectsz;
l->flag |= Dup;
return l;
}
}
- eventlog("%æ: out of links: %s:%E to %E\n", d, n->path, n->ea, c->src);
+ eventlog("%æ: out of links: %s:%E to %E\n", d, n->path, n->ea, h->src);
return 0;
}
@@ -1893,18 +1919,21 @@
static void
qcfgrsp(Block *b, Netlink *nl)
{
- int major, cmd, cslen, blen;
- unsigned n;
+ int cmd, cslen, blen;
+ uint n, major;
Aoedev *d;
- Aoeqc *ch;
+ Aoehdr *h;
+ Aoecfg *ch;
Devlink *l;
Frame *f;
+ Srb *srb;
- ch = (Aoeqc*)b->rp;
- major = nhgets(ch->major);
- n = nhgetl(ch->tag);
+ h = (Aoehdr*)b->rp;
+ ch = (Aoecfg*)(b->rp + Aoehsz);
+ major = nhgets(h->major);
+ n = nhgetl(h->tag);
if(n != Tmgmt){
- d = mm2dev(major, ch->minor);
+ d = mm2dev(major, h->minor);
if(d == nil)
return;
qlock(d);
@@ -1915,7 +1944,7 @@
return;
}
cslen = nhgets(ch->cslen);
- blen = BLEN(b) - AOEQCSZ;
+ blen = BLEN(b) - (Aoehsz + Aoecfgsz);
if(cslen < blen && BLEN(b) > 60)
eventlog("%æ: cfgrsp: tag %.8ux oversized %d %d\n",
d, n, cslen, blen);
@@ -1924,12 +1953,16 @@
d, n, cslen, blen);
cslen = blen;
}
- memmove(f->dp, ch + 1, cslen);
- f->srb->nout--;
- wakeup(f->srb);
- d->nout--;
+ memmove(f->dp, (uchar*)ch + Aoehsz + Aoecfgsz, cslen);
+ srb = f->srb;
+ f->dp = nil;
f->srb = nil;
- f->tag = Tfree;
+ if(srb){
+ srb->nout--;
+ srbwakeup(srb);
+ d->nout--;
+ f->tag = Tfree;
+ }
qunlock(d);
return;
}
@@ -1936,7 +1969,7 @@
cmd = ch->verccmd & 0xf;
if(cmd != 0){
- eventlog("aoe%d.%d: cfgrsp: bad command %d\n", major, ch->minor, cmd);
+ eventlog("aoe%ud.%ud: cfgrsp: bad command %d\n", major, h->minor, cmd);
return;
}
n = nhgets(ch->bufcnt);
@@ -1944,10 +1977,10 @@
n = Maxframes;
if(waserror()){
- eventlog("getdev: %d.%d ignored: %s\n", major, ch->minor, up->errstr);
+ eventlog("getdev: %ud.%ud ignored: %s\n", major, h->minor, up->errstr);
return;
}
- d = getdev(major, ch->minor, n);
+ d = getdev(major, h->minor, n);
poperror();
if(d == 0)
return;
@@ -1960,7 +1993,7 @@
nexterror();
}
- l = newdevlink(d, nl, ch); /* add this interface. */
+ l = newdevlink(d, nl, h); /* add this interface. */
d->fwver = nhgets(ch->fwver);
n = nhgets(ch->cslen);
@@ -1967,15 +2000,17 @@
if(n > sizeof d->config)
n = sizeof d->config;
d->nconfig = n;
- memmove(d->config, ch + 1, n);
- if(l != 0 && d->flag & Djumbo){
- n = getmtu(nl->mtu) - AOEATASZ;
- n /= Aoesectsz;
- if(n > ch->scnt)
- n = ch->scnt;
- n = n? n * Aoesectsz: Dbcnt;
+ memmove(d->config, (uchar*)ch + Aoehsz + Aoecfgsz, n);
+
+ /* manually set mtu may be reset lower if conditions warrant */
+ if(l){
+ n = devmaxdata(d);
+ if((d->flag & Djumbo) == 0)
+ n = Dbcnt;
+ if(n > d->maxmtu)
+ n = d->maxmtu;
if(n != d->maxbcnt){
- eventlog("%æ: setting %d byte data frames on %s:%E\n",
+ eventlog("%æ: setting %d byte mtu on %s:%E\n",
d, n, nl->path, nl->ea);
d->maxbcnt = n;
}
@@ -1986,59 +2021,18 @@
qunlock(d);
}
-void
-aoeidmove(char *p, ushort *u, unsigned n)
-{
- int i;
- char *op, *e, *s;
-
- op = p;
- /*
- * the ushort `*u' is sometimes not aligned on a short boundary,
- * so dereferencing u[i] causes an alignment exception on
- * some machines.
- */
- s = (char *)u;
- for(i = 0; i < n; i += 2){
- *p++ = s[i + 1];
- *p++ = s[i];
- }
- *p = 0;
- while(p > op && *--p == ' ')
- *p = 0;
- e = p;
- p = op;
- while(*p == ' ')
- p++;
- memmove(op, p, n - (e - p));
-}
-
static vlong
aoeidentify(Aoedev *d, ushort *id)
{
- int i;
vlong s;
- d->flag &= ~(Dllba|Dpower|Dsmart|Dnop|Dup);
-
- i = gbit16(id+83) | gbit16(id+86);
- if(i & (1<<10)){
- d->flag |= Dllba;
- s = gbit64(id+100);
- }else
- s = gbit32(id+60);
-
- i = gbit16(id+83);
- if((i>>14) == 1) {
- if(i & (1<<3))
- d->flag |= Dpower;
- i = gbit16(id+82);
- if(i & 1)
- d->flag |= Dsmart;
- if(i & (1<<14))
- d->flag |= Dnop;
+ s = idfeat(d, id);
+ if(s == -1)
+ return -1;
+ if((d->feat&Dlba) == 0){
+ dprint("%æ: no lba support\n", d);
+ return -1;
}
-// eventlog("%æ up\n", d);
d->flag |= Dup;
memmove(d->ident, id, sizeof d->ident);
return s;
@@ -2064,13 +2058,14 @@
osectors = d->realbsize;
memmove(oserial, d->serial, sizeof d->serial);
- aoeidmove(d->serial, id+10, 20);
- aoeidmove(d->firmware, id+23, 8);
- aoeidmove(d->model, id+27, 40);
+ idmove(d->serial, id+10, 20);
+ idmove(d->firmware, id+23, 8);
+ idmove(d->model, id+27, 40);
+ /* idss() */
+ /* d->wwn = idwwn(d, id); */
s *= Aoesectsz;
- if((osectors == 0 || osectors != s) &&
- memcmp(oserial, d->serial, sizeof oserial) != 0){
+ if(osectors != s || memcmp(oserial, d->serial, sizeof oserial)){
d->bsize = s;
d->realbsize = s;
// d->mediachange = 1;
@@ -2082,24 +2077,26 @@
static void
atarsp(Block *b)
{
- unsigned n;
- short major;
+ uint n;
+ ushort major;
Aoeata *ahin, *ahout;
+ Aoehdr *h;
Aoedev *d;
Frame *f;
Srb *srb;
- ahin = (Aoeata*)b->rp;
- major = nhgets(ahin->major);
- d = mm2dev(major, ahin->minor);
+ h = (Aoehdr*)b->rp;
+ major = nhgets(h->major);
+ d = mm2dev(major, h->minor);
if(d == nil)
return;
+ ahin = (Aoeata*)(b->rp + Aoehsz);
qlock(d);
if(waserror()){
qunlock(d);
nexterror();
}
- n = nhgetl(ahin->tag);
+ n = nhgetl(h->tag);
f = getframe(d, n);
if(f == nil){
dprint("%æ: unexpected response; tag %ux\n", d, n);
@@ -2106,7 +2103,7 @@
goto bail;
}
rtupdate(f->dl, tsince(f->tag));
- ahout = (Aoeata*)f->hdr;
+ ahout = (Aoeata*)(f->hdr + Aoehsz);
srb = f->srb;
if(ahin->cmdstat & 0xa9){
@@ -2119,12 +2116,12 @@
switch(ahout->cmdstat){
case Crd:
case Crdext:
- if(BLEN(b) - AOEATASZ < n){
- eventlog("%æ: runt read blen %ld expect %d\n",
+ if(BLEN(b) - (Aoehsz + Aoeatasz) != n){
+ eventlog("%æ: misread blen %ld expect %d\n",
d, BLEN(b), n);
goto bail;
}
- memmove(f->dp, (uchar *)ahin + AOEATASZ, n);
+ memmove(f->dp, b->rp + Aoehsz + Aoeatasz, n);
case Cwr:
case Cwrext:
if(n > Dbcnt)
@@ -2137,12 +2134,14 @@
}
break;
case Cid:
- if(BLEN(b) - AOEATASZ < 512){
+ if(BLEN(b) - (Aoehsz + Aoeatasz) < 512){
eventlog("%æ: runt identify blen %ld expect %d\n",
- d, BLEN(b), n);
+ d, BLEN(b), 512 + Aoehsz + Aoeatasz);
goto bail;
}
- identify(d, (ushort*)((uchar *)ahin + AOEATASZ));
+ identify(d, (ushort*)(b->rp + Aoehsz + Aoeatasz));
+ free(srb); /* BOTCH */
+ srb = nil;
break;
default:
eventlog("%æ: unknown ata command %.2ux \n",
@@ -2150,9 +2149,11 @@
}
}
- if(srb && --srb->nout == 0 && srb->len == 0)
- wakeup(srb);
f->srb = nil;
+ if(srb){
+ srb->nout--;
+ srbwakeup(srb);
+ }
f->tag = Tfree;
d->nout--;
@@ -2177,7 +2178,7 @@
kstrcpy(name, nl->path, Maxpath);
if(waserror()){
- eventlog("netrdaoe exiting: %s\n", up->errstr);
+ eventlog("netrdaoe@%s: exiting: %s\n", name, up->errstr);
netlinks.reader[idx] = 0;
wakeup(netlinks.rendez + idx);
pexit(up->errstr, 1);
@@ -2185,38 +2186,27 @@
if(autodiscover)
discover(0xffff, 0xff);
for (;;) {
- if(!(nl->flag & Dup)) {
- uprint("%s: netlink is down", name);
- error(up->genbuf);
- }
- if (nl->dc == nil)
+ if((nl->flag & Dup) == 0)
+ error("netlink is down");
+ if(nl->dc == nil)
panic("netrdaoe: nl->dc == nil");
b = devtab[nl->dc->type]->bread(nl->dc, 1<<16, 0);
- if(b == nil) {
- uprint("%s: nil read from network", name);
- error(up->genbuf);
- }
+ if(b == nil)
+ error("network read");
h = (Aoehdr*)b->rp;
if(h->verflag & AFrsp)
if(s = aoeerror(h)){
- eventlog("%s: %s\n", nl->path, up->errstr);
+ eventlog("%s: %s\n", nl->path, s);
errrsp(b, s);
- }else
- switch(h->cmd){
- case ACata:
- atarsp(b);
- break;
- case ACconfig:
- qcfgrsp(b, nl);
- break;
- default:
- if((h->cmd & 0xf0) == 0){
- eventlog("%s: unknown cmd %d\n",
- nl->path, h->cmd);
- errrsp(b, "unknown command");
- }
- break;
- }
+ }else if(h->cmd == ACata)
+ atarsp(b);
+ else if(h->cmd == ACconfig)
+ qcfgrsp(b, nl);
+ else if((h->cmd & 0xf0) == 0){
+ eventlog("%s: unknown cmd %d\n",
+ nl->path, h->cmd);
+ errrsp(b, "unknown command");
+ }
freeb(b);
}
}
@@ -2234,7 +2224,7 @@
cclose(c);
nexterror();
}
- if (c == nil)
+ if(c == nil)
panic("æ: getaddr: c == nil");
n = devtab[c->type]->read(c, buf, sizeof buf-1, 0);
poperror();
@@ -2252,7 +2242,7 @@
Chan *dc, *cc, *mtu;
Netlink *nl;
- snprint(addr, sizeof addr, "%s!%#x", path, Aoetype);
+ snprint(addr, sizeof addr, "%s!0x%x", path, Aoetype);
dc = chandial(addr, nil, nil, &cc);
snprint(addr, sizeof addr, "%s/mtu", path);
if(waserror())
@@ -2302,7 +2292,7 @@
if(n->dc && strcmp(n->path, path) == 0)
break;
unlock(&netlinks);
- if (n >= e)
+ if(n == e)
error("device not bound");
/*
@@ -2371,7 +2361,7 @@
wlock(&devs);
for(p = d = devs.d; d; d = next){
next = d->next;
- if(d->ndl > 0) {
+ if(d->ndl > 0){
p = d;
continue;
}
@@ -2392,8 +2382,37 @@
}
static void
-removeaoedev(Aoedev *d)
+strtoss(char *f, uint *shelf, uint *slot)
{
+ char *s;
+
+ *shelf = 0xffff;
+ *slot = 0xff;
+ if(!f)
+ return;
+ *shelf = strtol(f, &s, 0);
+ if(s == f || *shelf > 0xffff)
+ error("bad shelf");
+ f = s;
+ if(*f++ == '.'){
+ *slot = strtol(f, &s, 0);
+ if(s == f || *slot > 0xff)
+ error("bad slot");
+ }
+}
+
+static void
+discoverstr(char *f)
+{
+ uint shelf, slot;
+
+ strtoss(f, &shelf, &slot);
+ discover(shelf, slot);
+}
+
+static void
+removedev(Aoedev *d)
+{
int i;
Aoedev *p;
@@ -2400,9 +2419,9 @@
wlock(&devs);
p = 0;
if(d != devs.d)
- for(p = devs.d; p; p = p->next)
- if(p->next == d)
- break;
+ for(p = devs.d; p; p = p->next)
+ if(p->next == d)
+ break;
qlock(d);
d->flag &= ~Dup;
newvers(d);
@@ -2421,60 +2440,45 @@
wunlock(&devs);
}
-static void
-removedev(char *name)
-{
- Aoedev *d, *p;
- wlock(&devs);
- for(p = d = devs.d; d; p = d, d = d->next)
- if(strcmp(name, unitname(d)) == 0) {
- wunlock(&devs);
- removeaoedev(p);
- return;
- }
- wunlock(&devs);
- error("device not bound");
-}
-
static void
-discoverstr(char *f)
-{
- ushort shelf, slot;
- ulong sh;
- char *s;
-
- if(f == 0){
- discover(0xffff, 0xff);
- return;
- }
-
- shelf = sh = strtol(f, &s, 0);
- if(s == f || sh > 0xffff)
- error("bad shelf");
- f = s;
- if(*f++ == '.'){
- slot = strtol(f, &s, 0);
- if(s == f || slot > 0xff)
- error("bad shelf");
- }else
- slot = 0xff;
- discover(shelf, slot);
-}
-
-
-static void
aoeremove(Chan *c)
{
switch(TYPE(c->qid)){
default:
+ case Qzero:
+ case Qtopdir:
+ case Qtoplog:
+ case Qtopctl:
+ case Qctl:
+ case Qdata:
+ case Qconfig:
+ case Qident:
error(Eperm);
case Qunitdir:
- removeaoedev(unit2dev(UNIT(c->qid)));
+ removedev(unit2dev(UNIT(c->qid)));
break;
}
}
+static void
+removestr(char *f)
+{
+ uint shelf, slot;
+ Aoedev *d;
+
+ strtoss(f, &shelf, &slot);
+ wlock(&devs);
+ for(d = devs.d; d; d = d->next)
+ if(shelf == d->major && slot == d->minor){
+ wunlock(&devs); /* BOTCH */
+ removedev(d);
+ return;
+ }
+ wunlock(&devs);
+ error("device not bound");
+}
+
static long
topctlwrite(void *db, long n)
{
@@ -2509,28 +2513,26 @@
f = cb->f[1];
switch(ct->index){
case Autodiscover:
- autodiscover = toggle(f, autodiscover);
+ autodiscover = toggle(f, autodiscover, 1);
break;
case Bind:
netbind(f);
break;
case Debug:
- debug = toggle(f, debug);
+ debug = toggle(f, debug, 1);
break;
case Discover:
discoverstr(f);
break;
case Rediscover:
- rediscover = toggle(f, rediscover);
+ rediscover = toggle(f, rediscover, 1);
break;
case Remove:
- removedev(f);
+ removestr(f); /* depricated */
break;
case Unbind:
netunbind(f);
break;
- default:
- cmderror(cb, "unknown aoe control message");
}
poperror();
free(cb);
--- a/sys/src/9/port/devsd.c
+++ b/sys/src/9/port/devsd.c
@@ -15,7 +15,9 @@
extern Dev sddevtab;
extern SDifc* sdifc[];
-static char Echange[] = "media or partition has changed";
+static char Echange[] = "media or partition has changed";
+static char Enoata[] = "raw ata commands not supported";
+static char Enoscsi[] = "raw scsi commands not supported";
static char devletters[] = "0123456789"
"abcdefghijklmnopqrstuvwxyz"
@@ -23,8 +25,13 @@
static SDev *devs[sizeof devletters-1];
static QLock devslock;
+static SDunit topctlunit;
enum {
+ Ahdrsz = 2,
+};
+
+enum {
Rawcmd,
Rawdata,
Rawstatus,
@@ -40,6 +47,7 @@
Qctl = Qunitbase,
Qraw,
Qpart,
+ Qextra,
TypeLOG = 4,
NType = (1<<TypeLOG),
@@ -292,7 +300,7 @@
}
sdev->unitflg[subno] = 1;
- snprint(buf, sizeof(buf), "%s%d", sdev->name, subno);
+ snprint(buf, sizeof buf, "%s%x", sdev->name, subno);
kstrdup(&unit->name, buf);
kstrdup(&unit->user, eve);
unit->perm = 0555;
@@ -323,15 +331,14 @@
sdreset(void)
{
int i;
- SDev *sdev;
/*
* Probe all known controller types and register any devices found.
*/
for(i = 0; sdifc[i] != nil; i++){
- if(sdifc[i]->pnp == nil || (sdev = sdifc[i]->pnp()) == nil)
+ if(sdifc[i]->pnp == nil)
continue;
- sdadddevs(sdev);
+ sdadddevs(sdifc[i]->pnp());
}
}
@@ -344,8 +351,8 @@
for(; sdev; sdev=next){
next = sdev->next;
- sdev->unit = (SDunit**)malloc(sdev->nunit * sizeof(SDunit*));
- sdev->unitflg = (int*)malloc(sdev->nunit * sizeof(int));
+ sdev->unit = malloc(sdev->nunit * sizeof(SDunit*));
+ sdev->unitflg = malloc(sdev->nunit * sizeof(int));
if(sdev->unit == nil || sdev->unitflg == nil){
print("sdadddevs: out of memory\n");
giveup:
@@ -392,11 +399,12 @@
{
Qid q;
uvlong l;
+ SDfile *e;
SDpart *pp;
SDperm *perm;
SDunit *unit;
SDev *sdev;
- int rv;
+ int rv, t;
sdev = sdgetdev(DEV(c->qid));
assert(sdev);
@@ -438,6 +446,18 @@
devdir(c, q, pp->name, l, pp->user, pp->perm, dp);
rv = 1;
break;
+ case Qextra:
+ t = PART(c->qid);
+ if(t >= unit->nefile)
+ break;
+ mkqid(&q, QID(DEV(c->qid), UNIT(c->qid), PART(c->qid), Qextra),
+ unit->vers, QTFILE);
+ e = unit->efile + t;
+ if(emptystr(e->user))
+ kstrdup(&e->user, eve);
+ devdir(c, q, e->name, 0, e->user, e->perm, dp);
+ rv = 1;
+ break;
}
decref(&sdev->r);
@@ -448,11 +468,20 @@
sd1gen(Chan* c, int i, Dir* dp)
{
Qid q;
+ SDperm *p;
switch(i){
case Qtopctl:
mkqid(&q, QID(0, 0, 0, Qtopctl), 0, QTFILE);
- devdir(c, q, "sdctl", 0, eve, 0640, dp);
+ qlock(&topctlunit.ctl);
+ p = &topctlunit.ctlperm;
+ if(p->user == nil || p->user[0] == 0){
+ kstrdup(&p->name, "sdctl");
+ kstrdup(&p->user, eve);
+ p->perm = 0640;
+ }
+ devdir(c, q, p->name, 0, p->user, p->perm, dp);
+ qunlock(&topctlunit.ctl);
return 1;
}
return -1;
@@ -459,6 +488,26 @@
}
static int
+efilegen(Chan *c, SDunit *unit, int i, Dir *dp)
+{
+ Qid q;
+ SDfile *e;
+
+ i -= SDnpart;
+ if(unit->nefile == 0 || i >= unit->nefile)
+ return -1;
+ if(i < 0)
+ return 0;
+ e = unit->efile + i;
+ if(emptystr(e->user))
+ kstrdup(&e->user, eve);
+ mkqid(&q, QID(DEV(c->qid), UNIT(c->qid), i, Qextra),
+ unit->vers, QTFILE);
+ devdir(c, q, e->name, 0, e->user, e->perm, dp);
+ return 1;
+}
+
+static int
sdgen(Chan* c, char*, Dirtab*, int, int s, Dir* dp)
{
Qid q;
@@ -553,17 +602,18 @@
}
i -= Qpart;
if(unit->part == nil || i >= unit->npart){
+ r = efilegen(c, unit, i, dp);
qunlock(&unit->ctl);
decref(&sdev->r);
- break;
+ return r;
}
pp = &unit->part[i];
- if(!pp->valid){
+ if(!pp->valid || unit->sectors == 0){
qunlock(&unit->ctl);
decref(&sdev->r);
return 0;
}
- l = (pp->end - pp->start) * unit->secsize;
+ l = (pp->end - pp->start) * (uvlong)unit->secsize;
mkqid(&q, QID(DEV(c->qid), UNIT(c->qid), i, Qpart),
unit->vers+pp->vers, QTFILE);
if(emptystr(pp->user))
@@ -575,6 +625,7 @@
case Qraw:
case Qctl:
case Qpart:
+ case Qextra:
if((sdev = sdgetdev(DEV(c->qid))) == nil){
devdir(c, q, "unavailable", 0, eve, 0, dp);
return 1;
@@ -715,10 +766,12 @@
}
}
+#define iskaddr(a) ((uintptr)(a) > KZERO)
+
static long
sdbio(Chan* c, int write, char* a, long len, uvlong off)
{
- int nchange;
+ int nchange, hard, allocd;
long l;
uchar *b;
SDpart *pp;
@@ -785,21 +838,30 @@
poperror();
}
- b = sdmalloc(nb*unit->secsize);
- if(b == nil)
- error(Enomem);
+ offset = off%unit->secsize;
+ if(offset+len > nb*unit->secsize)
+ len = nb*unit->secsize - offset;
+ hard = offset || write && len%unit->secsize;
+
+ if(iskaddr(a) && !hard) {
+ b = (uchar*)a;
+ allocd = 0;
+ }else{
+ b = sdmalloc(nb*unit->secsize);
+ if(b == nil)
+ error(Enomem);
+ allocd = 1;
+ }
if(waserror()){
- sdfree(b);
+ if(allocd)
+ sdfree(b);
if(!(unit->inquiry[1] & 0x80))
decref(&sdev->r); /* gadverdamme! */
nexterror();
}
- offset = off%unit->secsize;
- if(offset+len > nb*unit->secsize)
- len = nb*unit->secsize - offset;
if(write){
- if(offset || (len%unit->secsize)){
+ if(hard){
l = unit->dev->ifc->bio(unit, 0, 0, b, nb, bno);
if(l < 0)
error(Eio);
@@ -810,7 +872,8 @@
len = l;
}
}
- memmove(b+offset, a, len);
+ if(allocd)
+ memmove(b+offset, a, len);
l = unit->dev->ifc->bio(unit, 0, 1, b, nb, bno);
if(l < 0)
error(Eio);
@@ -827,9 +890,11 @@
len = 0;
else if(len > l - offset)
len = l - offset;
- memmove(a, b+offset, len);
+ if(allocd)
+ memmove(a, b+offset, len);
}
- sdfree(b);
+ if(allocd)
+ sdfree(b);
poperror();
if(unit->inquiry[1] & 0x80){
@@ -844,35 +909,60 @@
static long
sdrio(SDreq* r, void* a, long n)
{
+ char *errstr;
+ int rv;
void *data;
+ SDunit *u;
+ int (*f)(SDreq*);
if(n >= SDmaxio || n < 0)
error(Etoobig);
-
- data = nil;
- if(n){
- if((data = sdmalloc(n)) == nil)
- error(Enomem);
- if(r->write)
- memmove(data, a, n);
+ u = r->unit;
+ if(u->haversense && r->cmd[0] == 0x03){
+ u->haversense = 0;
+ r->rlen = sizeof u->rsense;
+ if(r->rlen > n)
+ r->rlen = n;
+ memmove(a, u->rsense, r->rlen);
+ r->status = SDok;
+ return r->rlen;
}
- r->data = data;
- r->dlen = n;
+ data = nil;
+ if(n > 0 && (data = sdmalloc(n)) == nil)
+ error(Enomem);
if(waserror()){
sdfree(data);
r->data = nil;
nexterror();
}
+ if(r->write && n > 0)
+ memmove(data, a, n);
+ r->data = data;
+ r->dlen = n;
- if(r->unit->dev->ifc->rio(r) != SDok)
+ if(r->proto == SData){
+ f = u->dev->ifc->ataio;
+ errstr = Enoata;
+ }else{
+ f = u->dev->ifc->rio;
+ errstr = Enoscsi;
+ }
+ if(f == nil)
+ error(errstr);
+ rv = f(r);
+ if(r->flags & SDvalidsense){
+ memmove(u->rsense, r->sense, sizeof u->rsense);
+ u->haversense = 1;
+ }
+ if(rv != SDok)
error(Eio);
if(!r->write && r->rlen > 0)
memmove(a, data, r->rlen);
+ poperror();
sdfree(data);
r->data = nil;
- poperror();
return r->rlen;
}
@@ -879,6 +969,9 @@
/*
* SCSI simulation for non-SCSI devices
+ *
+ * see /sys/src/cmd/scuzz/sense.c for information on key.
+ * see /sys/lib/scsicodes for asc:ascq codes
*/
int
sdsetsense(SDreq *r, int status, int key, int asc, int ascq)
@@ -908,37 +1001,8 @@
}
int
-sdmodesense(SDreq *r, uchar *cmd, void *info, int ilen)
+sdfakescsi(SDreq *r)
{
- int len;
- uchar *data;
-
- /*
- * Fake a vendor-specific request with page code 0,
- * return the drive info.
- */
- if((cmd[2] & 0x3F) != 0 && (cmd[2] & 0x3F) != 0x3F)
- return sdsetsense(r, SDcheck, 0x05, 0x24, 0);
- len = (cmd[7]<<8)|cmd[8];
- if(len == 0)
- return SDok;
- if(len < 8+ilen)
- return sdsetsense(r, SDcheck, 0x05, 0x1A, 0);
- if(r->data == nil || r->dlen < len)
- return sdsetsense(r, SDcheck, 0x05, 0x20, 1);
- data = r->data;
- memset(data, 0, 8);
- data[0] = ilen>>8;
- data[1] = ilen;
- if(ilen)
- memmove(data+8, info, ilen);
- r->rlen = 8+ilen;
- return sdsetsense(r, SDok, 0, 0, 0);
-}
-
-int
-sdfakescsi(SDreq *r, void *info, int ilen)
-{
uchar *cmd, *p;
uvlong len;
SDunit *unit;
@@ -948,25 +1012,6 @@
unit = r->unit;
/*
- * Rewrite read(6)/write(6) into read(10)/write(10).
- */
- switch(cmd[0]){
- case 0x08: /* read */
- case 0x0A: /* write */
- cmd[9] = 0;
- cmd[8] = cmd[4];
- cmd[7] = 0;
- cmd[6] = 0;
- cmd[5] = cmd[3];
- cmd[4] = cmd[2];
- cmd[3] = cmd[1] & 0x0F;
- cmd[2] = 0;
- cmd[1] &= 0xE0;
- cmd[0] |= 0x20;
- break;
- }
-
- /*
* Map SCSI commands into ATA commands for discs.
* Fail any command with a LUN except INQUIRY which
* will return 'logical unit not supported'.
@@ -1018,13 +1063,15 @@
/*
* Read capacity returns the LBA of the last sector.
*/
- len = unit->sectors - 1;
+ len = unit->sectors;
+ if(len > 0)
+ len--;
p = r->data;
*p++ = len>>24;
*p++ = len>>16;
*p++ = len>>8;
*p++ = len;
- len = 512;
+ len = unit->secsize;
*p++ = len>>24;
*p++ = len>>16;
*p++ = len>>8;
@@ -1040,7 +1087,9 @@
/*
* Read capcity returns the LBA of the last sector.
*/
- len = unit->sectors - 1;
+ len = unit->sectors;
+ if(len > 0)
+ len--;
p = r->data;
*p++ = len>>56;
*p++ = len>>48;
@@ -1050,7 +1099,7 @@
*p++ = len>>16;
*p++ = len>>8;
*p++ = len;
- len = 512;
+ len = unit->secsize;
*p++ = len>>24;
*p++ = len>>16;
*p++ = len>>8;
@@ -1057,12 +1106,12 @@
*p++ = len;
r->rlen = p - (uchar*)r->data;
return sdsetsense(r, SDok, 0, 0, 0);
-
- case 0x5A: /* mode sense */
- return sdmodesense(r, cmd, info, ilen);
-
- case 0x28: /* read */
- case 0x2A: /* write */
+ case 0x08: /* read6 */
+ case 0x0a: /* write6 */
+ case 0x28: /* read10 */
+ case 0x2a: /* write10 */
+ case 0xa8: /* read12 */
+ case 0xaa: /* write12 */
case 0x88: /* read16 */
case 0x8a: /* write16 */
return SDnostatus;
@@ -1069,13 +1118,105 @@
}
}
+int
+sdfakescsirw(SDreq *r, uvlong *llba, int *nsec, int *rwp)
+{
+ uchar *c;
+ int rw, count;
+ uvlong lba;
+
+ c = r->cmd;
+ rw = SDread;
+ if((c[0] & 0xf) == 0xa)
+ rw = SDwrite;
+ switch(c[0]){
+ case 0x08: /* read6 */
+ case 0x0a:
+ lba = (c[1] & 0xf)<<16 | c[2]<<8 | c[3];
+ count = c[4];
+ break;
+ case 0x28: /* read10 */
+ case 0x2a:
+ lba = c[2]<<24 | c[3]<<16 | c[4]<<8 | c[5];
+ count = c[7]<<8 | c[8];
+ break;
+ case 0xa8: /* read12 */
+ case 0xaa:
+ lba = c[2]<<24 | c[3]<<16 | c[4]<<8 | c[5];
+ count = c[6]<<24 | c[7]<<16 | c[8]<<8 | c[9];
+ break;
+ case 0x88: /* read16 */
+ case 0x8a:
+ /* ata commands only go to 48-bit lba */
+ if(c[2] || c[3])
+ return sdsetsense(r, SDcheck, 3, 0xc, 2);
+ lba = (uvlong)c[4]<<40 | (uvlong)c[5]<<32;
+ lba |= c[6]<<24 | c[7]<<16 | c[8]<<8 | c[9];
+ count = c[10]<<24 | c[11]<<16 | c[12]<<8 | c[13];
+ break;
+ default:
+ print("%s: bad cmd 0x%.2ux\n", r->unit->name, c[0]);
+ r->status = sdsetsense(r, SDcheck, 0x05, 0x20, 0);
+ return SDcheck;
+ }
+ if(r->data == nil)
+ return SDok;
+ if(r->dlen < count * r->unit->secsize)
+ count = r->dlen/r->unit->secsize;
+ if(rwp)
+ *rwp = rw;
+ *llba = lba;
+ *nsec = count;
+ return SDnostatus;
+}
+
static long
+extrarw(int write, Chan *c, void *a, long n, vlong off)
+{
+ int i;
+ SDrw *f;
+ SDev *sdev;
+ SDunit *unit;
+
+ sdev = sdgetdev(DEV(c->qid));
+ if(sdev == nil)
+ error(Enonexist);
+ if(waserror()){
+ decref(&sdev->r);
+ nexterror();
+ }
+ unit = sdev->unit[UNIT(c->qid)];
+ if(unit->vers != c->qid.vers)
+ error(Echange);
+ unit = sdev->unit[UNIT(c->qid)];
+ i = PART(c->qid);
+ if(i >= unit->nefile)
+ error(Enonexist);
+ f = unit->efile[i].r;
+ if(write)
+ f = unit->efile[i].w;
+ if(i >= unit->nefile || f == nil)
+ error(Eperm);
+ n = f(unit, c, a, n, off);
+ poperror();
+ decref(&sdev->r);
+ return n;
+}
+
+static char*
+deftopctl(SDev *s, char *p, char *e)
+{
+ return seprint(p, e, "sd%c %s %d units\n", s->idno, s->ifc->name, s->nunit);
+}
+
+static long
sdread(Chan *c, void *a, long n, vlong off)
{
char *p, *e, *buf;
+ SDev *sdev;
SDpart *pp;
+ SDreq *r;
SDunit *unit;
- SDev *sdev;
ulong offset;
int i, l, m, status;
@@ -1093,6 +1234,8 @@
sdev = devs[i];
if(sdev && sdev->ifc->rtopctl)
p = sdev->ifc->rtopctl(sdev, p, e);
+ else if(sdev)
+ p = deftopctl(sdev, p, e);
}
qunlock(&devslock);
n = readstr(off, a, n, buf);
@@ -1157,23 +1300,38 @@
}
if(unit->state == Rawdata){
unit->state = Rawstatus;
- i = sdrio(unit->req, a, n);
+ r = unit->req;
+ r->timeout = 0;
+ i = sdrio(r, a, n);
}
else if(unit->state == Rawstatus){
- status = unit->req->status;
- unit->state = Rawcmd;
- free(unit->req);
+ r = unit->req;
unit->req = nil;
- i = readnum(0, a, n, status, NUMSIZE);
+ unit->state = Rawcmd;
+ status = r->status;
+ if(r->proto == SData){
+ p = a;
+ i = 16 + Ahdrsz;
+ if(n < i)
+ i = n;
+ if(i > 0)
+ p[0] = status;
+ if(i > Ahdrsz)
+ memmove(p + Ahdrsz, r->cmd, i - Ahdrsz);
+ }else
+ i = readnum(0, a, n, status, NUMSIZE);
+ free(r);
} else
i = 0;
+ poperror();
qunlock(&unit->raw);
decref(&sdev->r);
- poperror();
return i;
case Qpart:
return sdbio(c, 0, a, n, off);
+ case Qextra:
+ return extrarw(0, c, a, n, off);
}
}
@@ -1183,7 +1341,8 @@
sdwrite(Chan* c, void* a, long n, vlong off)
{
char *f0;
- int i;
+ int i, atacdb, proto, ataproto;
+ uchar *u;
uvlong end, start;
Cmdbuf *cb;
SDifc *ifc;
@@ -1250,7 +1409,7 @@
error(Ebadctl);
poperror();
poperror();
- if (sdev)
+ if(sdev)
decref(&sdev->r);
free(cb);
break;
@@ -1299,6 +1458,9 @@
break;
case Qraw:
+ proto = SDcdb;
+ ataproto = 0;
+ atacdb = 0;
sdev = sdgetdev(DEV(c->qid));
if(sdev == nil)
error(Enonexist);
@@ -1311,18 +1473,34 @@
}
switch(unit->state){
case Rawcmd:
+ /* sneaky ata commands */
+ u = a;
+ if(n > 1 && *u == 0xff){
+ proto = SData;
+ ataproto = u[1];
+ a = u + 2;
+ atacdb = Ahdrsz;
+ n -= Ahdrsz;
+ }
if(n < 6 || n > sizeof(req->cmd))
error(Ebadarg);
if((req = malloc(sizeof(SDreq))) == nil)
error(Enomem);
req->unit = unit;
+ if(waserror()){
+ free(req);
+ nexterror();
+ }
memmove(req->cmd, a, n);
+ poperror();
req->clen = n;
- req->flags = SDnosense;
+ /* req->flags = SDnosense; */
req->status = ~0;
-
+ req->proto = proto;
+ req->ataproto = ataproto;
unit->req = req;
unit->state = Rawdata;
+ n += atacdb;
break;
case Rawstatus:
@@ -1333,15 +1511,18 @@
case Rawdata:
unit->state = Rawstatus;
- unit->req->write = 1;
- n = sdrio(unit->req, a, n);
+ req = unit->req;
+ req->write = 1;
+ n = sdrio(req, a, n);
}
+ poperror();
qunlock(&unit->raw);
decref(&sdev->r);
- poperror();
break;
case Qpart:
return sdbio(c, 1, a, n, off);
+ case Qextra:
+ return extrarw(1, c, a, n, off);
}
return n;
@@ -1358,17 +1539,23 @@
if(c->qid.type & QTDIR)
error(Eperm);
-
- sdev = sdgetdev(DEV(c->qid));
- if(sdev == nil)
- error(Enonexist);
- unit = sdev->unit[UNIT(c->qid)];
+ if(TYPE(c->qid) == Qtopctl){
+ unit = &topctlunit;
+ sdev = nil;
+ }else{
+ sdev = sdgetdev(DEV(c->qid));
+ if(sdev == nil)
+ error(Enonexist);
+ unit = sdev->unit[UNIT(c->qid)];
+ }
qlock(&unit->ctl);
+
d = nil;
if(waserror()){
free(d);
qunlock(&unit->ctl);
- decref(&sdev->r);
+ if(sdev != nil)
+ decref(&sdev->r);
nexterror();
}
@@ -1375,6 +1562,7 @@
switch(TYPE(c->qid)){
default:
error(Eperm);
+ case Qtopctl:
case Qctl:
perm = &unit->ctlperm;
break;
@@ -1396,14 +1584,22 @@
n = convM2D(dp, n, &d[0], (char*)&d[1]);
if(n == 0)
error(Eshortstat);
+ if(d->atime != ~0 || d->mtime != ~0 || d->length != ~0)
+ error(Eperm);
+ if(!emptystr(d[0].muid) || !emptystr(d[0].name))
+ error(Eperm);
if(!emptystr(d[0].uid))
kstrdup(&perm->user, d[0].uid);
+ if(!emptystr(d[0].gid) && strcmp(d[0].gid, eve) != 0)
+ error(Eperm);
if(d[0].mode != ~0UL)
perm->perm = (perm->perm & ~0777) | (d[0].mode & 0777);
free(d);
+ d = nil; USED(d);
qunlock(&unit->ctl);
- decref(&sdev->r);
+ if(sdev != nil)
+ decref(&sdev->r);
poperror();
return n;
}
@@ -1487,6 +1683,55 @@
return unconfigure(spec);
}
+int
+sdaddfile(SDunit *unit, char *s, int perm, char *u, SDrw *r, SDrw *w)
+{
+ int i;
+ SDfile *e;
+ static Lock lk;
+
+ if(unit == nil)
+ return -1;
+ lock(&lk);
+ for(i = 0; i < unit->nefile; i++)
+ if(strcmp(unit->efile[i].name, s) == 0)
+ break;
+ if(i >= nelem(unit->efile)){
+ unlock(&lk);
+ return -1;
+ }
+ if(i >= unit->nefile)
+ unit->nefile = i + 1;
+ e = unit->efile + i;
+ if(e->name == nil)
+ kstrdup(&e->name, s);
+ if(e->user == nil)
+ kstrdup(&e->user, u);
+ e->perm = perm;
+ e->r = r;
+ e->w = w;
+ unlock(&lk);
+ return 0;
+}
+
+static void
+sdshutdown(void)
+{
+ int i;
+ SDev *sd;
+
+ for(i = 0; i < nelem(devs); i++){
+ sd = devs[i];
+ if(sd == nil)
+ continue;
+ if(sd->ifc->disable == nil){
+ print("#S/sd%c: no disable function\n", devletters[i]);
+ continue;
+ }
+ sd->ifc->disable(sd);
+ }
+}
+
Dev sddevtab = {
'S',
"sd",
@@ -1493,7 +1738,7 @@
sdreset,
devinit,
- devshutdown,
+ sdshutdown,
sdattach,
sdwalk,
sdstat,
@@ -1544,7 +1789,7 @@
{
Devport *p;
- p = (Devport *)malloc((dc->nports + 1) * sizeof(Devport));
+ p = malloc((dc->nports + 1) * sizeof(Devport));
if(dc->nports > 0){
memmove(p, dc->ports, dc->nports * sizeof(Devport));
free(dc->ports);
@@ -1635,7 +1880,6 @@
if(j == nelem(options))
error(Ebadarg);
}
- /* this has been rewritten to accomodate sdaoe */
if(cd.on < 0 || cd.spec == 0)
error(Ebadarg);
if(cd.on && cd.cf.type == nil)
--- /dev/null
+++ b/sys/src/9/port/led.c
@@ -1,0 +1,62 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "../port/error.h"
+#include "fns.h"
+#include "led.h"
+
+static char *ibpinames[Ibpilast] = {
+[Ibpinone] "none",
+[Ibpinormal] "normal",
+[Ibpilocate] "locate",
+[Ibpifail] "fail",
+[Ibpirebuild] "rebuild",
+[Ibpipfa] "pfa",
+[Ibpispare] "spare",
+[Ibpicritarray] "critarray",
+[Ibpifailarray] "failarray",
+};
+
+char*
+ledname(int c)
+{
+ if(c >= 0 && c < Ibpilast)
+ return ibpinames[c];
+ return "bad index";
+}
+
+ int
+name2led(char *s)
+{
+ int i;
+
+ for(i = 0; i < nelem(ibpinames); i++)
+ if(strcmp(ibpinames[i], s) == 0)
+ return i;
+ return -1;
+}
+
+long
+ledr(Ledport *p, Chan*, void *a, long n, vlong off)
+{
+ char buf[64];
+
+ snprint(buf, sizeof buf, "%s\n", ledname(p->led));
+ return readstr(off, a, n, buf);
+}
+
+long
+ledw(Ledport *p, Chan*, void *a, long n, vlong)
+{
+ int i;
+ Cmdbuf *cb;
+
+ cb = parsecmd(a, n);
+ i = name2led(cb->f[0]);
+ free(cb);
+ if(i == -1)
+ error(Ebadarg);
+ p->led = i;
+ return n;
+}
--- /dev/null
+++ b/sys/src/9/port/led.h
@@ -1,0 +1,26 @@
+typedef struct Ledport Ledport;
+
+struct Ledport {
+ uchar nled;
+ uchar led;
+ ushort ledbits; /* implementation dependent */
+};
+
+/* http://en.wikipedia.org/wiki/IBPI */
+enum {
+ Ibpinone,
+ Ibpinormal,
+ Ibpilocate,
+ Ibpifail,
+ Ibpirebuild,
+ Ibpipfa,
+ Ibpispare,
+ Ibpicritarray,
+ Ibpifailarray,
+ Ibpilast,
+};
+
+char *ledname(int);
+int name2led(char*);
+long ledr(Ledport*, Chan*, void*, long, vlong);
+long ledw(Ledport*, Chan*, void*, long, vlong);
--- a/sys/src/9/port/lib.h
+++ b/sys/src/9/port/lib.h
@@ -85,18 +85,24 @@
#pragma varargck argpos snprint 3
#pragma varargck argpos sprint 2
+#pragma varargck type "llb" vlong
#pragma varargck type "lld" vlong
#pragma varargck type "llx" vlong
+#pragma varargck type "llb" uvlong
#pragma varargck type "lld" uvlong
#pragma varargck type "llx" uvlong
+#pragma varargck type "lb" long
#pragma varargck type "ld" long
#pragma varargck type "lx" long
+#pragma varargck type "lb" ulong
#pragma varargck type "ld" ulong
#pragma varargck type "lx" ulong
+#pragma varargck type "b" int
#pragma varargck type "d" int
#pragma varargck type "x" int
#pragma varargck type "c" int
#pragma varargck type "C" int
+#pragma varargck type "b" uint
#pragma varargck type "d" uint
#pragma varargck type "x" uint
#pragma varargck type "c" uint
--- a/sys/src/9/port/sd.h
+++ b/sys/src/9/port/sd.h
@@ -2,6 +2,7 @@
* Storage Device.
*/
typedef struct SDev SDev;
+typedef struct SDfile SDfile;
typedef struct SDifc SDifc;
typedef struct SDpart SDpart;
typedef struct SDperm SDperm;
@@ -22,11 +23,20 @@
ulong vers;
};
+typedef long SDrw(SDunit*, Chan*, void*, long, vlong);
+struct SDfile {
+ SDperm;
+ SDrw *r;
+ SDrw *w;
+};
+
struct SDunit {
SDev* dev;
int subno;
uchar inquiry[255]; /* format follows SCSI spec */
uchar sense[18]; /* format follows SCSI spec */
+ uchar rsense[18]; /* support seperate rq sense and inline return */
+ uchar haversense;
SDperm;
QLock ctl;
@@ -42,6 +52,8 @@
int state;
SDreq* req;
SDperm rawperm;
+ SDfile efile[5];
+ int nefile;
};
/*
@@ -67,7 +79,7 @@
char* name;
SDev* (*pnp)(void);
- SDev* (*legacy)(int, int);
+ SDev* (*xxlegacy)(int, int); /* unused. remove me */
int (*enable)(SDev*);
int (*disable)(SDev*);
@@ -82,22 +94,26 @@
void (*clear)(SDev*);
char* (*rtopctl)(SDev*, char*, char*);
int (*wtopctl)(SDev*, Cmdbuf*);
+ int (*ataio)(SDreq*);
};
struct SDreq {
SDunit* unit;
int lun;
- int write;
- uchar cmd[16];
+ char write;
+ char proto;
+ char ataproto;
+ uchar cmd[0x20];
int clen;
void* data;
int dlen;
int flags;
+ ulong timeout; /* in ticks */
int status;
long rlen;
- uchar sense[256];
+ uchar sense[32];
};
enum {
@@ -119,6 +135,12 @@
SDmaxio = 2048*1024,
SDnpart = 16,
+
+ SDread = 0,
+ SDwrite,
+
+ SData = 1,
+ SDcdb = 2,
};
#define sdmalloc(n) malloc(n)
@@ -127,11 +149,11 @@
/* devsd.c */
extern void sdadddevs(SDev*);
extern int sdsetsense(SDreq*, int, int, int, int);
-extern int sdmodesense(SDreq*, uchar*, void*, int);
-extern int sdfakescsi(SDreq*, void*, int);
+extern int sdfakescsi(SDreq*);
+extern int sdfakescsirw(SDreq*, uvlong*, int*, int*);
+extern int sdaddfile(SDunit*, char*, int, char*, SDrw*, SDrw*);
/* sdscsi.c */
extern int scsiverify(SDunit*);
extern int scsionline(SDunit*);
extern long scsibio(SDunit*, int, int, void*, long, uvlong);
-extern SDev* scsiid(SDev*, SDifc*);
--- a/sys/src/9/port/sdaoe.c
+++ b/sys/src/9/port/sdaoe.c
@@ -1,5 +1,5 @@
/*
- * aoe sd driver, copyright © 2007 coraid
+ * aoe sd driver, copyright © 2007-9 coraid
*/
#include "u.h"
@@ -12,6 +12,7 @@
#include "../port/sd.h"
#include "../port/netif.h"
#include "../port/aoe.h"
+#include <fis.h>
extern char Echange[];
extern char Enotup[];
@@ -19,34 +20,14 @@
#define uprint(...) snprint(up->genbuf, sizeof up->genbuf, __VA_ARGS__);
enum {
- Nctlr = 32,
- Maxpath = 128,
+ Maxpath = 128,
Probeintvl = 100, /* ms. between probes */
- Probemax = 20, /* max probes */
+ Probemax = 10*1000, /* max ms. to wait */
};
-enum {
- /* sync with ahci.h */
- Dllba = 1<<0,
- Dsmart = 1<<1,
- Dpower = 1<<2,
- Dnop = 1<<3,
- Datapi = 1<<4,
- Datapi16= 1<<5,
-};
-
-static char *flagname[] = {
- "llba",
- "smart",
- "power",
- "nop",
- "atapi",
- "atapi16",
-};
-
typedef struct Ctlr Ctlr;
-struct Ctlr{
+struct Ctlr {
QLock;
Ctlr *next;
@@ -56,11 +37,8 @@
Chan *c;
ulong vers;
- uchar mediachange;
- uchar flag;
- uchar smart;
- uchar smartrs;
- uchar feat;
+ uchar drivechange;
+ Sfis;
uvlong sectors;
char serial[20+1];
@@ -69,8 +47,6 @@
char ident[0x100];
};
-void aoeidmove(char *p, ushort *a, unsigned n);
-
static Lock ctlrlock;
static Ctlr *head;
static Ctlr *tail;
@@ -77,80 +53,54 @@
SDifc sdaoeifc;
-static ushort
-gbit16(void *a)
-{
- uchar *i;
-
- i = a;
- return i[1] << 8 | i[0];
-}
-
-static ulong
-gbit32(void *a)
-{
- ulong j;
- uchar *i;
-
- i = a;
- j = i[3] << 24;
- j |= i[2] << 16;
- j |= i[1] << 8;
- j |= i[0];
- return j;
-}
-
-static uvlong
-gbit64(void *a)
-{
- uchar *i;
-
- i = a;
- return (uvlong)gbit32(i+4)<<32 | gbit32(i);
-}
-
static int
identify(Ctlr *c, ushort *id)
{
- int i;
uchar oserial[21];
- uvlong osectors, s;
+ vlong osectors, s;
osectors = c->sectors;
memmove(oserial, c->serial, sizeof c->serial);
-
- c->feat &= ~(Dllba|Dpower|Dsmart|Dnop);
- i = gbit16(id+83) | gbit16(id+86);
- if(i & (1<<10)){
- c->feat |= Dllba;
- s = gbit64(id+100);
- }else
- s = gbit32(id+60);
-
- i = gbit16(id+83);
- if((i>>14) == 1) {
- if(i & (1<<3))
- c->feat |= Dpower;
- i = gbit16(id+82);
- if(i & 1)
- c->feat |= Dsmart;
- if(i & (1<<14))
- c->feat |= Dnop;
+ s = idfeat(c, id);
+ if(s == -1){
+ uprint("%s: identify fails", c->unit->name);
+ print("%s\n", up->genbuf);
+ error(up->genbuf);
}
+ idmove(c->serial, id+10, 20);
+ idmove(c->firmware, id+23, 8);
+ idmove(c->model, id+27, 40);
- aoeidmove(c->serial, id+10, 20);
- aoeidmove(c->firmware, id+23, 8);
- aoeidmove(c->model, id+27, 40);
-
if((osectors == 0 || osectors != s) &&
memcmp(oserial, c->serial, sizeof oserial) != 0){
c->sectors = s;
- c->mediachange = 1;
+ c->drivechange = 1;
c->vers++;
}
return 0;
}
+static void
+aoectl(Ctlr *d, char *s)
+{
+ Chan *c;
+
+ c = nil;
+ if(waserror()){
+ if(c)
+ cclose(c);
+ print("sdaoectl: %s\n", up->errstr);
+ nexterror();
+ }
+
+ uprint("%s/ctl", d->path);
+ c = namec(up->genbuf, Aopen, OWRITE, 0);
+ devtab[c->type]->write(c, s, strlen(s), 0);
+
+ poperror();
+ cclose(c);
+}
+
/* must call with d qlocked */
static int
aoeidentify(Ctlr *d, SDunit *u)
@@ -173,7 +123,6 @@
cclose(c);
d->feat = 0;
- d->smart = 0;
identify(d, (ushort*)d->ident);
memset(u->inquiry, 0, sizeof u->inquiry);
@@ -203,7 +152,6 @@
{
Ctlr *c;
- /* race? */
if(ctlrlookup(path))
error(Eexist);
@@ -248,7 +196,6 @@
free(x);
}
-/* don't call aoeprobe from within a loop; it loops internally retrying open. */
static SDev*
aoeprobe(char *path, SDev *s)
{
@@ -273,21 +220,24 @@
poperror();
cclose(c);
- for(i = 0; i < Probemax; i++){
+ for(i = 0;; i += Probeintvl){
+ if(i > Probemax || waserror())
+ error(Etimedout);
tsleep(&up->sleep, return0, 0, Probeintvl);
+ poperror();
+
uprint("%s/ident", path);
- if(!waserror()) {
- c = namec(up->genbuf, Aopen, OREAD, 0);
- poperror();
- cclose(c);
- break;
- }
+ if(waserror())
+ continue;
+ c = namec(up->genbuf, Aopen, OREAD, 0);
+ poperror();
+ cclose(c);
+
+ ctlr = newctlr(path);
+ break;
}
- if(i >= Probemax)
- error(Etimedout);
- uprint("%s/ident", path);
- ctlr = newctlr(path);
- if(ctlr == nil || s == nil && (s = malloc(sizeof *s)) == nil)
+
+ if(s == nil && (s = malloc(sizeof *s)) == nil)
return nil;
s->ctlr = ctlr;
s->ifc = &sdaoeifc;
@@ -296,14 +246,20 @@
}
static char *probef[32];
+static char *probebuf;
static int nprobe;
static int
pnpprobeid(char *s)
{
+ int id;
+
if(strlen(s) < 2)
return 0;
- return s[1] == '!'? s[0]: 'e';
+ id = 'e';
+ if(s[1] == '!')
+ id = s[0];
+ return id;
}
static SDev*
@@ -315,7 +271,8 @@
if((p = getconf("aoedev")) == 0)
return 0;
- nprobe = tokenize(p, probef, nelem(probef));
+ kstrdup(&probebuf, p);
+ nprobe = tokenize(probebuf, probef, nelem(probef));
h = t = 0;
for(i = 0; i < nprobe; i++){
id = pnpprobeid(probef[i]);
@@ -341,7 +298,7 @@
static Ctlr*
pnpprobe(SDev *sd)
{
- ulong start;
+ int j;
char *p;
static int i;
@@ -353,17 +310,21 @@
if(p[1] == '!')
p += 2;
- start = TK2MS(MACHP(0)->ticks);
- if(waserror()){
- print("#æ: pnpprobe failed in %lud ms: %s: %s\n",
- TK2MS(MACHP(0)->ticks) - start, probef[i-1],
- up->errstr);
- return nil;
+ for(j = 0;; j += Probeintvl){
+ if(j > Probemax){
+ print("#æ: pnpprobe: %s: %s\n", probef[i-1], up->errstr);
+ return 0;
+ }
+ if(waserror()){
+ tsleep(&up->sleep, return0, 0, Probeintvl);
+ continue;
+ }
+ sd = aoeprobe(p, sd);
+ poperror();
+ break;
}
- sd = aoeprobe(p, sd); /* does a round of probing */
- poperror();
- print("#æ: pnpprobe established %s in %lud ms\n",
- probef[i-1], TK2MS(MACHP(0)->ticks) - start);
+ print("#æ: pnpprobe establishes %s in %dms\n", probef[i-1], j);
+ aoectl(sd->ctlr, "nofail on");
return sd->ctlr;
}
@@ -378,7 +339,7 @@
c = s->ctlr;
if(c == nil && (s->ctlr = c = pnpprobe(s)) == nil)
return 0;
- c->mediachange = 1;
+ c->drivechange = 1;
return 1;
}
@@ -412,17 +373,17 @@
c = u->dev->ctlr;
r = 0;
- if((c->feat&Datapi) && c->mediachange){
+ if((c->feat&Datapi) && c->drivechange){
if(aoeconnect(u, c) == 0 && (r = scsionline(u)) > 0)
- c->mediachange = 0;
+ c->drivechange = 0;
return r;
}
- if(c->mediachange){
+ if(c->drivechange){
if(aoeconnect(u, c) == -1)
return 0;
r = 2;
- c->mediachange = 0;
+ c->drivechange = 0;
u->sectors = c->sectors;
u->secsize = Aoesectsz;
} else
@@ -431,103 +392,70 @@
return r;
}
-static int
-aoerio(SDreq *r)
+static long
+aoebio(SDunit *u, int, int write, void *a, long count, uvlong lba)
{
- int i, count;
- uvlong lba;
- char *name;
- uchar *cmd;
+ uchar *data;
+ int n;
long (*rio)(Chan*, void*, long, vlong);
Ctlr *c;
- SDunit *unit;
- unit = r->unit;
- c = unit->dev->ctlr;
+ c = u->dev->ctlr;
// if(c->feat & Datapi)
-// return aoeriopkt(r, d);
-
- cmd = r->cmd;
- name = unit->name;
-
- if(r->cmd[0] == 0x35 || r->cmd[0] == 0x91){
-// qlock(c);
-// i = flushcache();
-// qunlock(c);
-// if(i == 0)
-// return sdsetsense(r, SDok, 0, 0, 0);
- return sdsetsense(r, SDcheck, 3, 0xc, 2);
- }
-
- if((i = sdfakescsi(r, c->ident, sizeof c->ident)) != SDnostatus){
- r->status = i;
- return i;
- }
-
- switch(*cmd){
- case 0x88:
- case 0x28:
- rio = devtab[c->c->type]->read;
- break;
- case 0x8a:
- case 0x2a:
+// return scsibio(u, lun, write, a, count, lba);
+ data = a;
+ if(write)
rio = devtab[c->c->type]->write;
- break;
- default:
- print("%s: bad cmd %#.2ux\n", name, cmd[0]);
- r->status = SDcheck;
- return SDcheck;
- }
+ else
+ rio = devtab[c->c->type]->read;
- if(r->data == nil)
- return SDok;
-
- if(r->clen == 16){
- if(cmd[2] || cmd[3])
- return sdsetsense(r, SDcheck, 3, 0xc, 2);
- lba = (uvlong)cmd[4]<<40 | (uvlong)cmd[5]<<32;
- lba |= cmd[6]<<24 | cmd[7]<<16 | cmd[8]<<8 | cmd[9];
- count = cmd[10]<<24 | cmd[11]<<16 | cmd[12]<<8 | cmd[13];
- }else{
- lba = cmd[2]<<24 | cmd[3]<<16 | cmd[4]<<8 | cmd[5];
- count = cmd[7]<<8 | cmd[8];
- }
-
- count *= Aoesectsz;
-
- if(r->dlen < count)
- count = r->dlen & ~0x1ff;
-
if(waserror()){
if(strcmp(up->errstr, Echange) == 0 ||
strcmp(up->errstr, Enotup) == 0)
- unit->sectors = 0;
+ u->sectors = 0;
nexterror();
}
- r->rlen = rio(c->c, r->data, count, Aoesectsz * lba);
+ n = rio(c->c, data, Aoesectsz * count, Aoesectsz * lba);
poperror();
- r->status = SDok;
- return SDok;
+ return n;
}
-static char *smarttab[] = {
- "unset",
- "error",
- "threshold exceeded",
- "normal"
-};
+static int
+flushcache(Ctlr *)
+{
+ return -1;
+}
-static char *
-pflag(char *s, char *e, uchar f)
+static int
+aoerio(SDreq *r)
{
- uchar i, m;
+ int i, count, rw;
+ uvlong lba;
+ Ctlr *c;
+ SDunit *u;
- for(i = 0; i < 8; i++){
- m = 1 << i;
- if(f & m)
- s = seprint(s, e, "%s ", flagname[i]);
+ u = r->unit;
+ c = u->dev->ctlr;
+// if(c->feat & Datapi)
+// return aoeriopkt(r, d);
+
+ if(r->cmd[0] == 0x35 || r->cmd[0] == 0x91){
+ qlock(c);
+ i = flushcache(c);
+ qunlock(c);
+ if(i == 0)
+ return sdsetsense(r, SDok, 0, 0, 0);
+ return sdsetsense(r, SDcheck, 3, 0xc, 2);
}
- return seprint(s, e, "\n");
+
+ if((i = sdfakescsi(r)) != SDnostatus){
+ r->status = i;
+ return i;
+ }
+ if((i = sdfakescsirw(r, &lba, &count, &rw)) != SDnostatus)
+ return i;
+ r->rlen = aoebio(u, r->lun, rw == SDwrite, r->data, count, lba);
+ return r->status = SDok;
}
static int
@@ -544,14 +472,8 @@
p = seprint(p, e, "model\t%s\n", c->model);
p = seprint(p, e, "serial\t%s\n", c->serial);
p = seprint(p, e, "firm %s\n", c->firmware);
- if(c->smartrs == 0xff)
- p = seprint(p, e, "smart\tenable error\n");
- else if(c->smartrs == 0)
- p = seprint(p, e, "smart\tdisabled\n");
- else
- p = seprint(p, e, "smart\t%s\n", smarttab[c->smart]);
p = seprint(p, e, "flag ");
- p = pflag(p, e, c->feat);
+ p = pflag(p, e, c);
p = seprint(p, e, "geometry %llud %d\n", c->sectors, Aoesectsz);
return p-op;
}
@@ -590,7 +512,7 @@
Ctlr *c;
c = s->ctlr;
- return seprint(p, e, "%s aoe %s\n", s->name, c->path);
+ return seprint(p, e, "%s aoe %s\n", s->name, c? c->path: "");
}
static int
@@ -617,7 +539,7 @@
aoerctl,
aoewctl,
- scsibio,
+ aoebio,
aoeprobew, /* probe */
aoeclear, /* clear */
aoertopctl,
--- /dev/null
+++ b/sys/src/9/port/sdloop.c
@@ -1,0 +1,415 @@
+/*
+ * sd loopback driver,
+ * copyright © 2009-10 erik quanstrom
+ */
+
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+#include "../port/sd.h"
+#include "../port/netif.h"
+
+extern char Echange[];
+extern char Enotup[];
+
+#define uprint(...) snprint(up->genbuf, sizeof up->genbuf, __VA_ARGS__);
+
+enum {
+ Maxpath = 256,
+ Devsectsize = 512,
+};
+
+typedef struct Ctlr Ctlr;
+struct Ctlr {
+ QLock;
+
+ Ctlr *next;
+ SDunit *unit;
+
+ char path[Maxpath];
+ Chan *c;
+
+ uint vers;
+ uchar drivechange;
+
+ uvlong sectors;
+ uint sectsize;
+};
+
+static Lock ctlrlock;
+static Ctlr *head;
+static Ctlr *tail;
+
+SDifc sdloopifc;
+
+/* must call with c qlocked */
+static void
+identify(Ctlr *c, SDunit *u)
+{
+ int n;
+ uvlong s, osectors;
+ uchar buf[sizeof(Dir) + 100];
+ Dir dir;
+
+ if(waserror()){
+ iprint("sdloop: identify: %s\n", up->errstr);
+ nexterror();
+ }
+ osectors = c->sectors;
+ n = devtab[c->c->type]->stat(c->c, buf, sizeof buf);
+ if(convM2D(buf, n, &dir, nil) == 0)
+ error("internal error: stat error in seek");
+ s = dir.length / c->sectsize;
+ poperror();
+
+ memset(u->inquiry, 0, sizeof u->inquiry);
+ u->inquiry[2] = 2;
+ u->inquiry[3] = 2;
+ u->inquiry[4] = sizeof u->inquiry - 4;
+ memmove(u->inquiry+8, c->path, 40);
+
+ if(osectors == 0 || osectors != s){
+ c->sectors = s;
+ c->drivechange = 1;
+ c->vers++;
+ }
+}
+
+static Ctlr*
+ctlrlookup(char *path)
+{
+ Ctlr *c;
+
+ lock(&ctlrlock);
+ for(c = head; c; c = c->next)
+ if(strcmp(c->path, path) == 0)
+ break;
+ unlock(&ctlrlock);
+ return c;
+}
+
+static Ctlr*
+newctlr(char *path)
+{
+ Ctlr *c;
+
+ if(ctlrlookup(path))
+ error(Eexist);
+ if((c = malloc(sizeof *c)) == nil)
+ error(Enomem);
+ if(waserror()){
+ free(c);
+ nexterror();
+ }
+ c->c = namec(path, Aopen, ORDWR, 0);
+ poperror();
+ kstrcpy(c->path, path, sizeof c->path);
+ lock(&ctlrlock);
+ if(head != nil)
+ tail->next = c;
+ else
+ head = c;
+ tail = c;
+ unlock(&ctlrlock);
+ return c;
+}
+
+static void
+delctlr(Ctlr *c)
+{
+ Ctlr *x, *prev;
+
+ lock(&ctlrlock);
+
+ for(prev = 0, x = head; x; prev = x, x = c->next)
+ if(strcmp(c->path, x->path) == 0)
+ break;
+ if(x == 0){
+ unlock(&ctlrlock);
+ error(Enonexist);
+ }
+
+ if(prev)
+ prev->next = x->next;
+ else
+ head = x->next;
+ if(x->next == nil)
+ tail = prev;
+ unlock(&ctlrlock);
+
+ if(x->c)
+ cclose(x->c);
+ free(x);
+}
+
+static SDev*
+probe(char *path, SDev *s)
+{
+ char *p;
+ uint sectsize;
+ Ctlr *c;
+
+ sectsize = 0;
+ if(p = strchr(path, '!')){
+ *p = 0;
+ sectsize = strtoul(p + 1, 0, 0);
+ }
+ c = newctlr(path);
+ c->sectsize = sectsize? sectsize: Devsectsize;
+ if(s == nil && (s = malloc(sizeof *s)) == nil)
+ return nil;
+ s->ctlr = c;
+ s->ifc = &sdloopifc;
+ s->nunit = 1;
+ return s;
+}
+
+static char *probef[32];
+static int nprobe;
+
+static int
+pnpprobeid(char *s)
+{
+ int id;
+
+ if(strlen(s) < 2)
+ return 0;
+ id = 'l';
+ if(s[1] == '!')
+ id = s[0];
+ return id;
+}
+
+static SDev*
+pnp(void)
+{
+ int i, id;
+ char *p;
+ SDev *h, *t, *s;
+
+ if((p = getconf("loopdev")) == 0)
+ return 0;
+ nprobe = tokenize(p, probef, nelem(probef));
+ h = t = 0;
+ for(i = 0; i < nprobe; i++){
+ id = pnpprobeid(probef[i]);
+ if(id == 0)
+ continue;
+ s = malloc(sizeof *s);
+ if(s == nil)
+ break;
+ s->ctlr = 0;
+ s->idno = id;
+ s->ifc = &sdloopifc;
+ s->nunit = 1;
+
+ if(h)
+ t->next = s;
+ else
+ h = s;
+ t = s;
+ }
+ return h;
+}
+
+static Ctlr*
+pnpprobe(SDev *s)
+{
+ char *p;
+ static int i;
+
+ if(i > nprobe)
+ return 0;
+ p = probef[i++];
+ if(strlen(p) < 2)
+ return 0;
+ if(p[1] == '!')
+ p += 2;
+ s = probe(p, s);
+ return s->ctlr;
+}
+
+
+static int
+loopverify(SDunit *u)
+{
+ SDev *s;
+ Ctlr *c;
+
+ s = u->dev;
+ c = s->ctlr;
+ if(c == nil){
+ if(waserror())
+ return 0;
+ s->ctlr = c = pnpprobe(s);
+ poperror();
+ }
+ c->drivechange = 1;
+ return 1;
+}
+
+static int
+connect(SDunit *u, Ctlr *c)
+{
+ qlock(c);
+ if(waserror()){
+ qunlock(c);
+ return -1;
+ }
+ identify(u->dev->ctlr, u);
+ qunlock(c);
+ poperror();
+ return 0;
+}
+
+static int
+looponline(SDunit *u)
+{
+ Ctlr *c;
+ int r;
+
+ c = u->dev->ctlr;
+ if(c->drivechange){
+ if(connect(u, c) == -1)
+ return 0;
+ r = 2;
+ c->drivechange = 0;
+ u->sectors = c->sectors;
+ u->secsize = c->sectsize;
+ } else
+ r = 1;
+ return r;
+}
+
+static long
+loopbio(SDunit *u, int, int write, void *a, long count, uvlong lba)
+{
+ uchar *data;
+ int n;
+ long (*rio)(Chan*, void*, long, vlong);
+ Ctlr *c;
+
+ c = u->dev->ctlr;
+ data = a;
+ if(write)
+ rio = devtab[c->c->type]->write;
+ else
+ rio = devtab[c->c->type]->read;
+
+ if(waserror()){
+ if(strcmp(up->errstr, Echange) == 0 ||
+ strcmp(up->errstr, Enotup) == 0)
+ u->sectors = 0;
+ nexterror();
+ }
+ n = rio(c->c, data, c->sectsize * count, c->sectsize * lba);
+ poperror();
+ return n;
+}
+
+static int
+looprio(SDreq *r)
+{
+ int i, count, rw;
+ uvlong lba;
+ SDunit *u;
+
+ u = r->unit;
+
+ if(r->cmd[0] == 0x35 || r->cmd[0] == 0x91)
+ return sdsetsense(r, SDok, 0, 0, 0);
+
+ if((i = sdfakescsi(r)) != SDnostatus)
+ return r->status = i;
+ if((i = sdfakescsirw(r, &lba, &count, &rw)) != SDnostatus)
+ return i;
+ r->rlen = loopbio(u, r->lun, rw == SDwrite, r->data, count, lba);
+ return r->status = SDok;
+}
+
+static int
+looprctl(SDunit *u, char *p, int l)
+{
+ Ctlr *c;
+ char *e, *op;
+
+ if((c = u->dev->ctlr) == nil)
+ return 0;
+ e = p+l;
+ op = p;
+
+ p = seprint(p, e, "path\t%s\n", c->path);
+ p = seprint(p, e, "geometry %llud %d\n", c->sectors, c->sectsize);
+ return p - op;
+}
+
+static int
+loopwctl(SDunit *, Cmdbuf *cmd)
+{
+ cmderror(cmd, Ebadarg);
+ return 0;
+}
+
+static SDev*
+loopprobew(DevConf *c)
+{
+ char *p;
+
+ p = strchr(c->type, '/');
+ if(p == nil || strlen(p) > Maxpath - 1)
+ error(Ebadarg);
+ p++;
+ if(ctlrlookup(p))
+ error(Einuse);
+ return probe(p, 0);
+}
+
+static void
+loopclear(SDev *s)
+{
+ delctlr((Ctlr *)s->ctlr);
+}
+
+static char*
+looprtopctl(SDev *s, char *p, char *e)
+{
+ Ctlr *c;
+
+ c = s->ctlr;
+ return seprint(p, e, "%s loop %s\n", s->name, c? c->path: "");
+}
+
+static int
+loopwtopctl(SDev *, Cmdbuf *cmd)
+{
+ switch(cmd->nf){
+ default:
+ cmderror(cmd, Ebadarg);
+ }
+ return 0;
+}
+
+SDifc sdloopifc = {
+ "loop",
+
+ pnp,
+ nil, /* legacy */
+ nil, /* enable */
+ nil, /* disable */
+
+ loopverify,
+ looponline,
+ looprio,
+ looprctl,
+ loopwctl,
+
+ loopbio,
+ loopprobew, /* probe */
+ loopclear, /* clear */
+ looprtopctl,
+ loopwtopctl,
+};
--- a/sys/src/9/port/sdscsi.c
+++ b/sys/src/9/port/sdscsi.c
@@ -153,7 +153,8 @@
/*
* If no medium present, bail out.
* If unit is becoming ready, rather than not
- * not ready, wait a little then poke it again. */
+ * not ready, wait a little then poke it again.
+ */
if(r->sense[12] == 0x3A)
break;
if(r->sense[12] != 0x04 || r->sense[13] != 0x01)
@@ -175,6 +176,74 @@
return -1;
}
+static void
+cap10(SDreq *r)
+{
+ r->cmd[0] = 0x25;
+ r->cmd[1] = r->lun<<5;
+ r->clen = 10;
+ r->dlen = 8;
+}
+
+static void
+cap16(SDreq *r)
+{
+ uint i;
+
+ i = 32;
+ r->cmd[0] = 0x9e;
+ r->cmd[1] = 0x10;
+ r->cmd[10] = i>>24;
+ r->cmd[11] = i>>16;
+ r->cmd[12] = i>>8;
+ r->cmd[13] = i;
+ r->clen = 16;
+ r->dlen = i;
+}
+
+static uint
+belong(uchar *u)
+{
+ return u[0]<<24 | u[1]<<16 | u[2]<<8 | u[3];
+}
+
+static uvlong
+capreply(SDreq *r, ulong *secsize)
+{
+ uchar *u;
+ ulong ss;
+ uvlong s;
+
+ *secsize = 0;
+ u = r->data;
+ if(r->clen == 16){
+ s = (uvlong)belong(u)<<32 | belong(u + 4);
+ ss = belong(u + 8);
+ }else{
+ s = belong(u);
+ ss = belong(u + 4);
+ }
+ /*
+ * Some ATAPI CD readers lie about the block size.
+ * Since we don't read audio via this interface
+ * it's okay to always fudge this.
+ */
+ if(ss == 2352)
+ ss = 2048;
+ /*
+ * Devices with removable media may return 0 sectors
+ * when they have empty media (e.g. sata dvd writers);
+ * if so, keep the count zero.
+ *
+ * Read-capacity returns the LBA of the last sector,
+ * therefore the number of sectors must be incremented.
+ */
+ if(s != 0)
+ s++;
+ *secsize = ss;
+ return s;
+}
+
int
scsionline(SDunit* unit)
{
@@ -181,16 +250,17 @@
SDreq *r;
uchar *p;
int ok, retries;
+ void (*cap)(SDreq*);
- if((r = malloc(sizeof(SDreq))) == nil)
+ if((r = malloc(sizeof *r)) == nil)
return 0;
- if((p = sdmalloc(8)) == nil){
+ if((p = sdmalloc(32)) == nil){
free(r);
return 0;
}
ok = 0;
-
+ cap = cap10;
r->unit = unit;
r->lun = 0; /* ??? */
for(retries = 0; retries < 10; retries++){
@@ -201,13 +271,10 @@
* 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;
+ memset(r->cmd, 0, sizeof r->cmd);
+ cap(r);
r->status = ~0;
switch(scsirio(r)){
@@ -214,26 +281,11 @@
default:
break;
case 0:
- unit->sectors = (p[0]<<24)|(p[1]<<16)|(p[2]<<8)|p[3];
- unit->secsize = (p[4]<<24)|(p[5]<<16)|(p[6]<<8)|p[7];
-
- /*
- * Some ATAPI CD readers lie about the block size.
- * Since we don't read audio via this interface
- * it's okay to always fudge this.
- */
- if(unit->secsize == 2352)
- unit->secsize = 2048;
- /*
- * Devices with removable media may return 0 sectors
- * when they have empty media (e.g. sata dvd writers);
- * if so, keep the count zero.
- *
- * Read-capacity returns the LBA of the last sector,
- * therefore the number of sectors must be incremented.
- */
- if(unit->sectors != 0)
- unit->sectors++;
+ unit->sectors = capreply(r, &unit->secsize);
+ if(unit->sectors == 0xffffffff && cap == cap10){
+ cap = cap16;
+ continue;
+ }
ok = 1;
break;
case 1:
@@ -253,56 +305,6 @@
return 0;
}
-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;
-}
-
static void
scsifmt10(SDreq *r, int write, int lun, ulong nb, uvlong bno)
{
@@ -367,7 +369,7 @@
r->lun = lun;
again:
r->write = write;
- if(bno >= (1ULL<<32))
+ if(bno > 0xffffffff)
scsifmt16(r, write, lun, nb, bno);
else
scsifmt10(r, write, lun, nb, bno);
@@ -381,8 +383,19 @@
rlen = -1;
break;
case 0:
- rlen = r->rlen;
- break;
+ /*
+ * scsi allows commands to return successfully
+ * but return sense data, indicating that the
+ * operation didn't proceed as expected.
+ * (confusing, no). this allows the raw commands
+ * to successfully return errors. but any sense
+ * data bio sees must be an error. bomb out.
+ */
+ if(r->status == SDok && r->rlen > 0
+ && ((r->flags & SDvalidsense) == 0 || r->sense[2] == 0)){
+ rlen = r->rlen;
+ break;
+ }
case 2:
rlen = -1;
if(!(r->flags & SDvalidsense))
@@ -415,6 +428,10 @@
goto again;
break;
}
+ snprint(up->genbuf, sizeof up->genbuf, "%s %.2ux%.2ux%.2ux %lld",
+ Eio, r->sense[2], r->sense[12], r->sense[13], bno);
+ free(r);
+ error(up->genbuf);
break;
}
free(r);
--- /dev/null
+++ b/sys/src/libfis/fis.c
@@ -1,0 +1,545 @@
+/*
+ * sata fises and sas frames
+ * copyright © 2009-2010 erik quanstrom
+ */
+#include <u.h>
+#include <libc.h>
+#include <fis.h>
+
+static char *flagname[9] = {
+ "lba",
+ "llba",
+ "smart",
+ "power",
+ "nop",
+ "atapi",
+ "atapi16",
+ "ata8",
+ "sct",
+};
+
+/*
+ * ata8 standard (llba) cmd layout
+ *
+ * feature 16 bits
+ * count 16 bits
+ * lba 48 bits
+ * device 8 bits
+ * command 8 bits
+ *
+ * response:
+ *
+ * status 8 bits
+ * error 8 bits
+ * reason 8 bits
+ * count 8 bits
+ * sstatus 8 bits
+ * sactive 8 bits
+*/
+
+/*
+ * sata fis layout for fistype 0x27: host-to-device:
+ *
+ * 0 fistype
+ * 1 fis flags
+ * 2 ata command
+ * 3 features
+ * 4 sector lba low 7:0
+ * 5 cyl low lba mid 15:8
+ * 6 cyl hi lba hi 23:16
+ * 7 device / head
+ * 8 sec exp lba 31:24
+ * 9 cy low e lba 39:32
+ * 10 cy hi e lba 48:40
+ * 11 features (exp)
+ * 12 sector count
+ * 13 sector count (exp)
+ * 14 r
+ * 15 control
+ */
+
+void
+setfissig(Sfis *x, uint sig)
+{
+ x->sig = sig;
+}
+
+void
+skelfis(uchar *c)
+{
+ memset(c, 0, Fissize);
+ c[Ftype] = H2dev;
+ c[Fflags] = Fiscmd;
+ c[Fdev] = Ataobs;
+}
+
+int
+nopfis(Sfis*, uchar *c, int srst)
+{
+ skelfis(c);
+ if(srst){
+ c[Fflags] &= ~Fiscmd;
+ c[Fcontrol] = 1<<2;
+ return Preset|P28;
+ }
+ return Pnd|P28;
+}
+
+int
+txmodefis(Sfis *f, uchar *c, uchar d)
+{
+ int m;
+
+ /* hack */
+ if((f->sig >> 16) == 0xeb14)
+ return -1;
+ m = 0x40;
+ if(d == 0xff){
+ d = 0;
+ m = 0;
+ }
+ skelfis(c);
+ c[Fcmd] = 0xef;
+ c[Ffeat] = 3; /* set transfer mode */
+ c[Fsc] = m | d; /* sector count */
+ return Pnd|P28;
+}
+
+int
+featfis(Sfis*, uchar *c, uchar f)
+{
+ skelfis(c);
+ c[Fcmd] = 0xef;
+ c[Ffeat] = f;
+ return Pnd|P28;
+}
+
+int
+identifyfis(Sfis *f, uchar *c)
+{
+ static uchar tab[] = { 0xec, 0xa1, };
+
+ skelfis(c);
+ c[Fcmd] = tab[f->sig>>16 == 0xeb14];
+ return Pin|Ppio|P28|P512;
+}
+
+int
+flushcachefis(Sfis *f, uchar *c)
+{
+ static uchar tab[2] = {0xe7, 0xea};
+ static uchar ptab[2] = {Pnd|P28, Pnd|P48};
+ int llba;
+
+ llba = (f->feat & Dllba) != 0;
+ skelfis(c);
+ c[Fcmd] = tab[llba];
+ return ptab[llba];
+}
+
+static ushort
+gbit16(void *a)
+{
+ ushort j;
+ uchar *i;
+
+ i = a;
+ j = i[1] << 8;
+ j |= i[0];
+ return j;
+}
+
+static uint
+gbit32(void *a)
+{
+ uint j;
+ uchar *i;
+
+ i = a;
+ j = i[3] << 24;
+ j |= i[2] << 16;
+ j |= i[1] << 8;
+ j |= i[0];
+ return j;
+}
+
+static uvlong
+gbit64(void *a)
+{
+ uchar *i;
+
+ i = a;
+ return (uvlong)gbit32(i+4) << 32 | gbit32(a);
+}
+
+ushort
+id16(ushort *id, int i)
+{
+ return gbit16(id+i);
+}
+
+uint
+id32(ushort *id, int i)
+{
+ return gbit32(id+i);
+}
+
+uvlong
+id64(ushort *id, int i)
+{
+ return gbit64(id+i);
+}
+
+/* acs-2 §7.18.7.4 */
+static ushort puistab[] = {
+ 0x37c8, Pspinup,
+ 0x738c, Pspinup | Pidready,
+ 0x8c73, 0,
+ 0xc837, Pidready,
+};
+
+int
+idpuis(ushort *id)
+{
+ ushort u, i;
+
+ u = gbit16(id + 2);
+ for(i = 0; i < nelem(puistab); i += 2)
+ if(u == puistab[i])
+ return puistab[i + 1];
+ return Pidready; /* annoying cdroms */
+}
+
+static ushort
+onesc(ushort *id)
+{
+ ushort u;
+
+ u = gbit16(id);
+ if(u == 0xffff)
+ u = 0;
+ return u;
+}
+
+enum{
+ Idmasp = 1<<8,
+ Ilbasp = 1<<9,
+ Illba = 1<<10,
+};
+
+vlong
+idfeat(Sfis *f, ushort *id)
+{
+ int i, j;
+ vlong s;
+
+ f->feat = 0;
+ if(f->sig>>16 == 0xeb14)
+ f->feat |= Datapi;
+ i = gbit16(id + 49);
+ if((i & Ilbasp) == 0){
+ if(gbit16(id + 53) & 1){
+ f->c = gbit16(id + 1);
+ f->h = gbit16(id + 3);
+ f->s = gbit16(id + 6);
+ }else{
+ f->c = gbit16(id + 54);
+ f->h = gbit16(id + 55);
+ f->s = gbit16(id + 56);
+ }
+ s = f->c*f->h*f->s;
+ }else{
+ f->c = f->h = f->s = 0;
+ f->feat |= Dlba;
+ j = gbit16(id + 83) | gbit16(id + 86);
+ if(j & Illba){
+ f->feat |= Dllba;
+ s = gbit64(id + 100);
+ }else
+ s = gbit32(id + 60);
+ }
+ f->udma = 0xff;
+ if(i & Idmasp)
+ if(gbit16(id + 53) & 4)
+ for(i = gbit16(id + 88) & 0x7f; i; i >>= 1)
+ f->udma++;
+
+ if(f->feat & Datapi){
+ i = gbit16(id + 0);
+ if(i & 1)
+ f->feat |= Datapi16;
+ }
+
+ i = gbit16(id+83);
+ if((i>>14) == 1){
+ if(i & (1<<3))
+ f->feat |= Dpower;
+ i = gbit16(id + 82);
+ if(i & 1)
+ f->feat |= Dsmart;
+ if(i & (1<<14))
+ f->feat |= Dnop;
+ }
+ i = onesc(id + 80);
+ if(i & 1<<8){
+ f->feat |= Data8;
+ i = onesc(id + 222); /* sata? */
+ j = onesc(id + 76);
+ if(i != 0 && i >> 12 == 1 && j != 0){
+ j >>= 1;
+ f->speeds = j & 7;
+ i = gbit16(id + 78) & gbit16(id + 79);
+ /*
+ * not acceptable for comreset to
+ * wipe out device configuration.
+ * reject drive.
+ */
+ if((i & 1<<6) == 0)
+ return -1;
+ }
+ }
+ if(gbit16(id + 206) & 1)
+ f->feat |= Dsct;
+ idss(f, id);
+ return s;
+}
+
+int
+idss(Sfis *f, ushort *id)
+{
+ uint sw, i;
+
+ if(f->sig>>16 == 0xeb14)
+ return 0;
+ f->lsectsz = 512;
+ f->physshift = 0;
+ i = gbit16(id + 106);
+ if(i >> 14 != 1)
+ return f->lsectsz;
+ if((sw = gbit32(id + 117)) >= 256)
+ f->lsectsz = sw * 2;
+ if(i & 1<<13)
+ f->physshift = i & 7;
+ return f->lsectsz * (1<<f->physshift);
+}
+
+uvlong
+idwwn(Sfis*, ushort *id)
+{
+ uvlong u;
+
+ u = 0;
+ if(id[108]>>12 == 5){
+ u |= (uvlong)gbit16(id + 108) << 48;
+ u |= (uvlong)gbit16(id + 109) << 32;
+ u |= gbit16(id + 110) << 16;
+ u |= gbit16(id + 111) << 0;
+ }
+ return u;
+}
+
+void
+idmove(char *p, ushort *u, int n)
+{
+ int i;
+ char *op, *e, *s;
+
+ op = p;
+ s = (char*)u;
+ for(i = 0; i < n; i += 2){
+ *p++ = s[i + 1];
+ *p++ = s[i + 0];
+ }
+ *p = 0;
+ while(p > op && *--p == ' ')
+ *p = 0;
+ e = p;
+ p = op;
+ while(*p == ' ')
+ p++;
+ memmove(op, p, n - (e - p));
+}
+
+char*
+pflag(char *s, char *e, Sfis *f)
+{
+ ushort i, u;
+
+ u = f->feat;
+ for(i = 0; i < Dnflag; i++)
+ if(u & (1 << i))
+ s = seprint(s, e, "%s ", flagname[i]);
+ return seprint(s, e, "\n");
+}
+
+int
+atapirwfis(Sfis *f, uchar *c, uchar *cdb, int cdblen, int ndata)
+{
+ int fill, len;
+
+ fill = f->feat&Datapi16? 16: 12;
+ if((len = cdblen) > fill)
+ len = fill;
+ memmove(c + 0x40, cdb, len);
+ memset(c + 0x40 + len, 0, fill - len);
+
+ c[Ftype] = H2dev;
+ c[Fflags] = Fiscmd;
+ c[Fcmd] = Ataobs;
+ if(ndata != 0)
+ c[Ffeat] = 1; /* dma */
+ else
+ c[Ffeat] = 0; /* features (exp); */
+ c[Flba0] = 0;
+ c[Flba8] = ndata;
+ c[Flba16] = ndata >> 8;
+ c[Fdev] = Ataobs;
+ memset(c + 8, 0, Fissize - 8);
+ return P28|Ppkt;
+}
+
+int
+rwfis(Sfis *f, uchar *c, int rw, int nsect, uvlong lba)
+{
+ uchar acmd, llba, udma;
+ static uchar tab[2][2][2] = { 0x20, 0x24, 0x30, 0x34, 0xc8, 0x25, 0xca, 0x35, };
+ static uchar ptab[2][2][2] = {
+ Pin|Ppio|P28, Pin|Ppio|P48,
+ Pout|Ppio|P28, Pout|Ppio|P48,
+ Pin|Pdma|P28, Pin|Pdma|P48,
+ Pout|Pdma|P28, Pout|Pdma|P48,
+ };
+
+ nsect >>= f->physshift;
+ lba >>= f->physshift;
+
+ udma = f->udma != 0xff;
+ llba = (f->feat & Dllba) != 0;
+ acmd = tab[udma][rw][llba];
+
+ c[Ftype] = 0x27;
+ c[Fflags] = 0x80;
+ c[Fcmd] = acmd;
+ c[Ffeat] = 0;
+
+ c[Flba0] = lba;
+ c[Flba8] = lba >> 8;
+ c[Flba16] = lba >> 16;
+ c[Fdev] = Ataobs | Atalba;
+ if(llba == 0)
+ c[Fdev] |= (lba>>24) & 0xf;
+
+ c[Flba24] = lba >> 24;
+ c[Flba32] = lba >> 32;
+ c[Flba40] = lba >> 48;
+ c[Ffeat8] = 0;
+
+ c[Fsc] = nsect;
+ c[Fsc8] = nsect >> 8;
+ c[Ficc] = 0;
+ c[Fcontrol] = 0;
+
+ memset(c + 16, 0, Fissize - 16);
+ return ptab[udma][rw][llba];
+}
+
+uvlong
+fisrw(Sfis *f, uchar *c, int *n)
+{
+ uvlong lba;
+
+ lba = c[Flba0];
+ lba |= c[Flba8] << 8;
+ lba |= c[Flba16] << 16;
+ lba |= c[Flba24] << 24;
+ lba |= (uvlong)(c[Flba32] | c[Flba40]<<8) << 32;
+
+ *n = c[Fsc];
+ *n |= c[Fsc8] << 8;
+
+ *n >>= f->physshift;
+ lba >>= f->physshift;
+
+ return lba;
+}
+
+void
+sigtofis(Sfis *f, uchar *c)
+{
+ uint u;
+
+ u = f->sig;
+ memset(c, 0, Fissize);
+ c[Ftype] = 0x34;
+ c[Fflags] = 0x00;
+ c[Fcmd] = 0x50;
+ c[Ffeat] = 0x01;
+ c[Flba0] = u >> 8;
+ c[Flba8] = u >> 16;
+ c[Flba16] = u >> 24;
+ c[Fdev] = Ataobs;
+ c[Fsc] = u;
+}
+
+uint
+fistosig(uchar *u)
+{
+ return u[Fsc] | u[Flba0]<<8 | u[Flba8]<<16 | u[Flba16]<<24;
+}
+
+
+/* sas smp */
+void
+smpskelframe(Cfis *f, uchar *c, int m)
+{
+ memset(c, 0, Fissize);
+ c[Ftype] = 0x40;
+ c[Fflags] = m;
+ if(f->phyid)
+ c[Flba32] = f->phyid;
+}
+
+uint
+sashash(uvlong u)
+{
+ uint poly, msb, l, r;
+ uvlong m;
+
+ r = 0;
+ poly = 0x01db2777;
+ msb = 0x01000000;
+ for(m = 1ull<<63; m > 0; m >>= 1){
+ l = 0;
+ if(m & u)
+ l = msb;
+ r <<= 1;
+ r ^= l;
+ if(r & msb)
+ r ^= poly;
+ }
+ return r & 0xffffff;
+}
+
+uchar*
+sasbhash(uchar *t, uchar *s)
+{
+ uint poly, msb, l, r, i, j;
+
+ r = 0;
+ poly = 0x01db2777;
+ msb = 0x01000000;
+ for(i = 0; i < 8; i++)
+ for(j = 0x80; j != 0; j >>= 1){
+ l = 0;
+ if(s[i] & j)
+ l = msb;
+ r <<= 1;
+ r ^= l;
+ if(r & msb)
+ r ^= poly;
+ }
+ t[0] = r>>16;
+ t[1] = r>>8;
+ t[2] = r;
+ return t;
+}
--- /dev/null
+++ b/sys/src/libfis/mkfile
@@ -1,0 +1,15 @@
+</$objtype/mkfile
+
+LIB=/$objtype/lib/libfis.a
+OFILES=\
+ fis.$O\
+
+HFILES=/sys/include/fis.h
+
+UPDATE=\
+ mkfile\
+ $HFILES\
+ ${OFILES:%.$O=%.c}\
+ ${LIB:/$objtype/%=/386/%}\
+
+</sys/src/cmd/mksyslib