diff options
Diffstat (limited to 'os/pc')
127 files changed, 71931 insertions, 0 deletions
diff --git a/os/pc/NOTICE b/os/pc/NOTICE new file mode 100644 index 00000000..95b421c4 --- /dev/null +++ b/os/pc/NOTICE @@ -0,0 +1,8 @@ +Most of these files have been issued at some time under the Lucent Public License 1.02, +except devbench.c, fpi387.c, flashzpc.c, devtv.c, devmouse.c, devmpeg.c (which are Vita Nuova MIT-template). + +The copyright is not necessarily Lucent's in all cases (eg, ether83815.c, sd53c8xx.c, +and much of the USB and wavelan support) because some things were written outside Lucent. +A more accurate summary will appear here shortly. +Meanwhile, if you'd like to use the software in some other program, +you can always ask us to work out the detailed appropriate copyright notice. diff --git a/os/pc/README b/os/pc/README new file mode 100644 index 00000000..092eceb6 --- /dev/null +++ b/os/pc/README @@ -0,0 +1,9 @@ +PC kernel + +The PC kernel is currently being updated to bring it up-to-date with +Plan 9 support (the original Inferno PC kernel was derived from an +extended subset of an earlier edition of Plan 9). In particular, +although it can be configured as a server, graphics support is +still being provided. (The kernel level code in vga*.c, devvga.c and screen.c +is probably near the final version, but some Limbo code is needed to +set the right graphics modes.) diff --git a/os/pc/apbootstrap.h b/os/pc/apbootstrap.h new file mode 100644 index 00000000..61aca70d --- /dev/null +++ b/os/pc/apbootstrap.h @@ -0,0 +1,15 @@ +uchar apbootstrap[]={ +0xea,0x14,0x10,0x00,0x00,0x90,0x90,0x90,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x8c,0xc8,0x8e,0xd8,0x0f,0x01,0x16,0xac,0x10,0x0f,0x20,0xc0, +0x83,0xc8,0x01,0x0f,0x22,0xc0,0xeb,0x00,0xb8,0x08,0x00,0x8e,0xd8,0x8e,0xc0,0x8e, +0xe0,0x8e,0xe8,0x8e,0xd0,0x66,0xea,0x3d,0x10,0x00,0x00,0x10,0x00,0x8b,0x0d,0x0c, +0x10,0x00,0x00,0x8b,0x91,0x00,0x08,0x00,0x00,0x89,0x11,0x0f,0x22,0xd9,0x0f,0x20, +0xc2,0x81,0xca,0x00,0x00,0x01,0x80,0x81,0xe2,0xf5,0xff,0xff,0x9f,0xb8,0x67,0x10, +0x00,0x80,0x0f,0x22,0xc2,0xff,0xe0,0x89,0xc8,0x0d,0x00,0x00,0x00,0x80,0xc7,0x00, +0x00,0x00,0x00,0x00,0x0f,0x22,0xd9,0xbc,0xfc,0x5f,0x00,0x80,0x31,0xc0,0x50,0x9d, +0x8b,0x05,0x10,0x10,0x00,0x80,0x89,0x04,0x24,0x8b,0x05,0x08,0x10,0x00,0x80,0xff, +0xd0,0xf4,0xeb,0xfd,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x00,0x00, +0x00,0x92,0xcf,0x00,0xff,0xff,0x00,0x00,0x00,0x9a,0xcf,0x00,0x17,0x00,0x94,0x10, +0x00,0x00, + +}; diff --git a/os/pc/apbootstrap.s b/os/pc/apbootstrap.s new file mode 100644 index 00000000..3887d71d --- /dev/null +++ b/os/pc/apbootstrap.s @@ -0,0 +1,110 @@ +#include "mem.h" + +#define NOP BYTE $0x90 /* NOP */ +#define LGDT(gdtptr) BYTE $0x0F; /* LGDT */ \ + BYTE $0x01; BYTE $0x16; \ + WORD $gdtptr +#define FARJUMP16(s, o) BYTE $0xEA; /* far jump to ptr16:16 */ \ + WORD $o; WORD $s; \ + NOP; NOP; NOP +#define FARJUMP32(s, o) BYTE $0x66; /* far jump to ptr32:16 */ \ + BYTE $0xEA; LONG $o; WORD $s + +#define DELAY BYTE $0xEB; /* JMP .+2 */ \ + BYTE $0x00 +#define INVD BYTE $0x0F; BYTE $0x08 +#define WBINVD BYTE $0x0F; BYTE $0x09 + +/* + * Macros for calculating offsets within the page directory base + * and page tables. Note that these are assembler-specific hence + * the '<<2'. + */ +#define PDO(a) (((((a))>>22) & 0x03FF)<<2) +#define PTO(a) (((((a))>>12) & 0x03FF)<<2) + +/* + * Start an Application Processor. This must be placed on a 4KB boundary + * somewhere in the 1st MB of conventional memory (APBOOTSTRAP). However, + * due to some shortcuts below it's restricted further to within the 1st + * 64KB. The AP starts in real-mode, with + * CS selector set to the startup memory address/16; + * CS base set to startup memory address; + * CS limit set to 64KB; + * CPL and IP set to 0. + */ +TEXT apbootstrap(SB), $0 + FARJUMP16(0, _apbootstrap(SB)) +TEXT _apvector(SB), $0 /* address APBOOTSTRAP+0x08 */ + LONG $0 +TEXT _appdb(SB), $0 /* address APBOOTSTRAP+0x0C */ + LONG $0 +TEXT _apapic(SB), $0 /* address APBOOTSTRAP+0x10 */ + LONG $0 +TEXT _apbootstrap(SB), $0 /* address APBOOTSTRAP+0x14 */ + MOVW CS, AX + MOVW AX, DS /* initialise DS */ + + LGDT(gdtptr(SB)) /* load a basic gdt */ + + MOVL CR0, AX + ORL $1, AX + MOVL AX, CR0 /* turn on protected mode */ + DELAY /* JMP .+2 */ + + BYTE $0xB8; WORD $SELECTOR(1, SELGDT, 0)/* MOVW $SELECTOR(1, SELGDT, 0), AX */ + MOVW AX, DS + MOVW AX, ES + MOVW AX, FS + MOVW AX, GS + MOVW AX, SS + + FARJUMP32(SELECTOR(2, SELGDT, 0), _ap32-KZERO(SB)) + +/* + * For Pentiums and higher, the code that enables paging must come from + * pages that are identity mapped. + * To this end double map KZERO at virtual 0 and undo the mapping once virtual + * nirvana has been obtained. + */ +TEXT _ap32(SB), $0 + MOVL _appdb-KZERO(SB), CX /* physical address of PDB */ + MOVL (PDO(KZERO))(CX), DX /* double-map KZERO at 0 */ + MOVL DX, (PDO(0))(CX) + MOVL CX, CR3 /* load and flush the mmu */ + + MOVL CR0, DX + ORL $0x80010000, DX /* PG|WP */ + ANDL $~0x6000000A, DX /* ~(CD|NW|TS|MP) */ + + MOVL $_appg(SB), AX + MOVL DX, CR0 /* turn on paging */ + JMP* AX + +TEXT _appg(SB), $0 + MOVL CX, AX /* physical address of PDB */ + ORL $KZERO, AX + MOVL $0, (PDO(0))(AX) /* undo double-map of KZERO at 0 */ + MOVL CX, CR3 /* load and flush the mmu */ + + MOVL $(MACHADDR+MACHSIZE-4), SP + + MOVL $0, AX + PUSHL AX + POPFL + + MOVL _apapic(SB), AX + MOVL AX, (SP) + MOVL _apvector(SB), AX + CALL* AX +_aphalt: + HLT + JMP _aphalt + +TEXT gdt(SB), $0 + LONG $0x0000; LONG $0 + LONG $0xFFFF; LONG $(SEGG|SEGB|(0xF<<16)|SEGP|SEGPL(0)|SEGDATA|SEGW) + LONG $0xFFFF; LONG $(SEGG|SEGD|(0xF<<16)|SEGP|SEGPL(0)|SEGEXEC|SEGR) +TEXT gdtptr(SB), $0 + WORD $(3*8-1) + LONG $gdt-KZERO(SB) diff --git a/os/pc/apic.c b/os/pc/apic.c new file mode 100644 index 00000000..ffb24584 --- /dev/null +++ b/os/pc/apic.c @@ -0,0 +1,378 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include "mp.h" + +enum { /* Local APIC registers */ + LapicID = 0x0020, /* ID */ + LapicVER = 0x0030, /* Version */ + LapicTPR = 0x0080, /* Task Priority */ + LapicAPR = 0x0090, /* Arbitration Priority */ + LapicPPR = 0x00A0, /* Processor Priority */ + LapicEOI = 0x00B0, /* EOI */ + LapicLDR = 0x00D0, /* Logical Destination */ + LapicDFR = 0x00E0, /* Destination Format */ + LapicSVR = 0x00F0, /* Spurious Interrupt Vector */ + LapicISR = 0x0100, /* Interrupt Status (8 registers) */ + LapicTMR = 0x0180, /* Trigger Mode (8 registers) */ + LapicIRR = 0x0200, /* Interrupt Request (8 registers) */ + LapicESR = 0x0280, /* Error Status */ + LapicICRLO = 0x0300, /* Interrupt Command */ + LapicICRHI = 0x0310, /* Interrupt Command [63:32] */ + LapicTIMER = 0x0320, /* Local Vector Table 0 (TIMER) */ + LapicPCINT = 0x0340, /* Performance COunter LVT */ + LapicLINT0 = 0x0350, /* Local Vector Table 1 (LINT0) */ + LapicLINT1 = 0x0360, /* Local Vector Table 2 (LINT1) */ + LapicERROR = 0x0370, /* Local Vector Table 3 (ERROR) */ + LapicTICR = 0x0380, /* Timer Initial Count */ + LapicTCCR = 0x0390, /* Timer Current Count */ + LapicTDCR = 0x03E0, /* Timer Divide Configuration */ +}; + +enum { /* LapicSVR */ + LapicENABLE = 0x00000100, /* Unit Enable */ + LapicFOCUS = 0x00000200, /* Focus Processor Checking Disable */ +}; + +enum { /* LapicICRLO */ + /* [14] IPI Trigger Mode Level (RW) */ + LapicDEASSERT = 0x00000000, /* Deassert level-sensitive interrupt */ + LapicASSERT = 0x00004000, /* Assert level-sensitive interrupt */ + + /* [17:16] Remote Read Status */ + LapicINVALID = 0x00000000, /* Invalid */ + LapicWAIT = 0x00010000, /* In-Progress */ + LapicVALID = 0x00020000, /* Valid */ + + /* [19:18] Destination Shorthand */ + LapicFIELD = 0x00000000, /* No shorthand */ + LapicSELF = 0x00040000, /* Self is single destination */ + LapicALLINC = 0x00080000, /* All including self */ + LapicALLEXC = 0x000C0000, /* All Excluding self */ +}; + +enum { /* LapicESR */ + LapicSENDCS = 0x00000001, /* Send CS Error */ + LapicRCVCS = 0x00000002, /* Receive CS Error */ + LapicSENDACCEPT = 0x00000004, /* Send Accept Error */ + LapicRCVACCEPT = 0x00000008, /* Receive Accept Error */ + LapicSENDVECTOR = 0x00000020, /* Send Illegal Vector */ + LapicRCVVECTOR = 0x00000040, /* Receive Illegal Vector */ + LapicREGISTER = 0x00000080, /* Illegal Register Address */ +}; + +enum { /* LapicTIMER */ + /* [17] Timer Mode (RW) */ + LapicONESHOT = 0x00000000, /* One-shot */ + LapicPERIODIC = 0x00020000, /* Periodic */ + + /* [19:18] Timer Base (RW) */ + LapicCLKIN = 0x00000000, /* use CLKIN as input */ + LapicTMBASE = 0x00040000, /* use TMBASE */ + LapicDIVIDER = 0x00080000, /* use output of the divider */ +}; + +enum { /* LapicTDCR */ + LapicX2 = 0x00000000, /* divide by 2 */ + LapicX4 = 0x00000001, /* divide by 4 */ + LapicX8 = 0x00000002, /* divide by 8 */ + LapicX16 = 0x00000003, /* divide by 16 */ + LapicX32 = 0x00000008, /* divide by 32 */ + LapicX64 = 0x00000009, /* divide by 64 */ + LapicX128 = 0x0000000A, /* divide by 128 */ + LapicX1 = 0x0000000B, /* divide by 1 */ +}; + +static ulong* lapicbase; + +struct +{ + uvlong hz; + ulong max; + ulong min; + ulong div; +} lapictimer; + +static int +lapicr(int r) +{ + return *(lapicbase+(r/sizeof(*lapicbase))); +} + +static void +lapicw(int r, int data) +{ + *(lapicbase+(r/sizeof(*lapicbase))) = data; + data = *(lapicbase+(LapicID/sizeof(*lapicbase))); + USED(data); +} + +void +lapiconline(void) +{ + /* + * Reload the timer to de-synchronise the processors, + * then lower the task priority to allow interrupts to be + * accepted by the APIC. + */ + microdelay((TK2MS(1)*1000/conf.nmach) * m->machno); + lapicw(LapicTICR, lapictimer.max); + lapicw(LapicTIMER, LapicCLKIN|LapicPERIODIC|(VectorPIC+IrqTIMER)); + + lapicw(LapicTPR, 0); +} + +/* + * use the i8253 clock to figure out our lapic timer rate. + */ +static void +lapictimerinit(void) +{ + uvlong x, v, hz; + + v = m->cpuhz/1000; + lapicw(LapicTDCR, LapicX1); + lapicw(LapicTIMER, ApicIMASK|LapicCLKIN|LapicONESHOT|(VectorPIC+IrqTIMER)); + + if(lapictimer.hz == 0ULL){ + x = fastticks(&hz); + x += hz/10; + lapicw(LapicTICR, 0xffffffff); + do{ + v = fastticks(nil); + }while(v < x); + + lapictimer.hz = (0xffffffffUL-lapicr(LapicTCCR))*10; + lapictimer.max = lapictimer.hz/HZ; + lapictimer.min = lapictimer.hz/(100*HZ); + + if(lapictimer.hz > hz) + panic("lapic clock faster than cpu clock"); + lapictimer.div = hz/lapictimer.hz; + } +} + +void +lapicinit(Apic* apic) +{ + ulong r, lvt; + + if(lapicbase == 0) + lapicbase = apic->addr; + + lapicw(LapicDFR, 0xFFFFFFFF); + r = (lapicr(LapicID)>>24) & 0xFF; + lapicw(LapicLDR, (1<<r)<<24); + lapicw(LapicTPR, 0xFF); + lapicw(LapicSVR, LapicENABLE|(VectorPIC+IrqSPURIOUS)); + + lapictimerinit(); + + /* + * Some Pentium revisions have a bug whereby spurious + * interrupts are generated in the through-local mode. + */ + switch(m->cpuidax & 0xFFF){ + case 0x526: /* stepping cB1 */ + case 0x52B: /* stepping E0 */ + case 0x52C: /* stepping cC0 */ + wrmsr(0x0E, 1<<14); /* TR12 */ + break; + } + + /* + * Set the local interrupts. It's likely these should just be + * masked off for SMP mode as some Pentium Pros have problems if + * LINT[01] are set to ExtINT. + * Acknowledge any outstanding interrupts. + lapicw(LapicLINT0, apic->lintr[0]); + lapicw(LapicLINT1, apic->lintr[1]); + */ + lapiceoi(0); + + lvt = (lapicr(LapicVER)>>16) & 0xFF; + if(lvt >= 4) + lapicw(LapicPCINT, ApicIMASK); + lapicw(LapicERROR, VectorPIC+IrqERROR); + lapicw(LapicESR, 0); + lapicr(LapicESR); + + /* + * Issue an INIT Level De-Assert to synchronise arbitration ID's. + */ + lapicw(LapicICRHI, 0); + lapicw(LapicICRLO, LapicALLINC|ApicLEVEL|LapicDEASSERT|ApicINIT); + while(lapicr(LapicICRLO) & ApicDELIVS) + ; + + /* + * Do not allow acceptance of interrupts until all initialisation + * for this processor is done. For the bootstrap processor this can be + * early duing initialisation. For the application processors this should + * be after the bootstrap processor has lowered priority and is accepting + * interrupts. + lapicw(LapicTPR, 0); + */ +} + +void +lapicstartap(Apic* apic, int v) +{ + int crhi, i; + + crhi = apic->apicno<<24; + lapicw(LapicICRHI, crhi); + lapicw(LapicICRLO, LapicFIELD|ApicLEVEL|LapicASSERT|ApicINIT); + microdelay(200); + lapicw(LapicICRLO, LapicFIELD|ApicLEVEL|LapicDEASSERT|ApicINIT); + delay(10); + + for(i = 0; i < 2; i++){ + lapicw(LapicICRHI, crhi); + lapicw(LapicICRLO, LapicFIELD|ApicEDGE|ApicSTARTUP|(v/BY2PG)); + microdelay(200); + } +} + +void +lapicerror(Ureg*, void*) +{ + int esr; + + lapicw(LapicESR, 0); + esr = lapicr(LapicESR); + switch(m->cpuidax & 0xFFF){ + case 0x526: /* stepping cB1 */ + case 0x52B: /* stepping E0 */ + case 0x52C: /* stepping cC0 */ + return; + } + print("cpu%d: lapicerror: 0x%8.8uX\n", m->machno, esr); +} + +void +lapicspurious(Ureg*, void*) +{ + print("cpu%d: lapicspurious\n", m->machno); +} + +int +lapicisr(int v) +{ + int isr; + + isr = lapicr(LapicISR + (v/32)); + + return isr & (1<<(v%32)); +} + +int +lapiceoi(int v) +{ + lapicw(LapicEOI, 0); + + return v; +} + +void +lapicicrw(int hi, int lo) +{ + lapicw(LapicICRHI, hi); + lapicw(LapicICRLO, lo); +} + +void +ioapicrdtr(Apic* apic, int sel, int* hi, int* lo) +{ + ulong *iowin; + + iowin = apic->addr+(0x10/sizeof(ulong)); + sel = IoapicRDT + 2*sel; + + lock(apic); + *apic->addr = sel+1; + if(hi) + *hi = *iowin; + *apic->addr = sel; + if(lo) + *lo = *iowin; + unlock(apic); +} + +void +ioapicrdtw(Apic* apic, int sel, int hi, int lo) +{ + ulong *iowin; + + iowin = apic->addr+(0x10/sizeof(ulong)); + sel = IoapicRDT + 2*sel; + + lock(apic); + *apic->addr = sel+1; + *iowin = hi; + *apic->addr = sel; + *iowin = lo; + unlock(apic); +} + +void +ioapicinit(Apic* apic, int apicno) +{ + int hi, lo, v; + ulong *iowin; + + /* + * Initialise the I/O APIC. + * The MultiProcessor Specification says it is the responsibility + * of the O/S to set the APIC id. + * Make sure interrupts are all masked off for now. + */ + iowin = apic->addr+(0x10/sizeof(ulong)); + lock(apic); + *apic->addr = IoapicVER; + apic->mre = (*iowin>>16) & 0xFF; + + *apic->addr = IoapicID; + *iowin = apicno<<24; + unlock(apic); + + hi = 0; + lo = ApicIMASK; + for(v = 0; v <= apic->mre; v++) + ioapicrdtw(apic, v, hi, lo); +} + +void +lapictimerset(uvlong next) +{ + vlong period; + int x; + + x = splhi(); + lock(&m->apictimerlock); + + period = lapictimer.max; + if(next != 0){ + period = next - fastticks(nil); + period /= lapictimer.div; + + if(period < lapictimer.min) + period = lapictimer.min; + else if(period > lapictimer.max - lapictimer.min) + period = lapictimer.max; + } + lapicw(LapicTICR, period); + + unlock(&m->apictimerlock); + splx(x); +} + +void +lapicclock(Ureg *u, void*) +{ + timerintr(u, 0); +} diff --git a/os/pc/apm.c b/os/pc/apm.c new file mode 100644 index 00000000..2ea18b4d --- /dev/null +++ b/os/pc/apm.c @@ -0,0 +1,151 @@ +/* + * Interface to Advanced Power Management 1.2 BIOS + * + * This is, in many ways, a giant hack, and when things settle down + * a bit and standardize, hopefully we can write a driver that deals + * more directly with the hardware and thus might be a bit cleaner. + * + * ACPI might be the answer, but at the moment this is simpler + * and more widespread. + */ + +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" + +extern int apmfarcall(ushort, ulong, Ureg*); /* apmjump.s */ + +static int +getreg(ulong *reg, ISAConf *isa, char *name) +{ + int i; + int nl; + + nl = strlen(name); + for(i=0; i<isa->nopt; i++){ + if(cistrncmp(isa->opt[i], name, nl)==0 && isa->opt[i][nl] == '='){ + *reg = strtoul(isa->opt[i]+nl+1, nil, 16); + return 0; + } + } + return -1; +} + +/* + * Segment descriptors look like this. + * + * d1: [base 31:24] [gran] [is32bit] [0] [unused] [limit 19:16] + [present] [privlev] [type 3:0] [base 23:16] + * d0: [base 15:00] [limit 15:00] + * + * gran is 0 for 1-byte granularity, 1 for 4k granularity + * type is 0 for system segment, 1 for code/data. + * + * clearly we know way too much about the memory unit. + * however, knowing this much about the memory unit + * means that the memory unit need not know anything + * about us. + * + * what a crock. + */ +static void +setgdt(int sel, ulong base, ulong limit, int flag) +{ + if(sel < 0 || sel >= NGDT) + panic("setgdt"); + + base = (ulong)KADDR(base); + m->gdt[sel].d0 = (base<<16) | (limit&0xFFFF); + m->gdt[sel].d1 = (base&0xFF000000) | (limit&0x000F0000) | + ((base>>16)&0xFF) | SEGP | SEGPL(0) | flag; +} + +static ulong ax, cx, dx, di, ebx, esi; +static Ureg apmu; +static long +apmread(Chan*, void *a, long n, vlong off) +{ + if(off < 0) + error("badarg"); + + if(n+off > sizeof apmu) + n = sizeof apmu - off; + if(n <= 0) + return 0; + memmove(a, (char*)&apmu+off, n); + return n; +} + +static long +apmwrite(Chan*, void *a, long n, vlong off) +{ + int s; + if(off || n != sizeof apmu) + error("write a Ureg"); + + memmove(&apmu, a, sizeof apmu); + s = splhi(); + apmfarcall(APMCSEL, ebx, &apmu); + splx(s); + return n; +} + +void +apmlink(void) +{ + ISAConf isa; + char *s; + + if(isaconfig("apm", 0, &isa) == 0) + return; + + /* + * APM info passed from boot loader. + * Now we need to set up the GDT entries for APM. + * + * AX = 32-bit code segment base address + * EBX = 32-bit code segment offset + * CX = 16-bit code segment base address + * DX = 32-bit data segment base address + * ESI = <16-bit code segment length> <32-bit code segment length> (hi then lo) + * DI = 32-bit data segment length + */ + + if(getreg(&ax, &isa, s="ax") < 0 + || getreg(&ebx, &isa, s="ebx") < 0 + || getreg(&cx, &isa, s="cx") < 0 + || getreg(&dx, &isa, s="dx") < 0 + || getreg(&esi, &isa, s="esi") < 0 + || getreg(&di, &isa, s="di") < 0){ + print("apm: missing register %s\n", s); + return; + } + + /* + * The NEC Versa SX bios does not report the correct 16-bit code + * segment length when loaded directly from mbr -> 9load (as compared + * with going through ld.com). We'll make both code segments 64k-1 bytes. + */ + esi = 0xFFFFFFFF; + + /* + * We are required by the BIOS to set up three consecutive segments, + * one for the APM 32-bit code, one for the APM 16-bit code, and + * one for the APM data. The BIOS handler uses the code segment it + * get called with to determine the other two segment selector. + */ + setgdt(APMCSEG, ax<<4, ((esi&0xFFFF)-1)&0xFFFF, SEGEXEC|SEGR|SEGD); + setgdt(APMCSEG16, cx<<4, ((esi>>16)-1)&0xFFFF, SEGEXEC|SEGR); + setgdt(APMDSEG, dx<<4, (di-1)&0xFFFF, SEGDATA|SEGW|SEGD); + + addarchfile("apm", 0660, apmread, apmwrite); + +print("apm0: configured cbase %.8lux off %.8lux\n", ax<<4, ebx); + + return; +} + diff --git a/os/pc/apmjump.s b/os/pc/apmjump.s new file mode 100644 index 00000000..6dbd19d0 --- /dev/null +++ b/os/pc/apmjump.s @@ -0,0 +1,98 @@ +/* + * Far call, absolute indirect. + * The argument is the offset. + * We use a global structure for the jump params, + * so this is *not* reentrant or thread safe. + */ + +#include "mem.h" + +#define SSOVERRIDE BYTE $0x36 +#define CSOVERRIDE BYTE $0x2E +#define RETF BYTE $0xCB + +GLOBL apmjumpstruct+0(SB), $8 + +TEXT fortytwo(SB), $0 + MOVL $42, AX + RETF + +TEXT getcs(SB), $0 + PUSHL CS + POPL AX + RET + +TEXT apmfarcall(SB), $0 + /* + * We call push and pop ourselves. + * As soon as we do the first push or pop, + * we can't use FP anymore. + */ + MOVL off+4(FP), BX + MOVL seg+0(FP), CX + MOVL BX, apmjumpstruct+0(SB) + MOVL CX, apmjumpstruct+4(SB) + + /* load necessary registers from Ureg */ + MOVL ureg+8(FP), DI + MOVL 28(DI), AX + MOVL 16(DI), BX + MOVL 24(DI), CX + MOVL 20(DI), DX + + /* save registers, segments */ + PUSHL DS + PUSHL ES + PUSHL FS + PUSHL GS + PUSHL BP + PUSHL DI + + /* + * paranoia: zero the segments, since it's the + * BIOS's responsibility to initialize them. + * (trick picked up from Linux driver). + PUSHL DX + XORL DX, DX + PUSHL DX + POPL DS + PUSHL DX + POPL ES + PUSHL DX + POPL FS + PUSHL DX + POPL GS + POPL DX + */ + + PUSHL $APMDSEG + POPL DS + + /* + * The actual call. + */ + CSOVERRIDE; BYTE $0xFF; BYTE $0x1D + LONG $apmjumpstruct+0(SB) + + /* restore segments, registers */ + POPL DI + POPL BP + POPL GS + POPL FS + POPL ES + POPL DS + + PUSHFL + POPL 64(DI) + + /* store interesting registers back in Ureg */ + MOVL AX, 28(DI) + MOVL BX, 16(DI) + MOVL CX, 24(DI) + MOVL DX, 20(DI) + MOVL SI, 4(DI) + + PUSHFL + POPL AX + ANDL $1, AX /* carry flag */ + RET diff --git a/os/pc/archmp.c b/os/pc/archmp.c new file mode 100644 index 00000000..fbbda091 --- /dev/null +++ b/os/pc/archmp.c @@ -0,0 +1,138 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include "mp.h" + +#define cpuserver 1 + +_MP_ *_mp_; + +static _MP_* +mpscan(uchar *addr, int len) +{ + uchar *e, *p, sum; + int i; + + e = addr+len; + for(p = addr; p < e; p += sizeof(_MP_)){ + if(memcmp(p, "_MP_", 4)) + continue; + sum = 0; + for(i = 0; i < sizeof(_MP_); i++) + sum += p[i]; + if(sum == 0) + return (_MP_*)p; + } + return 0; +} + +static _MP_* +mpsearch(void) +{ + uchar *bda; + ulong p; + _MP_ *mp; + + /* + * Search for the MP Floating Pointer Structure: + * 1) in the first KB of the EBDA; + * 2) in the last KB of system base memory; + * 3) in the BIOS ROM between 0xE0000 and 0xFFFFF. + */ + bda = KADDR(0x400); + if((p = (bda[0x0F]<<8)|bda[0x0E])){ + if(mp = mpscan(KADDR(p), 1024)) + return mp; + } + else{ + p = ((bda[0x14]<<8)|bda[0x13])*1024; + if(mp = mpscan(KADDR(p-1024), 1024)) + return mp; + } + return mpscan(KADDR(0xF0000), 0x10000); +} + +static int identify(void); + +PCArch archmp = { +.id= "_MP_", +.ident= identify, +.reset= mpshutdown, +.intrinit= mpinit, +.intrenable= mpintrenable, +.fastclock= i8253read, +.timerset= lapictimerset, +}; + +static int +identify(void) +{ + PCMP *pcmp; + uchar *p, sum; + ulong length; + + if(getconf("*nomp")) + return 1; + + /* + * Search for an MP configuration table. For now, + * don't accept the default configurations (physaddr == 0). + * Check for correct signature, calculate the checksum and, + * if correct, check the version. + * To do: check extended table checksum. + */ + if((_mp_ = mpsearch()) == 0 || _mp_->physaddr == 0) + return 1; + + pcmp = KADDR(_mp_->physaddr); + if(memcmp(pcmp, "PCMP", 4)) + return 1; + + length = pcmp->length; + sum = 0; + for(p = (uchar*)pcmp; length; length--) + sum += *p++; + + if(sum || (pcmp->version != 1 && pcmp->version != 4)) + return 1; + + if(cpuserver && m->havetsc) + archmp.fastclock = tscticks; + return 0; +} + +Lock mpsynclock; + +void +syncclock(void) +{ + uvlong x; + + if(arch->fastclock != tscticks) + return; + + if(m->machno == 0){ + wrmsr(0x10, 0); + m->tscticks = 0; + } else { + x = MACHP(0)->tscticks; + while(x == MACHP(0)->tscticks) + ; + wrmsr(0x10, MACHP(0)->tscticks); + cycles(&m->tscticks); + } +} + +uvlong +tscticks(uvlong *hz) +{ + if(hz != nil) + *hz = m->cpuhz; + + cycles(&m->tscticks); /* Uses the rdtsc instruction */ + return m->tscticks; +} diff --git a/os/pc/audio.h b/os/pc/audio.h new file mode 100644 index 00000000..d06c7bcc --- /dev/null +++ b/os/pc/audio.h @@ -0,0 +1,15 @@ +enum +{ + Bufsize = 1024, /* 5.8 ms each, must be power of two */ + Nbuf = 128, /* .74 seconds total */ + Dma = 6, + IrqAUDIO = 7, + SBswab = 0, +}; + +#define seteisadma(a, b) dmainit(a, Bufsize); +#define CACHELINESZ 8 +#define UNCACHED(type, v) (type*)((ulong)(v)) + +#define Int0vec +#define setvec(v, f, a) intrenable(v, f, a, BUSUNKNOWN, "audio") diff --git a/os/pc/cga.c b/os/pc/cga.c new file mode 100644 index 00000000..6365d44c --- /dev/null +++ b/os/pc/cga.c @@ -0,0 +1,127 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +enum { + Black, + Blue, + Green, + Cyan, + Red, + Magenta, + Brown, + Grey, + + Bright = 0x08, + Blinking = 0x80, + + Yellow = Bright|Brown, + White = Bright|Grey, +}; + +enum { + Width = 80*2, + Height = 25, + + Attr = (Black<<4)|Grey, /* high nibble background + * low foreground + */ +}; + +#define CGASCREENBASE ((uchar*)KADDR(0xB8000)) + +static int cgapos; +static Lock cgascreenlock; + +static uchar +cgaregr(int index) +{ + outb(0x3D4, index); + return inb(0x3D4+1) & 0xFF; +} + +static void +cgaregw(int index, int data) +{ + outb(0x3D4, index); + outb(0x3D4+1, data); +} + +static void +movecursor(void) +{ + cgaregw(0x0E, (cgapos/2>>8) & 0xFF); + cgaregw(0x0F, cgapos/2 & 0xFF); + CGASCREENBASE[cgapos+1] = Attr; +} + +static void +cgascreenputc(int c) +{ + int i; + uchar *p; + + if(c == '\n'){ + cgapos = cgapos/Width; + cgapos = (cgapos+1)*Width; + } + else if(c == '\t'){ + i = 8 - ((cgapos/2)&7); + while(i-->0) + cgascreenputc(' '); + } + else if(c == '\b'){ + if(cgapos >= 2) + cgapos -= 2; + cgascreenputc(' '); + cgapos -= 2; + } + else{ + CGASCREENBASE[cgapos++] = c; + CGASCREENBASE[cgapos++] = Attr; + } + if(cgapos >= Width*Height){ + memmove(CGASCREENBASE, &CGASCREENBASE[Width], Width*(Height-1)); + p = &CGASCREENBASE[Width*(Height-1)]; + for(i=0; i<Width/2; i++){ + *p++ = ' '; + *p++ = Attr; + } + cgapos = Width*(Height-1); + } + movecursor(); +} + +static void +cgascreenputs(char* s, int n) +{ + if(!islo()){ + /* + * Don't deadlock trying to + * print in an interrupt. + */ + if(!canlock(&cgascreenlock)) + return; + } + else + lock(&cgascreenlock); + + while(n-- > 0) + cgascreenputc(*s++); + + unlock(&cgascreenlock); +} + +void +screeninit(void) +{ + + cgapos = cgaregr(0x0E)<<8; + cgapos |= cgaregr(0x0F); + cgapos *= 2; + + screenputs = cgascreenputs; +} diff --git a/os/pc/cgamemscr.c b/os/pc/cgamemscr.c new file mode 100644 index 00000000..7509614a --- /dev/null +++ b/os/pc/cgamemscr.c @@ -0,0 +1,203 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#include <draw.h> +#include <memdraw.h> +#include <memlayer.h> + +enum { + Width = 160, + Height = 25, + + Attr = 7, /* white on black */ +}; + +#define CGASCREENBASE ((uchar*)KADDR(0xB8000)) + +static int cgapos; +static int screeninitdone; +static Lock cgascreenlock; +void (*vgascreenputc)(char*); + +static uchar +cgaregr(int index) +{ + outb(0x3D4, index); + return inb(0x3D4+1) & 0xFF; +} + +static void +cgaregw(int index, int data) +{ + outb(0x3D4, index); + outb(0x3D4+1, data); +} + +static void +movecursor(void) +{ + cgaregw(0x0E, (cgapos/2>>8) & 0xFF); + cgaregw(0x0F, cgapos/2 & 0xFF); + CGASCREENBASE[cgapos+1] = Attr; +} + +static void +cgascreenputc(int c) +{ + int i; + + if(c == '\n'){ + cgapos = cgapos/Width; + cgapos = (cgapos+1)*Width; + } + else if(c == '\t'){ + i = 8 - ((cgapos/2)&7); + while(i-->0) + cgascreenputc(' '); + } + else if(c == '\b'){ + if(cgapos >= 2) + cgapos -= 2; + cgascreenputc(' '); + cgapos -= 2; + } + else{ + CGASCREENBASE[cgapos++] = c; + CGASCREENBASE[cgapos++] = Attr; + } + if(cgapos >= Width*Height){ + memmove(CGASCREENBASE, &CGASCREENBASE[Width], Width*(Height-1)); + memset(&CGASCREENBASE[Width*(Height-1)], 0, Width); + cgapos = Width*(Height-1); + } + movecursor(); +} + +void +screeninit(void) +{ + memimageinit(); + cgapos = cgaregr(0x0E)<<8; + cgapos |= cgaregr(0x0F); + cgapos *= 2; + screeninitdone = 1; +} + +void +cgascreenputs(char* s, int n) +{ + int i; + Rune r; + char buf[4]; + + if(!islo()){ + if(!canlock(&cgascreenlock)) + return; + } + else + lock(&cgascreenlock); + + if(vgascreenputc == nil){ + while(n-- > 0) + cgascreenputc(*s++); + unlock(&cgascreenlock); + return; + } + + while(n > 0) { + i = chartorune(&r, s); + if(i == 0){ + s++; + --n; + continue; + } + memmove(buf, s, i); + buf[i] = 0; + n -= i; + s += i; + vgascreenputc(buf); + } + + unlock(&cgascreenlock); +} + +void +cursorenable(void) +{ +} + +void +cursordisable(void) +{ +} + +typedef struct Drawcursor Drawcursor; + + + +void +cursorupdate(Rectangle r) +{ + USED(r); +} + +void +drawcursor(Drawcursor *c) +{ + USED(c); +} + +uchar* +attachscreen(Rectangle *r, ulong *chan, int* d, int *width, int *softscreen) +{ + static Rectangle screenr = {0, 0, 0, 0}; + static uchar *bdata; + if (bdata == nil) + if ((bdata = malloc(1)) == nil) + return nil; + *r = screenr; + *chan = RGB24; + *d = chantodepth(RGB24); + *width = 0; + *softscreen = 0; + return bdata; +} + +void +flushmemscreen(Rectangle r) +{ + USED(r); +} + +void +blankscreen(int i) +{ + USED(i); +} + +void +getcolor(ulong p, ulong *pr, ulong *pg, ulong *pb) +{ + USED(p); + USED(pr); + USED(pg); + USED(pb); + +} + +int +setcolor(ulong p, ulong r, ulong g, ulong b) +{ + USED(p); + USED(r); + USED(g); + USED(b); + return ~0; +} + +void (*screenputs)(char*, int) = cgascreenputs; diff --git a/os/pc/crystal.h b/os/pc/crystal.h new file mode 100644 index 00000000..63152c8f --- /dev/null +++ b/os/pc/crystal.h @@ -0,0 +1,1118 @@ +/* + * Micro code for the crystal musicam decoder on the Boffin MPEG decoder + */ +static +uchar crystal[] = +{ + 0x10,0x10,0x06,0x00,0x00,0x00,0x00,0x00,0x03,0xa0,0x00,0x1a,0x9f,0x00,0x39,0x5f, + 0x00,0xfe,0x9f,0x02,0x84,0x1f,0x03,0x35,0xbf,0x12,0x4e,0x1f,0x24,0xa3,0xc0,0xed, + 0xb1,0xe1,0x03,0x35,0xbf,0xfd,0x7b,0xe1,0x00,0xfe,0x9f,0xff,0xc6,0xa1,0x00,0x1a, + 0x9f,0xff,0xfc,0x60,0xff,0xff,0xe0,0x00,0x03,0x40,0x00,0x19,0xff,0x00,0x32,0x1f, + 0x01,0x01,0xe0,0x02,0x56,0x7f,0x03,0x7b,0xbf,0x11,0x66,0xff,0x24,0x9d,0xff,0xec, + 0xcb,0x00,0x02,0xe8,0xdf,0xfd,0x4e,0x61,0x00,0xf9,0xff,0xff,0xbf,0x20,0x00,0x1b, + 0x3f,0xff,0xfc,0x21,0xff,0xff,0xe0,0x00,0x03,0x00,0x00,0x19,0x3f,0x00,0x2b,0x60, + 0x01,0x03,0xff,0x02,0x29,0x20,0x03,0xba,0xff,0x10,0x7f,0xdf,0x24,0x8c,0xff,0xeb, + 0xe5,0x01,0x02,0x95,0x00,0xfd,0x21,0x20,0x00,0xf3,0xff,0xff,0xb7,0x61,0x00,0x1b, + 0xbf,0xff,0xfb,0xa0,0xff,0xff,0xe0,0x00,0x02,0xa0,0x00,0x18,0x80,0x00,0x24,0xc0, + 0x01,0x04,0xe0,0x01,0xfb,0xe0,0x03,0xf3,0x7f,0x0f,0x99,0x5f,0x24,0x70,0xc0,0xeb, + 0x00,0x41,0x02,0x3a,0x20,0xfc,0xf4,0x61,0x00,0xec,0xa0,0xff,0xaf,0x60,0x00,0x1c, + 0x20,0xff,0xfb,0x40,0xff,0xff,0xe0,0x00,0x02,0x60,0x00,0x17,0xc0,0x00,0x1e,0x80, + 0x01,0x04,0x9f,0x01,0xcf,0x1f,0x04,0x25,0x80,0x0e,0xb3,0xff,0x24,0x49,0x20,0xea, + 0x1d,0x60,0x01,0xd7,0xff,0xfc,0xc8,0x61,0x00,0xe3,0xc0,0xff,0xa7,0x21,0x00,0x1c, + 0x5f,0xff,0xfa,0xe1,0xff,0xff,0xe0,0x00,0x02,0x1f,0x00,0x16,0xdf,0x00,0x18,0x9f, + 0x01,0x03,0x5f,0x01,0xa2,0xdf,0x04,0x50,0xff,0x0d,0xd0,0x20,0x24,0x16,0x7f,0xe9, + 0x3c,0xe0,0x01,0x6e,0xe0,0xfc,0x9d,0x21,0x00,0xd9,0x5f,0xff,0x9e,0xa0,0x00,0x1c, + 0x80,0xff,0xfa,0x60,0xff,0xff,0xe0,0x00,0x02,0x00,0x00,0x16,0x00,0x00,0x13,0x20, + 0x01,0x01,0x1f,0x01,0x77,0x7f,0x04,0x76,0x5f,0x0c,0xee,0x40,0x23,0xd8,0xdf,0xe8, + 0x5f,0x40,0x00,0xfe,0x9f,0xfc,0x73,0x21,0x00,0xcd,0x7f,0xff,0x96,0x01,0x00,0x1c, + 0x80,0xff,0xf9,0xe0,0xff,0xff,0xc0,0x00,0x01,0xbf,0x00,0x15,0x1f,0x00,0x0d,0xe0, + 0x00,0xfd,0xff,0x01,0x4c,0xdf,0x04,0x95,0xa0,0x0c,0x0e,0xbf,0x23,0x90,0x5f,0xe7, + 0x84,0xe1,0x00,0x87,0x40,0xfc,0x4a,0x60,0x00,0xbf,0xdf,0xff,0x8d,0x21,0x00,0x1c, + 0x5f,0xff,0xf9,0x60,0xff,0xff,0xc0,0x00,0x01,0x9f,0x00,0x14,0x1f,0x00,0x09,0x00, + 0x00,0xfa,0x20,0x01,0x23,0x40,0x04,0xaf,0x00,0x0b,0x32,0x1f,0x23,0x3d,0x20,0xe6, + 0xae,0x61,0x00,0x08,0xbf,0xfc,0x23,0x41,0x00,0xb0,0xc0,0xff,0x84,0x20,0x00,0x1c, + 0x00,0xff,0xf8,0xc0,0xff,0xff,0xc0,0x00,0x01,0x60,0x00,0x13,0x40,0x00,0x04,0x7f, + 0x00,0xf5,0x3f,0x00,0xfa,0xc0,0x04,0xc2,0xbf,0x0a,0x58,0x9f,0x22,0xdf,0x80,0xe5, + 0xdc,0x40,0xff,0x83,0x41,0xfb,0xfd,0xe1,0x00,0xa0,0x00,0xff,0x7b,0x00,0x00,0x1b, + 0x9f,0xff,0xf8,0x20,0xff,0xff,0xc0,0x00,0x01,0x40,0x00,0x12,0x60,0x00,0x00,0x40, + 0x00,0xef,0xdf,0x00,0xd3,0x7f,0x04,0xd0,0xe0,0x09,0x82,0xbf,0x22,0x77,0xc0,0xe5, + 0x0e,0xc0,0xfe,0xf6,0xc1,0xfb,0xda,0xa0,0x00,0x8d,0x5f,0xff,0x71,0xe1,0x00,0x1a, + 0xe0,0xff,0xf7,0x80,0xff,0xff,0xa1,0x00,0x01,0x1f,0x00,0x11,0x60,0xff,0xfc,0x60, + 0x00,0xe9,0xc0,0x00,0xad,0x7f,0x04,0xd9,0xdf,0x08,0xb0,0xe0,0x22,0x05,0xdf,0xe4, + 0x46,0xc1,0xfe,0x63,0x80,0xfb,0xb9,0xa1,0x00,0x79,0x3f,0xff,0x68,0xc0,0x00,0x19, + 0xff,0xff,0xf6,0xe0,0xff,0xff,0xa1,0x00,0x00,0xff,0x00,0x10,0x7f,0xff,0xf8,0xe0, + 0x00,0xe3,0x20,0x00,0x88,0xdf,0x04,0xdd,0xc0,0x07,0xe3,0x5f,0x21,0x8a,0x7f,0xe3, + 0x84,0x61,0xfd,0xc9,0x60,0xfb,0x9b,0x40,0x00,0x63,0x40,0xff,0x5f,0xa1,0x00,0x19, + 0x00,0xff,0xf6,0x21,0xff,0xff,0x81,0x00,0x00,0xe0,0x00,0x0f,0xa0,0xff,0xf5,0xa1, + 0x00,0xdb,0xe0,0x00,0x65,0xbf,0x04,0xdc,0xdf,0x07,0x1a,0x7f,0x21,0x05,0xa0,0xe2, + 0xc8,0x40,0xfd,0x28,0xc0,0xfb,0x7f,0xa1,0x00,0x4b,0x9f,0xff,0x56,0x80,0x00,0x17, + 0x9f,0xff,0xf5,0x61,0xff,0xff,0x81,0x00,0x00,0xe0,0x00,0x0e,0x9f,0xff,0xf2,0xc0, + 0x00,0xd4,0x40,0x00,0x44,0x1f,0x04,0xd7,0x7f,0x06,0x56,0x7f,0x20,0x77,0xc0,0xe2, + 0x12,0xe0,0xfc,0x81,0xc0,0xfb,0x67,0x00,0x00,0x32,0x3f,0xff,0x4d,0x80,0x00,0x16, + 0x20,0xff,0xf4,0xa0,0xff,0xff,0x60,0x00,0x00,0xc0,0x00,0x0d,0xe0,0xff,0xf0,0x21, + 0x00,0xcc,0x3f,0x00,0x23,0xff,0x04,0xcd,0xc0,0x05,0x97,0xe0,0x1f,0xe1,0x40,0xe1, + 0x64,0x80,0xfb,0xd4,0x80,0xfb,0x51,0xe1,0x00,0x17,0x20,0xff,0x44,0xc1,0x00,0x14, + 0x60,0xff,0xf3,0xe0,0xff,0xff,0x60,0x00,0x00,0xa0,0x00,0x0c,0xff,0xff,0xed,0xc1, + 0x00,0xc3,0xdf,0x00,0x05,0xa0,0x04,0xbf,0xdf,0x04,0xde,0xe0,0x1f,0x42,0x60,0xe0, + 0xbd,0xa0,0xfb,0x21,0x20,0xfb,0x40,0x21,0xff,0xfa,0x60,0xff,0x3c,0x21,0x00,0x12, + 0x3f,0xff,0xf3,0x01,0xff,0xff,0x40,0x00,0x00,0xa0,0x00,0x0c,0x20,0xff,0xeb,0xa0, + 0x00,0xbb,0x3f,0xff,0xe8,0xe0,0x04,0xae,0x1f,0x04,0x2b,0x80,0x1e,0x9b,0x80,0xe0, + 0x1e,0xc0,0xfa,0x68,0x20,0xfb,0x32,0x40,0xff,0xdc,0x01,0xff,0x33,0xc1,0x00,0x0f, + 0xdf,0xff,0xf2,0x20,0xff,0xff,0x20,0x00,0x00,0x7f,0x00,0x0b,0x60,0xff,0xe9,0xe0, + 0x00,0xb2,0x80,0xff,0xcd,0xc1,0x04,0x99,0x00,0x03,0x7e,0x40,0x1d,0xed,0x20,0xdf, + 0x88,0x40,0xf9,0xa9,0x81,0xfb,0x28,0x81,0xff,0xbb,0xe1,0xff,0x2b,0xc0,0x00,0x0d, + 0x40,0xff,0xf1,0x61,0xff,0xff,0x20,0x00,0x00,0x7f,0x00,0x0a,0x9f,0xff,0xe8,0x61, + 0x00,0xa9,0x80,0xff,0xb4,0x61,0x04,0x80,0x5f,0x02,0xd7,0x40,0x1d,0x37,0xc0,0xde, + 0xfa,0x60,0xf8,0xe5,0x81,0xfb,0x23,0x21,0xff,0x9a,0x41,0xff,0x24,0x20,0x00,0x0a, + 0x5f,0xff,0xf0,0x60,0xff,0xff,0x01,0x00,0x00,0x5f,0x00,0x09,0xdf,0xff,0xe7,0x00, + 0x00,0xa0,0x5f,0xff,0x9c,0xc0,0x04,0x64,0xc0,0x02,0x36,0xa0,0x1c,0x7b,0x9f,0xde, + 0x75,0x81,0xf8,0x1c,0xa1,0xfb,0x22,0x40,0xff,0x77,0x21,0xff,0x1c,0xe0,0x00,0x07, + 0x20,0xff,0xef,0x81,0xff,0xfe,0xe1,0x00,0x00,0x5f,0x00,0x09,0x20,0xff,0xe6,0x01, + 0x00,0x97,0x40,0xff,0x86,0xc1,0x04,0x46,0x5f,0x01,0x9c,0x80,0x1b,0xb9,0x3f,0xdd, + 0xfa,0x21,0xf7,0x4f,0x20,0xfb,0x26,0x21,0xff,0x52,0x81,0xff,0x16,0x40,0x00,0x03, + 0xa0,0xff,0xee,0xa0,0xff,0xfe,0xc0,0x00,0x00,0x40,0x00,0x08,0x80,0xff,0xe5,0x20, + 0x00,0x8e,0x1f,0xff,0x72,0xa1,0x04,0x25,0x60,0x01,0x09,0x3f,0x1a,0xf1,0x40,0xdd, + 0x88,0x40,0xf6,0x7d,0x41,0xfb,0x2f,0x20,0xff,0x2c,0x81,0xff,0x10,0x21,0xff,0xff, + 0xc0,0xff,0xed,0xa0,0xff,0xfe,0xa0,0x00,0x00,0x40,0x00,0x07,0xe0,0xff,0xe4,0x61, + 0x00,0x85,0x00,0xff,0x60,0x00,0x04,0x02,0x1f,0x00,0x7c,0xbf,0x1a,0x23,0xc0,0xdd, + 0x20,0x80,0xf5,0xa7,0x61,0xfb,0x3d,0x41,0xff,0x05,0x40,0xff,0x0a,0xc1,0xff,0xfb, + 0x81,0xff,0xec,0xc0,0xff,0xfe,0x61,0x00,0x00,0x40,0x00,0x07,0x40,0xff,0xe4,0x00, + 0x00,0x7b,0xe0,0xff,0x4f,0x40,0x03,0xdc,0xbf,0xff,0xf7,0x41,0x19,0x51,0x9f,0xdc, + 0xc2,0xe0,0xf4,0xcd,0xe1,0xfb,0x51,0x00,0xfe,0xdc,0xc0,0xff,0x05,0xe0,0xff,0xf7, + 0x00,0xff,0xeb,0xe1,0xff,0xfe,0x41,0x00,0x00,0x40,0x00,0x06,0xa0,0xff,0xe3,0xa1, + 0x00,0x72,0xdf,0xff,0x40,0x21,0x03,0xb5,0xa0,0xff,0x78,0xc0,0x18,0x7b,0x1f,0xdc, + 0x6f,0xa1,0xf3,0xf1,0x41,0xfb,0x6a,0x60,0xfe,0xb3,0x21,0xff,0x02,0x01,0xff,0xf2, + 0x20,0xff,0xea,0xe1,0xff,0xfe,0x00,0x00,0x00,0x20,0x00,0x06,0x20,0xff,0xe3,0x80, + 0x00,0x69,0xff,0xff,0x32,0x81,0x03,0x8c,0xdf,0xff,0x01,0x61,0x17,0xa0,0xc0,0xdc, + 0x27,0x21,0xf3,0x11,0xc0,0xfb,0x89,0xa1,0xfe,0x88,0x81,0xfe,0xfe,0xe1,0xff,0xec, + 0xe0,0xff,0xea,0x00,0xff,0xfd,0xe1,0x00,0x00,0x20,0x00,0x05,0xa0,0xff,0xe3,0x80, + 0x00,0x61,0x60,0xff,0x26,0xa1,0x03,0x62,0xdf,0xfe,0x91,0x20,0x16,0xc3,0x20,0xdb, + 0xe9,0x81,0xf2,0x2f,0xe0,0xfb,0xaf,0x01,0xfe,0x5d,0x21,0xfe,0xfc,0xa1,0xff,0xe7, + 0x61,0xff,0xe9,0x21,0xff,0xfd,0xa0,0x00,0x00,0x20,0x00,0x05,0x1f,0xff,0xe3,0xa1, + 0x00,0x58,0xdf,0xff,0x1c,0x40,0x03,0x37,0x9f,0xfe,0x28,0x01,0x15,0xe2,0xa0,0xdb, + 0xb6,0xe0,0xf1,0x4c,0x01,0xfb,0xda,0x80,0xfe,0x30,0xe1,0xfe,0xfb,0x61,0xff,0xe1, + 0x80,0xff,0xe8,0x40,0xff,0xfd,0x60,0x00,0x00,0x20,0x00,0x04,0xc0,0xff,0xe3,0xe0, + 0x00,0x50,0xa0,0xff,0x13,0x60,0x03,0x0b,0x9f,0xfd,0xc5,0xe0,0x14,0xff,0xbf,0xdb, + 0x8f,0x40,0xf0,0x66,0xa1,0xfc,0x0c,0x81,0xfe,0x04,0x20,0xfe,0xfb,0x20,0xff,0xdb, + 0x40,0xff,0xe7,0x80,0xff,0xfd,0x00,0x00,0x00,0x20,0x00,0x04,0x60,0xff,0xe4,0x41, + 0x00,0x48,0x9f,0xff,0x0c,0x01,0x02,0xde,0xe0,0xfd,0x6b,0x00,0x14,0x1a,0xff,0xdb, + 0x73,0x01,0xef,0x80,0x21,0xfc,0x45,0x01,0xfd,0xd6,0xe0,0xfe,0xfc,0x01,0xff,0xd4, + 0xa0,0xff,0xe6,0xc1,0xff,0xfc,0xc0,0x00,0x00,0x20,0x00,0x03,0xdf,0xff,0xe4,0xc1, + 0x00,0x40,0xe0,0xff,0x06,0x01,0x02,0xb1,0x9f,0xfd,0x17,0x21,0x13,0x35,0x00,0xdb, + 0x62,0x01,0xee,0x99,0x01,0xfc,0x84,0x41,0xfd,0xa9,0x81,0xfe,0xfe,0x20,0xff,0xcd, + 0xe1,0xff,0xe6,0x01,0x12,0x28,0x00,0xba,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00, + 0x04,0x00,0x00,0x05,0x00,0x00,0x06,0x00,0x00,0x07,0x00,0x00,0x08,0x00,0x00,0x09, + 0x00,0x00,0x0a,0x00,0x00,0x0b,0x00,0x00,0x0c,0x00,0x00,0x0d,0x00,0x00,0x0e,0x00, + 0x00,0x0f,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x02,0x00,0x00, + 0x03,0x00,0x00,0x04,0x00,0x00,0x05,0x00,0x00,0x06,0x00,0x00,0x07,0x00,0x00,0x08, + 0x00,0x00,0x09,0x00,0x00,0x0a,0x00,0x00,0x0b,0x00,0x00,0x0c,0x00,0x00,0x0d,0x00, + 0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x02,0x00,0x00,0x03,0x00,0x00, + 0x04,0x00,0x00,0x05,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x10, + 0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x03,0x00,0x00,0x04,0x00,0x00,0x05,0x00, + 0x00,0x06,0x00,0x00,0x07,0x00,0x00,0x08,0x00,0x00,0x09,0x00,0x00,0x0a,0x00,0x00, + 0x0b,0x00,0x00,0x0c,0x00,0x00,0x0d,0x00,0x00,0x0e,0x00,0x00,0x0f,0x00,0x00,0x00, + 0x00,0x00,0x01,0x00,0x00,0x03,0x00,0x00,0x04,0x00,0x00,0x05,0x00,0x00,0x06,0x00, + 0x00,0x07,0x12,0x66,0x00,0xbd,0x40,0x00,0x00,0x32,0xcb,0xfd,0x28,0x51,0x45,0x20, + 0x00,0x00,0x19,0x65,0xfe,0x14,0x28,0xa2,0x10,0x00,0x00,0x0c,0xb2,0xff,0x0a,0x14, + 0x51,0x08,0x00,0x00,0x06,0x59,0x7f,0x05,0x0a,0x28,0x04,0x00,0x00,0x03,0x2c,0xbf, + 0x02,0x85,0x14,0x02,0x00,0x00,0x01,0x96,0x5f,0x01,0x42,0x8a,0x01,0x00,0x00,0x00, + 0xcb,0x2f,0x00,0xa1,0x45,0x00,0x80,0x00,0x00,0x65,0x97,0x00,0x50,0xa2,0x00,0x40, + 0x00,0x00,0x32,0xcb,0x00,0x28,0x51,0x00,0x20,0x00,0x00,0x19,0x65,0x00,0x14,0x28, + 0x00,0x10,0x00,0x00,0x0c,0xb2,0x00,0x0a,0x14,0x00,0x08,0x00,0x00,0x06,0x59,0x00, + 0x05,0x0a,0x00,0x04,0x00,0x00,0x03,0x2c,0x00,0x02,0x85,0x00,0x02,0x00,0x00,0x01, + 0x96,0x00,0x01,0x42,0x00,0x01,0x00,0x00,0x00,0xcb,0x00,0x00,0xa1,0x00,0x00,0x80, + 0x00,0x00,0x65,0x00,0x00,0x50,0x00,0x00,0x40,0x00,0x00,0x32,0x00,0x00,0x28,0x00, + 0x00,0x1f,0x00,0x00,0x19,0x00,0x00,0x14,0x00,0x00,0x0f,0x00,0x00,0x0c,0x00,0x00, + 0x0a,0x00,0x00,0x08,0x00,0x00,0x06,0x00,0x00,0x05,0x00,0x00,0x03,0x00,0x00,0x03, + 0x00,0x00,0x02,0x12,0x10,0x00,0x48,0x00,0x00,0x01,0x00,0x00,0x02,0x00,0x00,0x04, + 0x00,0x00,0x08,0x00,0x00,0x10,0x00,0x00,0x20,0x00,0x00,0x40,0x00,0x00,0x80,0x00, + 0x01,0x00,0x00,0x02,0x00,0x00,0x04,0x00,0x00,0x08,0x00,0x00,0x10,0x00,0x00,0x20, + 0x00,0x00,0x40,0x00,0x00,0x80,0x00,0x01,0x00,0x00,0x02,0x00,0x00,0x04,0x00,0x00, + 0x08,0x00,0x00,0x10,0x00,0x00,0x20,0x00,0x00,0x40,0x00,0x00,0x80,0x00,0x00,0x12, + 0xa5,0x00,0x33,0xff,0xff,0xfb,0xff,0xff,0xf9,0x00,0x00,0x03,0xff,0xff,0xf6,0x00, + 0x00,0x04,0x00,0x00,0x05,0x00,0x00,0x06,0x00,0x00,0x07,0x00,0x00,0x08,0x00,0x00, + 0x09,0x00,0x00,0x0a,0x00,0x00,0x0b,0x00,0x00,0x0c,0x00,0x00,0x0d,0x00,0x00,0x0e, + 0x00,0x00,0x0f,0x00,0x00,0x10,0x12,0xb6,0x00,0x7e,0x00,0x00,0x04,0x00,0x00,0x04, + 0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00, + 0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x03,0x00,0x00, + 0x03,0x00,0x00,0x03,0x00,0x00,0x03,0x00,0x00,0x03,0x00,0x00,0x03,0x00,0x00,0x03, + 0x00,0x00,0x03,0x00,0x00,0x03,0x00,0x00,0x03,0x00,0x00,0x03,0x00,0x00,0x03,0x00, + 0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00, + 0x02,0x00,0x00,0x02,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x03,0x00,0x00,0x03, + 0x00,0x00,0x03,0x00,0x00,0x03,0x00,0x00,0x03,0x00,0x00,0x03,0x00,0x00,0x03,0x00, + 0x00,0x03,0x00,0x00,0x03,0x00,0x00,0x03,0x12,0xe0,0x00,0x0c,0x00,0x00,0x19,0x00, + 0x00,0x1c,0x00,0x00,0x06,0x00,0x00,0x0a,0x12,0xe4,0x00,0x0c,0x00,0x00,0x03,0x00, + 0x00,0x07,0x00,0x00,0x0b,0x00,0x00,0x0f,0x12,0xe8,0x00,0x0c,0x00,0x02,0xb6,0x00, + 0x02,0xb6,0x00,0x02,0xd4,0x00,0x02,0xd4,0x12,0xec,0x00,0x0c,0x00,0x02,0x28,0x00, + 0x02,0x28,0x00,0x02,0x50,0x00,0x02,0x50,0x12,0xf0,0x00,0x33,0x00,0x00,0x03,0x00, + 0x00,0x05,0x00,0x00,0x07,0x00,0x00,0x09,0x00,0x00,0x0f,0x00,0x00,0x1f,0x00,0x00, + 0x3f,0x00,0x00,0x7f,0x00,0x00,0xff,0x00,0x01,0xff,0x00,0x03,0xff,0x00,0x07,0xff, + 0x00,0x0f,0xff,0x00,0x1f,0xff,0x00,0x3f,0xff,0x00,0x7f,0xff,0x00,0xff,0xff,0x13, + 0x01,0x00,0x2a,0x00,0x01,0x40,0x00,0x01,0xe0,0x00,0x02,0x30,0x00,0x02,0x80,0x00, + 0x03,0x20,0x00,0x03,0xc0,0x00,0x04,0x60,0x00,0x05,0x00,0x00,0x06,0x40,0x00,0x07, + 0x80,0x00,0x08,0xc0,0x00,0x0a,0x00,0x00,0x0c,0x80,0x00,0x0f,0x00,0x13,0x0f,0x00, + 0x09,0x00,0x01,0xb9,0x00,0x01,0xe0,0x00,0x01,0x40,0x13,0x12,0x00,0x0c,0x00,0x00, + 0x58,0x00,0x00,0x5e,0x00,0x00,0x1a,0x00,0x00,0x26,0x13,0x16,0x00,0x6f,0x16,0xa0, + 0x9e,0xe9,0x5f,0x62,0x1d,0x90,0x6b,0xe2,0x6f,0x95,0x0c,0x3e,0xf1,0xf3,0xc1,0x0f, + 0x1f,0x62,0x97,0xe0,0x9d,0x69,0x1a,0x9b,0x66,0x11,0xc7,0x3b,0xee,0x38,0xc5,0x06, + 0x3e,0x2e,0x1f,0xd8,0x8d,0x1e,0x9f,0x41,0x1c,0x38,0xb2,0x18,0xbc,0x80,0x14,0x4c, + 0xf3,0x0f,0x15,0xae,0x09,0x4a,0x03,0x03,0x22,0xf4,0x01,0x91,0xf6,0x17,0xb5,0xdf, + 0x0d,0xae,0x88,0x1e,0x21,0x21,0x07,0xc6,0x7e,0x1b,0x72,0x83,0x13,0x0f,0xf7,0x1f, + 0xa7,0x55,0x1f,0xf6,0x21,0x15,0x7d,0x69,0x1c,0xed,0x7a,0x0a,0xc7,0xcd,0x1f,0x0a, + 0x7e,0x10,0x73,0x87,0x19,0xb3,0xe0,0x04,0xb2,0x04,0x00,0x00,0x00,0x1d,0x40,0x05, + 0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x1f,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x01,0x5f,0x00,0x00,0x01,0x00,0x00,0x03,0x00, + 0x00,0x07,0x00,0x00,0x0f,0x00,0x00,0x1f,0x00,0x00,0x3f,0x00,0x00,0x7f,0x00,0x00, + 0xff,0x00,0x01,0xff,0x00,0x03,0xff,0x00,0x07,0xff,0x00,0x0f,0xff,0x00,0x1f,0xff, + 0x00,0x3f,0xff,0x00,0x7f,0xff,0x00,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x08,0x00, + 0x00,0x10,0xff,0xff,0xfd,0xff,0xff,0xff,0xff,0xf0,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00, + 0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x00, + 0x00,0x0d,0x40,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x05,0xc0,0x00,0x05,0xc0,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x00,0x00,0x0f,0x00,0x00,0x0f,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x00, + 0x02,0x00,0x00,0x00,0x00,0x05,0xe0,0x00,0x05,0xe0,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0xc0,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x06,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x04,0x80,0x00,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x04,0xa0,0x00,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0xc0,0x00,0x60,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0xe0,0x00,0x60, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x05,0x00,0x00,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x05,0x20,0x00,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x40,0x00,0x60,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x60,0x00,0x60, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x07,0xc0,0x00,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x05,0xc0,0x00,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0xe0,0x00,0x30,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x00,0x05,0x40, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x05,0x80,0x00,0x66,0x2a,0xaa,0xaa,0x33,0x33,0x33,0x24,0x92,0x49,0x38,0xe3,0x8e, + 0x22,0x22,0x22,0x21,0x08,0x42,0x20,0x82,0x08,0x20,0x40,0x81,0x20,0x20,0x20,0x20, + 0x10,0x08,0x20,0x08,0x02,0x20,0x04,0x00,0x20,0x02,0x00,0x20,0x01,0x00,0x20,0x00, + 0x80,0x20,0x00,0x40,0x20,0x00,0x20,0x10,0x00,0x00,0x10,0x00,0x00,0x08,0x00,0x00, + 0x10,0x00,0x00,0x04,0x00,0x00,0x02,0x00,0x00,0x01,0x00,0x00,0x00,0x80,0x00,0x00, + 0x40,0x00,0x00,0x20,0x00,0x00,0x10,0x00,0x00,0x08,0x00,0x00,0x04,0x00,0x00,0x02, + 0x00,0x00,0x00,0xff,0x00,0x00,0x80,0x00,0x00,0x40,0x04,0x78,0x00,0x18,0x01,0xe8, + 0x0d,0x03,0x6c,0x16,0x02,0x40,0x0a,0x00,0x10,0x03,0xa8,0x00,0x00,0x00,0xcc,0x00, + 0x30,0x00,0x00,0x00,0x00,0x02,0x10,0x00,0x00,0x1b,0xcf,0x83,0x40,0x00,0x00,0x00, + 0xc7,0x8b,0x17,0x70,0x41,0xc1,0xc7,0x8c,0x26,0xc7,0x8c,0x26,0xc7,0x8b,0x36,0xc7, + 0x8b,0xf2,0xc7,0x8c,0x52,0x13,0x40,0x1b,0xea,0x97,0x80,0xc0,0x00,0x04,0x00,0x97, + 0x80,0xc8,0x00,0x04,0x00,0x70,0x78,0xcf,0x70,0x7b,0xcd,0x0f,0xd7,0xc2,0x00,0x00, + 0x01,0xc9,0x83,0x46,0x70,0x02,0xec,0x97,0x80,0xdc,0x00,0x00,0x05,0x97,0x80,0xe0, + 0x00,0x07,0xbf,0xd7,0x02,0x8f,0x70,0x7f,0xd7,0x70,0x7c,0xc7,0x70,0x7d,0xcb,0x70, + 0x7e,0xd5,0x97,0x80,0x4c,0x00,0x06,0x00,0x97,0x80,0x4d,0x00,0x0d,0x40,0x70,0x50, + 0x51,0x70,0x10,0x52,0x70,0x10,0x55,0x70,0x5a,0x5b,0x70,0x5a,0x5c,0x70,0x4e,0x62, + 0x70,0x10,0x3e,0x0f,0xcd,0xcd,0xff,0xdf,0xff,0x0f,0x4b,0x4b,0xff,0xff,0xf7,0x70, + 0x10,0x46,0xd5,0x03,0x3a,0x70,0x10,0xc2,0xd4,0x40,0x00,0xd1,0x80,0x00,0xd5,0x80, + 0x00,0x70,0x10,0xd4,0xc7,0x8a,0x47,0x70,0x00,0x1e,0x70,0x10,0x1b,0xc7,0x89,0xb0, + 0x23,0x10,0xc2,0xc9,0x83,0x70,0xcf,0x83,0x5a,0x27,0x1b,0x1b,0x00,0x00,0x01,0x37, + 0x1b,0xc2,0x00,0x00,0x0c,0xc9,0x83,0x6c,0x70,0x10,0x34,0xc7,0x89,0xb0,0x0b,0x00, + 0x20,0xc9,0x03,0x6a,0xc7,0x89,0xb0,0x0b,0x00,0xc2,0xc9,0x03,0x6a,0xc7,0x89,0xb0, + 0x0b,0x00,0xc2,0xc9,0x83,0x7c,0x97,0x80,0x21,0x00,0x00,0x02,0x70,0x00,0x1e,0xc7, + 0x89,0xb0,0x0b,0x00,0x22,0x70,0x12,0x1e,0xc7,0x89,0xb0,0x0b,0x0f,0x61,0x47,0x61, + 0x23,0x00,0x10,0x00,0x70,0x24,0x6b,0x47,0x61,0xc2,0x00,0x40,0x00,0x0b,0x01,0x24, + 0x47,0x61,0xc2,0x00,0x80,0x00,0x0b,0x00,0x25,0x47,0x61,0xc2,0x01,0x00,0x00,0x0b, + 0x00,0x26,0x47,0x61,0xc2,0x04,0x00,0x00,0x0b,0x01,0x27,0x47,0x61,0xc2,0x10,0x00, + 0x00,0x0b,0x01,0x28,0x47,0x61,0xc2,0x20,0x00,0x00,0x0b,0x00,0x29,0x47,0x61,0xc2, + 0x40,0x00,0x00,0x0b,0x00,0x2a,0x0f,0x61,0x2b,0x00,0x00,0x03,0x27,0x24,0xc2,0x00, + 0x03,0x0f,0xd1,0x40,0x00,0x90,0x00,0x1f,0x4f,0x1f,0x1f,0x00,0x10,0x00,0x27,0x23, + 0xc2,0x00,0x03,0x00,0xd1,0x40,0x00,0x90,0x00,0x30,0x4f,0x30,0x30,0x00,0x00,0x90, + 0x40,0x10,0xc2,0x70,0x30,0xc2,0xd6,0x00,0x17,0x7b,0x1f,0xc2,0x58,0x10,0x3d,0x47, + 0x3d,0x3d,0x00,0x20,0x00,0x4f,0x3d,0x3d,0x00,0x00,0x08,0x4f,0x25,0x25,0x00,0x00, + 0x08,0x70,0x25,0xc2,0x23,0x3d,0x3d,0x37,0x3d,0x3e,0x00,0x00,0x20,0xca,0x03,0x6a, + 0x27,0x3e,0xc2,0x00,0x35,0xe0,0xca,0x83,0x6a,0x27,0x23,0xc2,0x00,0x00,0x00,0xc9, + 0x03,0xeb,0x37,0x27,0xc2,0x00,0x00,0x03,0xc9,0x03,0xcc,0x37,0x23,0x1f,0x00,0x00, + 0x06,0x37,0x1f,0xc2,0x00,0x00,0x00,0xcb,0x83,0xd9,0xcf,0x83,0xd1,0x37,0x23,0x1f, + 0x00,0x00,0x02,0x37,0x1f,0xc2,0x00,0x00,0x00,0xcb,0x83,0xd9,0x37,0x24,0xc2,0x00, + 0x00,0x02,0xc9,0x03,0xd7,0x97,0x80,0x2d,0x00,0x00,0x02,0xcf,0x83,0xef,0x70,0x01, + 0x2d,0xcf,0x83,0xef,0x37,0x27,0xc2,0x00,0x00,0x03,0xc9,0x03,0xe3,0x37,0x23,0x1f, + 0x00,0x00,0x0a,0xcb,0x83,0xee,0x37,0x24,0xc2,0x00,0x00,0x01,0xc9,0x03,0xee,0xcf, + 0x83,0xe9,0x37,0x23,0xc2,0x00,0x00,0x06,0xcb,0x83,0xee,0x37,0x24,0xc2,0x00,0x00, + 0x01,0xc9,0x03,0xee,0x70,0x00,0x2d,0xcf,0x83,0xef,0x37,0x24,0xc2,0x00,0x00,0x01, + 0xc9,0x83,0xe9,0x70,0x10,0x2d,0x27,0x22,0xc2,0x00,0x00,0x00,0xc9,0x83,0xf5,0x70, + 0x12,0x1e,0xc7,0x89,0xb0,0x0b,0x0f,0x2c,0x27,0x62,0x62,0x00,0x00,0x00,0xc9,0x03, + 0xfb,0x27,0x62,0x62,0xff,0xff,0xff,0xcb,0x83,0x66,0x37,0x27,0xc2,0x00,0x00,0x03, + 0xc9,0x84,0x01,0x70,0x00,0x2e,0x70,0x00,0x2f,0xcf,0x84,0x0a,0x97,0x80,0x2e,0x00, + 0x00,0x02,0x37,0x27,0xc2,0x00,0x00,0x01,0xc9,0x84,0x08,0x70,0x01,0x2f,0xcf,0x84, + 0x0a,0x97,0x80,0x2f,0x00,0x00,0x02,0x70,0x1d,0x37,0x70,0x3e,0x38,0x70,0x1c,0x39, + 0x70,0x5b,0x5c,0x27,0x2d,0xc2,0x00,0x03,0x12,0xd1,0x40,0x00,0x90,0x00,0x5d,0x37, + 0x27,0xc2,0x00,0x00,0x03,0xc9,0x04,0x17,0x4f,0x5d,0x5d,0x00,0x00,0x02,0x70,0x2e, + 0x32,0x27,0x2d,0xc2,0x00,0x02,0xe8,0xd1,0x40,0x00,0x90,0x00,0x30,0x97,0x80,0xd8, + 0x00,0x04,0x80,0x27,0x28,0xc2,0x00,0x02,0xe4,0xd5,0x40,0x00,0x94,0x00,0x31,0x94, + 0x00,0xc2,0xd6,0x40,0x00,0xc7,0x89,0x85,0x70,0x2f,0x32,0x27,0x2d,0xc2,0x00,0x02, + 0xe0,0xd1,0x40,0x00,0x30,0x31,0xc2,0xd6,0x40,0x00,0xc7,0x89,0x85,0x70,0x2e,0x32, + 0x97,0x80,0xf0,0x00,0x04,0x80,0x97,0x80,0xd8,0x00,0x04,0xa0,0x97,0x80,0x1e,0x00, + 0x00,0x02,0x27,0x28,0xc2,0x00,0x02,0xe4,0xd5,0x40,0x00,0x94,0x00,0x31,0x94,0x00, + 0xc2,0xd6,0x40,0x00,0xc7,0x89,0x61,0x70,0x2f,0x32,0x27,0x2d,0xc2,0x00,0x02,0xe0, + 0xd1,0x40,0x00,0x30,0x31,0xc2,0xd6,0x40,0x00,0xc7,0x89,0x61,0x27,0x64,0xc2,0x00, + 0x00,0x00,0xc9,0x84,0x6d,0x27,0x22,0xc2,0x00,0x00,0x00,0xc9,0x84,0x6d,0x70,0x37, + 0x1d,0x70,0x38,0x3e,0x70,0x39,0x1c,0x97,0x80,0x63,0x00,0xff,0xff,0x0f,0x61,0xc2, + 0x00,0xff,0x00,0xc7,0x8a,0x19,0x4f,0x61,0x5f,0x00,0x01,0x00,0x0f,0x5f,0xc2,0x00, + 0xff,0x00,0xc7,0x8a,0x19,0x70,0x11,0x1e,0x27,0x5d,0x5d,0xff,0xff,0xf8,0xca,0x84, + 0x5a,0xc7,0x8a,0x15,0xcf,0x84,0x55,0x70,0x00,0x1e,0x27,0x5d,0xc2,0x00,0x00,0x08, + 0xc9,0x04,0x61,0x23,0x14,0xc2,0xd6,0x40,0x00,0xc7,0x8a,0x3a,0x0f,0x63,0x63,0x00, + 0xff,0xff,0x70,0x2c,0xc2,0x33,0x63,0xc2,0xc9,0x04,0x6d,0x0f,0x4b,0x4b,0xff,0xff, + 0xf7,0x17,0x4b,0x4b,0x00,0x00,0x07,0x27,0x59,0x59,0x00,0x00,0x01,0xcf,0x83,0x66, + 0x70,0x5c,0x5b,0x70,0x24,0xc2,0x33,0x6b,0xc2,0xc9,0x83,0x5a,0x27,0x24,0xc2,0x00, + 0x04,0x74,0xcf,0xc0,0x00,0xcf,0x84,0x78,0xcf,0x84,0x7c,0xcf,0x84,0x80,0xcf,0x83, + 0x5a,0x70,0x79,0xcf,0x0f,0xcd,0xcd,0xff,0xfb,0xff,0xcf,0x84,0x83,0x70,0x78,0xcf, + 0x0f,0xcd,0xcd,0xff,0xfb,0xff,0xcf,0x84,0x83,0x70,0x7a,0xcf,0x17,0xcd,0xcd,0x00, + 0x04,0x00,0x0f,0xcd,0xc2,0x00,0x20,0x00,0xc9,0x84,0x8d,0x27,0x57,0xc2,0x00,0x00, + 0x00,0xc9,0x84,0x8d,0x0f,0x4b,0x4b,0xff,0xff,0xfb,0x17,0x4b,0x4b,0x00,0x00,0x0b, + 0x70,0x2e,0x32,0x97,0x80,0xf0,0x00,0x04,0x80,0x97,0x80,0xd8,0x00,0x04,0xa0,0x97, + 0x80,0xe8,0x00,0x07,0xc0,0x97,0x80,0x1e,0x00,0x00,0x06,0x27,0x28,0xc2,0x00,0x02, + 0xe4,0xd5,0x40,0x00,0x94,0x00,0x31,0x94,0x00,0xc2,0xd6,0x40,0x00,0xc7,0x89,0x06, + 0x70,0x2f,0x32,0x27,0x2d,0xc2,0x00,0x02,0xe0,0xd1,0x40,0x00,0x30,0x31,0xc2,0xd6, + 0x40,0x00,0xc7,0x89,0x06,0x70,0x2e,0x32,0x70,0x10,0x1b,0x97,0x80,0xf0,0x00,0x04, + 0x80,0x27,0x28,0xc2,0x00,0x02,0xe4,0xd5,0x40,0x00,0x94,0x00,0x31,0x94,0x00,0xc2, + 0xd6,0x40,0x00,0xc7,0x88,0xba,0x70,0x2f,0x32,0x27,0x2d,0xc2,0x00,0x02,0xe0,0xd1, + 0x40,0x00,0x30,0x31,0xc2,0xd6,0x40,0x00,0xc7,0x88,0xba,0x70,0x2e,0x32,0x70,0x10, + 0x1b,0x97,0x80,0xf0,0x00,0x04,0x80,0x27,0x28,0xc2,0x00,0x02,0xe4,0xd5,0x40,0x00, + 0x94,0x00,0x31,0x94,0x00,0xc2,0xd6,0x40,0x00,0xc7,0x86,0xbd,0x70,0x2f,0x32,0x27, + 0x2d,0xc2,0x00,0x02,0xe0,0xd1,0x40,0x00,0x30,0x31,0xc2,0x90,0x00,0x31,0xd6,0x40, + 0x00,0xc7,0x86,0xbd,0x37,0x31,0xc2,0x00,0x00,0x1d,0xd6,0x40,0x00,0xc7,0x88,0x74, + 0x97,0x80,0x1f,0x00,0x00,0x06,0x37,0x27,0xc2,0x00,0x00,0x03,0xc9,0x84,0xd5,0x97, + 0x80,0x30,0x00,0x00,0x02,0xcf,0x84,0xd6,0x70,0x00,0x30,0x27,0x1f,0xc2,0x00,0x04, + 0xd9,0xcf,0xc0,0x00,0xcf,0x85,0x18,0xcf,0x85,0x13,0xcf,0x85,0x01,0xcf,0x84,0xfc, + 0xcf,0x84,0xea,0xcf,0x84,0xe5,0xcf,0x84,0xe0,0x97,0x80,0x37,0x00,0x04,0xc0,0x97, + 0x80,0x1a,0x00,0x00,0x00,0xcf,0x85,0x34,0x97,0x80,0x37,0x00,0x05,0x20,0x97,0x80, + 0x1a,0x00,0x02,0x00,0xcf,0x85,0x34,0x22,0x14,0x16,0x0f,0x16,0x16,0x00,0x00,0x0f, + 0x27,0x17,0x17,0x00,0x00,0x01,0x27,0x19,0x19,0x00,0x00,0x01,0x0f,0x19,0x19,0x00, + 0x00,0x0f,0x97,0x80,0x37,0x00,0x04,0xe0,0x97,0x80,0x1a,0x00,0x00,0x00,0x27,0x4f, + 0x4f,0x00,0x00,0x00,0xc9,0x05,0x34,0x70,0x10,0x4f,0xcf,0x85,0x34,0x97,0x80,0x37, + 0x00,0x05,0x40,0x97,0x80,0x1a,0x00,0x02,0x00,0xcf,0x85,0x34,0x22,0x14,0x16,0x0f, + 0x16,0x16,0x00,0x00,0x0f,0x27,0x17,0x17,0x00,0x00,0x01,0x27,0x19,0x19,0x00,0x00, + 0x01,0x0f,0x19,0x19,0x00,0x00,0x0f,0x97,0x80,0x37,0x00,0x05,0x00,0x97,0x80,0x1a, + 0x00,0x00,0x00,0x27,0x4f,0x4f,0x00,0x00,0x00,0xc9,0x05,0x34,0x70,0x10,0x4f,0xcf, + 0x85,0x34,0x97,0x80,0x37,0x00,0x05,0x60,0x97,0x80,0x1a,0x00,0x02,0x00,0xcf,0x85, + 0x34,0x22,0x14,0x16,0x0f,0x16,0x16,0x00,0x00,0x0f,0x27,0x17,0x17,0x00,0x00,0x01, + 0x27,0x19,0x19,0x00,0x00,0x01,0x0f,0x19,0x19,0x00,0x00,0x0f,0x27,0x4f,0x4f,0x00, + 0x00,0x00,0xc9,0x05,0x25,0x70,0x10,0x4f,0x97,0x80,0xf0,0x00,0x04,0xc0,0x97,0x80, + 0xd0,0x00,0x05,0x20,0xd5,0x03,0x3a,0xd1,0x80,0x00,0xd6,0x00,0x02,0xc7,0x8a,0x9f, + 0xd1,0x80,0x04,0x27,0x34,0x34,0x00,0x00,0x01,0x37,0x34,0xc2,0x00,0x00,0x0c,0xc9, + 0x84,0xb6,0xcf,0x83,0x66,0x70,0x37,0xf0,0x27,0x37,0xd8,0x00,0x00,0x1f,0xd6,0x00, + 0x07,0xc7,0x8c,0x5a,0x70,0x37,0xf0,0x27,0x37,0xd8,0x00,0x00,0x0f,0xd6,0x00,0x03, + 0xc7,0x8c,0x5a,0x27,0x37,0xf0,0x00,0x00,0x1b,0x27,0x37,0xd8,0x00,0x00,0x14,0xd1, + 0x03,0x16,0xd5,0x03,0x16,0xc7,0x8c,0x61,0xc7,0x8c,0x61,0xc7,0x8c,0x61,0xc7,0x8c, + 0x61,0x70,0x37,0xf0,0x27,0x37,0xd8,0x00,0x00,0x07,0xd6,0x00,0x01,0xc7,0x8c,0x5a, + 0x27,0x37,0xf0,0x00,0x00,0x0d,0x27,0x37,0xd8,0x00,0x00,0x0a,0xd6,0x00,0x01,0xc7, + 0x8c,0x61,0x27,0x37,0xf0,0x00,0x00,0x10,0x27,0x37,0xd8,0x00,0x00,0x17,0xd6,0x00, + 0x01,0xc7,0x8c,0x5a,0x27,0x37,0xf0,0x00,0x00,0x1c,0x27,0x37,0xd8,0x00,0x00,0x1b, + 0xd6,0x00,0x01,0xc7,0x8c,0x5a,0x70,0x37,0xf0,0x27,0x37,0xd8,0x00,0x00,0x03,0xc7, + 0x8c,0x5a,0x27,0x37,0xf0,0x00,0x00,0x06,0x27,0x37,0xd8,0x00,0x00,0x05,0xc7,0x8c, + 0x61,0x27,0x37,0xf0,0x00,0x00,0x08,0x27,0x37,0xd8,0x00,0x00,0x0b,0xc7,0x8c,0x5a, + 0x27,0x37,0xf0,0x00,0x00,0x0e,0x27,0x37,0xd8,0x00,0x00,0x0d,0xc7,0x8c,0x5a,0x27, + 0x37,0xf0,0x00,0x00,0x1d,0x27,0x37,0xd8,0x00,0x00,0x12,0xd1,0x03,0x1a,0xd5,0x03, + 0x18,0xd6,0x00,0x01,0xc7,0x8c,0x61,0xd1,0x03,0x19,0xd5,0x03,0x1a,0xd6,0x00,0x01, + 0xc7,0x8c,0x61,0x70,0x37,0xf0,0x27,0x37,0xd8,0x00,0x00,0x01,0xd1,0x03,0x16,0xd5, + 0x03,0x16,0xc7,0x8c,0x61,0x27,0x37,0xf0,0x00,0x00,0x03,0x27,0x37,0xd8,0x00,0x00, + 0x02,0xd1,0x03,0x1a,0xd5,0x03,0x18,0xc7,0x8c,0x6e,0x27,0x37,0xf0,0x00,0x00,0x04, + 0x27,0x37,0xd8,0x00,0x00,0x07,0xc7,0x8c,0x7b,0x27,0x37,0xf0,0x00,0x00,0x0e,0x27, + 0x37,0xd8,0x00,0x00,0x09,0xd1,0x03,0x1a,0xd5,0x03,0x18,0xc7,0x8c,0x61,0xd1,0x03, + 0x19,0xd5,0x03,0x1a,0xc7,0x8c,0x61,0x27,0x37,0xf0,0x00,0x00,0x10,0x27,0x37,0xd8, + 0x00,0x00,0x13,0xc7,0x8c,0x5a,0x27,0x37,0xf0,0x00,0x00,0x16,0x27,0x37,0xd8,0x00, + 0x00,0x15,0xc7,0x8c,0x5a,0x27,0x37,0xf0,0x00,0x00,0x18,0x27,0x37,0xd8,0x00,0x00, + 0x1b,0xc7,0x8c,0x5a,0x27,0x37,0xf0,0x00,0x00,0x1e,0x27,0x37,0xd8,0x00,0x00,0x1d, + 0xc7,0x8c,0x5a,0x27,0x37,0xf0,0x00,0x00,0x07,0x27,0x37,0xd8,0x00,0x00,0x04,0xd1, + 0x03,0x21,0xd5,0x03,0x1c,0xc7,0x8c,0x6e,0xd1,0x03,0x1e,0xd5,0x03,0x1f,0xc7,0x8c, + 0x6e,0x27,0x37,0xf0,0x00,0x00,0x08,0x27,0x37,0xd8,0x00,0x00,0x0b,0xc7,0x8c,0x7b, + 0x27,0x37,0xf0,0x00,0x00,0x0c,0x27,0x37,0xd8,0x00,0x00,0x0f,0xc7,0x8c,0x7b,0x27, + 0x37,0xf0,0x00,0x00,0x1e,0x27,0x37,0xd8,0x00,0x00,0x11,0xd1,0x03,0x21,0xd5,0x03, + 0x1c,0xc7,0x8c,0x61,0xd1,0x03,0x1d,0xd5,0x03,0x21,0xc7,0x8c,0x61,0x27,0x37,0xf0, + 0x00,0x00,0x1a,0x27,0x37,0xd8,0x00,0x00,0x15,0xd1,0x03,0x1e,0xd5,0x03,0x1f,0xc7, + 0x8c,0x61,0xd1,0x03,0x20,0xd5,0x03,0x1e,0xc7,0x8c,0x61,0x27,0x37,0xf0,0x00,0x00, + 0x0f,0x27,0x37,0xd8,0x00,0x00,0x08,0xd1,0x03,0x29,0xd5,0x03,0x22,0xc7,0x8c,0x6e, + 0xd1,0x03,0x25,0xd5,0x03,0x26,0xc7,0x8c,0x6e,0xd1,0x03,0x27,0xd5,0x03,0x24,0xc7, + 0x8c,0x6e,0xd1,0x03,0x23,0xd5,0x03,0x28,0xc7,0x8c,0x6e,0x27,0x37,0xf0,0x00,0x00, + 0x10,0x27,0x37,0xd8,0x00,0x00,0x13,0xc7,0x8c,0x7b,0x27,0x37,0xf0,0x00,0x00,0x14, + 0x27,0x37,0xd8,0x00,0x00,0x17,0xc7,0x8c,0x7b,0x27,0x37,0xf0,0x00,0x00,0x18,0x27, + 0x37,0xd8,0x00,0x00,0x1b,0xc7,0x8c,0x7b,0x27,0x37,0xf0,0x00,0x00,0x1c,0x27,0x37, + 0xd8,0x00,0x00,0x1f,0xc7,0x8c,0x7b,0xd1,0x80,0x00,0xd5,0x80,0x00,0x70,0x37,0xf0, + 0x97,0x80,0xd0,0x00,0x04,0xa0,0x97,0x80,0xd4,0x00,0x00,0x05,0x47,0xb2,0xc2,0x2d, + 0x41,0x3c,0x57,0x11,0xc2,0x02,0x00,0x00,0x86,0x00,0x96,0xd6,0x00,0x0e,0x70,0xb2, + 0x96,0x27,0x37,0xd8,0x00,0x00,0x1f,0xd1,0x03,0x2a,0xd5,0x03,0x32,0xd6,0x00,0x07, + 0xc7,0x8c,0x82,0xd1,0x03,0x31,0xd5,0x03,0x39,0x97,0x00,0xc2,0x13,0x12,0xc2,0xd7, + 0x40,0x00,0xd6,0x00,0x07,0xc7,0x8c,0x88,0x97,0x00,0xc2,0x1b,0x12,0xc2,0xd7,0x40, + 0x00,0x97,0x80,0xd4,0x00,0x00,0x09,0x97,0x80,0xd0,0x00,0x04,0xa0,0x70,0x1a,0xc2, + 0x23,0x19,0xf0,0xc7,0x8b,0x07,0x0f,0x17,0x17,0x00,0x00,0x01,0xc9,0x86,0x27,0x97, + 0x80,0xd0,0x00,0x04,0xbf,0xd6,0x00,0x1e,0xc7,0x8b,0x03,0xcf,0x86,0x29,0xd6,0x00, + 0x1e,0xc7,0x8b,0x07,0x70,0x37,0xe8,0xd1,0x80,0x04,0xd5,0x80,0x01,0x0f,0x17,0x17, + 0x00,0x00,0x01,0xc9,0x86,0x44,0x27,0x16,0xc2,0x00,0x00,0x00,0xd1,0x40,0x00,0x27, + 0x1a,0xf0,0x00,0x01,0x20,0xd6,0x00,0x0f,0xc7,0x86,0x5e,0x27,0x16,0xc2,0x00,0x01, + 0x10,0xd1,0x40,0x00,0x27,0x1a,0xf0,0x00,0x00,0x01,0xc7,0x86,0x99,0x27,0x16,0xc2, + 0x00,0x01,0x10,0xd1,0x40,0x00,0x27,0x1a,0xf0,0x00,0x00,0x10,0xd6,0x00,0x0e,0xc7, + 0x86,0x89,0xcf,0x86,0x57,0x27,0x16,0xc2,0x00,0x00,0x00,0xd1,0x40,0x00,0x27,0x1a, + 0xf0,0x00,0x01,0x00,0xd6,0x00,0x0f,0xc7,0x86,0x74,0x27,0x16,0xc2,0x00,0x01,0x10, + 0xd1,0x40,0x00,0x70,0x1a,0xf0,0xc7,0x86,0xab,0x27,0x16,0xc2,0x00,0x01,0x10,0xd1, + 0x40,0x00,0x27,0x1a,0xf0,0x00,0x02,0x10,0xd6,0x00,0x0e,0xc7,0x86,0x90,0x27,0x1a, + 0xc2,0x00,0x00,0x00,0xc9,0x06,0x5b,0xcf,0x86,0x5b,0x70,0x1f,0xc2,0x33,0x30,0x1f, + 0xcf,0x84,0xd6,0x22,0x12,0xc2,0xd1,0x40,0x00,0x27,0xf0,0xf0,0xff,0xff,0xe0,0x41, + 0xb2,0xc2,0x61,0xb2,0xc2,0x51,0xb2,0xc2,0x61,0xb2,0xc2,0x51,0xb2,0xc2,0x61,0xb2, + 0xc2,0x51,0xb2,0xc2,0x61,0xb2,0xc2,0x51,0xb2,0xc2,0x61,0xb2,0xc2,0x51,0xb2,0xc2, + 0x61,0xb2,0xc2,0x51,0xb2,0xc2,0x61,0xb2,0xc2,0x51,0xb2,0xc2,0x61,0xb2,0xc2,0x86, + 0x00,0xaa,0xdf,0x80,0x0a,0x22,0x12,0xc2,0xd1,0x40,0x00,0x40,0x10,0xc2,0x61,0xb2, + 0xc2,0x51,0xb2,0xc2,0x61,0xb2,0xc2,0x51,0xb2,0xc2,0x61,0xb2,0xc2,0x51,0xb2,0xc2, + 0x61,0xb2,0xc2,0x51,0xb2,0xc2,0x61,0xb2,0xc2,0x51,0xb2,0xc2,0x61,0xb2,0xc2,0x51, + 0xb2,0xc2,0x61,0xb2,0xc2,0x51,0xb2,0xc2,0x61,0xb2,0xc2,0x51,0xb2,0xc2,0x86,0x00, + 0xaa,0xdf,0x80,0x0a,0x22,0x12,0xc2,0xd1,0x40,0x00,0x40,0x10,0xc2,0xd6,0x00,0x0f, + 0x61,0xb2,0xc2,0x86,0x00,0xaa,0xdf,0x80,0x0a,0x22,0x12,0xc2,0xd1,0x40,0x00,0x27, + 0xf0,0xf0,0xff,0xff,0xe0,0x40,0x10,0xc2,0xd6,0x00,0x0f,0x61,0xb2,0xc2,0x86,0x00, + 0xaa,0xdf,0x80,0x0a,0x41,0x10,0xc2,0x61,0xb2,0xc2,0x61,0xb2,0x39,0x61,0xb2,0xc2, + 0x61,0xb2,0x39,0x61,0xb2,0xc2,0x61,0xb2,0x39,0x61,0xb2,0xc2,0x61,0xb2,0x39,0x61, + 0xb2,0xc2,0x61,0xb2,0x39,0x61,0xb2,0xc2,0x61,0xb2,0x39,0x61,0xb2,0xc2,0x61,0xb2, + 0x39,0x60,0xb2,0xc2,0x86,0x00,0xaa,0xdf,0x80,0x0a,0x40,0x10,0xc2,0x61,0xb2,0xc2, + 0x61,0xb2,0x39,0x61,0xb2,0xc2,0x61,0xb2,0x39,0x61,0xb2,0xc2,0x61,0xb2,0x39,0x61, + 0xb2,0xc2,0x61,0xb2,0x39,0x61,0xb2,0xc2,0x61,0xb2,0x39,0x61,0xb2,0xc2,0x61,0xb2, + 0x39,0x61,0xb2,0xc2,0x61,0xb2,0x39,0x60,0xb2,0xc2,0x86,0x00,0xaa,0xdf,0x80,0x0a, + 0x0f,0xb0,0x1f,0x00,0x0f,0xff,0xc9,0x06,0xdf,0x27,0x1b,0xc2,0x00,0x07,0xc0,0x23, + 0x1b,0xe8,0x37,0x34,0xc2,0x00,0x00,0x03,0xca,0x06,0xd1,0x37,0x34,0xc2,0x00,0x00, + 0x07,0xca,0x06,0xd8,0x47,0xa8,0xc2,0x00,0x01,0x00,0x0b,0x05,0x18,0x27,0x18,0xc2, + 0x00,0x02,0x66,0xd1,0x40,0x00,0x20,0x10,0x18,0xcf,0x86,0xdf,0x0f,0xa8,0x18,0x00, + 0x00,0x3f,0x27,0x18,0xc2,0x00,0x02,0x66,0xd1,0x40,0x00,0x20,0x10,0x18,0xcf,0x86, + 0xdf,0x47,0xa8,0xc2,0x01,0x00,0x00,0x0b,0x05,0x18,0x27,0x18,0xc2,0x00,0x02,0x66, + 0xd1,0x40,0x00,0x20,0x10,0x18,0x0f,0xb0,0xc2,0xff,0xf0,0x00,0xc9,0x07,0x01,0x27, + 0x1b,0xc2,0x00,0x07,0xc1,0x23,0x1b,0xe8,0x37,0x34,0xc2,0x00,0x00,0x03,0xca,0x06, + 0xf3,0x37,0x34,0xc2,0x00,0x00,0x07,0xca,0x06,0xfa,0x47,0xa8,0xc2,0x00,0x01,0x00, + 0x0b,0x05,0x36,0x27,0x36,0xc2,0x00,0x02,0x66,0xd1,0x40,0x00,0x20,0x10,0x36,0xcf, + 0x87,0x01,0x0f,0xa8,0x36,0x00,0x00,0x3f,0x27,0x36,0xc2,0x00,0x02,0x66,0xd1,0x40, + 0x00,0x20,0x10,0x36,0xcf,0x87,0x01,0x47,0xa8,0xc2,0x01,0x00,0x00,0x0b,0x05,0x36, + 0x27,0x36,0xc2,0x00,0x02,0x66,0xd1,0x40,0x00,0x20,0x10,0x36,0x0f,0xb0,0x1f,0x00, + 0x0f,0xff,0xc9,0x08,0x23,0x27,0x1b,0x37,0x00,0x04,0xc0,0x27,0x1b,0x38,0x00,0x04, + 0xe0,0x27,0x1b,0x39,0x00,0x05,0x00,0x27,0x1f,0xc2,0x00,0x02,0xa4,0xd1,0x40,0x00, + 0x20,0x10,0x1e,0xca,0x87,0x31,0x70,0x37,0xe8,0xc7,0x89,0xb0,0xc7,0x88,0xaa,0x57, + 0x11,0xc2,0x02,0x00,0x00,0x86,0x00,0xc2,0x23,0x10,0x3a,0x43,0x18,0xc2,0x57,0x11, + 0xc2,0x02,0x00,0x00,0x86,0x00,0xa8,0x70,0x38,0xe8,0xc7,0x89,0xb0,0xc7,0x88,0xaa, + 0x57,0x11,0xc2,0x02,0x00,0x00,0x86,0x00,0xc2,0x23,0x10,0x3b,0x43,0x18,0xc2,0x57, + 0x11,0xc2,0x02,0x00,0x00,0x86,0x00,0xa8,0x70,0x39,0xe8,0xc7,0x89,0xb0,0xc7,0x88, + 0xaa,0x57,0x11,0xc2,0x02,0x00,0x00,0x86,0x00,0xc2,0x23,0x10,0x3c,0x43,0x18,0xc2, + 0x57,0x11,0xc2,0x02,0x00,0x00,0x86,0x00,0xa8,0xcf,0x88,0x2c,0x37,0x1e,0x1e,0x00, + 0x00,0x00,0x27,0x1e,0xd8,0x00,0x03,0xff,0x70,0x37,0xe8,0xc7,0x89,0xb0,0x0b,0x98, + 0xa8,0x37,0x1f,0xc2,0x00,0x00,0x04,0xc9,0x07,0x89,0x37,0x1f,0xc2,0x00,0x00,0x02, + 0xc9,0x07,0xd6,0x27,0x10,0x35,0x00,0x00,0x30,0x40,0x10,0xc2,0x70,0xa8,0xc2,0xd6, + 0x00,0x05,0x7b,0x35,0xc2,0x5f,0x10,0x30,0x00,0x00,0x00,0x4f,0x30,0x30,0x00,0x00, + 0x02,0x47,0x30,0x30,0x40,0x00,0x00,0x47,0x35,0xc2,0xf0,0x00,0x00,0x4b,0x30,0x35, + 0x70,0xa8,0xc2,0x23,0x35,0xa8,0xc7,0x88,0x9f,0x57,0x11,0xc2,0x02,0x00,0x00,0x86, + 0x00,0xc2,0x23,0x10,0x3a,0x43,0x18,0xc2,0x57,0x11,0xc2,0x02,0x00,0x00,0x86,0x00, + 0xa8,0x70,0x38,0xe8,0x70,0x30,0xa8,0x27,0x10,0x35,0x00,0x00,0x18,0x40,0x10,0xc2, + 0x70,0x30,0xc2,0xd6,0x00,0x03,0x7b,0x35,0xc2,0x5f,0x10,0x30,0x00,0x00,0x00,0x47, + 0x35,0xc2,0xe0,0x00,0x00,0x4b,0x30,0x35,0x70,0xa8,0xc2,0x23,0x35,0xa8,0xc7,0x88, + 0x9f,0x57,0x11,0xc2,0x02,0x00,0x00,0x86,0x00,0xc2,0x23,0x10,0x3b,0x43,0x18,0xc2, + 0x57,0x11,0xc2,0x02,0x00,0x00,0x86,0x00,0xa8,0x70,0x39,0xe8,0x70,0x30,0xa8,0x27, + 0x10,0x35,0x00,0x00,0x0c,0x40,0x10,0xc2,0x70,0x30,0xc2,0xd6,0x00,0x02,0x7b,0x35, + 0xc2,0x5f,0x10,0x30,0x00,0x00,0x00,0x47,0x35,0xc2,0xc0,0x00,0x00,0x4b,0x30,0x35, + 0x70,0xa8,0xc2,0x23,0x35,0xa8,0xc7,0x88,0x9f,0x57,0x11,0xc2,0x02,0x00,0x00,0x86, + 0x00,0xc2,0x23,0x10,0x3c,0x43,0x18,0xc2,0x57,0x11,0xc2,0x02,0x00,0x00,0x86,0x00, + 0xa8,0xcf,0x88,0x2c,0x27,0x10,0x35,0x00,0x04,0x80,0x40,0x10,0xc2,0x70,0xa8,0xc2, + 0xd6,0x00,0x0a,0x7b,0x35,0xc2,0x5f,0x10,0x30,0x00,0x00,0x00,0x47,0x30,0x30,0x20, + 0x00,0x00,0x47,0x35,0xc2,0xfe,0x00,0x00,0x4b,0x30,0x35,0x70,0xa8,0xc2,0x23,0x35, + 0xa8,0xc7,0x88,0x94,0x57,0x11,0xc2,0x02,0x00,0x00,0x86,0x00,0xc2,0x23,0x10,0x3a, + 0x43,0x18,0xc2,0x57,0x11,0xc2,0x02,0x00,0x00,0x86,0x00,0xa8,0x70,0x38,0xe8,0x70, + 0x30,0xa8,0x27,0x10,0x35,0x00,0x00,0x90,0x40,0x10,0xc2,0x70,0x30,0xc2,0xd6,0x00, + 0x07,0x7b,0x35,0xc2,0x5f,0x10,0x30,0x00,0x00,0x00,0x47,0x30,0x30,0x20,0x00,0x00, + 0x47,0x35,0xc2,0xf0,0x00,0x00,0x4b,0x30,0x35,0x70,0xa8,0xc2,0x23,0x35,0xa8,0xc7, + 0x88,0x94,0x57,0x11,0xc2,0x02,0x00,0x00,0x86,0x00,0xc2,0x23,0x10,0x3b,0x43,0x18, + 0xc2,0x57,0x11,0xc2,0x02,0x00,0x00,0x86,0x00,0xa8,0x70,0x39,0xe8,0x70,0x30,0xa8, + 0x27,0x10,0x35,0x00,0x00,0x12,0x40,0x10,0xc2,0x70,0x30,0xc2,0xd6,0x00,0x05,0x7b, + 0x35,0xc2,0x5f,0x10,0x30,0x00,0x00,0x00,0x47,0x30,0x30,0x08,0x00,0x00,0x47,0x35, + 0xc2,0x80,0x00,0x00,0x4b,0x30,0x35,0x70,0xa8,0xc2,0x23,0x35,0xa8,0xc7,0x88,0x94, + 0x57,0x11,0xc2,0x02,0x00,0x00,0x86,0x00,0xc2,0x23,0x10,0x3c,0x43,0x18,0xc2,0x57, + 0x11,0xc2,0x02,0x00,0x00,0x86,0x00,0xa8,0xcf,0x88,0x2c,0x27,0x10,0x35,0x00,0x00, + 0xa0,0x40,0x10,0xc2,0x70,0xa8,0xc2,0xd6,0x00,0x07,0x7b,0x35,0xc2,0x5f,0x10,0x30, + 0x00,0x00,0x00,0x47,0x30,0x30,0x40,0x00,0x00,0x47,0x35,0xc2,0xf8,0x00,0x00,0x4b, + 0x30,0x35,0x70,0xa8,0xc2,0x23,0x35,0xa8,0xc7,0x88,0x89,0x57,0x11,0xc2,0x02,0x00, + 0x00,0x86,0x00,0xc2,0x23,0x10,0x3a,0x43,0x18,0xc2,0x57,0x11,0xc2,0x02,0x00,0x00, + 0x86,0x00,0xa8,0x70,0x38,0xe8,0x70,0x30,0xa8,0x27,0x10,0x35,0x00,0x00,0x28,0x40, + 0x10,0xc2,0x70,0x30,0xc2,0xd6,0x00,0x05,0x7b,0x35,0xc2,0x5f,0x10,0x30,0x00,0x00, + 0x00,0x47,0x30,0x30,0x40,0x00,0x00,0x47,0x35,0xc2,0xe0,0x00,0x00,0x4b,0x30,0x35, + 0x70,0xa8,0xc2,0x23,0x35,0xa8,0xc7,0x88,0x89,0x57,0x11,0xc2,0x02,0x00,0x00,0x86, + 0x00,0xc2,0x23,0x10,0x3b,0x43,0x18,0xc2,0x57,0x11,0xc2,0x02,0x00,0x00,0x86,0x00, + 0xa8,0x70,0x39,0xe8,0x70,0x30,0xa8,0x27,0x10,0x35,0x00,0x00,0x0a,0x40,0x10,0xc2, + 0x70,0x30,0xc2,0xd6,0x00,0x03,0x7b,0x35,0xc2,0x5f,0x10,0x30,0x00,0x00,0x00,0x47, + 0x30,0x30,0x40,0x00,0x00,0x47,0x35,0xc2,0x80,0x00,0x00,0x4b,0x30,0x35,0x70,0xa8, + 0xc2,0x23,0x35,0xa8,0xc7,0x88,0x89,0x57,0x11,0xc2,0x02,0x00,0x00,0x86,0x00,0xc2, + 0x23,0x10,0x3c,0x43,0x18,0xc2,0x57,0x11,0xc2,0x02,0x00,0x00,0x86,0x00,0xa8,0xcf, + 0x88,0x2c,0x27,0x1b,0xe8,0x00,0x04,0xc0,0x70,0x10,0xa8,0x27,0x1b,0xe8,0x00,0x04, + 0xe0,0x70,0x10,0xa8,0x27,0x1b,0xe8,0x00,0x05,0x00,0x70,0x10,0xa8,0x37,0x32,0xc2, + 0x00,0x00,0x02,0xc9,0x88,0x44,0x0f,0xe8,0xc2,0x00,0x00,0x20,0xc9,0x88,0x3f,0x27, + 0x1b,0x37,0x00,0x05,0x20,0x27,0x1b,0x38,0x00,0x05,0x40,0x27,0x1b,0x39,0x00,0x05, + 0x60,0x70,0x36,0x18,0x0f,0xb0,0x1f,0xff,0xf0,0x00,0x47,0x1f,0x1f,0x00,0x10,0x00, + 0xc9,0x87,0x0a,0xcf,0x88,0x66,0x27,0xf0,0xf0,0x00,0x00,0x01,0x27,0x1b,0x1b,0x00, + 0x00,0x01,0xdf,0x80,0x0a,0x37,0x32,0xc2,0x00,0x00,0x03,0xc9,0x88,0x66,0x0f,0xb0, + 0x1f,0xff,0xf0,0x00,0x47,0x1f,0x1f,0x00,0x10,0x00,0xc9,0x08,0x66,0x70,0x3a,0xc2, + 0x27,0x1b,0xe8,0x00,0x05,0x20,0x43,0x36,0xc2,0x57,0x11,0xc2,0x02,0x00,0x00,0x86, + 0x00,0xa8,0x70,0x3b,0xc2,0x27,0x1b,0xe8,0x00,0x05,0x40,0x43,0x36,0xc2,0x57,0x11, + 0xc2,0x02,0x00,0x00,0x86,0x00,0xa8,0x70,0x3c,0xc2,0x27,0x1b,0xe8,0x00,0x05,0x60, + 0x43,0x36,0xc2,0x57,0x11,0xc2,0x02,0x00,0x00,0x86,0x00,0xa8,0x27,0x1b,0x1b,0x00, + 0x00,0x01,0x27,0xf0,0xf0,0x00,0x00,0x01,0xdf,0x80,0x0a,0x27,0x1b,0xe8,0x00,0x05, + 0x20,0x70,0x10,0xa8,0x27,0x1b,0xe8,0x00,0x05,0x40,0x70,0x10,0xa8,0x27,0x1b,0xe8, + 0x00,0x05,0x60,0x70,0x10,0xa8,0x27,0x1b,0x1b,0x00,0x00,0x01,0x27,0xf0,0xf0,0x00, + 0x00,0x01,0xdf,0x80,0x0a,0x27,0x1b,0xe8,0x00,0x04,0xc0,0x70,0x10,0xa8,0x27,0x1b, + 0xe8,0x00,0x04,0xe0,0x70,0x10,0xa8,0x27,0x1b,0xe8,0x00,0x05,0x00,0x70,0x10,0xa8, + 0x27,0x1b,0xe8,0x00,0x05,0x20,0x70,0x10,0xa8,0x27,0x1b,0xe8,0x00,0x05,0x40,0x70, + 0x10,0xa8,0x27,0x1b,0xe8,0x00,0x05,0x60,0x70,0x10,0xa8,0x27,0x1b,0x1b,0x00,0x00, + 0x01,0xdf,0x80,0x0a,0x27,0xa8,0xa8,0xff,0xff,0xfc,0x4f,0xa8,0xc2,0x08,0x00,0x00, + 0x27,0x1f,0xf8,0x00,0x05,0x90,0x23,0xb8,0xc2,0x27,0x1f,0xf8,0x00,0x05,0x7f,0x43, + 0xb8,0xc2,0xdf,0x80,0x0a,0x27,0xa8,0xa8,0xff,0xff,0xf8,0x4f,0xa8,0xc2,0x04,0x00, + 0x00,0x27,0x1f,0xf8,0x00,0x05,0x90,0x23,0xb8,0xc2,0x27,0x1f,0xf8,0x00,0x05,0x7f, + 0x43,0xb8,0xc2,0xdf,0x80,0x0a,0x27,0xa8,0xa8,0xff,0xff,0xfe,0x4f,0xa8,0xc2,0x10, + 0x00,0x00,0x27,0x1f,0xf8,0x00,0x05,0x90,0x23,0xb8,0xc2,0x27,0x1f,0xf8,0x00,0x05, + 0x7f,0x43,0xb8,0xc2,0xdf,0x80,0x0a,0x93,0x00,0xa8,0x37,0x1e,0xc2,0x00,0x02,0x28, + 0xd1,0x40,0x00,0x48,0xa8,0xa8,0x1f,0xa8,0xa8,0x80,0x00,0x00,0x47,0xa8,0xc2,0x40, + 0x00,0x00,0x27,0x1f,0xf8,0x00,0x05,0x90,0x23,0xb8,0xc2,0x27,0x1f,0xf8,0x00,0x05, + 0x7f,0x43,0xb8,0xc2,0xdf,0x80,0x0a,0x37,0x32,0xc2,0x00,0x00,0x01,0xc9,0x88,0xc6, + 0x0f,0xb0,0x30,0x00,0x0f,0xff,0x70,0x10,0xb0,0xc9,0x08,0xdb,0xc7,0x88,0xdf,0x20, + 0x00,0xb2,0x27,0x1b,0x1b,0x00,0x00,0x01,0xdf,0x80,0x0a,0x70,0xb0,0x1f,0x70,0x10, + 0xb0,0x0f,0x1f,0x30,0x00,0x0f,0xff,0xc9,0x08,0xcd,0xc7,0x88,0xdf,0x20,0x00,0xb0, + 0x0f,0x1f,0x30,0xff,0xf0,0x00,0xc9,0x08,0xdb,0x47,0x30,0x30,0x00,0x10,0x00,0xc7, + 0x88,0xdf,0x20,0x00,0x1f,0x47,0x1f,0xc2,0x00,0x10,0x00,0x5f,0xb0,0xb2,0x00,0x00, + 0x01,0x27,0x1b,0x1b,0x00,0x00,0x01,0xdf,0x80,0x0a,0x70,0xb0,0xb2,0x27,0x1b,0x1b, + 0x00,0x00,0x01,0xdf,0x80,0x0a,0x37,0x2d,0xc2,0x00,0x00,0x01,0xca,0x88,0xfb,0x37, + 0x1b,0xc2,0x00,0x00,0x02,0xca,0x08,0xf7,0x37,0x1b,0xc2,0x00,0x00,0x0a,0xca,0x08, + 0xf3,0x37,0x1b,0xc2,0x00,0x00,0x16,0xca,0x08,0xef,0x27,0x30,0xc2,0x00,0x02,0x4c, + 0xd1,0x40,0x00,0xdf,0x80,0x0a,0x27,0x30,0xc2,0x00,0x02,0x45,0xd1,0x40,0x00,0xdf, + 0x80,0x0a,0x27,0x30,0xc2,0x00,0x02,0x36,0xd1,0x40,0x00,0xdf,0x80,0x0a,0x27,0x30, + 0xc2,0x00,0x02,0x27,0xd1,0x40,0x00,0xdf,0x80,0x0a,0x37,0x1b,0xc2,0x00,0x00,0x01, + 0xca,0x09,0x02,0x27,0x30,0xc2,0x00,0x02,0x5e,0xd1,0x40,0x00,0xdf,0x80,0x0a,0x27, + 0x30,0xc2,0x00,0x02,0x4f,0xd1,0x40,0x00,0xdf,0x80,0x0a,0x37,0x32,0xc2,0x00,0x00, + 0x01,0xc9,0x89,0x13,0x0f,0xb2,0xc2,0x00,0x0f,0xff,0xc9,0x09,0x2b,0x0f,0x98,0x30, + 0x00,0x0f,0xff,0x27,0x30,0xc2,0x00,0x09,0x2f,0xc7,0xc0,0x00,0xcf,0x89,0x2b,0xdf, + 0x80,0x0a,0x70,0xb2,0x1f,0x0f,0x1f,0xc2,0x00,0x0f,0xff,0xc9,0x09,0x1c,0x0f,0x98, + 0x30,0x00,0x0f,0xff,0x27,0x30,0xc2,0x00,0x09,0x2f,0xc7,0xc0,0x00,0x0f,0x1f,0xc2, + 0xff,0xf0,0x00,0xc9,0x09,0x2b,0x27,0xe8,0xe8,0x00,0x00,0x01,0x0f,0x9a,0x30,0xff, + 0xf0,0x00,0x47,0x30,0x30,0x00,0x10,0x00,0x27,0x30,0xc2,0x00,0x09,0x2f,0xc7,0xc0, + 0x00,0x27,0xe8,0xe8,0x00,0x00,0x01,0xdf,0x80,0x0a,0x27,0xe8,0xe8,0x00,0x00,0x02, + 0x70,0x98,0x9a,0xdf,0x80,0x0a,0xcf,0x89,0x52,0xcf,0x89,0x47,0xcf,0x89,0x3e,0xcf, + 0x89,0x33,0xc7,0x89,0xb0,0x0b,0x05,0x30,0xc7,0x89,0xb0,0x0b,0x05,0xa8,0x47,0x30, + 0xc2,0x00,0x00,0x01,0x57,0xa8,0xc2,0x00,0x01,0x00,0x5f,0xa8,0xa8,0x01,0x00,0x00, + 0xdf,0x80,0x0a,0xc7,0x89,0xb0,0x0b,0x05,0xa8,0x47,0xa8,0xc2,0x00,0x00,0x01,0x57, + 0xa8,0xc2,0x00,0x01,0x00,0x5f,0xa8,0xa8,0x01,0x00,0x00,0xdf,0x80,0x0a,0xc7,0x89, + 0xb0,0x0b,0x05,0x30,0xc7,0x89,0xb0,0x0b,0x05,0xa8,0x47,0x30,0xc2,0x00,0x00,0x01, + 0x57,0x30,0xc2,0x00,0x01,0x00,0x5f,0xa8,0xa8,0x01,0x00,0x00,0xdf,0x80,0x0a,0xc7, + 0x89,0xb0,0x0b,0x05,0x30,0xc7,0x89,0xb0,0x0b,0x05,0xa8,0x47,0x30,0xc2,0x00,0x00, + 0x01,0x5f,0xa8,0x30,0x00,0x01,0x00,0xc7,0x89,0xb0,0x0b,0x05,0xa8,0x47,0x30,0xc2, + 0x00,0x00,0x01,0x5f,0xa8,0xa8,0x01,0x00,0x00,0xdf,0x80,0x0a,0x37,0x32,0xc2,0x00, + 0x00,0x01,0xc9,0x89,0x6c,0x0f,0xb2,0xc2,0x00,0x0f,0xff,0xc9,0x09,0x83,0xc7,0x89, + 0xd8,0x0b,0x01,0x9a,0x27,0x5d,0x5d,0x00,0x00,0x02,0xdf,0x80,0x0a,0x70,0xb2,0x1f, + 0x70,0x10,0x98,0x0f,0x1f,0xc2,0x00,0x0f,0xff,0xc9,0x09,0x75,0xc7,0x89,0xd8,0x0b, + 0x01,0x98,0x27,0x5d,0x5d,0x00,0x00,0x02,0x0f,0x1f,0xc2,0xff,0xf0,0x00,0xc9,0x09, + 0x81,0xc7,0x89,0xd8,0x0b,0x01,0x1f,0x47,0x1f,0xc2,0x00,0x10,0x00,0x5f,0x98,0x9a, + 0x00,0x00,0x01,0x27,0x5d,0x5d,0x00,0x00,0x02,0xdf,0x80,0x0a,0x70,0x98,0x9a,0xdf, + 0x80,0x0a,0x70,0x10,0x9a,0xdf,0x80,0x0a,0x37,0x32,0xc2,0x00,0x00,0x02,0xc9,0x09, + 0x94,0x37,0x32,0xc2,0x00,0x00,0x03,0xc9,0x09,0xa3,0x70,0x30,0xc2,0xd1,0x40,0x00, + 0x21,0x10,0x1e,0x22,0x10,0x30,0x27,0x1e,0xf0,0x00,0x03,0xff,0xc7,0x89,0xd8,0x0b, + 0xb0,0x9a,0xdf,0x80,0x0a,0x70,0x30,0xc2,0xd1,0x40,0x00,0x21,0x10,0x1e,0x22,0x10, + 0x30,0x27,0x1e,0xf0,0x00,0x03,0xff,0xc7,0x89,0xd8,0x0b,0xb0,0x98,0xc7,0x89,0xd8, + 0x0b,0xb0,0x1f,0x47,0x1f,0xc2,0x00,0x10,0x00,0x5f,0x98,0x9a,0x00,0x00,0x01,0xdf, + 0x80,0x0a,0x70,0x30,0xc2,0xd1,0x40,0x00,0x21,0x10,0x1e,0x22,0x10,0x30,0x27,0x1e, + 0xf0,0x00,0x03,0xff,0xc7,0x89,0xd8,0x0b,0xb0,0x98,0x47,0x98,0xc2,0x00,0x10,0x00, + 0x5f,0x98,0x9a,0x00,0x00,0x01,0xdf,0x80,0x0a,0x70,0x1e,0xc2,0x23,0x3e,0x3e,0x27, + 0x1e,0xc2,0x00,0x02,0x10,0xd1,0x40,0x00,0x70,0x1d,0xc2,0x33,0x1e,0x1d,0xca,0x09, + 0xd4,0x37,0x1d,0xd0,0x00,0x03,0xff,0x37,0x1d,0xc2,0x00,0x02,0x10,0xd5,0x40,0x00, + 0x40,0x1c,0x1c,0x70,0x5b,0xc2,0xd1,0x40,0x00,0x32,0x5a,0xc2,0xc9,0x89,0xc4,0xc7, + 0x8a,0x0d,0xcf,0x89,0xc0,0xc7,0x8a,0x00,0x90,0x00,0x33,0x22,0x00,0x5b,0x37,0x5b, + 0xc2,0x00,0x0f,0xff,0xca,0x09,0xcc,0x97,0x80,0x5b,0x00,0x0f,0x00,0x44,0x33,0xc2, + 0x0b,0x90,0xc2,0x13,0x1c,0xc2,0x27,0x1d,0x1d,0x00,0x00,0x18,0x5f,0x10,0x1c,0x00, + 0x00,0x00,0xdf,0x80,0x0a,0x40,0x1c,0xc2,0x5f,0x10,0x1c,0x00,0x00,0x00,0xdf,0x80, + 0x0a,0x70,0x1e,0xc2,0x23,0x3e,0x3e,0x27,0x1e,0xc2,0x00,0x02,0x10,0xd1,0x40,0x00, + 0x70,0x1d,0xc2,0x33,0x1e,0x1d,0xca,0x09,0xfc,0x37,0x1d,0xd0,0x00,0x03,0xff,0x37, + 0x1d,0xc2,0x00,0x02,0x10,0xd5,0x40,0x00,0x40,0x1c,0x1c,0x70,0x5c,0xc2,0xd1,0x40, + 0x00,0x32,0x5a,0xc2,0xc9,0x89,0xec,0xc7,0x8a,0x0d,0xcf,0x89,0xe8,0xc7,0x8a,0x00, + 0x90,0x00,0x33,0x22,0x00,0x5c,0x37,0x5c,0xc2,0x00,0x0f,0xff,0xca,0x09,0xf4,0x97, + 0x80,0x5c,0x00,0x0f,0x00,0x44,0x33,0xc2,0x0b,0x90,0xc2,0x13,0x1c,0xc2,0x27,0x1d, + 0x1d,0x00,0x00,0x18,0x5f,0x10,0x1c,0x00,0x00,0x00,0xdf,0x80,0x0a,0x40,0x1c,0xc2, + 0x5f,0x10,0x1c,0x00,0x00,0x00,0xdf,0x80,0x0a,0x4b,0x6c,0xc2,0xd9,0x00,0x02,0xcb, + 0x8a,0x04,0x23,0x6e,0xc2,0x33,0x6d,0xc2,0xdb,0x80,0x02,0x0f,0x73,0x73,0x00,0xff, + 0xff,0x17,0x73,0x58,0x0e,0x00,0x00,0xc7,0x8b,0x0b,0x70,0x10,0x6c,0xdf,0x80,0x0a, + 0x27,0x6c,0x6c,0x00,0x00,0x00,0xd9,0x00,0x02,0x97,0x80,0x58,0x0e,0x00,0x00,0xc7, + 0x8b,0x0b,0x70,0x10,0x6c,0xdf,0x80,0x0a,0xc7,0x89,0xb0,0x0b,0x07,0x5e,0x4f,0x5e, + 0xc2,0x00,0x01,0x00,0x1b,0x63,0x5e,0x0f,0x5e,0x5e,0x00,0xff,0x00,0x47,0x5e,0x60, + 0x04,0x00,0x00,0x47,0x5e,0xc2,0x02,0x00,0x00,0x1b,0x60,0x60,0x47,0x5e,0x5e,0x01, + 0x00,0x00,0x70,0x5e,0xc2,0x80,0x00,0xc2,0x1b,0x5e,0x5f,0x80,0x00,0xc2,0x1b,0x5f, + 0x5f,0x80,0x00,0xc2,0x1b,0x5f,0x5f,0x80,0x00,0xc2,0x1b,0x5f,0x5f,0x80,0x00,0xc2, + 0x1b,0x5f,0x5f,0x80,0x00,0xc2,0x1b,0x5f,0x5f,0x80,0x00,0xc2,0x1b,0x5f,0xc2,0x0b, + 0x00,0x5f,0xc9,0x0a,0x36,0x1f,0x60,0x60,0x00,0x80,0x03,0x4f,0x63,0xc2,0x00,0x01, + 0x00,0x1b,0x60,0x63,0xdf,0x80,0x0a,0xc7,0x89,0xb0,0x0b,0x00,0x5e,0x40,0x10,0xc2, + 0x70,0x63,0xc2,0x82,0x00,0x63,0x47,0x63,0xc2,0x00,0x01,0x00,0x0b,0x00,0xc2,0x1b, + 0x5e,0xc2,0xc9,0x0a,0x46,0x1f,0x63,0x63,0x00,0x80,0x05,0xdf,0x80,0x0a,0x70,0x66, + 0xc2,0x23,0x3e,0xc2,0xcb,0x8a,0x67,0x27,0x66,0xc2,0x00,0x02,0x10,0xd1,0x40,0x00, + 0x48,0x68,0x68,0x70,0x66,0x1e,0xc7,0x89,0xb0,0x27,0x66,0xd8,0x00,0x03,0xff,0x0b, + 0x98,0xc2,0x13,0x68,0x68,0xc7,0x8a,0x74,0x70,0x11,0x66,0x70,0x11,0x1e,0x37,0x3e, + 0xc2,0xff,0xff,0xf8,0xca,0x8a,0x5e,0xc7,0x89,0xb0,0x0b,0x07,0x68,0xc7,0x8a,0x74, + 0xcf,0x8a,0x57,0x37,0x3e,0x1e,0x00,0x00,0x00,0x37,0x1e,0x66,0x00,0x00,0x08,0xc7, + 0x89,0xb0,0x27,0x1e,0xd8,0x00,0x03,0xff,0x0b,0x98,0x68,0xdf,0x80,0x0a,0x93,0x00, + 0x66,0x37,0x3e,0x1e,0x00,0x00,0x00,0x27,0x1e,0xc2,0x00,0x02,0x10,0xd1,0x40,0x00, + 0x48,0x68,0x68,0xc7,0x89,0xb0,0x27,0x1e,0xd8,0x00,0x03,0xff,0x0b,0x98,0xc2,0x13, + 0x68,0x68,0xdf,0x80,0x0a,0x70,0x69,0xd8,0x37,0x67,0xc2,0x00,0x00,0x02,0xca,0x83, + 0x5a,0x27,0x67,0xc2,0x00,0x0a,0x7b,0xcf,0xc0,0x00,0xcf,0x8a,0x7e,0xcf,0x8a,0x94, + 0xcf,0x8a,0x9a,0x97,0x80,0x67,0x00,0x00,0x02,0x70,0x9a,0xc2,0x0f,0xd8,0xd8,0x00, + 0x00,0x0f,0x27,0xd8,0xc2,0x00,0x05,0xe0,0x33,0x6a,0xc2,0xc9,0x8a,0x8c,0x70,0x9a, + 0xc2,0x0f,0xd8,0xd8,0x00,0x00,0x0f,0x27,0xd8,0x6a,0x00,0x05,0xe0,0x70,0x69,0xd8, + 0x70,0x68,0xc2,0x13,0x98,0x9a,0x0f,0xd8,0xd8,0x00,0x00,0x0f,0x27,0xd8,0x69,0x00, + 0x05,0xe0,0xdf,0x80,0x0a,0x97,0x80,0x67,0x00,0x00,0x00,0x4f,0x68,0xc2,0x00,0x01, + 0x00,0x13,0x98,0x98,0xdf,0x80,0x0a,0x97,0x80,0x67,0x00,0x00,0x01,0x4f,0x68,0x98, + 0x01,0x00,0x00,0xdf,0x80,0x0a,0x70,0x4c,0xc2,0x33,0xe0,0x31,0xcb,0x0a,0xa4,0x27, + 0x31,0x31,0xff,0xfe,0x40,0x27,0x31,0x31,0x00,0x00,0x20,0xca,0x8a,0xa8,0xcf,0x8a, + 0x9f,0xc7,0x8a,0xc1,0x70,0x4c,0xf8,0x70,0x46,0xc2,0xd6,0x00,0x1f,0x43,0xb2,0xba, + 0x70,0xf8,0x4c,0x70,0x4d,0xc2,0xd1,0x40,0x00,0xd6,0x00,0x1f,0xc7,0x8a,0xbe,0x91, + 0x00,0x4d,0x37,0x4c,0xc2,0x00,0x07,0xc0,0xc9,0x8a,0xb8,0x97,0x80,0x4c,0x00,0x06, + 0x00,0x37,0x4d,0xc2,0x00,0x0f,0x00,0xc9,0x8a,0xbd,0x97,0x80,0x4d,0x00,0x0d,0x40, + 0xdf,0x80,0x0a,0x44,0x92,0xc2,0xd0,0xc0,0x00,0xdf,0x80,0x0a,0x0f,0x4b,0xc2,0x00, + 0x00,0x04,0xc9,0x0a,0xcf,0x70,0x10,0x47,0x70,0x10,0x48,0x27,0x46,0x46,0x00,0x00, + 0x00,0xc9,0x8a,0xcf,0x24,0x10,0xc2,0xc9,0x8a,0xcf,0x0f,0xcd,0xcd,0xff,0xdf,0xff, + 0x0f,0x4b,0x4b,0xff,0xff,0xfb,0x0f,0x4b,0xc2,0x00,0x00,0x08,0xc9,0x0a,0xdb,0x0f, + 0xd7,0xc2,0x00,0x00,0x01,0xc9,0x8a,0xdb,0x70,0x49,0x47,0x70,0x4a,0x48,0x17,0xcd, + 0xcd,0x00,0x20,0x00,0x0f,0x4b,0x4b,0xff,0xff,0xf7,0x0f,0x4b,0xc2,0x00,0x00,0x01, + 0xc9,0x0a,0xee,0x70,0x47,0xc2,0x33,0x46,0xc2,0xcb,0x8a,0xe6,0x23,0x0b,0xc2,0xca, + 0x0a,0xeb,0x27,0x46,0x46,0xff,0xf0,0x00,0xcf,0x8a,0xee,0x33,0x0b,0xc2,0xcb,0x0a, + 0xeb,0x27,0x46,0x46,0x00,0x0f,0xff,0xcf,0x8a,0xee,0x0f,0x4b,0x4b,0xff,0xff,0xfe, + 0x70,0x47,0x46,0x0f,0x4b,0xc2,0x00,0x00,0x02,0xc9,0x0b,0x02,0xd5,0x03,0x3a,0x34, + 0x48,0xc2,0xca,0x8a,0xf9,0x33,0x0b,0xc2,0xcb,0x0a,0xfe,0x24,0x15,0xc2,0xd4,0x40, + 0x00,0xcf,0x8b,0x02,0x23,0x0b,0xc2,0xca,0x0a,0xfe,0x24,0x0b,0xc2,0xd4,0x40,0x00, + 0xcf,0x8b,0x02,0x0f,0x4b,0x4b,0xff,0xff,0xfd,0x70,0x48,0xc2,0xd4,0x40,0x00,0xdf, + 0x80,0x0a,0x70,0x93,0xb0,0x27,0xf0,0xf0,0x00,0x00,0x10,0xdf,0x80,0x0a,0x70,0x92, + 0xb0,0x27,0xf0,0xf0,0x00,0x00,0x10,0xdf,0x80,0x0a,0x70,0xd8,0x6f,0x70,0x51,0xd8, + 0x70,0x58,0x9a,0x70,0xd8,0x51,0x0f,0xcb,0xd8,0x00,0x20,0x00,0xd9,0x00,0x02,0x47, + 0x58,0xc9,0x00,0x01,0x00,0x70,0x00,0x52,0x70,0x6f,0xd8,0xdf,0x80,0x0a,0x93,0x00, + 0x43,0x91,0x00,0x45,0x70,0xa2,0xc2,0x37,0xe0,0xc2,0x00,0x07,0xc0,0xcb,0x8b,0x1f, + 0x97,0x80,0xe0,0x00,0x06,0x00,0x27,0xe0,0xc2,0x00,0x07,0x40,0xd1,0x40,0x00,0x32, + 0x4d,0xc2,0xc9,0x0b,0x2b,0x48,0x11,0x41,0x4f,0xa0,0xc1,0x00,0x00,0x08,0x70,0x45, + 0xc2,0xd1,0x40,0x00,0x70,0x43,0xc2,0xdf,0xc0,0x0b,0x70,0xa3,0xc2,0x37,0xe0,0xc2, + 0x00,0x06,0x00,0xcb,0x0b,0x31,0x97,0x80,0xe0,0x00,0x07,0xbf,0x70,0x00,0x4f,0x70, + 0x45,0xc2,0xd1,0x40,0x00,0x70,0x43,0xc2,0xdf,0xc0,0x0b,0x93,0x00,0x43,0x5b,0x10, + 0x44,0x70,0xd8,0x45,0x27,0x55,0xc2,0x00,0x0b,0x3c,0xcf,0xc0,0x00,0xcf,0x8b,0x42, + 0xcf,0x8b,0x47,0xcf,0x8b,0x4e,0xcf,0x8b,0x55,0xcf,0x8b,0x5a,0xcf,0x8b,0x61,0x4f, + 0xc9,0x53,0x01,0x00,0x00,0x27,0x55,0x55,0x00,0x00,0x01,0xcf,0x8b,0xed,0x4f,0x53, + 0xc2,0x00,0x00,0x01,0x5f,0xc9,0x53,0x00,0x01,0x00,0x27,0x55,0x55,0x00,0x00,0x01, + 0xcf,0x8b,0xed,0x4f,0x53,0xc2,0x00,0x00,0x01,0x5f,0xc9,0x53,0x00,0x00,0x01,0x27, + 0x55,0x55,0x00,0x00,0x01,0xcf,0x8b,0xed,0x4f,0xc9,0x54,0x01,0x00,0x00,0x27,0x55, + 0x55,0x00,0x00,0x01,0xcf,0x8b,0xed,0x4f,0x54,0xc2,0x00,0x00,0x01,0x5f,0xc9,0x54, + 0x00,0x01,0x00,0x27,0x55,0x55,0x00,0x00,0x01,0xcf,0x8b,0xed,0x4f,0x54,0xc2,0x00, + 0x00,0x01,0x5f,0xc9,0x54,0x00,0x00,0x01,0x70,0x53,0xc2,0x33,0x54,0xc2,0xc9,0x8b, + 0xe2,0x0f,0x53,0x56,0x7f,0x00,0x00,0x37,0x56,0xc2,0x10,0x00,0x00,0xcb,0x0b,0xe2, + 0x97,0x80,0xc2,0x00,0x0b,0x72,0x57,0x53,0xc2,0x00,0x01,0x00,0xcf,0xc0,0x00,0xcf, + 0x8b,0x82,0xcf,0x8b,0x88,0xcf,0x8b,0x90,0xcf,0x8b,0xa0,0xcf,0x8b,0xa8,0xcf,0x8b, + 0xae,0xcf,0x8b,0xb0,0xcf,0x8b,0xb4,0xcf,0x8b,0xb7,0xcf,0x8b,0xbd,0xcf,0x8b,0xce, + 0xcf,0x8b,0xd0,0xcf,0x8b,0xd3,0xcf,0x8b,0xd7,0xcf,0x8b,0xe2,0xcf,0x8b,0xe0,0x17, + 0x4b,0x4b,0x00,0x00,0x01,0x4f,0x53,0x47,0x00,0x00,0x80,0x70,0x47,0x49,0xcf,0x8b, + 0xec,0x17,0x4b,0x4b,0x00,0x00,0x02,0x0f,0x53,0x48,0x00,0xff,0xff,0x4f,0x48,0x48, + 0x00,0x00,0x80,0x70,0x48,0x4a,0xcf,0x8b,0xec,0x0f,0x53,0xc2,0x00,0x00,0x01,0xc9, + 0x8b,0x9a,0x27,0x57,0xc2,0x00,0x00,0x00,0xc9,0x0b,0x9f,0x17,0x4b,0x4b,0x00,0x00, + 0x0b,0x70,0x10,0x57,0xcf,0x8b,0x9f,0x17,0x4b,0x4b,0x00,0x00,0x07,0x70,0x00,0x57, + 0x70,0x10,0x47,0x70,0x10,0x48,0xcf,0x8b,0xec,0x0f,0x53,0xd8,0x00,0xff,0xff,0x27, + 0xd8,0xd8,0x00,0x04,0x20,0x17,0x98,0x58,0x03,0x00,0x00,0xc7,0x8b,0x0b,0xcf,0x8b, + 0xec,0x0f,0x59,0x59,0x00,0xff,0xff,0x17,0x59,0x58,0x04,0x00,0x00,0xc7,0x8b,0x0b, + 0xcf,0x8b,0xec,0x70,0x10,0x59,0xcf,0x8b,0xec,0x0f,0x53,0xc2,0x00,0x00,0x01,0x93, + 0x00,0x64,0xcf,0x8b,0xec,0x0f,0x53,0x4e,0x00,0xff,0xff,0xcf,0x8b,0xec,0x0f,0xd7, + 0x56,0x00,0x00,0x01,0x17,0x56,0x58,0x08,0x00,0x00,0xc7,0x8b,0x0b,0xcf,0x8b,0xec, + 0x70,0x69,0xc2,0x33,0x6a,0x56,0xca,0x0b,0xc2,0x27,0x56,0x56,0x00,0x00,0x10,0x17, + 0x56,0x58,0x09,0x00,0x00,0xc7,0x8b,0x0b,0x27,0x56,0xc2,0xff,0xff,0xff,0x70,0xf0, + 0x56,0x70,0x6a,0xf0,0xd6,0x40,0x00,0xc7,0x8c,0x23,0x70,0xf0,0x6a,0x70,0x56,0xf0, + 0xcf,0x8b,0xec,0xd7,0x01,0x00,0xcf,0x8b,0xec,0x0f,0x53,0x6d,0x00,0xff,0xff,0xcf, + 0x8b,0xec,0x0f,0x53,0xc2,0x00,0x00,0x01,0x4b,0x14,0x6c,0xcf,0x8b,0xec,0x70,0x5a, + 0xc2,0x33,0x5b,0xc2,0xca,0x0b,0xdb,0x23,0x6e,0xc2,0x0b,0x0f,0x56,0x17,0x56,0x58, + 0x0d,0x00,0x00,0xc7,0x8b,0x0b,0xcf,0x8b,0xec,0x70,0x10,0x55,0xd7,0x00,0x20,0x40, + 0x10,0xc2,0x4f,0x53,0x53,0x00,0x01,0x00,0x70,0x53,0xc2,0x57,0x54,0x53,0x00,0x01, + 0x00,0x5b,0x10,0x54,0x27,0x55,0x55,0xff,0xff,0xff,0xcf,0x8b,0xed,0x70,0x10,0x55, + 0x70,0x45,0xd8,0x47,0x44,0xc2,0x00,0x00,0x01,0x70,0x43,0xc2,0xdf,0xc0,0x0b,0x93, + 0x00,0x43,0x5b,0x10,0x44,0x70,0xd8,0x45,0x27,0x52,0xc2,0x00,0x00,0x00,0xc9,0x8b, + 0xfb,0x70,0x51,0xc2,0x33,0x50,0xc2,0xc9,0x0c,0x01,0x27,0x52,0xc2,0x00,0x0b,0xfe, + 0xcf,0xc0,0x00,0xcf,0x8c,0x06,0xcf,0x8c,0x10,0xcf,0x8c,0x1a,0x70,0x45,0xd8,0x47, + 0x44,0xc2,0x00,0x00,0x01,0x70,0x43,0xc2,0xdf,0xc0,0x0b,0x70,0x50,0xd8,0x47,0x98, + 0xc9,0x00,0x01,0x00,0x27,0x52,0x52,0x00,0x00,0x01,0x70,0x45,0xd8,0x47,0x44,0xc2, + 0x00,0x00,0x01,0x70,0x43,0xc2,0xdf,0xc0,0x0b,0x70,0x50,0xd8,0x47,0x98,0xc9,0x01, + 0x00,0x00,0x27,0x52,0x52,0x00,0x00,0x01,0x70,0x45,0xd8,0x47,0x44,0xc2,0x00,0x00, + 0x01,0x70,0x43,0xc2,0xdf,0xc0,0x0b,0x70,0x50,0xd8,0x70,0x9a,0xc9,0x70,0xd8,0x50, + 0x70,0x10,0x52,0x70,0x45,0xd8,0x47,0x44,0xc2,0x00,0x00,0x01,0x70,0x43,0xc2,0xdf, + 0xc0,0x0b,0x70,0xb2,0x58,0xc7,0x8b,0x0b,0xdf,0x80,0x0a,0x95,0x00,0x45,0x93,0x00, + 0x43,0x5b,0x10,0x44,0x70,0x5a,0xc2,0xd5,0x40,0x00,0x27,0x72,0xc2,0x00,0x0c,0x2e, + 0xcf,0xc0,0x00,0xcf,0x8c,0x31,0xcf,0x8c,0x38,0xcf,0x8c,0x41,0x0f,0xc5,0x70,0x00, + 0xff,0xff,0x4f,0x70,0x70,0x00,0x01,0x00,0x27,0x72,0x72,0x00,0x00,0x01,0xcf,0x8c, + 0x4c,0x0f,0xc5,0x71,0x00,0xff,0xff,0x47,0x71,0xc2,0x01,0x00,0x00,0x13,0x70,0xc2, + 0x5b,0x10,0x70,0x27,0x72,0x72,0x00,0x00,0x01,0xcf,0x8c,0x45,0x0f,0xc5,0xc2,0x00, + 0xff,0xff,0x13,0x70,0xc2,0x70,0x10,0x72,0xd4,0x40,0x00,0x26,0x00,0x5a,0x37,0x5a, + 0xc2,0x00,0x0f,0xff,0xca,0x0c,0x4c,0x97,0x80,0x5a,0x00,0x0f,0x00,0x70,0x45,0xc2, + 0xd5,0x40,0x00,0x47,0x44,0xc2,0x00,0x00,0x01,0x70,0x43,0xc2,0xdf,0xc0,0x0b,0x70, + 0xd8,0x45,0x0f,0xd7,0xc2,0x00,0x00,0x01,0xc9,0x0c,0x58,0x0f,0xcd,0xcd,0xff,0xdf, + 0xff,0x70,0x45,0xd8,0xdf,0xc0,0x0b,0x70,0xb0,0xc2,0x23,0x98,0xb2,0x33,0x98,0x9b, + 0x70,0xb0,0xc2,0x23,0x98,0xb2,0x33,0x98,0x9b,0xdf,0x80,0x0a,0x70,0xb0,0x38,0x70, + 0x98,0x39,0x40,0x38,0xc2,0x64,0x39,0xc2,0x57,0x11,0xc2,0x02,0x00,0x00,0x86,0x00, + 0x9a,0x44,0x38,0xc2,0x50,0x39,0xc2,0x57,0x11,0xc2,0x02,0x00,0x00,0x86,0x00,0xb3, + 0xdf,0x80,0x0a,0x70,0xb0,0x38,0x70,0x98,0x39,0x40,0x38,0xc2,0x64,0x39,0xc2,0x57, + 0x11,0xc2,0x02,0x00,0x00,0x86,0x00,0xb3,0x44,0x38,0xc2,0x50,0x39,0xc2,0x57,0x11, + 0xc2,0x02,0x00,0x00,0x86,0x00,0x9a,0xdf,0x80,0x0a,0x70,0xb2,0xc2,0x23,0xb3,0xb2, + 0x33,0xb0,0xb0,0x70,0x9b,0xc2,0x23,0x9a,0x9b,0x33,0x98,0x98,0xdf,0x80,0x0a,0x41, + 0xb2,0xc2,0x55,0x9b,0xc2,0x57,0x11,0xc2,0x02,0x00,0x00,0x86,0x00,0x96,0xdf,0x80, + 0x0a,0x41,0xb2,0xc2,0x65,0x9b,0xc2,0x57,0x11,0xc2,0x02,0x00,0x00,0x86,0x00,0x96, + 0xdf,0x80,0x0a,0xff,0xff,0x30,0xdf,0xde +}; diff --git a/os/pc/dat.h b/os/pc/dat.h new file mode 100644 index 00000000..78a0e4a8 --- /dev/null +++ b/os/pc/dat.h @@ -0,0 +1,258 @@ +typedef struct Conf Conf; +typedef struct FPU FPU; +typedef struct FPenv FPenv; +typedef ulong Instr; +typedef struct ISAConf ISAConf; +typedef struct Label Label; +typedef struct Lock Lock; +typedef struct MMU MMU; +typedef struct Mach Mach; +typedef struct Notsave Notsave; +typedef struct PCArch PCArch; +typedef struct Pcidev Pcidev; +typedef struct PCMmap PCMmap; +typedef struct PCMslot PCMslot; +typedef struct Page Page; +typedef struct PMMU PMMU; +typedef struct Segdesc Segdesc; +typedef struct Ureg Ureg; +typedef struct Vctl Vctl; + +#pragma incomplete Ureg +#pragma incomplete Vctl + + +struct Lock +{ + ulong key; + ulong sr; + ulong pc; + ulong pri; +}; + +struct Label +{ + ulong sp; + ulong pc; +}; + +/* + * FPenv.status + */ +enum +{ + FPINIT, + FPACTIVE, + FPINACTIVE, +}; + +/* + * This structure must agree with FPsave and FPrestore asm routines + */ +struct FPenv +{ + ushort control; + ushort r1; + ushort status; + ushort r2; + ushort tag; + ushort r3; + ulong pc; + ushort selector; + ushort r4; + ulong operand; + ushort oselector; + ushort r5; +}; + +/* + * This structure must agree with fpsave and fprestore asm routines + */ +struct FPU +{ + FPenv env; + uchar regs[80]; /* floating point registers */ +}; + +struct Conf +{ + ulong nmach; /* processors */ + ulong nproc; /* processes */ + ulong monitor; /* has monitor? */ + ulong npage0; /* total physical pages of memory */ + ulong npage1; /* total physical pages of memory */ + ulong npage; /* total physical pages of memory */ + ulong base0; /* base of bank 0 */ + ulong base1; /* base of bank 1 */ + ulong copymode; /* 0 is copy on write, 1 is copy on reference */ + ulong ialloc; /* max interrupt time allocation in bytes */ + ulong pipeqsize; /* size in bytes of pipe queues */ + int nuart; /* number of uart devices */ +}; + +#include "../port/portdat.h" + +typedef struct { + ulong link; /* link (old TSS selector) */ + ulong esp0; /* privilege level 0 stack pointer */ + ulong ss0; /* privilege level 0 stack selector */ + ulong esp1; /* privilege level 1 stack pointer */ + ulong ss1; /* privilege level 1 stack selector */ + ulong esp2; /* privilege level 2 stack pointer */ + ulong ss2; /* privilege level 2 stack selector */ + ulong cr3; /* page directory base register */ + ulong eip; /* instruction pointer */ + ulong eflags; /* flags register */ + ulong eax; /* general registers */ + ulong ecx; + ulong edx; + ulong ebx; + ulong esp; + ulong ebp; + ulong esi; + ulong edi; + ulong es; /* segment selectors */ + ulong cs; + ulong ss; + ulong ds; + ulong fs; + ulong gs; + ulong ldt; /* selector for task's LDT */ + ulong iomap; /* I/O map base address + T-bit */ +} Tss; + +struct Segdesc +{ + ulong d0; + ulong d1; +}; + +struct Mach +{ + int machno; /* physical id of processor (KNOWN TO ASSEMBLY) */ + ulong splpc; /* pc of last caller to splhi */ + + ulong* pdb; /* page directory base for this processor (va) */ + Tss* tss; /* tss for this processor */ + Segdesc *gdt; /* gdt for this processor */ + + Proc* externup; /* extern register Proc *up */ + + ulong ticks; /* of the clock since boot time */ + Proc* proc; /* current process on this processor */ + Label sched; /* scheduler wakeup */ + Lock alarmlock; /* access to alarm list */ + void* alarm; /* alarms bound to this clock */ + int inclockintr; + + int nrdy; + int ilockdepth; + + int loopconst; + + Lock apictimerlock; + int cpumhz; + uvlong cyclefreq; /* Frequency of user readable cycle counter */ + uvlong cpuhz; + int cpuidax; + int cpuiddx; + char cpuidid[16]; + char* cpuidtype; + int havetsc; + int havepge; + uvlong tscticks; + uvlong tscoff; + int intr; + ulong spuriousintr; + int lastintr; + + vlong mtrrcap; + vlong mtrrdef; + vlong mtrrfix[11]; + vlong mtrrvar[32]; /* 256 max. */ + + int stack[1]; +}; + +struct +{ + Lock; + int machs; /* bitmap of active CPUs */ + int exiting; /* shutdown */ + int ispanic; /* shutdown in response to a panic */ + int thunderbirdsarego; /* lets the added processors continue to schedinit */ +}active; + + +/* + * routines for things outside the PC model, like power management + */ +struct PCArch +{ + char* id; + int (*ident)(void); /* this should be in the model */ + void (*reset)(void); /* this should be in the model */ + int (*serialpower)(int); /* 1 == on, 0 == off */ + int (*modempower)(int); /* 1 == on, 0 == off */ + + void (*intrinit)(void); + int (*intrenable)(Vctl*); + int (*intrvecno)(int); + int (*intrdisable)(int); + + void (*clockenable)(void); + uvlong (*fastclock)(uvlong*); + void (*timerset)(uvlong); +}; + +/* + * a parsed plan9.ini line + */ +#define NISAOPT 8 + +struct ISAConf { + char *type; + ulong port; + int irq; + ulong dma; + ulong mem; + ulong size; + ulong freq; + + int nopt; + char *opt[NISAOPT]; +}; + +extern PCArch *arch; /* PC architecture */ + +/* + * Each processor sees its own Mach structure at address MACHADDR. + * However, the Mach structures must also be available via the per-processor + * MMU information array machp, mainly for disambiguation and access to + * the clock which is only maintained by the bootstrap processor (0). + */ +Mach* machp[MAXMACH]; + +#define MACHP(n) (machp[n]) + +extern Mach *m; +//extern Proc *up; +#define up (((Mach*)MACHADDR)->externup) + +extern int swcursor; + +/* + * hardware info about a device + */ +typedef struct { + ulong port; + int size; +} Devport; + +struct DevConf +{ + ulong intnum; /* interrupt number */ + char *type; /* card type, malloced */ + int nports; /* Number of ports */ + Devport *ports; /* The ports themselves */ +}; diff --git a/os/pc/devarch.c b/os/pc/devarch.c new file mode 100644 index 00000000..6a7b52fe --- /dev/null +++ b/os/pc/devarch.c @@ -0,0 +1,940 @@ +#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" + +typedef struct IOMap IOMap; +struct IOMap +{ + IOMap *next; + int reserved; + char tag[13]; + ulong start; + ulong end; +}; + +static struct +{ + Lock; + IOMap *m; + IOMap *free; + IOMap maps[32]; // some initial free maps + + QLock ql; // lock for reading map +} iomap; + +enum { + Qdir = 0, + Qioalloc = 1, + Qiob, + Qiow, + Qiol, + Qbase, + + Qmax = 16, +}; + +typedef long Rdwrfn(Chan*, void*, long, vlong); + +static Rdwrfn *readfn[Qmax]; +static Rdwrfn *writefn[Qmax]; + +static Dirtab archdir[Qmax] = { + ".", { Qdir, 0, QTDIR }, 0, 0555, + "ioalloc", { Qioalloc, 0 }, 0, 0444, + "iob", { Qiob, 0 }, 0, 0660, + "iow", { Qiow, 0 }, 0, 0660, + "iol", { Qiol, 0 }, 0, 0660, +}; +Lock archwlock; /* the lock is only for changing archdir */ +int narchdir = Qbase; +int (*_pcmspecial)(char*, ISAConf*); +void (*_pcmspecialclose)(int); + +static int doi8253set = 1; + +/* + * Add a file to the #P listing. Once added, you can't delete it. + * You can't add a file with the same name as one already there, + * and you get a pointer to the Dirtab entry so you can do things + * like change the Qid version. Changing the Qid path is disallowed. + */ +Dirtab* +addarchfile(char *name, int perm, Rdwrfn *rdfn, Rdwrfn *wrfn) +{ + int i; + Dirtab d; + Dirtab *dp; + + memset(&d, 0, sizeof d); + strcpy(d.name, name); + d.perm = perm; + + lock(&archwlock); + if(narchdir >= Qmax){ + unlock(&archwlock); + return nil; + } + + for(i=0; i<narchdir; i++) + if(strcmp(archdir[i].name, name) == 0){ + unlock(&archwlock); + return nil; + } + + d.qid.path = narchdir; + archdir[narchdir] = d; + readfn[narchdir] = rdfn; + writefn[narchdir] = wrfn; + dp = &archdir[narchdir++]; + unlock(&archwlock); + + return dp; +} + +void +ioinit(void) +{ + char *excluded; + int i; + + for(i = 0; i < nelem(iomap.maps)-1; i++) + iomap.maps[i].next = &iomap.maps[i+1]; + iomap.maps[i].next = nil; + iomap.free = iomap.maps; + + /* + * This is necessary to make the IBM X20 boot. + * Have not tracked down the reason. + */ + ioalloc(0x0fff, 1, 0, "dummy"); // i82557 is at 0x1000, the dummy + // entry is needed for swappable devs. + + if ((excluded = getconf("ioexclude")) != nil) { + char *s; + + s = excluded; + while (s && *s != '\0' && *s != '\n') { + char *ends; + int io_s, io_e; + + io_s = (int)strtol(s, &ends, 0); + if (ends == nil || ends == s || *ends != '-') { + print("ioinit: cannot parse option string\n"); + break; + } + s = ++ends; + + io_e = (int)strtol(s, &ends, 0); + if (ends && *ends == ',') + *ends++ = '\0'; + s = ends; + + ioalloc(io_s, io_e - io_s + 1, 0, "pre-allocated"); + } + } + +} + +// Reserve a range to be ioalloced later. +// This is in particular useful for exchangable cards, such +// as pcmcia and cardbus cards. +int +ioreserve(int, int size, int align, char *tag) +{ + IOMap *m, **l; + int i, port; + + lock(&iomap); + // find a free port above 0x400 and below 0x1000 + port = 0x400; + for(l = &iomap.m; *l; l = &(*l)->next){ + m = *l; + if (m->start < 0x400) continue; + i = m->start - port; + if(i > size) + break; + if(align > 0) + port = ((port+align-1)/align)*align; + else + port = m->end; + } + if(*l == nil){ + unlock(&iomap); + return -1; + } + m = iomap.free; + if(m == nil){ + print("ioalloc: out of maps"); + unlock(&iomap); + return port; + } + iomap.free = m->next; + m->next = *l; + m->start = port; + m->end = port + size; + m->reserved = 1; + strncpy(m->tag, tag, sizeof(m->tag)); + m->tag[sizeof(m->tag)-1] = 0; + *l = m; + + archdir[0].qid.vers++; + + unlock(&iomap); + return m->start; +} + +// +// alloc some io port space and remember who it was +// alloced to. if port < 0, find a free region. +// +int +ioalloc(int port, int size, int align, char *tag) +{ + IOMap *m, **l; + int i; + + lock(&iomap); + if(port < 0){ + // find a free port above 0x400 and below 0x1000 + port = 0x400; + for(l = &iomap.m; *l; l = &(*l)->next){ + m = *l; + if (m->start < 0x400) continue; + i = m->start - port; + if(i > size) + break; + if(align > 0) + port = ((port+align-1)/align)*align; + else + port = m->end; + } + if(*l == nil){ + unlock(&iomap); + return -1; + } + } else { + // Only 64KB I/O space on the x86. + if((port+size) > 0x10000){ + unlock(&iomap); + return -1; + } + // see if the space clashes with previously allocated ports + for(l = &iomap.m; *l; l = &(*l)->next){ + m = *l; + if(m->end <= port) + continue; + if(m->reserved && m->start == port && m->end == port + size) { + m->reserved = 0; + unlock(&iomap); + return m->start; + } + if(m->start >= port+size) + break; + unlock(&iomap); + return -1; + } + } + m = iomap.free; + if(m == nil){ + print("ioalloc: out of maps"); + unlock(&iomap); + return port; + } + iomap.free = m->next; + m->next = *l; + m->start = port; + m->end = port + size; + strncpy(m->tag, tag, sizeof(m->tag)); + m->tag[sizeof(m->tag)-1] = 0; + *l = m; + + archdir[0].qid.vers++; + + unlock(&iomap); + return m->start; +} + +void +iofree(int port) +{ + IOMap *m, **l; + + lock(&iomap); + for(l = &iomap.m; *l; l = &(*l)->next){ + if((*l)->start == port){ + m = *l; + *l = m->next; + m->next = iomap.free; + iomap.free = m; + break; + } + if((*l)->start > port) + break; + } + archdir[0].qid.vers++; + unlock(&iomap); +} + +int +iounused(int start, int end) +{ + IOMap *m; + + for(m = iomap.m; m; m = m->next){ + if(start >= m->start && start < m->end + || start <= m->start && end > m->start) + return 0; + } + return 1; +} + +static void +checkport(int start, int end) +{ + /* standard vga regs are OK */ + if(start >= 0x2b0 && end <= 0x2df+1) + return; + if(start >= 0x3c0 && end <= 0x3da+1) + return; + + if(iounused(start, end)) + return; + error(Eperm); +} + +static Chan* +archattach(char* spec) +{ + return devattach('P', spec); +} + +Walkqid* +archwalk(Chan* c, Chan *nc, char** name, int nname) +{ + return devwalk(c, nc, name, nname, archdir, narchdir, devgen); +} + +static int +archstat(Chan* c, uchar* dp, int n) +{ + return devstat(c, dp, n, archdir, narchdir, devgen); +} + +static Chan* +archopen(Chan* c, int omode) +{ + return devopen(c, omode, archdir, narchdir, devgen); +} + +static void +archclose(Chan*) +{ +} + +enum +{ + Linelen= 31, +}; + +static long +archread(Chan *c, void *a, long n, vlong offset) +{ + char *buf, *p; + int port; + ushort *sp; + ulong *lp; + IOMap *m; + Rdwrfn *fn; + + switch((ulong)c->qid.path){ + + case Qdir: + return devdirread(c, a, n, archdir, narchdir, devgen); + + case Qiob: + port = offset; + checkport(offset, offset+n); + for(p = a; port < offset+n; port++) + *p++ = inb(port); + return n; + + case Qiow: + if(n & 1) + error(Ebadarg); + checkport(offset, offset+n); + sp = a; + for(port = offset; port < offset+n; port += 2) + *sp++ = ins(port); + return n; + + case Qiol: + if(n & 3) + error(Ebadarg); + checkport(offset, offset+n); + lp = a; + for(port = offset; port < offset+n; port += 4) + *lp++ = inl(port); + return n; + + case Qioalloc: + break; + + default: + if(c->qid.path < narchdir && (fn = readfn[c->qid.path])) + return fn(c, a, n, offset); + error(Eperm); + break; + } + + if((buf = malloc(n)) == nil) + error(Enomem); + p = buf; + n = n/Linelen; + offset = offset/Linelen; + + lock(&iomap); + for(m = iomap.m; n > 0 && m != nil; m = m->next){ + if(offset-- > 0) + continue; + sprint(p, "%8lux %8lux %-12.12s\n", m->start, m->end-1, m->tag); + p += Linelen; + n--; + } + unlock(&iomap); + + n = p - buf; + memmove(a, buf, n); + free(buf); + + return n; +} + +static long +archwrite(Chan *c, void *a, long n, vlong offset) +{ + char *p; + int port; + ushort *sp; + ulong *lp; + Rdwrfn *fn; + + switch((ulong)c->qid.path){ + + case Qiob: + p = a; + checkport(offset, offset+n); + for(port = offset; port < offset+n; port++) + outb(port, *p++); + return n; + + case Qiow: + if(n & 1) + error(Ebadarg); + checkport(offset, offset+n); + sp = a; + for(port = offset; port < offset+n; port += 2) + outs(port, *sp++); + return n; + + case Qiol: + if(n & 3) + error(Ebadarg); + checkport(offset, offset+n); + lp = a; + for(port = offset; port < offset+n; port += 4) + outl(port, *lp++); + return n; + + default: + if(c->qid.path < narchdir && (fn = writefn[c->qid.path])) + return fn(c, a, n, offset); + error(Eperm); + break; + } + return 0; +} + +Dev archdevtab = { + 'P', + "arch", + + devreset, + devinit, + devshutdown, + archattach, + archwalk, + archstat, + archopen, + devcreate, + archclose, + archread, + devbread, + archwrite, + devbwrite, + devremove, + devwstat, +}; + +/* + * the following is a generic version of the + * architecture specific stuff + */ + +static int +unimplemented(int) +{ + return 0; +} + +static void +nop(void) +{ +} + +/* + * On a uniprocessor, you'd think that coherence could be nop, + * but it can't. We still need a barrier when using coherence() in + * device drivers. + * + * On VMware, it's safe (and a huge win) to set this to nop. + * Aux/vmware does this via the #P/archctl file. + */ +void (*coherence)(void) = nop; + +PCArch* arch; +extern PCArch* knownarch[]; + +PCArch archgeneric = { +.id= "generic", +.ident= 0, +.reset= i8042reset, +.serialpower= unimplemented, +.modempower= unimplemented, + +.intrinit= i8259init, +.intrenable= i8259enable, +.intrvecno= i8259vecno, +.intrdisable= i8259disable, + +.clockenable= i8253enable, +.fastclock= i8253read, +.timerset= i8253timerset, +}; + +typedef struct X86type X86type; +struct X86type { + int family; + int model; + int aalcycles; + char* name; +}; + +static X86type x86intel[] = +{ + { 4, 0, 22, "486DX", }, /* known chips */ + { 4, 1, 22, "486DX50", }, + { 4, 2, 22, "486SX", }, + { 4, 3, 22, "486DX2", }, + { 4, 4, 22, "486SL", }, + { 4, 5, 22, "486SX2", }, + { 4, 7, 22, "DX2WB", }, /* P24D */ + { 4, 8, 22, "DX4", }, /* P24C */ + { 4, 9, 22, "DX4WB", }, /* P24CT */ + { 5, 0, 23, "P5", }, + { 5, 1, 23, "P5", }, + { 5, 2, 23, "P54C", }, + { 5, 3, 23, "P24T", }, + { 5, 4, 23, "P55C MMX", }, + { 5, 7, 23, "P54C VRT", }, + { 6, 1, 16, "PentiumPro", },/* trial and error */ + { 6, 3, 16, "PentiumII", }, + { 6, 5, 16, "PentiumII/Xeon", }, + { 6, 6, 16, "Celeron", }, + { 6, 7, 16, "PentiumIII/Xeon", }, + { 6, 8, 16, "PentiumIII/Xeon", }, + { 6, 0xB, 16, "PentiumIII/Xeon", }, + { 0xF, 1, 16, "P4", }, /* P4 */ + { 0xF, 2, 16, "PentiumIV/Xeon", }, + + { 3, -1, 32, "386", }, /* family defaults */ + { 4, -1, 22, "486", }, + { 5, -1, 23, "P5", }, + { 6, -1, 16, "P6", }, + { 0xF, -1, 16, "P4", }, /* P4 */ + + { -1, -1, 16, "unknown", }, /* total default */ +}; + +/* + * The AMD processors all implement the CPUID instruction. + * The later ones also return the processor name via functions + * 0x80000002, 0x80000003 and 0x80000004 in registers AX, BX, CX + * and DX: + * K5 "AMD-K5(tm) Processor" + * K6 "AMD-K6tm w/ multimedia extensions" + * K6 3D "AMD-K6(tm) 3D processor" + * K6 3D+ ? + */ +static X86type x86amd[] = +{ + { 5, 0, 23, "AMD-K5", }, /* guesswork */ + { 5, 1, 23, "AMD-K5", }, /* guesswork */ + { 5, 2, 23, "AMD-K5", }, /* guesswork */ + { 5, 3, 23, "AMD-K5", }, /* guesswork */ + { 5, 6, 11, "AMD-K6", }, /* trial and error */ + { 5, 7, 11, "AMD-K6", }, /* trial and error */ + { 5, 8, 11, "AMD-K6-2", }, /* trial and error */ + { 5, 9, 11, "AMD-K6-III", },/* trial and error */ + + { 6, 1, 11, "AMD-Athlon", },/* trial and error */ + { 6, 2, 11, "AMD-Athlon", },/* trial and error */ + + { 4, -1, 22, "Am486", }, /* guesswork */ + { 5, -1, 23, "AMD-K5/K6", }, /* guesswork */ + { 6, -1, 11, "AMD-Athlon", },/* guesswork */ + { 0xF, -1, 11, "AMD64", }, /* guesswork */ + + { -1, -1, 11, "unknown", }, /* total default */ +}; + +/* + * WinChip 240MHz + */ +static X86type x86winchip[] = +{ + {5, 4, 23, "Winchip",}, /* guesswork */ + {6, 7, 23, "Via C3 Samuel 2 or Ezra",}, + {6, 8, 23, "Via C3 Ezra-T",}, + { -1, -1, 23, "unknown", }, /* total default */ +}; + +/* + * SiS 55x + */ +static X86type x86sis[] = +{ + {5, 0, 23, "SiS 55x",}, /* guesswork */ + { -1, -1, 23, "unknown", }, /* total default */ +}; + +static X86type *cputype; + +static void simplecycles(uvlong*); +void (*cycles)(uvlong*) = simplecycles; +void _cycles(uvlong*); /* in l.s */ + +static void +simplecycles(uvlong*x) +{ + *x = m->ticks; +} + +void +cpuidprint(void) +{ + int i; + char buf[128]; + + i = sprint(buf, "cpu%d: %dMHz ", m->machno, m->cpumhz); + if(m->cpuidid[0]) + i += sprint(buf+i, "%12.12s ", m->cpuidid); + sprint(buf+i, "%s (cpuid: AX 0x%4.4uX DX 0x%4.4uX)\n", + m->cpuidtype, m->cpuidax, m->cpuiddx); + print(buf); +} + +/* + * figure out: + * - cpu type + * - whether or not we have a TSC (cycle counter) + * - whether or not it supports page size extensions + * (if so turn it on) + * - whether or not it supports machine check exceptions + * (if so turn it on) + * - whether or not it supports the page global flag + * (if so turn it on) + */ +int +cpuidentify(void) +{ + char *p; + int family, model, nomce; + X86type *t, *tab; + ulong cr4; + vlong mca, mct; + + cpuid(m->cpuidid, &m->cpuidax, &m->cpuiddx); + if(strncmp(m->cpuidid, "AuthenticAMD", 12) == 0) + tab = x86amd; + else if(strncmp(m->cpuidid, "CentaurHauls", 12) == 0) + tab = x86winchip; + else if(strncmp(m->cpuidid, "SiS SiS SiS ", 12) == 0) + tab = x86sis; + else + tab = x86intel; + + family = X86FAMILY(m->cpuidax); + model = X86MODEL(m->cpuidax); + for(t=tab; t->name; t++) + if((t->family == family && t->model == model) + || (t->family == family && t->model == -1) + || (t->family == -1)) + break; + + m->cpuidtype = t->name; + + /* + * if there is one, set tsc to a known value + */ + if(m->cpuiddx & 0x10){ + m->havetsc = 1; + cycles = _cycles; + if(m->cpuiddx & 0x20) + wrmsr(0x10, 0); + } + + /* + * use i8253 to guess our cpu speed + */ + guesscpuhz(t->aalcycles); + + /* + * If machine check exception, page size extensions or page global bit + * are supported enable them in CR4 and clear any other set extensions. + * If machine check was enabled clear out any lingering status. + */ + if(m->cpuiddx & 0x2088){ + cr4 = 0; + if(m->cpuiddx & 0x08) + cr4 |= 0x10; /* page size extensions */ + if(p = getconf("*nomce")) + nomce = strtoul(p, 0, 0); + else + nomce = 0; + if((m->cpuiddx & 0x80) && !nomce){ + cr4 |= 0x40; /* machine check enable */ + if(family == 5){ + rdmsr(0x00, &mca); + rdmsr(0x01, &mct); + } + } + + /* + * Detect whether the chip supports the global bit + * in page directory and page table entries. When set + * in a particular entry, it means ``don't bother removing + * this from the TLB when CR3 changes.'' + * + * We flag all kernel pages with this bit. Doing so lessens the + * overhead of switching processes on bare hardware, + * even more so on VMware. See mmu.c:/^memglobal. + * + * For future reference, should we ever need to do a + * full TLB flush, it can be accomplished by clearing + * the PGE bit in CR4, writing to CR3, and then + * restoring the PGE bit. + */ + if(m->cpuiddx & 0x2000){ + cr4 |= 0x80; /* page global enable bit */ + m->havepge = 1; + } + + putcr4(cr4); + if(m->cpuiddx & 0x80) + rdmsr(0x01, &mct); + } + + cputype = t; + return t->family; +} + +static long +cputyperead(Chan*, void *a, long n, vlong offset) +{ + char str[32]; + ulong mhz; + + mhz = (m->cpuhz+999999)/1000000; + + snprint(str, sizeof(str), "%s %lud\n", cputype->name, mhz); + return readstr(offset, a, n, str); +} + +static long +archctlread(Chan*, void *a, long nn, vlong offset) +{ + char buf[256]; + int n; + + n = snprint(buf, sizeof buf, "cpu %s %lud%s\n", + cputype->name, (ulong)(m->cpuhz+999999)/1000000, + m->havepge ? " pge" : ""); + n += snprint(buf+n, sizeof buf-n, "pge %s\n", getcr4()&0x80 ? "on" : "off"); + n += snprint(buf+n, sizeof buf-n, "coherence "); + if(coherence == mb386) + n += snprint(buf+n, sizeof buf-n, "mb386\n"); + else if(coherence == mb586) + n += snprint(buf+n, sizeof buf-n, "mb586\n"); + else if(coherence == nop) + n += snprint(buf+n, sizeof buf-n, "nop\n"); + else + n += snprint(buf+n, sizeof buf-n, "0x%p\n", coherence); + n += snprint(buf+n, sizeof buf-n, "i8253set %s\n", doi8253set ? "on" : "off"); + buf[n] = 0; + return readstr(offset, a, nn, buf); +} + +enum +{ + CMpge, + CMcoherence, + CMi8253set, +}; + +static Cmdtab archctlmsg[] = +{ + CMpge, "pge", 2, + CMcoherence, "coherence", 2, + CMi8253set, "i8253set", 2, +}; + +static long +archctlwrite(Chan*, void *a, long n, vlong) +{ + Cmdbuf *cb; + Cmdtab *ct; + + cb = parsecmd(a, n); + if(waserror()){ + free(cb); + nexterror(); + } + ct = lookupcmd(cb, archctlmsg, nelem(archctlmsg)); + switch(ct->index){ + case CMpge: + if(!m->havepge) + error("processor does not support pge"); + if(strcmp(cb->f[1], "on") == 0) + putcr4(getcr4() | 0x80); + else if(strcmp(cb->f[1], "off") == 0) + putcr4(getcr4() & ~0x80); + else + cmderror(cb, "invalid pge ctl"); + break; + case CMcoherence: + if(strcmp(cb->f[1], "mb386") == 0) + coherence = mb386; + else if(strcmp(cb->f[1], "mb586") == 0){ + if(X86FAMILY(m->cpuidax) < 5) + error("invalid coherence ctl on this cpu family"); + coherence = mb586; + } + else if(strcmp(cb->f[1], "nop") == 0){ + /* only safe on vmware */ + if(conf.nmach > 1) + error("cannot disable coherence on a multiprocessor"); + coherence = nop; + }else + cmderror(cb, "invalid coherence ctl"); + break; + case CMi8253set: + if(strcmp(cb->f[1], "on") == 0) + doi8253set = 1; + else if(strcmp(cb->f[1], "off") == 0){ + doi8253set = 0; + (*arch->timerset)(0); + }else + cmderror(cb, "invalid i2853set ctl"); + break; + } + free(cb); + poperror(); + return n; +} + +void +archinit(void) +{ + PCArch **p; + + arch = 0; + for(p = knownarch; *p; p++){ + if((*p)->ident && (*p)->ident() == 0){ + arch = *p; + break; + } + } + if(arch == 0) + arch = &archgeneric; + else{ + if(arch->id == 0) + arch->id = archgeneric.id; + if(arch->reset == 0) + arch->reset = archgeneric.reset; + if(arch->serialpower == 0) + arch->serialpower = archgeneric.serialpower; + if(arch->modempower == 0) + arch->modempower = archgeneric.modempower; + if(arch->intrinit == 0) + arch->intrinit = archgeneric.intrinit; + if(arch->intrenable == 0) + arch->intrenable = archgeneric.intrenable; + } + + /* + * Decide whether to use copy-on-reference (386 and mp). + * We get another chance to set it in mpinit() for a + * multiprocessor. + */ + if(X86FAMILY(m->cpuidax) == 3) + conf.copymode = 1; + + if(X86FAMILY(m->cpuidax) >= 5) + coherence = mb586; + + addarchfile("cputype", 0444, cputyperead, nil); + addarchfile("archctl", 0664, archctlread, archctlwrite); +} + +/* + * call either the pcmcia or pccard device setup + */ +int +pcmspecial(char *idstr, ISAConf *isa) +{ + return (_pcmspecial != nil)? _pcmspecial(idstr, isa): -1; +} + +/* + * call either the pcmcia or pccard device teardown + */ +void +pcmspecialclose(int a) +{ + if (_pcmspecialclose != nil) + _pcmspecialclose(a); +} + +/* + * return value and speed of timer set in arch->clockenable + */ +uvlong +fastticks(uvlong *hz) +{ + return (*arch->fastclock)(hz); +} + +/* + * set next timer interrupt + */ +void +timerset(uvlong x) +{ + if(doi8253set) + (*arch->timerset)(x); +} diff --git a/os/pc/devds1620.c b/os/pc/devds1620.c new file mode 100644 index 00000000..b5e6fc83 --- /dev/null +++ b/os/pc/devds1620.c @@ -0,0 +1,368 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "io.h" + +enum { + // Ziatech 5512 Digital I/O ASIC register info + PortSelect = 0xE7, + Port = 0xE1, + DQ = 1<<0, + CLK = 1<<1, + RST = 1<<2, + TL = 1<<3, + TH = 1<<4, + + // ds1620 Masks + Mread = 0xA0, + Mwrite = 0, + + // ds1620 Registers + Rtemp = 0x0A, + Rcounter = 0x00, + Rslope = 0x09, + Rhi = 0x01, + Rlo = 0x02, + Rconfig = 0x0C, + Cdone = 1<<7, // conversion done + Cthf = 1<<6, // temp >= Rhi + Ctlf = 1<<5, // temp <= Rlo + Cnvb = 1<<4, // e^2 nvram busy (write may take up to 10ms) + Ccpu = 1<<1, // cpu use (0=clk starts conversion when rst lo) + C1shot = 1<<0, // perform one conversion then stop + + // ds1620 Commands + Startconv = 0xEE, + Stopconv = 0x22, + + ALOTEMP = 0, + AHITEMP = 1, +}; + +#define send(v) outb(Port, v); delay(1) +#define recv() (!(inb(Port) & 1)) + +enum { + Qdir = 0, + Qtemp, + Qalarm, +}; + +Dirtab ds1620tab[]={ + "temp", {Qtemp, 0}, 0, 0666, + "alarm", {Qalarm, 0}, 0, 0444, +}; + +typedef struct Temp Temp; +struct Temp +{ + Lock; + int lo; + int cur; + int hi; + + int alo; + int ahi; + int atime; + Queue *aq; +}; + +static Temp t; + +static void +sendreg(int r) +{ + int d, i; + + r = ~r; + for(i=0;i<8;i++) { + d = (r >> i) & 1; + send(CLK|d); + send(d); + send(CLK); + } +} + +static int +ds1620rdreg(int r, int nb) +{ + int i, s; + + s = splhi(); + + outb(PortSelect, 0); + send(RST|CLK); + sendreg(r|Mread); + r = 0; + for(i=0; i < nb; i++) { + r |= recv() << i; + delay(1); + send(0); + send(CLK); + } + send(RST); + + splx(s); + return r; +} + +static void +ds1620wrreg(int r, int v, int nb) +{ + int d, i, s; + + s = splhi(); + + outb(PortSelect, 0); + send(RST|CLK); + sendreg(r|Mwrite); + v = ~v; + for(i=0; i < nb; i++) { + d = (v >> i) & 1; + send(CLK|d); + send(0); + send(CLK); + } + send(RST); + + splx(s); +} + +static void +ds1620cmd(int r) +{ + int s; + + s = splhi(); + outb(PortSelect, 0); + send(RST|CLK); + sendreg(r); + send(RST); + splx(s); +} + +static char* +t2s(int t) +{ + static char s[16]; + + sprint(s, "%4d.", t>>1); + if(t&1) + strcat(s, "5"); + else + strcat(s, "0"); + return s; +} + +static int +s2t(char *s) +{ + int v; + char *p; + p = strchr(s, '.'); + if(p != nil) + *p++ = '\0'; + v = strtoul(s, nil, 0); + v <<= 1; + if(p != nil && *p != '\0' && *p >= '5') + v |= 1; + return v; +} + +static void +alarm(int code, Temp *tt) +{ + char buf[256], *end; + int s; + + s = seconds(); + + if(s - tt->atime < 60) + return; + tt->atime = s; + + end = buf; + end += sprint(buf, "(alarm) %8.8uX %uld temp ", code, seconds()); + switch(code) { + case ALOTEMP: + end += sprint(end, "%s below threshold ", t2s(tt->lo)); + end += sprint(end, "%s.\n", t2s(tt->alo)); + break; + case AHITEMP: + end += sprint(end, "%s above threshold ", t2s(tt->hi)); + end += sprint(end, "%s.\n", t2s(tt->ahi)); + break; + } + + qproduce(tt->aq, buf, end-buf); +} + +void +tmon(void *a) +{ + int r; + Temp *t; + + t = a; + r = ds1620rdreg(Rtemp, 9); + lock(t); + t->lo = t->cur = t->hi = r; + unlock(t); + for(;;) { + tsleep(&up->sleep, return0, nil, 1000); + r = ds1620rdreg(Rtemp, 9); + lock(t); + t->cur = r; + if(r < t->lo) + t->lo = r; + if(r > t->hi) + t->hi = r; + if(t->lo < t->alo) + alarm(ALOTEMP, t); + if(t->hi > t->ahi) + alarm(AHITEMP, t); + unlock(t); + } + pexit("", 0); +} + +static void +ds1620init(void) +{ + int r; + + t.aq = qopen(8*1024, Qmsg, nil, nil); + if(t.aq == nil) + error(Enomem); + + ds1620wrreg(Rconfig, Ccpu, 8); // continuous sample mode + ds1620cmd(Startconv); + r = ds1620rdreg(Rtemp, 9); + t.alo = ds1620rdreg(Rlo, 9); + t.ahi = ds1620rdreg(Rhi, 9); + + print("#L: temp %s (c) ", t2s(r)); + print("low threshold %s (c) ", t2s(t.alo)); + print("high threshold %s (c)\n", t2s(t.ahi)); + + kproc("tempmon", tmon, &t, 0); +} + +static Chan* +ds1620attach(char *spec) +{ + return devattach('L', spec); +} + +static int +ds1620walk(Chan *c, char* name) +{ + return devwalk(c, name, ds1620tab, nelem(ds1620tab), devgen); +} + +static void +ds1620stat(Chan *c, char* db) +{ + ds1620tab[1].length = qlen(t.aq); + devstat(c, db, ds1620tab, nelem(ds1620tab), devgen); +} + +static Chan* +ds1620open(Chan *c, int omode) +{ + return devopen(c, omode, ds1620tab, nelem(ds1620tab), devgen); +} + +static void +ds1620close(Chan*) +{ +} + +static long +ds1620read(Chan *c, void *a, long n, vlong offset) +{ + Temp tt; + char buf[64]; + char *s; + if(c->qid.path & CHDIR) + return devdirread(c, a, n, ds1620tab, nelem(ds1620tab), devgen); + buf[0] = 0; + switch(c->qid.path) { + case Qtemp: + lock(&t); + tt = t; + unlock(&t); + s = buf; + s+= sprint(s, "%s ", t2s(tt.lo)); + s+= sprint(s, "%s ", t2s(tt.cur)); + s+= sprint(s, "%s ", t2s(tt.hi)); + s+= sprint(s, "%s ", t2s(tt.alo)); + sprint(s, "%s", t2s(tt.ahi)); + return readstr(offset, a, n, buf); + case Qalarm: + return qread(t.aq, a, n); + default: + error(Egreg); + return 0; + } +} + +static long +ds1620write(Chan *c, void *a, long n, vlong) +{ + char buf[64]; + char *f[2]; + int lo, hi; + int nf; + + if(c->qid.path & CHDIR) + error(Eperm); + + if(c->qid.path == Qtemp) { + if(n >= sizeof(buf)) + n = sizeof(buf)-1; + memmove(buf, a, n); + buf[n] = '\0'; + nf = getfields(buf, f, 2, 1, " \t"); + if(nf != 2) + error(Ebadarg); + lo = s2t(f[0]); + hi = s2t(f[1]); + lock(&t); + t.alo = lo; + t.ahi = hi; + t.atime = 0; + ds1620wrreg(Rlo, lo, 9); + delay(1); + ds1620wrreg(Rhi, hi, 9); + unlock(&t); + return n; + } else + error(Eio); + return 0; + +} + +Dev ds1620devtab = { + 'L', + "ds1620", + devreset, + ds1620init, + ds1620attach, + devdetach, + devclone, + ds1620walk, + ds1620stat, + ds1620open, + devcreate, + ds1620close, + ds1620read, + devbread, + ds1620write, + devbwrite, + devremove, + devwstat, +}; diff --git a/os/pc/devether.c b/os/pc/devether.c new file mode 100644 index 00000000..4bdc693c --- /dev/null +++ b/os/pc/devether.c @@ -0,0 +1,539 @@ +#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/netif.h" + +#include "etherif.h" + +static Ether *etherxx[MaxEther]; + +Chan* +etherattach(char* spec) +{ + ulong ctlrno; + char *p; + Chan *chan; + + ctlrno = 0; + if(spec && *spec){ + ctlrno = strtoul(spec, &p, 0); + if((ctlrno == 0 && p == spec) || *p || (ctlrno >= MaxEther)) + error(Ebadarg); + } + if(etherxx[ctlrno] == 0) + error(Enodev); + + chan = devattach('l', spec); + chan->dev = ctlrno; + if(etherxx[ctlrno]->attach) + etherxx[ctlrno]->attach(etherxx[ctlrno]); + return chan; +} + +static Walkqid* +etherwalk(Chan* chan, Chan* nchan, char** name, int nname) +{ + return netifwalk(etherxx[chan->dev], chan, nchan, name, nname); +} + +static int +etherstat(Chan* chan, uchar* dp, int n) +{ + return netifstat(etherxx[chan->dev], chan, dp, n); +} + +static Chan* +etheropen(Chan* chan, int omode) +{ + return netifopen(etherxx[chan->dev], chan, omode); +} + +static void +ethercreate(Chan*, char*, int, ulong) +{ +} + +static void +etherclose(Chan* chan) +{ + netifclose(etherxx[chan->dev], chan); +} + +static long +etherread(Chan* chan, void* buf, long n, vlong off) +{ + Ether *ether; + ulong offset = off; + + ether = etherxx[chan->dev]; + if((chan->qid.type & QTDIR) == 0 && ether->ifstat){ + /* + * With some controllers it is necessary to reach + * into the chip to extract statistics. + */ + if(NETTYPE(chan->qid.path) == Nifstatqid) + return ether->ifstat(ether, buf, n, offset); + else if(NETTYPE(chan->qid.path) == Nstatqid) + ether->ifstat(ether, buf, 0, offset); + } + + return netifread(ether, chan, buf, n, offset); +} + +static Block* +etherbread(Chan* chan, long n, ulong offset) +{ + return netifbread(etherxx[chan->dev], chan, n, offset); +} + +static int +etherwstat(Chan* chan, uchar* dp, int n) +{ + return netifwstat(etherxx[chan->dev], chan, dp, n); +} + +static void +etherrtrace(Netfile* f, Etherpkt* pkt, int len) +{ + int i, n; + Block *bp; + + if(qwindow(f->in) <= 0) + return; + if(len > 58) + n = 58; + else + n = len; + bp = iallocb(64); + if(bp == nil) + return; + memmove(bp->wp, pkt->d, n); + i = TK2MS(MACHP(0)->ticks); + bp->wp[58] = len>>8; + bp->wp[59] = len; + bp->wp[60] = i>>24; + bp->wp[61] = i>>16; + bp->wp[62] = i>>8; + bp->wp[63] = i; + bp->wp += 64; + qpass(f->in, bp); +} + +Block* +etheriq(Ether* ether, Block* bp, int fromwire) +{ + Etherpkt *pkt; + ushort type; + int len, multi, tome, fromme; + Netfile **ep, *f, **fp, *fx; + Block *xbp; + + ether->inpackets++; + + pkt = (Etherpkt*)bp->rp; + len = BLEN(bp); + type = (pkt->type[0]<<8)|pkt->type[1]; + fx = 0; + ep = ðer->f[Ntypes]; + + multi = pkt->d[0] & 1; + /* check for valid multcast addresses */ + if(multi && memcmp(pkt->d, ether->bcast, sizeof(pkt->d)) != 0 && ether->prom == 0){ + if(!activemulti(ether, pkt->d, sizeof(pkt->d))){ + if(fromwire){ + freeb(bp); + bp = 0; + } + return bp; + } + } + + /* is it for me? */ + tome = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0; + fromme = memcmp(pkt->s, ether->ea, sizeof(pkt->s)) == 0; + + /* + * Multiplex the packet to all the connections which want it. + * If the packet is not to be used subsequently (fromwire != 0), + * attempt to simply pass it into one of the connections, thereby + * saving a copy of the data (usual case hopefully). + */ + for(fp = ether->f; fp < ep; fp++){ + if(f = *fp) + if(f->type == type || f->type < 0) + if(tome || multi || f->prom){ + /* Don't want to hear bridged packets */ + if(f->bridge && !fromwire && !fromme) + continue; + if(!f->headersonly){ + if(fromwire && fx == 0) + fx = f; + else if(xbp = iallocb(len)){ + memmove(xbp->wp, pkt, len); + xbp->wp += len; + if(qpass(f->in, xbp) < 0) + ether->soverflows++; + } + else + ether->soverflows++; + } + else + etherrtrace(f, pkt, len); + } + } + + if(fx){ + if(qpass(fx->in, bp) < 0) + ether->soverflows++; + return 0; + } + if(fromwire){ + freeb(bp); + return 0; + } + + return bp; +} + +static int +etheroq(Ether* ether, Block* bp) +{ + int len, loopback, s; + Etherpkt *pkt; + + ether->outpackets++; + + /* + * Check if the packet has to be placed back onto the input queue, + * i.e. if it's a loopback or broadcast packet or the interface is + * in promiscuous mode. + * If it's a loopback packet indicate to etheriq that the data isn't + * needed and return, etheriq will pass-on or free the block. + * To enable bridging to work, only packets that were originated + * by this interface are fed back. + */ + pkt = (Etherpkt*)bp->rp; + len = BLEN(bp); + loopback = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0; + if(loopback || memcmp(pkt->d, ether->bcast, sizeof(pkt->d)) == 0 || ether->prom){ + s = splhi(); + etheriq(ether, bp, 0); + splx(s); + } + + if(!loopback){ + qbwrite(ether->oq, bp); + if(ether->transmit != nil) + ether->transmit(ether); + } else + freeb(bp); + + return len; +} + +static long +etherwrite(Chan* chan, void* buf, long n, vlong) +{ + Ether *ether; + Block *bp; + int nn, onoff; + Cmdbuf *cb; + + ether = etherxx[chan->dev]; + if(NETTYPE(chan->qid.path) != Ndataqid) { + nn = netifwrite(ether, chan, buf, n); + if(nn >= 0) + return nn; + cb = parsecmd(buf, n); + if(strcmp(cb->f[0], "nonblocking") == 0){ + if(cb->nf <= 1) + onoff = 1; + else + onoff = atoi(cb->f[1]); + qnoblock(ether->oq, onoff); + free(cb); + return n; + } + free(cb); + if(ether->ctl!=nil) + return ether->ctl(ether,buf,n); + + error(Ebadctl); + } + + if(n > ether->maxmtu) + error(Etoobig); + if(n < ether->minmtu) + error(Etoosmall); + + bp = allocb(n); + if(waserror()){ + freeb(bp); + nexterror(); + } + memmove(bp->rp, buf, n); + memmove(bp->rp+Eaddrlen, ether->ea, Eaddrlen); + poperror(); + bp->wp += n; + + return etheroq(ether, bp); +} + +static long +etherbwrite(Chan* chan, Block* bp, ulong) +{ + Ether *ether; + long n; + + n = BLEN(bp); + if(NETTYPE(chan->qid.path) != Ndataqid){ + if(waserror()) { + freeb(bp); + nexterror(); + } + n = etherwrite(chan, bp->rp, n, 0); + poperror(); + freeb(bp); + return n; + } + ether = etherxx[chan->dev]; + + if(n > ether->maxmtu){ + freeb(bp); + error(Etoobig); + } + if(n < ether->minmtu){ + freeb(bp); + error(Etoosmall); + } + + return etheroq(ether, bp); +} + +static struct { + char* type; + int (*reset)(Ether*); +} cards[MaxEther+1]; + +void +addethercard(char* t, int (*r)(Ether*)) +{ + static int ncard; + + if(ncard == MaxEther) + panic("too many ether cards"); + cards[ncard].type = t; + cards[ncard].reset = r; + ncard++; +} + +int +parseether(uchar *to, char *from) +{ + char nip[4]; + char *p; + int i; + + p = from; + for(i = 0; i < Eaddrlen; i++){ + if(*p == 0) + return -1; + nip[0] = *p++; + if(*p == 0) + return -1; + nip[1] = *p++; + nip[2] = 0; + to[i] = strtoul(nip, 0, 16); + if(*p == ':') + p++; + } + return 0; +} + +static Ether* +etherprobe(int cardno, int ctlrno) +{ + int i; + Ether *ether; + char buf[128], name[32]; + + ether = malloc(sizeof(Ether)); + memset(ether, 0, sizeof(Ether)); + ether->ctlrno = ctlrno; + ether->tbdf = BUSUNKNOWN; + ether->mbps = 10; + ether->minmtu = ETHERMINTU; + ether->maxmtu = ETHERMAXTU; + + if(cardno < 0){ + if(isaconfig("ether", ctlrno, ether) == 0){ + free(ether); + return nil; + } + for(cardno = 0; cards[cardno].type; cardno++){ + if(cistrcmp(cards[cardno].type, ether->type)) + continue; + for(i = 0; i < ether->nopt; i++){ + if(strncmp(ether->opt[i], "ea=", 3)) + continue; + if(parseether(ether->ea, ðer->opt[i][3])) + memset(ether->ea, 0, Eaddrlen); + } + break; + } + } + + if(cardno >= MaxEther || cards[cardno].type == nil){ + free(ether); + return nil; + } + if(cards[cardno].reset(ether) < 0){ + free(ether); + return nil; + } + + /* + * IRQ2 doesn't really exist, it's used to gang the interrupt + * controllers together. A device set to IRQ2 will appear on + * the second interrupt controller as IRQ9. + */ + if(ether->irq == 2) + ether->irq = 9; + snprint(name, sizeof(name), "ether%d", ctlrno); + + /* + * If ether->irq is <0, it is a hack to indicate no interrupt + * used by ethersink. + */ + if(ether->irq >= 0) + intrenable(ether->irq, ether->interrupt, ether, ether->tbdf, name); + + i = sprint(buf, "#l%d: %s: %dMbps port 0x%luX irq %d", + ctlrno, cards[cardno].type, ether->mbps, ether->port, ether->irq); + if(ether->mem) + i += sprint(buf+i, " addr 0x%luX", PADDR(ether->mem)); + if(ether->size) + i += sprint(buf+i, " size 0x%luX", ether->size); + i += sprint(buf+i, ": %2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux", + ether->ea[0], ether->ea[1], ether->ea[2], + ether->ea[3], ether->ea[4], ether->ea[5]); + sprint(buf+i, "\n"); + print(buf); + + if (ether->mbps >= 1000) { + netifinit(ether, name, Ntypes, 512*1024); + if(ether->oq == 0) + ether->oq = qopen(512*1024, Qmsg, 0, 0); + } else if(ether->mbps >= 100){ + netifinit(ether, name, Ntypes, 256*1024); + if(ether->oq == 0) + ether->oq = qopen(256*1024, Qmsg, 0, 0); + } + else{ + netifinit(ether, name, Ntypes, 128*1024); + if(ether->oq == 0) + ether->oq = qopen(128*1024, Qmsg, 0, 0); + } + if(ether->oq == 0) + panic("etherreset %s", name); + ether->alen = Eaddrlen; + memmove(ether->addr, ether->ea, Eaddrlen); + memset(ether->bcast, 0xFF, Eaddrlen); + + return ether; +} + +static void +etherreset(void) +{ + Ether *ether; + int cardno, ctlrno; + + for(ctlrno = 0; ctlrno < MaxEther; ctlrno++){ + if((ether = etherprobe(-1, ctlrno)) == nil) + continue; + etherxx[ctlrno] = ether; + } + + if(getconf("*noetherprobe")) + return; + + cardno = ctlrno = 0; + while(cards[cardno].type != nil && ctlrno < MaxEther){ + if(etherxx[ctlrno] != nil){ + ctlrno++; + continue; + } + if((ether = etherprobe(cardno, ctlrno)) == nil){ + cardno++; + continue; + } + etherxx[ctlrno] = ether; + ctlrno++; + } +} + +static void +ethershutdown(void) +{ + Ether *ether; + int i; + + for(i = 0; i < MaxEther; i++){ + ether = etherxx[i]; + if(ether == nil) + continue; + if(ether->shutdown == nil) { + print("#l%d: no shutdown fuction\n", i); + continue; + } + (*ether->shutdown)(ether); + } +} + + +#define POLY 0xedb88320 + +/* really slow 32 bit crc for ethers */ +ulong +ethercrc(uchar *p, int len) +{ + int i, j; + ulong crc, b; + + crc = 0xffffffff; + for(i = 0; i < len; i++){ + b = *p++; + for(j = 0; j < 8; j++){ + crc = (crc>>1) ^ (((crc^b) & 1) ? POLY : 0); + b >>= 1; + } + } + return crc; +} + +Dev etherdevtab = { + 'l', + "ether", + + etherreset, + devinit, + ethershutdown, + etherattach, + etherwalk, + etherstat, + etheropen, + ethercreate, + etherclose, + etherread, + etherbread, + etherwrite, + etherbwrite, + devremove, + etherwstat, +}; diff --git a/os/pc/devfloppy.c b/os/pc/devfloppy.c new file mode 100644 index 00000000..2f29147c --- /dev/null +++ b/os/pc/devfloppy.c @@ -0,0 +1,1082 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#include "floppy.h" + +/* Intel 82077A (8272A compatible) floppy controller */ + +/* This module expects the following functions to be defined + * elsewhere: + * + * inb() + * outb() + * floppyexec() + * floppyeject() + * floppysetup0() + * floppysetup1() + * dmainit() + * dmasetup() + * dmaend() + * + * On DMA systems, floppyexec() should be an empty function; + * on non-DMA systems, dmaend() should be an empty function; + * dmasetup() may enforce maximum transfer sizes. + */ + +enum { + /* file types */ + Qdir= 0, + Qdata= (1<<2), + Qctl= (2<<2), + Qmask= (3<<2), + + DMAchan= 2, /* floppy dma channel */ +}; + +#define DPRINT if(floppydebug)print +int floppydebug = 0; + +/* + * types of drive (from PC equipment byte) + */ +enum +{ + Tnone= 0, + T360kb= 1, + T1200kb= 2, + T720kb= 3, + T1440kb= 4, +}; + +FType floppytype[] = +{ + { "3½HD", T1440kb, 512, 18, 2, 1, 80, 0x1B, 0x54, 0, }, + { "3½DD", T1440kb, 512, 9, 2, 1, 80, 0x1B, 0x54, 2, }, + { "3½DD", T720kb, 512, 9, 2, 1, 80, 0x1B, 0x54, 2, }, + { "5¼HD", T1200kb, 512, 15, 2, 1, 80, 0x2A, 0x50, 0, }, + { "5¼DD", T1200kb, 512, 9, 2, 2, 40, 0x2A, 0x50, 1, }, + { "ATT3B1", T1200kb, 512, 8, 2, 2, 48, 0x2A, 0x50, 1, }, + { "5¼DD", T360kb, 512, 9, 2, 1, 40, 0x2A, 0x50, 2, }, +}; + +/* + * bytes per sector encoding for the controller. + * - index for b2c is is (bytes per sector/128). + * - index for c2b is code from b2c + */ +static int b2c[] = +{ +[1] 0, +[2] 1, +[4] 2, +[8] 3, +}; +static int c2b[] = +{ + 128, + 256, + 512, + 1024, +}; + +FController fl; + +#define MOTORBIT(i) (1<<((i)+4)) + +/* + * predeclared + */ +static int cmddone(void*); +static void floppyformat(FDrive*, Cmdbuf*); +static void floppykproc(void*); +static void floppypos(FDrive*,long); +static int floppyrecal(FDrive*); +static int floppyresult(void); +static void floppyrevive(void); +static long floppyseek(FDrive*, long); +static int floppysense(void); +static void floppywait(int); +static long floppyxfer(FDrive*, int, void*, long, long); + +Dirtab floppydir[]={ + ".", {Qdir, 0, QTDIR}, 0, 0550, + "fd0disk", {Qdata + 0}, 0, 0660, + "fd0ctl", {Qctl + 0}, 0, 0660, + "fd1disk", {Qdata + 1}, 0, 0660, + "fd1ctl", {Qctl + 1}, 0, 0660, + "fd2disk", {Qdata + 2}, 0, 0660, + "fd2ctl", {Qctl + 2}, 0, 0660, + "fd3disk", {Qdata + 3}, 0, 0660, + "fd3ctl", {Qctl + 3}, 0, 0660, +}; +#define NFDIR 2 /* directory entries/drive */ + +enum +{ + CMdebug, + CMnodebug, + CMeject, + CMformat, + CMreset, +}; + +static Cmdtab floppyctlmsg[] = +{ + CMdebug, "debug", 1, + CMnodebug, "nodebug", 1, + CMeject, "eject", 1, + CMformat, "format", 0, + CMreset, "reset", 1, +}; + +static void +fldump(void) +{ + DPRINT("sra %ux srb %ux dor %ux msr %ux dir %ux\n", inb(Psra), inb(Psrb), + inb(Pdor), inb(Pmsr), inb(Pdir)); +} + +/* + * set floppy drive to its default type + */ +static void +floppysetdef(FDrive *dp) +{ + FType *t; + + for(t = floppytype; t < &floppytype[nelem(floppytype)]; t++) + if(dp->dt == t->dt){ + dp->t = t; + floppydir[1+NFDIR*dp->dev].length = dp->t->cap; + break; + } +} + +static void +floppyreset(void) +{ + FDrive *dp; + FType *t; + ulong maxtsize; + + floppysetup0(&fl); + if(fl.ndrive == 0) + return; + + /* + * init dependent parameters + */ + maxtsize = 0; + for(t = floppytype; t < &floppytype[nelem(floppytype)]; t++){ + t->cap = t->bytes * t->heads * t->sectors * t->tracks; + t->bcode = b2c[t->bytes/128]; + t->tsize = t->bytes * t->sectors; + if(maxtsize < t->tsize) + maxtsize = t->tsize; + } + + dmainit(DMAchan, maxtsize); + + /* + * allocate the drive storage + */ + fl.d = xalloc(fl.ndrive*sizeof(FDrive)); + fl.selected = fl.d; + + /* + * stop the motors + */ + fl.motor = 0; + delay(10); + outb(Pdor, fl.motor | Fintena | Fena); + delay(10); + + /* + * init drives + */ + for(dp = fl.d; dp < &fl.d[fl.ndrive]; dp++){ + dp->dev = dp - fl.d; + dp->dt = T1440kb; + floppysetdef(dp); + dp->cyl = -1; /* because we don't know */ + dp->cache = (uchar*)xspanalloc(maxtsize, BY2PG, 64*1024); + dp->ccyl = -1; + dp->vers = 0; + } + + /* + * first operation will recalibrate + */ + fl.confused = 1; + + floppysetup1(&fl); +} + +static Chan* +floppyattach(char *spec) +{ + static int kstarted; + + if(fl.ndrive == 0) + error(Enodev); + + if(kstarted == 0){ + /* + * watchdog to turn off the motors + */ + kstarted = 1; + kproc("floppy", floppykproc, 0, 0); + } + return devattach('f', spec); +} + +static Walkqid* +floppywalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, floppydir, 1+fl.ndrive*NFDIR, devgen); +} + +static int +floppystat(Chan *c, uchar *dp, int n) +{ + return devstat(c, dp, n, floppydir, 1+fl.ndrive*NFDIR, devgen); +} + +static Chan* +floppyopen(Chan *c, int omode) +{ + return devopen(c, omode, floppydir, 1+fl.ndrive*NFDIR, devgen); +} + +static void +floppyclose(Chan *) +{ +} + +static void +islegal(ulong offset, long n, FDrive *dp) +{ + if(offset % dp->t->bytes) + error(Ebadarg); + if(n % dp->t->bytes) + error(Ebadarg); +} + +/* + * check if the floppy has been replaced under foot. cause + * an error if it has. + * + * a seek and a read clears the condition. this was determined + * experimentally, there has to be a better way. + * + * if the read fails, cycle through the possible floppy + * density till one works or we've cycled through all + * possibilities for this drive. + */ +static void +changed(Chan *c, FDrive *dp) +{ + ulong old; + FType *start; + + /* + * if floppy has changed or first time through + */ + if((inb(Pdir)&Fchange) || dp->vers == 0){ + DPRINT("changed\n"); + fldump(); + dp->vers++; + start = dp->t; + dp->maxtries = 3; /* limit it when we're probing */ + + /* floppyon will fail if there's a controller but no drive */ + dp->confused = 1; /* make floppyon recal */ + if(floppyon(dp) < 0) + error(Eio); + + /* seek to the first track */ + floppyseek(dp, dp->t->heads*dp->t->tsize); + while(waserror()){ + /* + * if first attempt doesn't reset changed bit, there's + * no floppy there + */ + if(inb(Pdir)&Fchange) + nexterror(); + + while(++dp->t){ + if(dp->t == &floppytype[nelem(floppytype)]) + dp->t = floppytype; + if(dp->dt == dp->t->dt) + break; + } + floppydir[1+NFDIR*dp->dev].length = dp->t->cap; + + /* floppyon will fail if there's a controller but no drive */ + if(floppyon(dp) < 0) + error(Eio); + + DPRINT("changed: trying %s\n", dp->t->name); + fldump(); + if(dp->t == start) + nexterror(); + } + + /* if the read succeeds, we've got the density right */ + floppyxfer(dp, Fread, dp->cache, 0, dp->t->tsize); + poperror(); + dp->maxtries = 20; + } + + old = c->qid.vers; + c->qid.vers = dp->vers; + if(old && old != dp->vers) + error(Eio); +} + +static int +readtrack(FDrive *dp, int cyl, int head) +{ + int i, nn, sofar; + ulong pos; + + nn = dp->t->tsize; + if(dp->ccyl==cyl && dp->chead==head) + return nn; + pos = (cyl*dp->t->heads+head) * nn; + for(sofar = 0; sofar < nn; sofar += i){ + dp->ccyl = -1; + i = floppyxfer(dp, Fread, dp->cache + sofar, pos + sofar, nn - sofar); + if(i <= 0) + return -1; + } + dp->ccyl = cyl; + dp->chead = head; + return nn; +} + +static long +floppyread(Chan *c, void *a, long n, vlong off) +{ + FDrive *dp; + long rv; + int sec, head, cyl; + long len; + uchar *aa; + ulong offset = off; + + if(c->qid.type & QTDIR) + return devdirread(c, a, n, floppydir, 1+fl.ndrive*NFDIR, devgen); + + rv = 0; + dp = &fl.d[c->qid.path & ~Qmask]; + switch ((int)(c->qid.path & Qmask)) { + case Qdata: + islegal(offset, n, dp); + aa = a; + + qlock(&fl); + if(waserror()){ + qunlock(&fl); + nexterror(); + } + floppyon(dp); + changed(c, dp); + for(rv = 0; rv < n; rv += len){ + /* + * all xfers come out of the track cache + */ + dp->len = n - rv; + floppypos(dp, offset+rv); + cyl = dp->tcyl; + head = dp->thead; + len = dp->len; + sec = dp->tsec; + if(readtrack(dp, cyl, head) < 0) + break; + memmove(aa+rv, dp->cache + (sec-1)*dp->t->bytes, len); + } + qunlock(&fl); + poperror(); + + break; + case Qctl: + return readstr(offset, a, n, dp->t->name); + default: + panic("floppyread: bad qid"); + } + + return rv; +} + +static long +floppywrite(Chan *c, void *a, long n, vlong off) +{ + FDrive *dp; + long rv, i; + char *aa = a; + Cmdbuf *cb; + Cmdtab *ct; + ulong offset = off; + + rv = 0; + dp = &fl.d[c->qid.path & ~Qmask]; + switch ((int)(c->qid.path & Qmask)) { + case Qdata: + islegal(offset, n, dp); + qlock(&fl); + if(waserror()){ + qunlock(&fl); + nexterror(); + } + floppyon(dp); + changed(c, dp); + for(rv = 0; rv < n; rv += i){ + floppypos(dp, offset+rv); + if(dp->tcyl == dp->ccyl) + dp->ccyl = -1; + i = floppyxfer(dp, Fwrite, aa+rv, offset+rv, n-rv); + if(i < 0) + break; + if(i == 0) + error(Eio); + } + qunlock(&fl); + poperror(); + break; + case Qctl: + rv = n; + cb = parsecmd(a, n); + if(waserror()){ + free(cb); + nexterror(); + } + qlock(&fl); + if(waserror()){ + qunlock(&fl); + nexterror(); + } + ct = lookupcmd(cb, floppyctlmsg, nelem(floppyctlmsg)); + switch(ct->index){ + case CMeject: + floppyeject(dp); + break; + case CMformat: + floppyformat(dp, cb); + break; + case CMreset: + fl.confused = 1; + floppyon(dp); + break; + case CMdebug: + floppydebug = 1; + break; + case CMnodebug: + floppydebug = 0; + break; + } + poperror(); + qunlock(&fl); + poperror(); + free(cb); + break; + default: + panic("floppywrite: bad qid"); + } + + return rv; +} + +static void +floppykproc(void *) +{ + FDrive *dp; + + while(waserror()) + ; + for(;;){ + for(dp = fl.d; dp < &fl.d[fl.ndrive]; dp++){ + if((fl.motor&MOTORBIT(dp->dev)) + && TK2SEC(m->ticks - dp->lasttouched) > 5 + && canqlock(&fl)){ + if(TK2SEC(m->ticks - dp->lasttouched) > 5) + floppyoff(dp); + qunlock(&fl); + } + } + tsleep(&up->sleep, return0, 0, 1000); + } +} + +/* + * start a floppy drive's motor. + */ +static int +floppyon(FDrive *dp) +{ + int alreadyon; + int tries; + + if(fl.confused) + floppyrevive(); + + /* start motor and select drive */ + alreadyon = fl.motor & MOTORBIT(dp->dev); + fl.motor |= MOTORBIT(dp->dev); + outb(Pdor, fl.motor | Fintena | Fena | dp->dev); + if(!alreadyon){ + /* wait for drive to spin up */ + tsleep(&up->sleep, return0, 0, 750); + + /* clear any pending interrupts */ + floppysense(); + } + + /* set transfer rate */ + if(fl.rate != dp->t->rate){ + fl.rate = dp->t->rate; + outb(Pdsr, fl.rate); + } + + /* get drive to a known cylinder */ + if(dp->confused) + for(tries = 0; tries < 4; tries++) + if(floppyrecal(dp) >= 0) + break; + dp->lasttouched = m->ticks; + fl.selected = dp; + + /* return -1 if this didn't work */ + if(dp->confused) + return -1; + return 0; +} + +/* + * stop the floppy if it hasn't been used in 5 seconds + */ +static void +floppyoff(FDrive *dp) +{ + fl.motor &= ~MOTORBIT(dp->dev); + outb(Pdor, fl.motor | Fintena | Fena | dp->dev); +} + +/* + * send a command to the floppy + */ +static int +floppycmd(void) +{ + int i; + int tries; + + fl.nstat = 0; + for(i = 0; i < fl.ncmd; i++){ + for(tries = 0; ; tries++){ + if((inb(Pmsr)&(Ffrom|Fready)) == Fready) + break; + if(tries > 1000){ + DPRINT("cmd %ux can't be sent (%d)\n", fl.cmd[0], i); + fldump(); + + /* empty fifo, might have been a bad command */ + floppyresult(); + return -1; + } + microdelay(8); /* for machine independence */ + } + outb(Pfdata, fl.cmd[i]); + } + return 0; +} + +/* + * get a command result from the floppy + * + * when the controller goes ready waiting for a command + * (instead of sending results), we're done + * + */ +static int +floppyresult(void) +{ + int i, s; + int tries; + + /* get the result of the operation */ + for(i = 0; i < sizeof(fl.stat); i++){ + /* wait for status byte */ + for(tries = 0; ; tries++){ + s = inb(Pmsr)&(Ffrom|Fready); + if(s == Fready){ + fl.nstat = i; + return fl.nstat; + } + if(s == (Ffrom|Fready)) + break; + if(tries > 1000){ + DPRINT("floppyresult: %d stats\n", i); + fldump(); + fl.confused = 1; + return -1; + } + microdelay(8); /* for machine independence */ + } + fl.stat[i] = inb(Pfdata); + } + fl.nstat = sizeof(fl.stat); + return fl.nstat; +} + +/* + * calculate physical address of a logical byte offset into the disk + * + * truncate dp->length if it crosses a track boundary + */ +static void +floppypos(FDrive *dp, long off) +{ + int lsec; + int ltrack; + int end; + + lsec = off/dp->t->bytes; + ltrack = lsec/dp->t->sectors; + dp->tcyl = ltrack/dp->t->heads; + dp->tsec = (lsec % dp->t->sectors) + 1; + dp->thead = (lsec/dp->t->sectors) % dp->t->heads; + + /* + * can't read across track boundaries. + * if so, decrement the bytes to be read. + */ + end = (ltrack+1)*dp->t->sectors*dp->t->bytes; + if(off+dp->len > end) + dp->len = end - off; +} + +/* + * get the interrupt cause from the floppy. + */ +static int +floppysense(void) +{ + fl.ncmd = 0; + fl.cmd[fl.ncmd++] = Fsense; + if(floppycmd() < 0) + return -1; + if(floppyresult() < 2){ + DPRINT("can't read sense response\n"); + fldump(); + fl.confused = 1; + return -1; + } + return 0; +} + +static int +cmddone(void *) +{ + return fl.ncmd == 0; +} + +/* + * Wait for a floppy interrupt. If none occurs in 5 seconds, we + * may have missed one. This only happens on some portables which + * do power management behind our backs. Call the interrupt + * routine to try to clear any conditions. + */ +static void +floppywait(int slow) +{ + tsleep(&fl.r, cmddone, 0, slow ? 5000 : 1000); + if(!cmddone(0)){ + floppyintr(0); + fl.confused = 1; + } +} + +/* + * we've lost the floppy position, go to cylinder 0. + */ +static int +floppyrecal(FDrive *dp) +{ + dp->ccyl = -1; + dp->cyl = -1; + + fl.ncmd = 0; + fl.cmd[fl.ncmd++] = Frecal; + fl.cmd[fl.ncmd++] = dp->dev; + if(floppycmd() < 0) + return -1; + floppywait(1); + if(fl.nstat < 2){ + DPRINT("recalibrate: confused %ux\n", inb(Pmsr)); + fl.confused = 1; + return -1; + } + if((fl.stat[0] & (Codemask|Seekend)) != Seekend){ + DPRINT("recalibrate: failed\n"); + dp->confused = 1; + return -1; + } + dp->cyl = fl.stat[1]; + if(dp->cyl != 0){ + DPRINT("recalibrate: wrong cylinder %d\n", dp->cyl); + dp->cyl = -1; + dp->confused = 1; + return -1; + } + + dp->confused = 0; + return 0; +} + +/* + * if the controller or a specific drive is in a confused state, + * reset it and get back to a known state + */ +static void +floppyrevive(void) +{ + FDrive *dp; + + /* + * reset the controller if it's confused + */ + if(fl.confused){ + DPRINT("floppyrevive in\n"); + fldump(); + + /* reset controller and turn all motors off */ + splhi(); + fl.ncmd = 1; + fl.cmd[0] = 0; + outb(Pdor, 0); + delay(10); + outb(Pdor, Fintena|Fena); + delay(10); + spllo(); + fl.motor = 0; + fl.confused = 0; + floppywait(0); + + /* mark all drives in an unknown state */ + for(dp = fl.d; dp < &fl.d[fl.ndrive]; dp++) + dp->confused = 1; + + /* set rate to a known value */ + outb(Pdsr, 0); + fl.rate = 0; + + DPRINT("floppyrevive out\n"); + fldump(); + } +} + +/* + * seek to the target cylinder + * + * interrupt, no results + */ +static long +floppyseek(FDrive *dp, long off) +{ + floppypos(dp, off); + if(dp->cyl == dp->tcyl) + return dp->tcyl; + dp->cyl = -1; + + fl.ncmd = 0; + fl.cmd[fl.ncmd++] = Fseek; + fl.cmd[fl.ncmd++] = (dp->thead<<2) | dp->dev; + fl.cmd[fl.ncmd++] = dp->tcyl * dp->t->steps; + if(floppycmd() < 0) + return -1; + floppywait(1); + if(fl.nstat < 2){ + DPRINT("seek: confused\n"); + fl.confused = 1; + return -1; + } + if((fl.stat[0] & (Codemask|Seekend)) != Seekend){ + DPRINT("seek: failed\n"); + dp->confused = 1; + return -1; + } + + dp->cyl = dp->tcyl; + return dp->tcyl; +} + +/* + * read or write to floppy. try up to three times. + */ +static long +floppyxfer(FDrive *dp, int cmd, void *a, long off, long n) +{ + long offset; + int tries; + + if(off >= dp->t->cap) + return 0; + if(off + n > dp->t->cap) + n = dp->t->cap - off; + + /* retry on error (until it gets ridiculous) */ + tries = 0; + while(waserror()){ + if(tries++ >= dp->maxtries) + nexterror(); + DPRINT("floppyxfer: retrying\n"); + } + + dp->len = n; + if(floppyseek(dp, off) < 0){ + DPRINT("xfer: seek failed\n"); + dp->confused = 1; + error(Eio); + } + + /* + * set up the dma (dp->len may be trimmed) + */ + if(waserror()){ + dmaend(DMAchan); + nexterror(); + } + dp->len = dmasetup(DMAchan, a, dp->len, cmd==Fread); + if(dp->len < 0) + error(Eio); + + /* + * start operation + */ + fl.ncmd = 0; + fl.cmd[fl.ncmd++] = cmd | (dp->t->heads > 1 ? Fmulti : 0); + fl.cmd[fl.ncmd++] = (dp->thead<<2) | dp->dev; + fl.cmd[fl.ncmd++] = dp->tcyl; + fl.cmd[fl.ncmd++] = dp->thead; + fl.cmd[fl.ncmd++] = dp->tsec; + fl.cmd[fl.ncmd++] = dp->t->bcode; + fl.cmd[fl.ncmd++] = dp->t->sectors; + fl.cmd[fl.ncmd++] = dp->t->gpl; + fl.cmd[fl.ncmd++] = 0xFF; + if(floppycmd() < 0) + error(Eio); + + /* Poll ready bits and transfer data */ + floppyexec((char*)a, dp->len, cmd==Fread); + + /* + * give bus to DMA, floppyintr() will read result + */ + floppywait(0); + dmaend(DMAchan); + poperror(); + + /* + * check for errors + */ + if(fl.nstat < 7){ + DPRINT("xfer: confused\n"); + fl.confused = 1; + error(Eio); + } + if((fl.stat[0] & Codemask)!=0 || fl.stat[1] || fl.stat[2]){ + DPRINT("xfer: failed %ux %ux %ux\n", fl.stat[0], + fl.stat[1], fl.stat[2]); + DPRINT("offset %lud len %ld\n", off, dp->len); + if((fl.stat[0]&Codemask)==Cmdexec && fl.stat[1]==Overrun){ + DPRINT("DMA overrun: retry\n"); + } else + dp->confused = 1; + error(Eio); + } + + /* + * check for correct cylinder + */ + offset = fl.stat[3] * dp->t->heads + fl.stat[4]; + offset = offset*dp->t->sectors + fl.stat[5] - 1; + offset = offset * c2b[fl.stat[6]]; + if(offset != off+dp->len){ + DPRINT("xfer: ends on wrong cyl\n"); + dp->confused = 1; + error(Eio); + } + poperror(); + + dp->lasttouched = m->ticks; + return dp->len; +} + +/* + * format a track + */ +static void +floppyformat(FDrive *dp, Cmdbuf *cb) +{ + int cyl, h, sec; + ulong track; + uchar *buf, *bp; + FType *t; + + /* + * set the type + */ + if(cb->nf == 2){ + for(t = floppytype; t < &floppytype[nelem(floppytype)]; t++){ + if(strcmp(cb->f[1], t->name)==0 && t->dt==dp->dt){ + dp->t = t; + floppydir[1+NFDIR*dp->dev].length = dp->t->cap; + break; + } + } + if(t >= &floppytype[nelem(floppytype)]) + error(Ebadarg); + } else if(cb->nf == 1){ + floppysetdef(dp); + t = dp->t; + } else { + cmderror(cb, "invalid floppy format command"); + SET(t); + } + + /* + * buffer for per track info + */ + buf = smalloc(t->sectors*4); + if(waserror()){ + free(buf); + nexterror(); + } + + /* force a recalibrate to cylinder 0 */ + dp->confused = 1; + if(!waserror()){ + floppyon(dp); + poperror(); + } + + /* + * format a track at time + */ + for(track = 0; track < t->tracks*t->heads; track++){ + cyl = track/t->heads; + h = track % t->heads; + + /* + * seek to track, ignore errors + */ + floppyseek(dp, track*t->tsize); + dp->cyl = cyl; + dp->confused = 0; + + /* + * set up the dma (dp->len may be trimmed) + */ + bp = buf; + for(sec = 1; sec <= t->sectors; sec++){ + *bp++ = cyl; + *bp++ = h; + *bp++ = sec; + *bp++ = t->bcode; + } + if(waserror()){ + dmaend(DMAchan); + nexterror(); + } + if(dmasetup(DMAchan, buf, bp-buf, 0) < 0) + error(Eio); + + /* + * start operation + */ + fl.ncmd = 0; + fl.cmd[fl.ncmd++] = Fformat; + fl.cmd[fl.ncmd++] = (h<<2) | dp->dev; + fl.cmd[fl.ncmd++] = t->bcode; + fl.cmd[fl.ncmd++] = t->sectors; + fl.cmd[fl.ncmd++] = t->fgpl; + fl.cmd[fl.ncmd++] = 0x5a; + if(floppycmd() < 0) + error(Eio); + + /* Poll ready bits and transfer data */ + floppyexec((char *)buf, bp-buf, 0); + + /* + * give bus to DMA, floppyintr() will read result + */ + floppywait(1); + dmaend(DMAchan); + poperror(); + + /* + * check for errors + */ + if(fl.nstat < 7){ + DPRINT("format: confused\n"); + fl.confused = 1; + error(Eio); + } + if((fl.stat[0]&Codemask)!=0 || fl.stat[1]|| fl.stat[2]){ + DPRINT("format: failed %ux %ux %ux\n", + fl.stat[0], fl.stat[1], fl.stat[2]); + dp->confused = 1; + error(Eio); + } + } + free(buf); + dp->confused = 1; + poperror(); +} + +static void +floppyintr(Ureg *) +{ + switch(fl.cmd[0]&~Fmulti){ + case Fread: + case Fwrite: + case Fformat: + case Fdumpreg: + floppyresult(); + break; + case Fseek: + case Frecal: + default: + floppysense(); /* to clear interrupt */ + break; + } + fl.ncmd = 0; + wakeup(&fl.r); +} + +Dev floppydevtab = { + 'f', + "floppy", + + floppyreset, + devinit, + devshutdown, + floppyattach, + floppywalk, + floppystat, + floppyopen, + devcreate, + floppyclose, + floppyread, + devbread, + floppywrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/os/pc/devi82365.c b/os/pc/devi82365.c new file mode 100644 index 00000000..5c67847f --- /dev/null +++ b/os/pc/devi82365.c @@ -0,0 +1,1044 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "io.h" + +/* + * Intel 82365SL PCIC controller and compatibles. + */ +enum +{ + /* + * registers indices + */ + Rid= 0x0, /* identification and revision */ + Ris= 0x1, /* interface status */ + Rpc= 0x2, /* power control */ + Foutena= (1<<7), /* output enable */ + Fautopower= (1<<5), /* automatic power switching */ + Fcardena= (1<<4), /* PC card enable */ + Rigc= 0x3, /* interrupt and general control */ + Fiocard= (1<<5), /* I/O card (vs memory) */ + Fnotreset= (1<<6), /* reset if not set */ + FSMIena= (1<<4), /* enable change interrupt on SMI */ + Rcsc= 0x4, /* card status change */ + Rcscic= 0x5, /* card status change interrupt config */ + Fchangeena= (1<<3), /* card changed */ + Fbwarnena= (1<<1), /* card battery warning */ + Fbdeadena= (1<<0), /* card battery dead */ + Rwe= 0x6, /* address window enable */ + Fmem16= (1<<5), /* use A23-A12 to decode address */ + Rio= 0x7, /* I/O control */ + Fwidth16= (1<<0), /* 16 bit data width */ + Fiocs16= (1<<1), /* IOCS16 determines data width */ + Fzerows= (1<<2), /* zero wait state */ + Ftiming= (1<<3), /* timing register to use */ + Riobtm0lo= 0x8, /* I/O address 0 start low byte */ + Riobtm0hi= 0x9, /* I/O address 0 start high byte */ + Riotop0lo= 0xa, /* I/O address 0 stop low byte */ + Riotop0hi= 0xb, /* I/O address 0 stop high byte */ + Riobtm1lo= 0xc, /* I/O address 1 start low byte */ + Riobtm1hi= 0xd, /* I/O address 1 start high byte */ + Riotop1lo= 0xe, /* I/O address 1 stop low byte */ + Riotop1hi= 0xf, /* I/O address 1 stop high byte */ + Rmap= 0x10, /* map 0 */ + + /* + * CL-PD67xx extension registers + */ + Rmisc1= 0x16, /* misc control 1 */ + F5Vdetect= (1<<0), + Fvcc3V= (1<<1), + Fpmint= (1<<2), + Fpsirq= (1<<3), + Fspeaker= (1<<4), + Finpack= (1<<7), + Rfifo= 0x17, /* fifo control */ + Fflush= (1<<7), /* flush fifo */ + Rmisc2= 0x1E, /* misc control 2 */ + Flowpow= (1<<1), /* low power mode */ + Rchipinfo= 0x1F, /* chip information */ + Ratactl= 0x26, /* ATA control */ + + /* + * offsets into the system memory address maps + */ + Mbtmlo= 0x0, /* System mem addr mapping start low byte */ + Mbtmhi= 0x1, /* System mem addr mapping start high byte */ + F16bit= (1<<7), /* 16-bit wide data path */ + Mtoplo= 0x2, /* System mem addr mapping stop low byte */ + Mtophi= 0x3, /* System mem addr mapping stop high byte */ + Ftimer1= (1<<6), /* timer set 1 */ + Mofflo= 0x4, /* Card memory offset address low byte */ + Moffhi= 0x5, /* Card memory offset address high byte */ + Fregactive= (1<<6), /* attribute memory */ + + /* + * configuration registers - they start at an offset in attribute + * memory found in the CIS. + */ + Rconfig= 0, + Creset= (1<<7), /* reset device */ + Clevel= (1<<6), /* level sensitive interrupt line */ + Cirq= (1<<2), /* IRQ enable */ + Cdecode= (1<<1), /* address decode */ + Cfunc= (1<<0), /* function enable */ + Riobase0= 5, + Riobase1= 6, + Riosize= 9, +}; + +#define MAP(x,o) (Rmap + (x)*0x8 + o) + +typedef struct I82365 I82365; + +/* a controller */ +enum +{ + Ti82365, + Tpd6710, + Tpd6720, + Tvg46x, +}; +struct I82365 +{ + int type; + int dev; + int nslot; + int xreg; /* index register address */ + int dreg; /* data register address */ + int irq; +}; +static I82365 *controller[4]; +static int ncontroller; +static PCMslot *slot; +static PCMslot *lastslot; +static nslot; + +static void i82365intr(Ureg*, void*); +static int pcmio(int, ISAConf*); +static long pcmread(int, int, void*, long, vlong); +static long pcmwrite(int, int, void*, long, vlong); + +static void i82365dump(PCMslot*); + +/* + * reading and writing card registers + */ +static uchar +rdreg(PCMslot *pp, int index) +{ + outb(((I82365*)pp->cp)->xreg, pp->base + index); + return inb(((I82365*)pp->cp)->dreg); +} +static void +wrreg(PCMslot *pp, int index, uchar val) +{ + outb(((I82365*)pp->cp)->xreg, pp->base + index); + outb(((I82365*)pp->cp)->dreg, val); +} + +/* + * get info about card + */ +static void +slotinfo(PCMslot *pp) +{ + uchar isr; + + isr = rdreg(pp, Ris); + pp->occupied = (isr & (3<<2)) == (3<<2); + pp->powered = isr & (1<<6); + pp->battery = (isr & 3) == 3; + pp->wrprot = isr & (1<<4); + pp->busy = isr & (1<<5); + pp->msec = TK2MS(MACHP(0)->ticks); +} + +static int +vcode(int volt) +{ + switch(volt){ + case 5: + return 1; + case 12: + return 2; + default: + return 0; + } +} + +/* + * enable the slot card + */ +static void +slotena(PCMslot *pp) +{ + if(pp->enabled) + return; + + /* power up and unreset, wait's are empirical (???) */ + wrreg(pp, Rpc, Fautopower|Foutena|Fcardena); + delay(300); + wrreg(pp, Rigc, 0); + delay(100); + wrreg(pp, Rigc, Fnotreset); + delay(5000); + + /* get configuration */ + slotinfo(pp); + if(pp->occupied){ + pcmcisread(pp); + pp->enabled = 1; + } else + wrreg(pp, Rpc, Fautopower); +} + +/* + * disable the slot card + */ +static void +slotdis(PCMslot *pp) +{ + wrreg(pp, Rpc, 0); /* turn off card power */ + wrreg(pp, Rwe, 0); /* no windows */ + pp->enabled = 0; +} + +/* + * status change interrupt + */ +static void +i82365intr(Ureg *, void *) +{ + uchar csc, was; + PCMslot *pp; + + if(slot == 0) + return; + + for(pp = slot; pp < lastslot; pp++){ + csc = rdreg(pp, Rcsc); + was = pp->occupied; + slotinfo(pp); + if(csc & (1<<3) && was != pp->occupied){ + if(!pp->occupied) + slotdis(pp); + } + } +} + +enum +{ + Mshift= 12, + Mgran= (1<<Mshift), /* granularity of maps */ + Mmask= ~(Mgran-1), /* mask for address bits important to the chip */ +}; + +/* + * get a map for pc card region, return corrected len + */ +PCMmap* +pcmmap(int slotno, ulong offset, int len, int attr) +{ + PCMslot *pp; + uchar we, bit; + PCMmap *m, *nm; + int i; + ulong e; + + pp = slot + slotno; + lock(&pp->mlock); + + /* convert offset to granularity */ + if(len <= 0) + len = 1; + e = ROUND(offset+len, Mgran); + offset &= Mmask; + len = e - offset; + + /* look for a map that covers the right area */ + we = rdreg(pp, Rwe); + bit = 1; + nm = 0; + for(m = pp->mmap; m < &pp->mmap[nelem(pp->mmap)]; m++){ + if((we & bit)) + if(m->attr == attr) + if(offset >= m->ca && e <= m->cea){ + + m->ref++; + unlock(&pp->mlock); + return m; + } + bit <<= 1; + if(nm == 0 && m->ref == 0) + nm = m; + } + m = nm; + if(m == 0){ + unlock(&pp->mlock); + return 0; + } + + /* if isa space isn't big enough, free it and get more */ + if(m->len < len){ + if(m->isa){ + umbfree(m->isa, m->len); + m->len = 0; + } + m->isa = PADDR(umbmalloc(0, len, Mgran)); + if(m->isa == 0){ + print("pcmmap: out of isa space\n"); + unlock(&pp->mlock); + return 0; + } + m->len = len; + } + + /* set up new map */ + m->ca = offset; + m->cea = m->ca + m->len; + m->attr = attr; + i = m-pp->mmap; + bit = 1<<i; + wrreg(pp, Rwe, we & ~bit); /* disable map before changing it */ + wrreg(pp, MAP(i, Mbtmlo), m->isa>>Mshift); + wrreg(pp, MAP(i, Mbtmhi), (m->isa>>(Mshift+8)) | F16bit); + wrreg(pp, MAP(i, Mtoplo), (m->isa+m->len-1)>>Mshift); + wrreg(pp, MAP(i, Mtophi), ((m->isa+m->len-1)>>(Mshift+8))); + offset -= m->isa; + offset &= (1<<25)-1; + offset >>= Mshift; + wrreg(pp, MAP(i, Mofflo), offset); + wrreg(pp, MAP(i, Moffhi), (offset>>8) | (attr ? Fregactive : 0)); + wrreg(pp, Rwe, we | bit); /* enable map */ + m->ref = 1; + + unlock(&pp->mlock); + return m; +} + +void +pcmunmap(int slotno, PCMmap* m) +{ + PCMslot *pp; + + pp = slot + slotno; + lock(&pp->mlock); + m->ref--; + unlock(&pp->mlock); +} + +static void +increfp(PCMslot *pp) +{ + lock(pp); + if(pp->ref++ == 0) + slotena(pp); + unlock(pp); +} + +static void +decrefp(PCMslot *pp) +{ + lock(pp); + if(pp->ref-- == 1) + slotdis(pp); + unlock(pp); +} + +/* + * look for a card whose version contains 'idstr' + */ +static int +pcmcia_pcmspecial(char *idstr, ISAConf *isa) +{ + PCMslot *pp; + extern char *strstr(char*, char*); + int enabled; + + for(pp = slot; pp < lastslot; pp++){ + if(pp->special) + continue; /* already taken */ + + /* + * make sure we don't power on cards when we already know what's + * in them. We'll reread every two minutes if necessary + */ + enabled = 0; + if (pp->msec == ~0 || TK2MS(MACHP(0)->ticks) - pp->msec > 120000){ + increfp(pp); + enabled++; + } + + if(pp->occupied) { + if(strstr(pp->verstr, idstr)){ + if (!enabled){ + enabled = 1; + increfp(pp); + } + if(isa == 0 || pcmio(pp->slotno, isa) == 0){ + pp->special = 1; + return pp->slotno; + } + } + } else + pp->special = 1; + if (enabled) + decrefp(pp); + } + return -1; +} + +static void +pcmcia_pcmspecialclose(int slotno) +{ + PCMslot *pp; + + if(slotno >= nslot) + panic("pcmspecialclose"); + pp = slot + slotno; + pp->special = 0; + decrefp(pp); +} + +enum +{ + Qdir, + Qmem, + Qattr, + Qctl, + + Nents = 3, +}; + +#define SLOTNO(c) ((ulong)((c->qid.path>>8)&0xff)) +#define TYPE(c) ((ulong)(c->qid.path&0xff)) +#define QID(s,t) (((s)<<8)|(t)) + +static int +pcmgen(Chan *c, char*, Dirtab *, int , int i, Dir *dp) +{ + int slotno; + Qid qid; + long len; + PCMslot *pp; + + if(i == DEVDOTDOT){ + mkqid(&qid, Qdir, 0, QTDIR); + devdir(c, qid, "#y", 0, eve, 0555, dp); + return 1; + } + + if(i >= Nents*nslot) + return -1; + slotno = i/Nents; + pp = slot + slotno; + len = 0; + switch(i%Nents){ + case 0: + qid.path = QID(slotno, Qmem); + snprint(up->genbuf, sizeof up->genbuf, "pcm%dmem", slotno); + len = pp->memlen; + break; + case 1: + qid.path = QID(slotno, Qattr); + snprint(up->genbuf, sizeof up->genbuf, "pcm%dattr", slotno); + len = pp->memlen; + break; + case 2: + qid.path = QID(slotno, Qctl); + snprint(up->genbuf, sizeof up->genbuf, "pcm%dctl", slotno); + break; + } + qid.vers = 0; + qid.type = QTFILE; + devdir(c, qid, up->genbuf, len, eve, 0660, dp); + return 1; +} + +static char *chipname[] = +{ +[Ti82365] "Intel 82365SL", +[Tpd6710] "Cirrus Logic CL-PD6710", +[Tpd6720] "Cirrus Logic CL-PD6720", +[Tvg46x] "Vadem VG-46x", +}; + +static I82365* +i82365probe(int x, int d, int dev) +{ + uchar c, id; + I82365 *cp; + ISAConf isa; + int i, nslot; + + outb(x, Rid + (dev<<7)); + id = inb(d); + if((id & 0xf0) != 0x80) + return 0; /* not a memory & I/O card */ + if((id & 0x0f) == 0x00) + return 0; /* no revision number, not possible */ + + cp = xalloc(sizeof(I82365)); + cp->xreg = x; + cp->dreg = d; + cp->dev = dev; + cp->type = Ti82365; + cp->nslot = 2; + + switch(id){ + case 0x82: + case 0x83: + case 0x84: + /* could be a cirrus */ + outb(x, Rchipinfo + (dev<<7)); + outb(d, 0); + c = inb(d); + if((c & 0xc0) != 0xc0) + break; + c = inb(d); + if((c & 0xc0) != 0x00) + break; + if(c & 0x20){ + cp->type = Tpd6720; + } else { + cp->type = Tpd6710; + cp->nslot = 1; + } + + /* low power mode */ + outb(x, Rmisc2 + (dev<<7)); + c = inb(d); + outb(d, c & ~Flowpow); + break; + } + + /* if it's not a Cirrus, it could be a Vadem... */ + if(cp->type == Ti82365){ + /* unlock the Vadem extended regs */ + outb(x, 0x0E + (dev<<7)); + outb(x, 0x37 + (dev<<7)); + + /* make the id register show the Vadem id */ + outb(x, 0x3A + (dev<<7)); + c = inb(d); + outb(d, c|0xC0); + outb(x, Rid + (dev<<7)); + c = inb(d); + if(c & 0x08) + cp->type = Tvg46x; + + /* go back to Intel compatible id */ + outb(x, 0x3A + (dev<<7)); + c = inb(d); + outb(d, c & ~0xC0); + } + + memset(&isa, 0, sizeof(ISAConf)); + if(isaconfig("pcmcia", ncontroller, &isa) && isa.irq) + cp->irq = isa.irq; + else + cp->irq = IrqPCMCIA; + + for(i = 0; i < isa.nopt; i++){ + if(cistrncmp(isa.opt[i], "nslot=", 6)) + continue; + nslot = strtol(&isa.opt[i][6], nil, 0); + if(nslot > 0 && nslot <= 2) + cp->nslot = nslot; + } + + controller[ncontroller++] = cp; + return cp; +} + +static void +i82365dump(PCMslot *pp) +{ + int i; + + for(i = 0; i < 0x40; i++){ + if((i&0x0F) == 0) + print("\n%2.2uX: ", i); + print("%2.2uX ", rdreg(pp, i)); + if(((i+1) & 0x0F) == 0x08) + print(" - "); + } + print("\n"); +} + +/* + * set up for slot cards + */ +void +devi82365link(void) +{ + static int already; + int i, j; + I82365 *cp; + PCMslot *pp; + char buf[32], *p; + + if(already) + return; + already = 1; + + if((p=getconf("pcmcia0")) && strncmp(p, "disabled", 8)==0) + return; + + if(_pcmspecial) + return; + + /* look for controllers if the ports aren't already taken */ + if(ioalloc(0x3E0, 2, 0, "i82365.0") >= 0){ + i82365probe(0x3E0, 0x3E1, 0); + i82365probe(0x3E0, 0x3E1, 1); + if(ncontroller == 0) + iofree(0x3E0); + } + if(ioalloc(0x3E2, 2, 0, "i82365.1") >= 0){ + i = ncontroller; + i82365probe(0x3E2, 0x3E3, 0); + i82365probe(0x3E2, 0x3E3, 1); + if(ncontroller == i) + iofree(0x3E2); + } + + if(ncontroller == 0) + return; + + _pcmspecial = pcmcia_pcmspecial; + _pcmspecialclose = pcmcia_pcmspecialclose; + + for(i = 0; i < ncontroller; i++) + nslot += controller[i]->nslot; + slot = xalloc(nslot * sizeof(PCMslot)); + + lastslot = slot; + for(i = 0; i < ncontroller; i++){ + cp = controller[i]; + print("#y%d: %d slot %s: port 0x%uX irq %d\n", + i, cp->nslot, chipname[cp->type], cp->xreg, cp->irq); + for(j = 0; j < cp->nslot; j++){ + pp = lastslot++; + pp->slotno = pp - slot; + pp->memlen = 64*MB; + pp->base = (cp->dev<<7) | (j<<6); + pp->cp = cp; + pp->msec = ~0; + pp->verstr[0] = 0; + slotdis(pp); + + /* interrupt on status change */ + wrreg(pp, Rcscic, (cp->irq<<4) | Fchangeena); + rdreg(pp, Rcsc); + } + + /* for card management interrupts */ + snprint(buf, sizeof buf, "i82365.%d", i); + intrenable(cp->irq, i82365intr, 0, BUSUNKNOWN, buf); + } +} + +static Chan* +i82365attach(char *spec) +{ + return devattach('y', spec); +} + +static Walkqid* +i82365walk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, 0, 0, pcmgen); +} + +static int +i82365stat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, 0, 0, pcmgen); +} + +static Chan* +i82365open(Chan *c, int omode) +{ + if(c->qid.type & QTDIR){ + if(omode != OREAD) + error(Eperm); + } else + increfp(slot + SLOTNO(c)); + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + return c; +} + +static void +i82365close(Chan *c) +{ + if(c->flag & COPEN) + if((c->qid.type & QTDIR) == 0) + decrefp(slot+SLOTNO(c)); +} + +/* a memmove using only bytes */ +static void +memmoveb(uchar *to, uchar *from, int n) +{ + while(n-- > 0) + *to++ = *from++; +} + +/* a memmove using only shorts & bytes */ +static void +memmoves(uchar *to, uchar *from, int n) +{ + ushort *t, *f; + + if((((ulong)to) & 1) || (((ulong)from) & 1) || (n & 1)){ + while(n-- > 0) + *to++ = *from++; + } else { + n = n/2; + t = (ushort*)to; + f = (ushort*)from; + while(n-- > 0) + *t++ = *f++; + } +} + +static long +pcmread(int slotno, int attr, void *a, long n, vlong off) +{ + int i, len; + PCMmap *m; + uchar *ac; + PCMslot *pp; + ulong offset = off; + + pp = slot + slotno; + if(pp->memlen < offset) + return 0; + if(pp->memlen < offset + n) + n = pp->memlen - offset; + + m = 0; + if(waserror()){ + if(m) + pcmunmap(pp->slotno, m); + nexterror(); + } + + ac = a; + for(len = n; len > 0; len -= i){ + m = pcmmap(pp->slotno, offset, 0, attr); + if(m == 0) + error("cannot map PCMCIA card"); + if(offset + len > m->cea) + i = m->cea - offset; + else + i = len; + memmoveb(ac, KADDR(m->isa + offset - m->ca), i); + pcmunmap(pp->slotno, m); + offset += i; + ac += i; + } + + poperror(); + return n; +} + +static long +i82365read(Chan *c, void *a, long n, vlong off) +{ + char *p, *buf, *e; + PCMslot *pp; + ulong offset = off; + + switch(TYPE(c)){ + case Qdir: + return devdirread(c, a, n, 0, 0, pcmgen); + case Qmem: + case Qattr: + return pcmread(SLOTNO(c), TYPE(c) == Qattr, a, n, off); + case Qctl: + buf = p = malloc(READSTR); + e = p + READSTR; + pp = slot + SLOTNO(c); + + buf[0] = 0; + if(pp->occupied){ + p = seprint(p, e, "occupied\n"); + if(pp->verstr[0]) + p = seprint(p, e, "version %s\n", pp->verstr); + } + if(pp->enabled) + p = seprint(p, e, "enabled\n"); + if(pp->powered) + p = seprint(p, e, "powered\n"); + if(pp->configed) + p = seprint(p, e, "configed\n"); + if(pp->wrprot) + p = seprint(p, e, "write protected\n"); + if(pp->busy) + p = seprint(p, e, "busy\n"); + seprint(p, e, "battery lvl %d\n", pp->battery); + + n = readstr(offset, a, n, buf); + free(buf); + + return n; + } + error(Ebadarg); + return -1; /* not reached */ +} + +static long +pcmwrite(int dev, int attr, void *a, long n, vlong off) +{ + int i, len; + PCMmap *m; + uchar *ac; + PCMslot *pp; + ulong offset = off; + + pp = slot + dev; + if(pp->memlen < offset) + return 0; + if(pp->memlen < offset + n) + n = pp->memlen - offset; + + m = 0; + if(waserror()){ + if(m) + pcmunmap(pp->slotno, m); + nexterror(); + } + + ac = a; + for(len = n; len > 0; len -= i){ + m = pcmmap(pp->slotno, offset, 0, attr); + if(m == 0) + error("cannot map PCMCIA card"); + if(offset + len > m->cea) + i = m->cea - offset; + else + i = len; + memmoveb(KADDR(m->isa + offset - m->ca), ac, i); + pcmunmap(pp->slotno, m); + offset += i; + ac += i; + } + + poperror(); + return n; +} + +static long +i82365write(Chan *c, void *a, long n, vlong off) +{ + PCMslot *pp; + char buf[32]; + + switch(TYPE(c)){ + case Qctl: + if(n >= sizeof(buf)) + n = sizeof(buf) - 1; + strncpy(buf, a, n); + buf[n] = 0; + pp = slot + SLOTNO(c); + if(!pp->occupied) + error(Eio); + + /* set vpp on card */ + if(strncmp(buf, "vpp", 3) == 0) + wrreg(pp, Rpc, vcode(atoi(buf+3))|Fautopower|Foutena|Fcardena); + return n; + case Qmem: + case Qattr: + pp = slot + SLOTNO(c); + if(pp->occupied == 0 || pp->enabled == 0) + error(Eio); + n = pcmwrite(pp->slotno, TYPE(c) == Qattr, a, n, off); + if(n < 0) + error(Eio); + return n; + } + error(Ebadarg); + return -1; /* not reached */ +} + +Dev i82365devtab = { + 'y', + "i82365", + + devreset, + devinit, + devshutdown, + i82365attach, + i82365walk, + i82365stat, + i82365open, + devcreate, + i82365close, + i82365read, + devbread, + i82365write, + devbwrite, + devremove, + devwstat, +}; + +/* + * configure the PCMslot for IO. We assume very heavily that we can read + * configuration info from the CIS. If not, we won't set up correctly. + */ +static int +pcmio(int slotno, ISAConf *isa) +{ + uchar we, x, *p; + PCMslot *pp; + PCMconftab *ct, *et, *t; + PCMmap *m; + int i, index, irq; + char *cp; + + irq = isa->irq; + if(irq == 2) + irq = 9; + + if(slotno > nslot) + return -1; + pp = slot + slotno; + + if(!pp->occupied) + return -1; + + et = &pp->ctab[pp->nctab]; + + ct = 0; + for(i = 0; i < isa->nopt; i++){ + if(strncmp(isa->opt[i], "index=", 6)) + continue; + index = strtol(&isa->opt[i][6], &cp, 0); + if(cp == &isa->opt[i][6] || index >= pp->nctab) + return -1; + ct = &pp->ctab[index]; + } + + if(ct == 0){ + /* assume default is right */ + if(pp->def) + ct = pp->def; + else + ct = pp->ctab; + + /* try for best match */ + if(ct->nio == 0 + || ct->io[0].start != isa->port || ((1<<irq) & ct->irqs) == 0){ + for(t = pp->ctab; t < et; t++) + if(t->nio + && t->io[0].start == isa->port + && ((1<<irq) & t->irqs)){ + ct = t; + break; + } + } + if(ct->nio == 0 || ((1<<irq) & ct->irqs) == 0){ + for(t = pp->ctab; t < et; t++) + if(t->nio && ((1<<irq) & t->irqs)){ + ct = t; + break; + } + } + if(ct->nio == 0){ + for(t = pp->ctab; t < et; t++) + if(t->nio){ + ct = t; + break; + } + } + } + + if(ct == et || ct->nio == 0) + return -1; + if(isa->port == 0 && ct->io[0].start == 0) + return -1; + + /* route interrupts */ + isa->irq = irq; + wrreg(pp, Rigc, irq | Fnotreset | Fiocard); + + /* set power and enable device */ + x = vcode(ct->vpp1); + wrreg(pp, Rpc, x|Fautopower|Foutena|Fcardena); + + /* 16-bit data path */ + if(ct->bit16) + x = Ftiming|Fiocs16|Fwidth16; + else + x = Ftiming; + if(ct->nio == 2 && ct->io[1].start) + x |= x<<4; + wrreg(pp, Rio, x); + + /* + * enable io port map 0 + * the 'top' register value includes the last valid address + */ + if(isa->port == 0) + isa->port = ct->io[0].start; + we = rdreg(pp, Rwe); + wrreg(pp, Riobtm0lo, isa->port); + wrreg(pp, Riobtm0hi, isa->port>>8); + i = isa->port+ct->io[0].len-1; + wrreg(pp, Riotop0lo, i); + wrreg(pp, Riotop0hi, i>>8); + we |= 1<<6; + if(ct->nio >= 2 && ct->io[1].start){ + wrreg(pp, Riobtm1lo, ct->io[1].start); + wrreg(pp, Riobtm1hi, ct->io[1].start>>8); + i = ct->io[1].start+ct->io[1].len-1; + wrreg(pp, Riotop1lo, i); + wrreg(pp, Riotop1hi, i>>8); + we |= 1<<7; + } + wrreg(pp, Rwe, we); + + /* only touch Rconfig if it is present */ + m = pcmmap(slotno, pp->cfg[0].caddr + Rconfig, 0x20, 1); + p = KADDR(m->isa + pp->cfg[0].caddr - m->ca); + if(pp->cfg[0].cpresent & (1<<Rconfig)){ + /* Reset adapter */ + + /* set configuration and interrupt type. + * if level is possible on the card, use it. + */ + x = ct->index; + if(ct->irqtype & 0x20) + x |= Clevel; + + /* enable the device, enable address decode and + * irq enable. + */ + x |= Cfunc|Cdecode|Cirq; + + p[0] = x; + //delay(5); + microdelay(40); + } + + if(pp->cfg[0].cpresent & (1<<Riobase0)){ + /* set up the iobase 0 */ + p[Riobase0 << 1] = isa->port; + p[Riobase1 << 1] = isa->port >> 8; + } + + if(pp->cfg[0].cpresent & (1<<Riosize)) + p[Riosize << 1] = ct->io[0].len; + pcmunmap(slotno, m); + return 0; +} diff --git a/os/pc/devlm78.c b/os/pc/devlm78.c new file mode 100644 index 00000000..617d5cb9 --- /dev/null +++ b/os/pc/devlm78.c @@ -0,0 +1,346 @@ +#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" + +/* this driver doesn't implement the management interrupts. we + * leave the LM78 interrupts set to whatever the BIOS did. we do + * allow reading and writing the the readouts and alarm values. + * Read(2)ing or write(2)ing at offset 0x0-0x1f, is + * equivalent to reading or writing lm78 registers 0x20-0x3f. + */ +enum +{ + /* address of chip on serial interface */ + Serialaddr= 0x2d, + + /* parallel access registers */ + Rpaddr= 0x5, + Bbusy= (1<<7), + Rpdata= 0x6, + + /* internal register addresses */ + Rconfig= 0x40, + Bstart= (1<<0), + Bsmiena= (1<<1), + Birqena= (1<<2), + Bintclr= (1<<3), + Breset= (1<<4), + Bnmi= (1<<5), /* if set, use nmi, else irq */ + Bpowbypass= (1<<6), + Binit= (1<<7), + Ristat1= 0x41, + Ristat2= 0x42, + Rsmimask1= 0x43, + Rsmimask2= 0x44, + Rnmimask1= 0x45, + Rnmimask2= 0x46, + Rvidfan= 0x47, /* set fan counter, and read voltage level */ + Mvid= 0x0f, + Mfan= 0xf0, + Raddr= 0x48, /* address used on serial bus */ + Rresetid= 0x49, /* chip reset and ID register */ + Rpost= 0x00, /* start of post ram */ + Rvalue= 0x20, /* start of value ram */ + + VRsize= 0x20, /* size of value ram */ +}; + +enum +{ + Qdir, + Qlm78vram, +}; + +static Dirtab lm78dir[] = { + ".", { Qdir, 0, QTDIR}, 0, 0555, + "lm78vram", { Qlm78vram, 0 }, 0, 0444, +}; + +/* interface type */ +enum +{ + None= 0, + Smbus, + Parallel, +}; + +static struct { + QLock; + int probed; + int ifc; /* which interface is connected */ + SMBus *smbus; /* serial interface */ + int port; /* parallel interface */ +} lm78; + +extern SMBus* piix4smbus(void); + +/* wait for device to become quiescent and then set the */ +/* register address */ +static void +setreg(int reg) +{ + int tries; + + for(tries = 0; tries < 1000000; tries++) + if((inb(lm78.port+Rpaddr) & Bbusy) == 0){ + outb(lm78.port+Rpaddr, reg); + return; + } + error("lm78 broken"); +} + +/* routines that actually touch the device */ +static void +lm78wrreg(int reg, uchar val) +{ + if(waserror()){ + qunlock(&lm78); + nexterror(); + } + qlock(&lm78); + + switch(lm78.ifc){ + case Smbus: + lm78.smbus->transact(lm78.smbus, SMBbytewrite, Serialaddr, reg, &val); + break; + case Parallel: + setreg(reg); + outb(lm78.port+Rpdata, val); + break; + default: + error(Enodev); + break; + } + + qunlock(&lm78); + poperror(); +} + +static int +lm78rdreg(int reg) +{ + uchar val; + + if(waserror()){ + qunlock(&lm78); + nexterror(); + } + qlock(&lm78); + + switch(lm78.ifc){ + case Smbus: + lm78.smbus->transact(lm78.smbus, SMBsend, Serialaddr, reg, nil); + lm78.smbus->transact(lm78.smbus, SMBrecv, Serialaddr, 0, &val); + break; + case Parallel: + setreg(reg); + val = inb(lm78.port+Rpdata); + break; + default: + error(Enodev); + break; + } + + qunlock(&lm78); + poperror(); + return val; +} + +/* start the chip monitoring but don't change any smi + * interrupts and/or alarms that the BIOS may have set up. + * this isn't locked because it's thought to be idempotent + */ +static void +lm78enable(void) +{ + uchar config; + + if(lm78.ifc == None) + error(Enodev); + + if(lm78.probed == 0){ + /* make sure its really there */ + if(lm78rdreg(Raddr) != Serialaddr){ + lm78.ifc = None; + error(Enodev); + } else { + /* start the sampling */ + config = lm78rdreg(Rconfig); + config = (config | Bstart) & ~(Bintclr|Binit); + lm78wrreg(Rconfig, config); +pprint("Rvidfan %2.2ux\n", lm78rdreg(Rconfig), lm78rdreg(Rvidfan)); + } + lm78.probed = 1; + } +} + +enum +{ + IntelVendID= 0x8086, + PiixID= 0x122E, + Piix3ID= 0x7000, + + Piix4PMID= 0x7113, /* PIIX4 power management function */ + + PCSC= 0x78, /* programmable chip select control register */ + PCSC8bytes= 0x01, +}; + +/* figure out what kind of interface we could have */ +void +lm78reset(void) +{ + int pcs; + Pcidev *p; + + lm78.ifc = None; + p = nil; + while((p = pcimatch(p, IntelVendID, 0)) != nil){ + switch(p->did){ + /* these bridges use the PCSC to map the lm78 into port space. */ + /* for this case the lm78's CS# select is connected to the PIIX's */ + /* PCS# output and the bottom 3 bits of address are passed to the */ + /* LM78's A0-A2 inputs. */ + case PiixID: + case Piix3ID: + pcs = pcicfgr16(p, PCSC); + if(pcs & 3) { + /* already enabled */ + lm78.port = pcs & ~3; + lm78.ifc = Parallel; + return; + } + + /* enable the chip, use default address 0x50 */ + pcicfgw16(p, PCSC, 0x50|PCSC8bytes); + pcs = pcicfgr16(p, PCSC); + lm78.port = pcs & ~3; + lm78.ifc = Parallel; + return; + + /* this bridge puts the lm78's serial interface on the smbus */ + case Piix4PMID: + lm78.smbus = piix4smbus(); + if(lm78.smbus == nil) + continue; + print("found piix4 smbus, base %lud\n", lm78.smbus->base); + lm78.ifc = Smbus; + return; + } + } +} + +Walkqid * +lm78walk(Chan* c, Chan *nc, char** name, int nname) +{ + return devwalk(c, nc, name, nname, lm78dir, nelem(lm78dir), devgen); +} + +static int +lm78stat(Chan* c, uchar* dp, int n) +{ + return devstat(c, dp, n, lm78dir, nelem(lm78dir), devgen); +} + +static Chan* +lm78open(Chan* c, int omode) +{ + return devopen(c, omode, lm78dir, nelem(lm78dir), devgen); +} + +static void +lm78close(Chan*) +{ +} + +enum +{ + Linelen= 25, +}; + +static long +lm78read(Chan *c, void *a, long n, vlong offset) +{ + uchar *va = a; + int off, e; + + off = offset; + + switch((ulong)c->qid.path){ + case Qdir: + return devdirread(c, a, n, lm78dir, nelem(lm78dir), devgen); + + case Qlm78vram: + if(off >= VRsize) + return 0; + e = off + n; + if(e > VRsize) + e = VRsize; + for(; off < e; off++) + *va++ = lm78rdreg(Rvalue+off); + return (int)(va - (uchar*)a); + } + return 0; +} + +static long +lm78write(Chan *c, void *a, long n, vlong offset) +{ + uchar *va = a; + int off, e; + + off = offset; + + switch((ulong)c->qid.path){ + default: + error(Eperm); + + case Qlm78vram: + if(off >= VRsize) + return 0; + e = off + n; + if(e > VRsize) + e = VRsize; + for(; off < e; off++) + lm78wrreg(Rvalue+off, *va++); + return va - (uchar*)a; + } + return 0; +} + +extern Dev lm78devtab; + +static Chan* +lm78attach(char* spec) +{ + lm78enable(); + + return devattach(lm78devtab.dc, spec); +} + +Dev lm78devtab = { + 'T', + "lm78", + + lm78reset, + devinit, + devshutdown, + lm78attach, + lm78walk, + lm78stat, + lm78open, + devcreate, + lm78close, + lm78read, + devbread, + lm78write, + devbwrite, + devremove, + devwstat, +}; + diff --git a/os/pc/devlpt.c b/os/pc/devlpt.c new file mode 100644 index 00000000..2fbc2aba --- /dev/null +++ b/os/pc/devlpt.c @@ -0,0 +1,245 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +/* Centronix parallel (printer) port */ + +/* base addresses */ +static int lptbase[] = { + 0x378, /* lpt1 */ + 0x3bc, /* lpt2 */ + 0x278 /* lpt3 (sic) */ +}; +#define NDEV nelem(lptbase) +static int lptallocd[NDEV]; + +/* offsets, and bits in the registers */ +enum +{ + Qdir= 0x8000, + /* data latch register */ + Qdlr= 0x0, + /* printer status register */ + Qpsr= 0x1, + Fnotbusy= 0x80, + Fack= 0x40, + Fpe= 0x20, + Fselect= 0x10, + Fnoerror= 0x08, + /* printer control register */ + Qpcr= 0x2, + Fie= 0x10, + Fselectin= 0x08, + Finitbar= 0x04, + Faf= 0x02, + Fstrobe= 0x01, + /* fake `data register' */ + Qdata= 0x3, +}; + +static int lptready(void*); +static void outch(int, int); +static void lptintr(Ureg*, void*); + +static Rendez lptrendez; + +Dirtab lptdir[]={ + ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555, + "dlr", {Qdlr}, 1, 0666, + "psr", {Qpsr}, 5, 0444, + "pcr", {Qpcr}, 0, 0222, + "data", {Qdata}, 0, 0222, +}; + +static int +lptgen(Chan *c, char*, Dirtab *tab, int ntab, int i, Dir *dp) +{ + Qid qid; + + if(i == DEVDOTDOT){ + mkqid(&qid, Qdir, 0, QTDIR); + devdir(c, qid, ".", 0, eve, 0555, dp); + return 1; + } + i++; /* skip first element for . itself */ + if(tab==0 || i>=ntab) + return -1; + tab += i; + qid = tab->qid; + qid.path &= ~Qdir; + if(qid.path < Qdata) + qid.path += lptbase[c->dev]; + qid.vers = c->dev; + sprint(up->genbuf, "lpt%lud%s", c->dev+1, tab->name); + devdir(c, qid, up->genbuf, tab->length, eve, tab->perm, dp); + return 1; +} + +static Chan* +lptattach(char *spec) +{ + Chan *c; + int i = (spec && *spec) ? strtol(spec, 0, 0) : 1; + char name[5]; + static int set; + + if(!set){ + outb(lptbase[i-1]+Qpcr, 0); /* turn off interrupts */ + set = 1; + intrenable(IrqLPT, lptintr, 0, BUSUNKNOWN, "lpt"); + } + if(i < 1 || i > NDEV) + error(Ebadarg); + if(lptallocd[i-1] == 0){ + int ecr; + sprint(name, "lpt%d", i-1); + if(ioalloc(lptbase[i-1], 3, 0, name) < 0) + error("lpt port space in use"); + lptallocd[i-1] = 1; + // Detect ECP - if found, put into PS/2 mode to suit style of driver + ecr = lptbase[i-1] + 0x402; + if ((inb(ecr) & 3) == 1) { + outb(ecr, 0x34); + if (inb(ecr) == 0x35) { + outb(ecr, (inb(ecr) & 0x1f) | (1 << 5)); + if(ioalloc(ecr, 1, 0, name) < 0) + error("lpt ecr port space in use"); + } + } + } + c = devattach('L', spec); + c->qid.path = Qdir; + c->dev = i-1; + return c; +} + +static Walkqid* +lptwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, lptdir, nelem(lptdir), lptgen); +} + +static int +lptstat(Chan *c, uchar *dp, int n) +{ + return devstat(c, dp, n, lptdir, nelem(lptdir), lptgen); +} + +static Chan* +lptopen(Chan *c, int omode) +{ + return devopen(c, omode, lptdir, nelem(lptdir), lptgen); +} + +static void +lptclose(Chan *) +{ +} + +static long +lptread(Chan *c, void *a, long n, vlong) +{ + char str[16]; + int size; + ulong o; + + if(c->qid.path == Qdir) + return devdirread(c, a, n, lptdir, nelem(lptdir), lptgen); + size = sprint(str, "0x%2.2ux\n", inb(c->qid.path)); + o = c->offset; + if(o >= size) + return 0; + if(o+n > size) + n = size-c->offset; + memmove(a, str+o, n); + return n; +} + +static long +lptwrite(Chan *c, void *a, long n, vlong) +{ + char str[16], *p; + long base, k; + + if(n <= 0) + return 0; + if(c->qid.path != Qdata){ + if(n > sizeof str-1) + n = sizeof str-1; + memmove(str, a, n); + str[n] = 0; + outb(c->qid.path, strtoul(str, 0, 0)); + return n; + } + p = a; + k = n; + base = lptbase[c->dev]; + if(waserror()){ + outb(base+Qpcr, Finitbar); + nexterror(); + } + while(--k >= 0) + outch(base, *p++); + poperror(); + return n; +} + +static void +outch(int base, int c) +{ + int status, tries; + + for(tries=0;; tries++) { + status = inb(base+Qpsr); + if(status&Fnotbusy) + break; + if((status&Fpe)==0 && (status&(Fselect|Fnoerror)) != (Fselect|Fnoerror)) + error(Eio); + if(tries < 10) + tsleep(&lptrendez, return0, nil, 1); + else { + outb(base+Qpcr, Finitbar|Fie); + tsleep(&lptrendez, lptready, (void *)base, 100); + } + } + outb(base+Qdlr, c); + outb(base+Qpcr, Finitbar|Fstrobe); + outb(base+Qpcr, Finitbar); +} + +static int +lptready(void *base) +{ + return inb((int)base+Qpsr)&Fnotbusy; +} + +static void +lptintr(Ureg *, void *) +{ + wakeup(&lptrendez); +} + +Dev lptdevtab = { + 'L', + "lpt", + + devreset, + devinit, + devshutdown, + lptattach, + lptwalk, + lptstat, + lptopen, + devcreate, + lptclose, + lptread, + devbread, + lptwrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/os/pc/devmouse.c b/os/pc/devmouse.c new file mode 100644 index 00000000..1b3c55c6 --- /dev/null +++ b/os/pc/devmouse.c @@ -0,0 +1,672 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +/* + * TODO + * - shift key should modify right button with non-serial mice + * + intellimouse implementation + * - acceleration for all mouse types + * + spurious interrupt 7 after probing for ps2 mouse for the first time...? + * - test with ms busmouse + * - test with logitech serial mouse + */ + +/* + * mouse types + */ +enum +{ + Mouseother, + Mouseserial, + MousePS2, + Mousebus, + Mouseintelli, + Mousemsbus, +}; + +static int mousetype; +static int mouseswap; +static int mouseport; /* port for serial mice, irq for bus mice */ +static int mousesubtype; +static int accelerated; +static QLock mouselock; + +static int msbusmousedetect(void); +static int busmousedetect(void); +static void mousectl(char *buf); +static void mouseprobe(char *buf, int len); +static void mousestatus(char *buf, int len); + +enum{ + Qdir, + Qmousectl, + Qmouseprobe, +}; + +static +Dirtab mousetab[]={ + "mousectl", {Qmousectl, 0}, 0, 0600, + "mouseprobe", {Qmouseprobe, 0}, 0, 0400, +}; + +static Chan* +mouseattach(char* spec) +{ + return devattach('m', spec); +} + +static int +mousewalk(Chan* c, char* name) +{ + return devwalk(c, name, mousetab, nelem(mousetab), devgen); +} + +static void +mousestat(Chan* c, char* db) +{ + devstat(c, db, mousetab, nelem(mousetab), devgen); +} + +static Chan* +mouseopen(Chan* c, int omode) +{ + return devopen(c, omode, mousetab, nelem(mousetab), devgen); +} + +static void +mouseclose(Chan* c) +{ + USED(c); +} + +static long +mouseread(Chan* c, void* a, long n, vlong offset) +{ + char buf[64]; + USED(offset); + + switch(c->qid.path & ~CHDIR){ + case Qdir: + return devdirread(c, a, n, mousetab, nelem(mousetab), devgen); + case Qmousectl: + qlock(&mouselock); + mousestatus(buf, sizeof(buf)); + qunlock(&mouselock); + n = readstr(offset, a, n, buf); + break; + case Qmouseprobe: + if (mousetype) + error(Emouseset); + mouseprobe(buf, sizeof(buf)); + n = readstr(offset, a, n, buf); + break; + default: + n=0; + break; + } + return n; +} + +static long +mousewrite(Chan* c, void *a, long n, vlong) +{ + char buf[64]; + if ((c->qid.path & ~CHDIR) != Qmousectl) + error(Ebadusefd); + if (n >= sizeof(buf)) + n = sizeof(buf) - 1; + strncpy(buf, a, n); + buf[n] = 0; + + qlock(&mouselock); + if (waserror()) { + qunlock(&mouselock); + nexterror(); + } + mousectl(buf); + poperror(); + qunlock(&mouselock); + return n; +} + +static void +track(int b, int dx, int dy) +{ + static uchar map[8] = {0,4,2,6,1,5,3,7}; + if (mouseswap) + b = map[b&7]; + mousetrack(b, dx, dy); +} + +static void +setintellimouse(void) +{ + i8042auxcmd(0xF3); /* set sample */ + i8042auxcmd(0xC8); + i8042auxcmd(0xF3); /* set sample */ + i8042auxcmd(0x64); + i8042auxcmd(0xF3); /* set sample */ + i8042auxcmd(0x50); +} + +/* + * check for an Intellimouse. + * this is only used when we know there's an 8042 aux device + */ +static int +intellimousedetect(void) +{ + int id; + setintellimouse(); + /* check whether the mouse is now in extended mode */ + id = i8042auxcmdval(0xf2); /* identify device */ + if (id != 3) { + /* + * set back to standard sample rate (100 per sec) + */ + i8042auxcmd(0xf3); + i8042auxcmd(0x64); + return 0; + } + return 1; +} + +static void +mouseprobe(char *buf, int len) +{ + USED(len); + /* + * bus mice are easiest, so probe them first + */ + if (busmousedetect()) + sprint(buf, "bus\n"); + else if (msbusmousedetect()) + sprint(buf, "msbus\n"); + else if (i8042auxdetect()) { + if (intellimousedetect()) + sprint(buf, "ps2intellimouse\n"); + else + sprint(buf, "ps2\n"); + } + else + *buf = 0; +} + + +static void +mousestatus(char *buf, int len) +{ + char *s; + USED(len); + s = buf; + switch (mousetype) { + case Mouseserial: + if (mousesubtype) + s += sprint(s, "serial %d %c\n", mouseport, mousesubtype); + else + s += sprint(s, "serial %d\n", mouseport); + break; + case MousePS2: + s += sprint(s, "ps2\n"); + break; + case Mousebus: + s += sprint(s, "bus %d\n", mouseport); + break; + case Mouseintelli: + s += sprint(s, "intelli\n"); + break; + case Mousemsbus: + s += sprint(s, "msbus %d\n", mouseport); + break; + default: + case Mouseother: + s += sprint(s, "unknown\n"); + break; + } + if (accelerated) + s += sprint(s, "accelerated\n"); + if (mouseswap) + sprint(s, "swap\n"); +} + +/* + * Logitech 5 byte packed binary mouse format, 8 bit bytes + * + * shift & right button is the same as middle button (for 2 button mice) + */ +static int +logitechmouseputc(Queue *q, int c) +{ + static short msg[5]; + static int nb; + static uchar b[] = {0, 4, 2, 6, 1, 5, 3, 7, 0, 2, 2, 6, 1, 5, 3, 7}; + int dx, dy, newbuttons; + int mouseshifted; + + USED(q); + if((c&0xF0) == 0x80) + nb=0; + msg[nb] = c; + if(c & 0x80) + msg[nb] |= ~0xFF; /* sign extend */ + if(++nb == 5){ + mouseshifted = 0; /* XXX should be from keyboard shift key */ + newbuttons = b[((msg[0]&7)^7) | (mouseshifted ? 8 : 0)]; + dx = msg[1]+msg[3]; + dy = -(msg[2]+msg[4]); + track(newbuttons, dx, dy); + nb = 0; + } + return 0; +} + +/* + * microsoft 3 button, 7 bit bytes + * + * byte 0 - 1 L R Y7 Y6 X7 X6 + * byte 1 - 0 X5 X4 X3 X2 X1 X0 + * byte 2 - 0 Y5 Y4 Y3 Y2 Y1 Y0 + * byte 3 - 0 M x x x x x (optional) + * + * shift & right button is the same as middle button (for 2 button mice) + */ +static int +m3mouseputc(Queue*, int c) +{ + static uchar msg[3]; + static int nb; + static int middle; + static uchar b[] = { 0, 4, 1, 5, 0, 2, 1, 5 }; + short x; + int dx, dy, buttons; + + /* + * check bit 6 for consistency + */ + if(nb==0){ + if((c&0x40) == 0){ + /* an extra byte gets sent for the middle button */ + if(c & 0x1c) + return 0; + middle = (c&0x20) ? 2 : 0; + buttons = (mouse.b & ~2) | middle; + track(buttons, 0, 0); + return 0; + } + } + msg[nb] = c&0x3f; + if(++nb == 3){ + nb = 0; + buttons = middle | b[(msg[0]>>4)&3]; + x = (msg[0]&0x3)<<14; + dx = (x>>8) | msg[1]; + x = (msg[0]&0xc)<<12; + dy = (x>>8) | msg[2]; + track(buttons, dx, dy); + } + return 0; +} + +static void +serialmouse(int port, char *type, int setspeed) +{ + int (*putc)(Queue *, int) = 0; + char pn[KNAMELEN]; + + if(mousetype) + error(Emouseset); + + if(port >= 2 || port < 0) + error(Ebadarg); + + if (type == 0) + putc = logitechmouseputc; + else if (*type == 'M') + putc = m3mouseputc; + else + error(Ebadarg); + snprint(pn, sizeof(pn), "%d", port); + i8250mouse(pn, putc, setspeed); + mousetype = Mouseserial; + mouseport = port; + mousesubtype = (type && *type == 'M') ? 'M' : 0; +} + +/* + * ps/2 mouse message is three bytes + * + * byte 0 - 0 0 SDY SDX 1 M R L + * byte 1 - DX + * byte 2 - DY + * + * shift & left button is the same as middle button + */ +static void +ps2mouseputc(int c, int shift) +{ + static short msg[3]; + static int nb; + static uchar b[] = {0, 1, 4, 5, 2, 3, 6, 7, 0, 1, 2, 5, 2, 3, 6, 7 }; + int buttons, dx, dy; + + /* + * check byte 0 for consistency + */ + if(nb==0 && (c&0xc8)!=0x08) + return; + + msg[nb] = c; + if(++nb == 3){ + nb = 0; + if(msg[0] & 0x10) + msg[1] |= 0xFF00; + if(msg[0] & 0x20) + msg[2] |= 0xFF00; + + buttons = b[(msg[0]&7) | (shift ? 8 : 0)]; + dx = msg[1]; + dy = -msg[2]; + track(buttons, dx, dy); + } + return; +} + +/* + * set up a ps2 mouse + */ +static void +ps2mouse(void) +{ + if(mousetype) + error(Emouseset); + + i8042auxenable(ps2mouseputc); + /* make mouse streaming, enabled */ + i8042auxcmd(0xEA); + i8042auxcmd(0xF4); + + mousetype = MousePS2; +} + +/* logitech bus mouse ports and commands */ +enum { + /* ports */ + BMdatap = 0x23c, + BMsigp = 0x23d, + BMctlp = 0x23e, + BMintrp = 0x23e, + BMconfigp = 0x23f, + + /* commands */ + BMintron = 0x0, + BMintroff = 0x10, + BMrxlo = 0x80, + BMrxhi = 0xa0, + BMrylo = 0xc0, + BMryhi = 0xe0, + + BMconfig = 0x91, + BMdefault = 0x90, + + BMsigval = 0xa5 +}; + +static void +busmouseintr(Ureg *, void *) +{ + char dx, dy; + uchar b; + static uchar oldb; + static Lock intrlock; + ilock(&intrlock); + outb(BMintrp, BMintroff); + outb(BMctlp, BMrxlo); + dx = inb(BMdatap) & 0xf; + outb(BMctlp, BMrxhi); + dx |= (inb(BMdatap) & 0xf) << 4; + outb(BMctlp, BMrylo); + dy = inb(BMdatap) & 0xf; + outb(BMctlp, BMryhi); + b = inb(BMdatap); + dy |= (b & 0xf) << 4; + b = ~(b >> 5) & 7; + if (dx || dy || b != oldb) { + oldb = b; + track((b>>2)|(b&0x02)|((b&0x01)<<2), dx, dy); + } + iunlock(&intrlock); + outb(BMintrp, BMintron); +} + +static int +busmousedetect(void) +{ + outb(BMconfigp, BMconfig); + outb(BMsigp, BMsigval); + delay(2); + if (inb(BMsigp) != BMsigval) + return 0; + outb(BMconfigp, BMdefault); + return 1; +} + +/* + * set up a logitech bus mouse + */ +static void +busmouse(int irq) +{ + if (mousetype) + error(Emouseset); + if (!busmousedetect()) + error(Enodev); + + intrenable(irq >= 0 ? irq+VectorPIC : VectorBUSMOUSE, busmouseintr, 0, BUSUNKNOWN); + outb(BMintrp, BMintron); + mousetype = Mousebus; + mouseport = irq >= 0 ? irq : VectorBUSMOUSE-VectorPIC; +} + +/* microsoft bus mouse ports and commands */ +enum { + MBMdatap= 0x23d, + MBMsigp= 0x23e, + MBMctlp= 0x23c, + MBMconfigp= 0x23f, + + MBMintron= 0x11, + MBMintroff= 0x10, + MBMrbuttons= 0x00, + MBMrx= 0x01, + MBMry= 0x02, + MBMstart= 0x80, + MBMcmd= 0x07, +}; + +static void +msbusmouseintr(Ureg *, void *) +{ + char dx, dy; + uchar b; + static uchar oldb; + static Lock intrlock; + ilock(&intrlock); + outb(MBMctlp, MBMcmd); + outb(MBMdatap, inb(MBMdatap)|0x20); + + outb(MBMctlp, MBMrx); + dx = inb(MBMdatap); + + outb(MBMctlp, MBMry); + dy = inb(MBMdatap); + + outb(MBMctlp, MBMrbuttons); + b = inb(MBMdatap) & 0x7; + + outb(MBMctlp, MBMcmd); + outb(MBMdatap, inb(MBMdatap)&0xdf); + + if (dx != 0 || dy != 0 || b != oldb) { + oldb = b; + /* XXX this is almost certainly wrong */ + track((b>>2)|(b&0x02)|((b&0x01)<<2), dx, dy); + } + iunlock(&intrlock); +} + +static int +msbusmousedetect(void) +{ + if (inb(MBMsigp) == 0xde) { + int v, i; + delay(1); + v = inb(MBMsigp); + delay(1); + for (i = 0; i < 4; i++) { + if (inb(MBMsigp) != 0xde) + break; + delay(1); + if (inb(MBMsigp) != v) + break; + delay(1); + } + if (i == 4) { + outb(MBMctlp, MBMcmd); + return 1; + } + } + return 0; +} + +static void +msbusmouse(int irq) +{ + if (mousetype) + error(Emouseset); + if (!msbusmousedetect()) + error(Enodev); + mousetype = Mousemsbus; + mouseport = irq >= 0 ? irq : VectorBUSMOUSE-VectorPIC; + intrenable(irq >= 0 ? irq+VectorPIC : VectorBUSMOUSE, msbusmouseintr, 0, BUSUNKNOWN); + outb(MBMdatap, MBMintron); +} + +static void +mousectl(char *buf) +{ + int nf, x; + char *field[10]; + nf = getfields(buf, field, 10, 1, " \t\n"); + if (nf < 1) + return; + if(strncmp(field[0], "serial", 6) == 0){ + switch(nf){ + /* the difference between these two cases is intriguing - wrtp */ + case 1: + serialmouse(atoi(field[0]+6), 0, 1); + break; + case 2: + serialmouse(atoi(field[1]), 0, 0); + break; + case 3: + default: + serialmouse(atoi(field[1]), field[2], 0); + break; + } + } else if(strcmp(field[0], "ps2") == 0){ + ps2mouse(); + } else if (strcmp(field[0], "ps2intellimouse") == 0) { + ps2mouse(); + setintellimouse(); + } else if (strncmp(field[0], "bus", 3) == 0 || strncmp(field[0], "msbus", 5) == 0) { + int irq, isms; + + isms = (field[0][0] == 'm'); + if (nf == 1) + irq = atoi(field[0] + (isms ? 5 : 3)); + else + irq = atoi(field[1]); + if (irq < 1) + irq = -1; + if (isms) + msbusmouse(irq); + else + busmouse(irq); + } else if(strcmp(field[0], "accelerated") == 0){ + switch(mousetype){ + case MousePS2: + x = splhi(); + i8042auxcmd(0xE7); + splx(x); + accelerated = 1; + break; + } + } else if(strcmp(field[0], "linear") == 0){ + switch(mousetype){ + case MousePS2: + x = splhi(); + i8042auxcmd(0xE6); + splx(x); + accelerated = 0; + break; + } + } else if(strcmp(field[0], "res") == 0){ + int n,m; + switch(nf){ + default: + n = 0x02; + m = 0x23; + break; + case 2: + n = atoi(field[1])&0x3; + m = 0x7; + break; + case 3: + n = atoi(field[1])&0x3; + m = atoi(field[2])&0x7; + break; + } + + switch(mousetype){ + case MousePS2: + x = splhi(); + i8042auxcmd(0xE8); + i8042auxcmd(n); + i8042auxcmd(0x5A); + i8042auxcmd(0x30|m); + i8042auxcmd(0x5A); + i8042auxcmd(0x20|(m>>1)); + splx(x); + break; + } + } else if(strcmp(field[0], "swap") == 0) + mouseswap ^= 1; +} + +Dev mousedevtab = { /* defaults in dev.c */ + 'm', + "mouse", + + devreset, /* devreset */ + devinit, /* devinit */ + mouseattach, + devdetach, + devclone, /* devclone */ + mousewalk, + mousestat, + mouseopen, + devcreate, /* devcreate */ + mouseclose, + mouseread, + devbread, /* devbread */ + mousewrite, + devbwrite, /* devbwrite */ + devremove, /* devremove */ + devwstat, /* devwstat */ +}; + diff --git a/os/pc/devmpeg.c b/os/pc/devmpeg.c new file mode 100644 index 00000000..038cfa6c --- /dev/null +++ b/os/pc/devmpeg.c @@ -0,0 +1,1063 @@ +/* + * Boffin MPEG decoder + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "zoran.h" +#include "crystal.h" +#include "io.h" + +enum +{ + + CPUACCCTRL = 0x20, /* Trident Window Chip control registers */ + CPUACCMD = 0x21, + BNKADR = 0x22, + SYSCONFIG = 0x23, + VGACOMP = 0x24, + VGAMASK = 0x25, + VIDCOMPL = 0x26, + VIDCOMPH = 0x27, + MOS = 0x28, + DISPCTRL = 0x29, + CAPCTRL = 0x2a, + OVLKT = 0x2b, + OVLWINHSTRT = 0x2c, + OVLWINVSTRT = 0x2d, + OVLWINHEND = 0x2e, + OVLWINVEND = 0x2f, + RESERVED1 = 0x30, + RESERVED2 = 0x31, + DISPWINVSTRT1 = 0x32, + DISPWINVSTRT2 = 0x33, + DISPWINVEND = 0x34, + DISPWINHSTRT1 = 0x35, + DISPWINHSTRT2 = 0x36, + DISPWINHEND = 0x37, + CAPWINVSTRT = 0x38, + CAPWINHSTRT = 0x39, + CAPWINVMF = 0x3a, + CAPWINHMF = 0x3b, + RESERVED3 = 0x3c, + CAPMASK = 0x3d, + BNKPOLATION = 0x3e, + SYNCPOL = 0x3f, + DISPVTOTAL = 0x40, + DISPHTOTAL = 0x41, + DISPVSTRT = 0x42, + DISPVEND = 0x43, + DISPHSTRT = 0x44, + DISPHEND = 0x45, + DISPSYNCW = 0x46, + DISPCRTCCTRL = 0x47, + CAPVTOTAL = 0x48, + CAPHTOTAL = 0x49, + CAPVSTRT = 0x4a, + CAPVEND = 0x4b, + CAPHSTRT = 0x4c, + CAPHEND = 0x4d, + CAPSYNCW = 0x4e, + CAPCRTCCTRL = 0x4f, + VIDLUTDACRW = 0x50, + VIDLUTDACRW0 = (VIDLUTDACRW), + VIDLUTDACRW1 = (VIDLUTDACRW+1), + VIDLUTDACRW2 = (VIDLUTDACRW+2), + VIDLUTDACRW3 = (VIDLUTDACRW+3), + VIDLUTDACRW4 = (VIDLUTDACRW+4), + VIDLUTDACRW5 = (VIDLUTDACRW+5), + VIDLUTDACRW6 = (VIDLUTDACRW+6), + VIDLUTDACRW7 = (VIDLUTDACRW+7), + VGALUTDACRW = 0x58, + VGALUTDACRW0 = (VGALUTDACRW), + VGALUTDACRW1 = (VGALUTDACRW+1), + VGALUTDACRW2 = (VGALUTDACRW+2), + VGALUTDACRW3 = (VGALUTDACRW+3), + VGALUTDACRW4 = (VGALUTDACRW+4), + VGALUTDACRW5 = (VGALUTDACRW+5), + VGALUTDACRW6 = (VGALUTDACRW+6), + VGALUTDACRW7 = (VGALUTDACRW+7), + HZOOMF = 0x60, + VZOOMF = 0x61, + DELAY1 = 0x62, + DELAY2 = 0x63, + + TRILO = 0, + TRIHI = 1, + TRIINDEX = 2, + + SCL = 0x02, + SDA = 0x01, + I2CR = 0x2B, + SAA7110 = 0x9c, + WRITE_C = 0x00, + I2DLY = 5, +}; + +enum +{ + ZR36100 = 0x1e0, + ZRIRQ = 15, + ZRDMA = 6, + + ZRIDREG = 4, /* offset */ + ZRMACH210 = 6, /* offset */ + ZRREG0 = 8, /* offset */ + ZRREG1 = 10, /* offset */ + ZRSR = ZRREG1, /* offset */ + ZRRDY = (1<<3), + ZRIDLE = (1<<2), + ZRREG2 = 12, /* offset */ + ZRREG3 = 14, /* offset */ + + SIFwidth = 320, + SIFheight = 240, + + IDPCOUNT = 3064, + PMDPCOUNT = 2048, + SVMDPCOUNT = 2048, + + HIWAT = 2*128*1024, + DMABLK = 16384, +}; + +static struct { + int zrport; + int irq; + int dma; + int trport; +} mpegconf; + +static char Evmode[] = "video format not supported"; +static char Eaudio[] = "invalid audio layer"; +static char Earate[] = "bad audio sample rate"; + +/* Status bits depend on board revision */ +static short STDBY; +static short VIDSEL; +static short VSNIRQn; +static short INTENAn; +static short DSPBOOT; +static short DSPRST; +static short MPGRST; +static int machsr; +static int dopen; +static int started; +static int stop; +static int pause; +static int sp2br; +static int sp2cd; +static char properties[] = "video mpeg1,sif\naudio musicam,I musicam,II\n"; +static void inittrident(void); +static int initzoran(void); +static void initcrystal(void); +static void mpegintr(Ureg*, void*); +static void setwindow(int, char**); +static void freebufs(void); +static int mkbuf(char*, int); + +typedef struct Buf Buf; +struct Buf +{ + int nchar; + uchar* ptr; + Buf* link; + uchar data[1]; +}; + +static struct +{ + Lock; + int qlen; + Buf* head; + Buf* tail; + Rendez flow; +} bqueue; + +static int +zrstatus(void) +{ + return ins(mpegconf.zrport+ZRSR) & 0xf; +} + +static int +zrwaitrdy(int timo, char *msg) +{ + int i; + + for(i = 0; i < timo; i++) + if(ins(mpegconf.zrport+ZRSR) & ZRRDY) + return 0; + + print("devmpeg: device not ready %s\n", msg); + return 1; +} + +static void +zrdma(Buf *b) +{ + int n; + + n = dmasetup(mpegconf.dma, b->ptr, b->nchar, 0); + b->ptr += n; + b->nchar -= n; + bqueue.qlen -= n; +} + +static void +triwr(int reg, int val) +{ + outb(mpegconf.trport+TRIINDEX, reg); + outb(mpegconf.trport+TRILO, val); + outb(mpegconf.trport+TRIHI, val>>8); +} + +static int +trird(int reg) +{ + int v; + + outb(mpegconf.trport+TRIINDEX, reg); + v = inb(mpegconf.trport+TRILO); + v |= inb(mpegconf.trport+TRIHI)<<8; + + return v; +} + +enum +{ + Qdir, + Qdata, + Qctl, +}; +static Dirtab mpegtab[]= +{ + "mpeg", {Qdata, 0}, 0, 0666, + "mpegctl", {Qctl, 0}, 0, 0666, +}; + +static void +mpegreset(void) +{ + ISAConf isa; + + mpegconf.zrport = ZR36100; + mpegconf.irq = ZRIRQ; + mpegconf.dma = ZRDMA; + + memset(&isa, 0, sizeof(isa)); + if(isaconfig("mpeg", 0, &isa) == 0) + return; + if(isa.port) + mpegconf.zrport = isa.port; + if(isa.irq) + mpegconf.irq = isa.irq; + if(isa.dma) + mpegconf.dma = isa.dma; + dmainit(mpegconf.dma, 64*1024); + print("mpeg0: port 0x%uX, irq %d, dma %d\n", + mpegconf.zrport, mpegconf.irq, mpegconf.dma); + mpegconf.trport = mpegconf.zrport+0x100; + intrenable(VectorPIC+mpegconf.irq, mpegintr, 0, BUSUNKNOWN); +} + +static void +mpeginit(void) +{ + if(mpegconf.trport == 0) + return; + + inittrident(); + setwindow(0, 0); +} + +static Chan* +mpegattach(char *spec) +{ + if(mpegconf.trport == 0) + error(Enodev); + + return devattach('E', spec); +} + +static int +mpegwalk(Chan *c, char *name) +{ + return devwalk(c, name, mpegtab, nelem(mpegtab), devgen); +} + +static void +mpegstat(Chan *c, char *db) +{ + devstat(c, db, mpegtab, nelem(mpegtab), devgen); +} + +static Chan* +mpegopen(Chan *c, int omode) +{ + switch(c->qid.path) { + default: + break; + case Qdata: + if(dopen) + error(Einuse); + dopen = 1; + break; + } + return devopen(c, omode, mpegtab, nelem(mpegtab), devgen); +} + +static void +mpegclose(Chan *c) +{ + int i; + + switch(c->qid.path) { + default: + break; + case Qdata: + if((c->flag & COPEN) == 0) + break; + if(started) { + for(i = 0; i < 50; i++) { + if(ins(mpegconf.zrport+ZRSR) & ZRIDLE) + break; + tsleep(&up->sleep, return0, 0, 100); + } + } + if(stop != 0) + outs(mpegconf.zrport+ZRREG1, 0x1000); + microdelay(15); + outs(mpegconf.zrport+ZRREG1, 0x8000); + freebufs(); + dopen = 0; + } +} + +static long +mpegread(Chan *c, void *a, long n, ulong off) +{ + switch(c->qid.path & ~CHDIR){ + default: + error(Eperm); + case Qdir: + return devdirread(c, a, n, mpegtab, nelem(mpegtab), devgen); + case Qctl: + return readstr(off, a, n, properties); + } + return 0; +} + +#define SCALE(a, b) ((((a)<<10)/(b))-1024) +enum +{ + CWINVF = 0x3ff, + CWINHF = 0x1da, +}; + +static void +setwindow(int nf, char **field) +{ + int minx, miny, maxx, maxy, width, height; + + if(field == 0) { + minx = 0; + miny = 0; + maxx = 0; + maxy = 0; + } + else { + if(nf != 5) + error(Ebadarg); + + minx = strtoul(field[1], 0, 0); + miny = strtoul(field[2], 0, 0); + maxx = strtoul(field[3], 0, 0) + 8; + maxy = strtoul(field[4], 0, 0); + } + + triwr(OVLWINHSTRT, minx); + triwr(OVLWINVSTRT, miny); + triwr(OVLWINHEND, maxx+12); + triwr(OVLWINVEND, maxy); + + width = maxx - minx; + height = maxy - miny; + if(width >= SIFwidth) { + triwr(HZOOMF, SCALE(width, SIFwidth)); + triwr(CAPWINHMF, CWINHF); + } + else { + triwr(HZOOMF, SCALE(SIFwidth, SIFwidth)); + triwr(CAPWINHMF, width*CWINHF/SIFwidth); + } + if(height >= SIFheight) { + triwr(VZOOMF, SCALE(height, SIFheight)); + triwr(CAPWINVMF, CWINVF); + } + else { + triwr(VZOOMF, SCALE(SIFheight, SIFheight)); + triwr(CAPWINVMF, height*CWINVF/SIFheight); + } +} + +static int +mpegflow(void*) +{ + return bqueue.qlen < HIWAT || stop; +} + +static int +mkbuf(char *d, int n) +{ + Buf *b; + + b = malloc(sizeof(Buf)+n); + if(b == 0) + return 0; + + memmove(b->data, d, n); + b->ptr = b->data; + b->nchar = n; + b->link = 0; + + ilock(&bqueue); + bqueue.qlen += n; + if(bqueue.head) + bqueue.tail->link = b; + else + bqueue.head = b; + bqueue.tail = b; + iunlock(&bqueue); + + return 1; +} + +static void +freebufs(void) +{ + Buf *next; + + ilock(&bqueue); + bqueue.qlen = 0; + while(bqueue.head) { + next = bqueue.head->link; + free(bqueue.head); + bqueue.head = next; + } + iunlock(&bqueue); +} + +typedef struct Audio Audio; +struct Audio { + int rate; + int cd; + int br; +}; + +static Audio AudioclkI[] = +{ + 64000, 0x000000bb, 0x00071797, + 96000, 0x0000007d, 0x00071c71, + 128000, 0x0000005d, 0x00070de1, + 160000, 0x0000004b, 0x00071c71, + 192000, 0x0000003e, 0x00070de1, + 224000, 0x00000035, 0x00070906, + 256000, 0x0000002e, 0x0006fa76, + 288000, 0x00000029, 0x0006ff51, + 320000, 0x00000025, 0x0007042b, + 352000, 0x00000022, 0x00071797, + 384000, 0x0000001f, 0x00070de1, + 416000, 0x0000001c, 0x0006e70b, + 448000, 0x0000001a, 0x0006e70b, +}; + +static Audio AudioclkII[] = +{ + 48000, 0x000000fa, 0x00071c71, + 56000, 0x000000d6, 0x00071a04, + 64000, 0x000000bb, 0x00071797, + 80000, 0x00000096, 0x00071c71, + 96000, 0x0000007d, 0x00071c71, + 112000, 0x0000006b, 0x00071a04, + 128000, 0x0000005d, 0x00070de1, + 160000, 0x0000004b, 0x00071c71, + 192000, 0x0000003e, 0x00070de1, + 224000, 0x00000035, 0x00070906, + 256000, 0x0000002e, 0x0006fa76, + 320000, 0x00000025, 0x0007042b, + 384000, 0x0000001f, 0x00070de1, +}; + +static long +mpegwrite(Chan *c, char *a, long n, vlong) +{ + Audio *t; + int i, nf, l, x; + char buf[128], *field[10]; + + switch(c->qid.path & ~CHDIR) { + case Qctl: + if(n > sizeof(buf)-1) + n = sizeof(buf)-1; + memmove(buf, a, n); + buf[n] = '\0'; + + nf = getfields(buf, field, nelem(field), 1, " \t\n"); + if(nf < 1) + error(Ebadarg); + + if(strcmp(field[0], "stop") == 0) { + if(started == 0) + error("not started"); + if(pause) { + pause = 0; + outs(mpegconf.zrport+ZRREG1, 0x9000); + } + stop = 1; + outs(mpegconf.zrport+ZRREG1, 0x1000); + microdelay(15); + outs(mpegconf.zrport+ZRREG1, 0x8000); + wakeup(&bqueue.flow); + return n; + } + if(strcmp(field[0], "pause") == 0) { + if(started == 0) + error("not started"); + if(pause == 0) { + pause = 1; + outs(mpegconf.zrport+ZRREG1, 0x1000); + } + else { + pause = 0; + outs(mpegconf.zrport+ZRREG1, 0x9000); + } + return n; + } + if(strcmp(field[0], "window") == 0) { + setwindow(nf, field); + return n; + } + if(strcmp(field[0], "audio") == 0) { + if(nf < 3) + error(Ebadarg); + t = 0; + if(strcmp(field[1], "musicam,I") == 0) + t = AudioclkI; + else + if(strcmp(field[1], "musicam,II") == 0) + t = AudioclkII; + else + error(Eaudio); + x = strtoul(field[2], 0, 0); + for(i = 0; t[i].rate != 0; i++) { + if(t[i].rate == x) { + sp2cd = t[i].cd; + sp2br = t[i].br; + return n; + } + } + error(Earate); + } + if(strcmp(field[0], "video") == 0) { + if(nf != 3) + error(Ebadarg); + if(strcmp(field[1], "iso11172") != 0) + error(Evmode); + if(strcmp(field[2], "mpeg1,sif") != 0) + error(Evmode); + return n; + } + if(strcmp(field[0], "init") == 0) { + inittrident(); + for(i = 0; i < 3; i++) + if(initzoran() != -1) + break; + initcrystal(); + started = 0; + stop = 0; + pause = 0; + return n; + } + error(Ebadarg); + case Qdata: + if(n & 1) + error("odd write"); + + while(!mpegflow(0)) + sleep(&bqueue.flow, mpegflow, 0); + + if(stop) + error("stopped"); + + x = n; + while(x) { + l = x; + if(l > DMABLK) + l = DMABLK; + if(mkbuf(a, l) == 0) + error(Enomem); + x -= l; + a += l; + } + if(started || bqueue.qlen < (HIWAT*3)/4) + break; + + zrdma(bqueue.head); + outs(mpegconf.zrport+ZRREG1, 0x0000); + outs(mpegconf.zrport+ZRREG1, 0x0000); + started = 1; + break; + default: + error(Ebadusefd); + } + return n; +} + +Dev mpegdevtab = { + 'E', + "mpeg", + + mpegreset, + mpeginit, + mpegattach, + devdetach, + devclone, + mpegwalk, + mpegstat, + mpegopen, + devcreate, + mpegclose, + mpegread, + devbread, + mpegwrite, + devbwrite, + devremove, + devwstat, +}; + +static void +initctl(void) +{ + int boardid; + static int done; + + if(done) + return; + + boardid = ins(mpegconf.zrport+ZRIDREG); + if(boardid == 0xE3E3) { /* REV c/d */ + STDBY = 0x0000; + VIDSEL = 0x2020; + VSNIRQn = 0x1010; + INTENAn = 0x0808; + DSPBOOT = 0x0404; + DSPRST = 0x0202; + MPGRST = 0x0101; + } + else { /* REV b */ + STDBY = 0x0404; + VIDSEL = 0x1010; + VSNIRQn = 0x8080; + INTENAn = 0x4040; + DSPBOOT = 0x0202; + DSPRST = 0x0101; + MPGRST = 0x2020; + } + done = 1; + +} + +/* + * nbl (reg 0x1[ab]) was 0x0022, nblf (reg 1[cd]) was 0x0006 + */ +static uchar +zrparam[] = +{ +/* 00 */ 0xEF, 0x01, 0x01, 0x01, 0x80, 0x0E, 0x31, 0x00, +/* 08 */ 0x01, 0x60, 0x00, 0x00, 0x03, 0x5A, 0x00, 0x7A, +/* 10 */ 0x00, 0x10, 0x00, 0x08, 0x00, 0xF0, 0x00, 0x00, +/* 18 */ 0x02, 0x0D, 0x00, 0x1e, 0x00, 0x0a, 0x00, 0x02, +/* 20 */ 0x40, 0x06, 0x80, 0x00, 0x80, 0x00, 0x05, 0x9B, +/* 28 */ 0x07, 0x16, 0xFD, 0x25, 0xFE, 0xA0, 0x00, 0x00, +/* 30 */ 0x00, 0x07, 0x0d, 0xe1, 0x00, 0x00, 0x00, 0x3E, +/* 38 */ 0x00, 0x00, 0x09, 0x51, 0x00, 0x00, 0xCD, 0xFE, +/* 40 */ 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 48 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 50 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 58 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 60 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 68 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 70 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 78 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static int +initzoran(void) +{ + int i, nbytes, zrs; + + initctl(); + freebufs(); + + machsr = DSPRST|VSNIRQn; + outs(mpegconf.zrport+ZRMACH210, machsr); + microdelay(4000); + + machsr |= STDBY; + outs(mpegconf.zrport+ZRMACH210, machsr); + microdelay(4000); + + machsr |= MPGRST; + outs(mpegconf.zrport+ZRMACH210, machsr); + microdelay(4000); + machsr &= ~MPGRST; + outs(mpegconf.zrport+ZRMACH210, machsr); + microdelay(4000); + machsr |= MPGRST; + outs(mpegconf.zrport+ZRMACH210, machsr); + microdelay(4000); + + if(zrwaitrdy(2000, "load IDP")) + return -1; + + for(i = 0; i < IDPCOUNT; i++) + outb(mpegconf.zrport+ZRREG2, zrmpeg1[i]); + + if(((zrs = zrstatus()) & 3) != 3) { +/* print("devmpeg: error loading IDP sr=%2.2ux\n", zrs); */ + USED(zrs); + return -1; + } + + if(zrwaitrdy(2000, "load PMDP")) + return -1; + + for(i = 0; i < PMDPCOUNT; i++) + outb(mpegconf.zrport+ZRREG3, zrmpeg2[i]); + + if(((zrs = zrstatus()) & 3) != 3) { +/* print("devmpeg: error loading PMDP sr=%2.2ux\n", zrs); */ + USED(zrs); + return -1; + } + + zrparam[0x36] = sp2cd>>8; + zrparam[0x37] = sp2cd>>0; + zrparam[0x31] = sp2br>>16; + zrparam[0x32] = sp2br>>8; + zrparam[0x33] = sp2br>>0; + + nbytes = 16; + for(i = 0; i < 128; i++) { + if(nbytes >= 16) { + if(zrwaitrdy(2000, "load parameters")) + return -1; + nbytes = 0; + } + outb(mpegconf.zrport+ZRREG0, zrparam[i]); + nbytes++; + } + + if(zrwaitrdy(2000, "load SVMDP")) + return -1; + + for(i = 0; i < SVMDPCOUNT; i++) + outb(mpegconf.zrport+ZRREG3, zrmpeg3s[i]); + + if(((zrs = zrstatus()) & 3) != 3) { +/* print("devmpeg: error loading SVMDP sr=%2.2ux\n", zrs); */ + USED(zrs); + return -1; + } + return 0; +} + +static struct +{ + short reg; + ushort val; +} trireg[] = +{ + 0x20, 0x0400, + 0x21, 0x00e9, + 0x22, 0x0000, + 0x23, 0x07ee, + 0x24, 0x0005, + 0x25, 0xff00, + 0x26, 0x0000, + 0x27, 0x7fff, + 0x28, 0x0004, + 0x29, 0x88a0, + 0x2a, 0x0011, + 0x2b, 0x8540, + 0x2c, 0x00c4, + 0x2d, 0x00ac, + 0x2e, 0x020f, + 0x2f, 0x019d, + 0x30, 0x00bd, + 0x31, 0x00ff, + 0x32, 0x0000, + 0x33, 0x0000, + 0x34, 0x03ff, + 0x35, 0x0000, + 0x36, 0x0000, + 0x37, 0x03ff, + 0x38, 0x0000, + 0x39, 0x0000, + 0x3a, 0x03ff, + 0x3b, 0x01da, + 0x3c, 0xe8ce, + 0x3d, 0x2ac0, + 0x3e, 0x891f, + 0x3f, 0x3e25, + 0x40, 0x03ff, + 0x41, 0x01ff, + 0x42, 0x001f, + 0x43, 0x01ff, + 0x44, 0x003b, + 0x45, 0x0186, + 0x46, 0x1d06, + 0x47, 0x1a4f, + 0x48, 0x020d, + 0x49, 0x01ad, + 0x4a, 0x001b, + 0x4b, 0x01fd, + 0x4c, 0x003a, + 0x4d, 0x034b, + 0x4e, 0x2006, + 0x4f, 0x0083, + 0x50, 0xef08, + 0x51, 0xef3a, + 0x52, 0xefff, + 0x53, 0xef08, + 0x54, 0xef08, + 0x55, 0xef15, + 0x56, 0xefc0, + 0x57, 0xef08, + 0x58, 0xefef, + 0x59, 0xefef, + 0x5a, 0xefef, + 0x5b, 0xefef, + 0x5c, 0xefef, + 0x5d, 0xefef, + 0x5e, 0xefef, + 0x5f, 0xefef, + 0x60, 0x0000, + 0x61, 0x0004, + 0x62, 0x0020, + 0x63, 0x8080, + 0x64, 0x0300, + -1 +}; + +static void +clrI2C(uchar b) +{ + uchar t; + + outb(mpegconf.trport+TRIINDEX, I2CR); + t = inb(mpegconf.trport+TRIHI); + t &= ~b; + outb(mpegconf.trport+TRIHI, t); +} + +static void +setI2C(uchar b) +{ + uchar t; + + outb(mpegconf.trport+TRIINDEX, I2CR); + t = inb(mpegconf.trport+TRIHI); + t |= b; + outb(mpegconf.trport+TRIHI, t); +} + +static void +startI2C(void) +{ + setI2C(SDA); + setI2C(SCL); + microdelay(I2DLY); + clrI2C(SDA); + microdelay(I2DLY); + clrI2C(SCL); + microdelay(I2DLY); +} + +static void +endI2C(void) +{ + clrI2C(SDA); + clrI2C(SCL); + microdelay(I2DLY); + setI2C(SCL); + microdelay(I2DLY); + setI2C(SDA); + microdelay(I2DLY); +} + +static void +wrI2Cbit(uchar b) +{ + clrI2C(SDA); + clrI2C(SCL); + microdelay(I2DLY); + if(b & 1) { + setI2C(SDA); + microdelay(I2DLY); + setI2C(SCL); + microdelay(I2DLY); + clrI2C(SCL); + microdelay(I2DLY); + clrI2C(SDA); + microdelay(I2DLY); + } + else { + setI2C(SCL); + microdelay(I2DLY); + clrI2C(SCL); + microdelay(I2DLY); + } +} + +static void +wrI2CB(unsigned char data) +{ + int i; + + for(i = 0; i < 8; i++) + wrI2Cbit(data >>(7-i)); +} + +static int +rdI2CBit(void) +{ + int bit = 1; + + setI2C(SDA); + clrI2C(SCL); + setI2C(SCL); + outb(mpegconf.trport+TRIINDEX, I2CR); + if(inb(mpegconf.trport+TRIHI) & SDA) + bit = 0; + clrI2C(SDA); + clrI2C(SCL); + + return bit; +} + +static int +wrI2CD(uchar data) +{ + int r; + ulong s; + + s = splhi(); + wrI2CB(data); + r = rdI2CBit(); + splx(s); + return r; +} + +static uchar +setupSAA7110[] = +{ + /* Digital */ + 0x4c, 0x3c, 0x0d, 0xef, 0xbd, 0xf0, 0x40, 0x03, + 0xf8, 0xf8, 0x90, 0x90, 0x00, 0x02, 0x10, 0x77, + 0x00, 0x2c, 0x40, 0x40, 0x3b, 0x10, 0xfc, 0xd2, + 0xf0, 0x80, + + /* Analog */ + 0xd9, 0x16, 0x40, 0x40, 0x80, 0x40, 0x80, 0x4f, + 0xfe, 0x01, 0xcf, 0x0f, 0x03, 0x01, 0x81, 0x0a, + 0x40, 0x35, 0x02, 0x8c, 0x03 +}; + +static void +addrI2CB(int addr, int val) +{ + ulong s; + + s = splhi(); + startI2C(); + wrI2CD(SAA7110|WRITE_C); + wrI2CD(addr); + wrI2CD(val); + endI2C(); + splx(s); +} + +static void +inittrident(void) +{ + int i; + + for(i = 0; trireg[i].reg != -1; i++) + triwr(trireg[i].reg, trireg[i].val); + + for(i = 0; i < 47; i++) + addrI2CB(i, setupSAA7110[i]); +} + +static void +initcrystal(void) +{ + int i; + static int done; + + if(done) + return; + + done = 1; + + initctl(); + + /* Reboot the Musicam decoder */ + clrI2C(SCL); + clrI2C(SDA); + machsr |= DSPRST; + outs(mpegconf.zrport+ZRMACH210, machsr); + machsr |= DSPBOOT; + outs(mpegconf.zrport+ZRMACH210, machsr); + machsr &= ~DSPRST; + outs(mpegconf.zrport+ZRMACH210, machsr); + machsr |= DSPRST; + outs(mpegconf.zrport+ZRMACH210, machsr); + machsr &= ~DSPBOOT; + outs(mpegconf.zrport+ZRMACH210, machsr); + + startI2C(); + wrI2CD(0); + for(i = 0; i < sizeof(crystal); i++ ) + wrI2CD(crystal[i]); + endI2C(); +} + +static void +mpegintr(Ureg*, void*) +{ + Buf *b; + + b = bqueue.head; + if(b == 0 || dmadone(mpegconf.dma) == 0) + return; + + dmaend(mpegconf.dma); + if(b->nchar == 0) { + bqueue.head = b->link; + free(b); + + b = bqueue.head; + if(b == 0) { + started = 0; + return; + } + } + zrdma(b); + wakeup(&bqueue.flow); +} diff --git a/os/pc/devpccard.c b/os/pc/devpccard.c new file mode 100644 index 00000000..3f6a09ca --- /dev/null +++ b/os/pc/devpccard.c @@ -0,0 +1,1949 @@ +/* + cardbus and pcmcia (grmph) support. +*/ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "io.h" + +#define MAP(x,o) (Rmap + (x)*0x8 + o) + +enum { + TI_vid = 0x104c, + TI_1131_did = 0xAC15, + TI_1250_did = 0xAC16, + TI_1450_did = 0xAC1B, + TI_1251A_did = 0xAC1D, + TI_1420_did = 0xAC51, + + Ricoh_vid = 0x1180, + Ricoh_475_did = 0x0475, + Ricoh_476_did = 0x0476, + Ricoh_478_did = 0x0478, + + Nslots = 4, /* Maximum number of CardBus slots to use */ + + K = 1024, + M = K * K, + + LegacyAddr = 0x3e0, + NUMEVENTS = 10, + + TI1131xSC = 0x80, // system control + TI122X_SC_INTRTIE = 1 << 29, + TI12xxIM = 0x8c, // + TI1131xCC = 0x91, // card control + TI113X_CC_RIENB = 1 << 7, + TI113X_CC_ZVENABLE = 1 << 6, + TI113X_CC_PCI_IRQ_ENA = 1 << 5, + TI113X_CC_PCI_IREQ = 1 << 4, + TI113X_CC_PCI_CSC = 1 << 3, + TI113X_CC_SPKROUTEN = 1 << 1, + TI113X_CC_IFG = 1 << 0, + TI1131xDC = 0x92, // device control +}; + +typedef struct Variant Variant; +struct Variant { + ushort vid; + ushort did; + char *name; +}; + +static Variant variant[] = { +{ Ricoh_vid, Ricoh_475_did, "Ricoh 475 PCI/Cardbus bridge", }, +{ Ricoh_vid, Ricoh_476_did, "Ricoh 476 PCI/Cardbus bridge", }, +{ Ricoh_vid, Ricoh_478_did, "Ricoh 478 PCI/Cardbus bridge", }, +{ TI_vid, TI_1131_did, "TI PCI-1131 Cardbus Controller", }, +{ TI_vid, TI_1250_did, "TI PCI-1250 Cardbus Controller", }, +{ TI_vid, TI_1450_did, "TI PCI-1450 Cardbus Controller", }, +{ TI_vid, TI_1251A_did, "TI PCI-1251A Cardbus Controller", }, +{ TI_vid, TI_1420_did, "TI PCI-1420 Cardbus Controller", }, +}; + +/* Cardbus registers */ +enum { + SocketEvent = 0, + SE_CCD = 3 << 1, + SE_POWER = 1 << 3, + SocketMask = 1, + SocketState = 2, + SS_CCD = 3 << 1, + SS_POWER = 1 << 3, + SS_PC16 = 1 << 4, + SS_CBC = 1 << 5, + SS_NOTCARD = 1 << 7, + SS_BADVCC = 1 << 9, + SS_5V = 1 << 10, + SS_3V = 1 << 11, + SocketForce = 3, + SocketControl = 4, + SC_5V = 0x22, + SC_3V = 0x33, +}; + +enum { + PciPCR_IO = 1 << 0, + PciPCR_MEM = 1 << 1, + PciPCR_Master = 1 << 2, + + PciPMC = 0xa4, + + Nbars = 6, + Ncmd = 10, + CBIRQ = 9, + + PC16, + PC32, +}; + +enum { + Ti82365, + Tpd6710, + Tpd6720, + Tvg46x, +}; + +static char *chipname[] = { +[Ti82365] "Intel 82365SL", +[Tpd6710] "Cirrus Logic PD6710", +[Tpd6720] "Cirrus Logic PD6720", +[Tvg46x] "Vadem VG-46x", +}; + +/* + * Intel 82365SL PCIC controller for the PCMCIA or + * Cirrus Logic PD6710/PD6720 which is mostly register compatible + */ +enum +{ + /* + * registers indices + */ + Rid= 0x0, /* identification and revision */ + Ris= 0x1, /* interface status */ + Rpc= 0x2, /* power control */ + Foutena= (1<<7), /* output enable */ + Fautopower= (1<<5), /* automatic power switching */ + Fcardena= (1<<4), /* PC card enable */ + Rigc= 0x3, /* interrupt and general control */ + Fiocard= (1<<5), /* I/O card (vs memory) */ + Fnotreset= (1<<6), /* reset if not set */ + FSMIena= (1<<4), /* enable change interrupt on SMI */ + Rcsc= 0x4, /* card status change */ + Rcscic= 0x5, /* card status change interrupt config */ + Fchangeena= (1<<3), /* card changed */ + Fbwarnena= (1<<1), /* card battery warning */ + Fbdeadena= (1<<0), /* card battery dead */ + Rwe= 0x6, /* address window enable */ + Fmem16= (1<<5), /* use A23-A12 to decode address */ + Rio= 0x7, /* I/O control */ + Fwidth16= (1<<0), /* 16 bit data width */ + Fiocs16= (1<<1), /* IOCS16 determines data width */ + Fzerows= (1<<2), /* zero wait state */ + Ftiming= (1<<3), /* timing register to use */ + Riobtm0lo= 0x8, /* I/O address 0 start low byte */ + Riobtm0hi= 0x9, /* I/O address 0 start high byte */ + Riotop0lo= 0xa, /* I/O address 0 stop low byte */ + Riotop0hi= 0xb, /* I/O address 0 stop high byte */ + Riobtm1lo= 0xc, /* I/O address 1 start low byte */ + Riobtm1hi= 0xd, /* I/O address 1 start high byte */ + Riotop1lo= 0xe, /* I/O address 1 stop low byte */ + Riotop1hi= 0xf, /* I/O address 1 stop high byte */ + Rmap= 0x10, /* map 0 */ + + /* + * CL-PD67xx extension registers + */ + Rmisc1= 0x16, /* misc control 1 */ + F5Vdetect= (1<<0), + Fvcc3V= (1<<1), + Fpmint= (1<<2), + Fpsirq= (1<<3), + Fspeaker= (1<<4), + Finpack= (1<<7), + Rfifo= 0x17, /* fifo control */ + Fflush= (1<<7), /* flush fifo */ + Rmisc2= 0x1E, /* misc control 2 */ + Flowpow= (1<<1), /* low power mode */ + Rchipinfo= 0x1F, /* chip information */ + Ratactl= 0x26, /* ATA control */ + + /* + * offsets into the system memory address maps + */ + Mbtmlo= 0x0, /* System mem addr mapping start low byte */ + Mbtmhi= 0x1, /* System mem addr mapping start high byte */ + F16bit= (1<<7), /* 16-bit wide data path */ + Mtoplo= 0x2, /* System mem addr mapping stop low byte */ + Mtophi= 0x3, /* System mem addr mapping stop high byte */ + Ftimer1= (1<<6), /* timer set 1 */ + Mofflo= 0x4, /* Card memory offset address low byte */ + Moffhi= 0x5, /* Card memory offset address high byte */ + Fregactive= (1<<6), /* attribute memory */ + + /* + * configuration registers - they start at an offset in attribute + * memory found in the CIS. + */ + Rconfig= 0, + Creset= (1<<7), /* reset device */ + Clevel= (1<<6), /* level sensitive interrupt line */ +}; + +/* + * read and crack the card information structure enough to set + * important parameters like power + */ +/* cis memory walking */ +typedef struct Cisdat Cisdat; +struct Cisdat { + uchar *cisbase; + int cispos; + int cisskip; + int cislen; +}; + +typedef struct Pcminfo Pcminfo; +struct Pcminfo { + char verstr[512]; /* Version string */ + PCMmap mmap[4]; /* maps, last is always for the kernel */ + ulong conf_addr; /* Config address */ + uchar conf_present; /* Config register present */ + int nctab; /* In use configuration tables */ + PCMconftab ctab[8]; /* Configuration tables */ + PCMconftab *defctab; /* Default conftab */ + + int port; /* Actual port usage */ + int irq; /* Actual IRQ usage */ +}; + +typedef struct Cardbus Cardbus; +struct Cardbus { + Lock; + Variant *variant; /* Which CardBus chipset */ + Pcidev *pci; /* The bridge itself */ + ulong *regs; /* Cardbus registers */ + int ltype; /* Legacy type */ + int lindex; /* Legacy port index address */ + int ldata; /* Legacy port data address */ + int lbase; /* Base register for this socket */ + + int state; /* Current state of card */ + int type; /* Type of card */ + Pcminfo linfo; /* PCMCIA slot info */ + + int special; /* card is allocated to a driver */ + + int refs; /* Number of refs to slot */ + Lock refslock; /* inc/dev ref lock */ +}; + +static int managerstarted; + +enum { + Mshift= 12, + Mgran= (1<<Mshift), /* granularity of maps */ + Mmask= ~(Mgran-1), /* mask for address bits important to the chip */ +}; + +static Cardbus cbslots[Nslots]; +static int nslots; + +static ulong exponent[8] = { + 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, +}; + +static ulong vmant[16] = { + 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80, 90, +}; + +static ulong mantissa[16] = { + 0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80, +}; + +static char Enocard[] = "No card in slot"; + +enum +{ + CMdown, + CMpower, +}; + +static Cmdtab pccardctlmsg[] = +{ + CMdown, "down", 2, + CMpower, "power", 1, +}; + +static void cbint(Ureg *, void *); +static int powerup(Cardbus *); +static void configure(Cardbus *); +static void managecard(Cardbus *); +static void cardmanager(void *); +static void eject(Cardbus *); +static void interrupt(Ureg *, void *); +static void powerdown(Cardbus *cb); +static void unconfigure(Cardbus *cb); + +static void i82365probe(Cardbus *cb, int lindex, int ldata); +static void i82365configure(Cardbus *cb); +static PCMmap *isamap(Cardbus *cb, ulong offset, int len, int attr); +static void isaunmap(PCMmap* m); +static uchar rdreg(Cardbus *cb, int index); +static void wrreg(Cardbus *cb, int index, uchar val); +static int readc(Cisdat *cis, uchar *x); +static void tvers1(Cardbus *cb, Cisdat *cis, int ); +static void tcfig(Cardbus *cb, Cisdat *cis, int ); +static void tentry(Cardbus *cb, Cisdat *cis, int ); +static int vcode(int volt); +static int pccard_pcmspecial(char *idstr, ISAConf *isa); +static void pccard_pcmspecialclose(int slotno); + +enum { + CardDetected, + CardPowered, + CardEjected, + CardConfigured, +}; + +static char *messages[] = { +[CardDetected] "CardDetected", +[CardPowered] "CardPowered", +[CardEjected] "CardEjected", +[CardConfigured] "CardConfigured", +}; + +enum { + SlotEmpty, + SlotFull, + SlotPowered, + SlotConfigured, +}; + +static char *states[] = { +[SlotEmpty] "SlotEmpty", +[SlotFull] "SlotFull", +[SlotPowered] "SlotPowered", +[SlotConfigured] "SlotConfigured", +}; + +static void +engine(Cardbus *cb, int message) +{ + //print("engine(%d): %s(%s)\n", + // (int)(cb - cbslots), states[cb->state], messages[message]); + switch (cb->state) { + case SlotEmpty: + + switch (message) { + case CardDetected: + cb->state = SlotFull; + powerup(cb); + break; + case CardEjected: + break; + default: + //print("#Y%d: Invalid message %s in SlotEmpty state\n", + // (int)(cb - cbslots), messages[message]); + break; + } + break; + + case SlotFull: + + switch (message) { + case CardPowered: + cb->state = SlotPowered; + configure(cb); + break; + case CardEjected: + cb->state = SlotEmpty; + powerdown(cb); + break; + default: + //print("#Y%d: Invalid message %s in SlotFull state\n", + // (int)(cb - cbslots), messages[message]); + break; + } + break; + + case SlotPowered: + + switch (message) { + case CardConfigured: + cb->state = SlotConfigured; + break; + case CardEjected: + cb->state = SlotEmpty; + unconfigure(cb); + powerdown(cb); + break; + default: + //print("#Y%d: Invalid message %s in SlotPowered state\n", + // (int)(cb - cbslots), messages[message]); + break; + } + break; + + case SlotConfigured: + + switch (message) { + case CardEjected: + cb->state = SlotEmpty; + unconfigure(cb); + powerdown(cb); + break; + default: + //print("#Y%d: Invalid message %s in SlotConfigured state\n", + // (int)(cb - cbslots), messages[message]); + break; + } + break; + } +} + +static void +qengine(Cardbus *cb, int message) +{ + lock(cb); + engine(cb, message); + unlock(cb); +} + +typedef struct Events Events; +struct Events { + Cardbus *cb; + int message; +}; + +static Lock levents; +static Events events[NUMEVENTS]; +static Rendez revents; +static int nevents; + +static void +iengine(Cardbus *cb, int message) +{ + if (nevents >= NUMEVENTS) { + print("#Y: Too many events queued, discarding request\n"); + return; + } + ilock(&levents); + events[nevents].cb = cb; + events[nevents].message = message; + nevents++; + iunlock(&levents); + wakeup(&revents); +} + +static int +eventoccured(void) +{ + return nevents > 0; +} + +static void +processevents(void *) +{ + while (1) { + int message; + Cardbus *cb; + + sleep(&revents, (int (*)(void *))eventoccured, nil); + + cb = nil; + message = 0; + ilock(&levents); + if (nevents > 0) { + cb = events[0].cb; + message = events[0].message; + nevents--; + if (nevents > 0) + memmove(events, &events[1], nevents * sizeof(Events)); + } + iunlock(&levents); + + if (cb) + qengine(cb, message); + } +} + +static void +cbinterrupt(Ureg *, void *) +{ + int i; + + for (i = 0; i != nslots; i++) { + Cardbus *cb = &cbslots[i]; + ulong event, state; + + event= cb->regs[SocketEvent]; + state = cb->regs[SocketState]; + rdreg(cb, Rcsc); /* Ack the interrupt */ + + //print("interrupt: slot %d, event %.8lX, state %.8lX, (%s)\n", + // (int)(cb - cbslots), event, state, states[cb->state]); + + if (event & SE_CCD) { + cb->regs[SocketEvent] |= SE_CCD; /* Ack interrupt */ + if (state & SE_CCD) { + if (cb->state != SlotEmpty) { + print("#Y: take cardejected interrupt\n"); + iengine(cb, CardEjected); + } + } + else + iengine(cb, CardDetected); + } + + if (event & SE_POWER) { + cb->regs[SocketEvent] |= SE_POWER; /* Ack interrupt */ + iengine(cb, CardPowered); + } + } +} + +void +devpccardlink(void) +{ + static int initialized; + Pcidev *pci; + int i; + uchar intl; + char *p; + + if (initialized) + return; + initialized = 1; + + if((p=getconf("pccard0")) && strncmp(p, "disabled", 8)==0) + return; + + if(_pcmspecial) + return; + + /* Allocate legacy space */ + if (ioalloc(LegacyAddr, 2, 0, "i82365.0") < 0) + print("#Y: WARNING: Cannot allocate legacy ports\n"); + + /* Find all CardBus controllers */ + pci = nil; + intl = (uchar)-1; + while ((pci = pcimatch(pci, 0, 0)) != nil) { + ulong baddr; + Cardbus *cb; + int slot; + uchar pin; + + for (i = 0; i != nelem(variant); i++) + if (pci->vid == variant[i].vid && pci->did == variant[i].did) + break; + if (i == nelem(variant)) + continue; + + /* initialize this slot */ + slot = nslots++; + cb = &cbslots[slot]; + + cb->pci = pci; + cb->variant = &variant[i]; + + if (pci->vid != TI_vid) { + // Gross hack, needs a fix. Inherit the mappings from 9load + // for the TIs (pb) + pcicfgw32(pci, PciCBMBR0, 0xffffffff); + pcicfgw32(pci, PciCBMLR0, 0); + pcicfgw32(pci, PciCBMBR1, 0xffffffff); + pcicfgw32(pci, PciCBMLR1, 0); + pcicfgw32(pci, PciCBIBR0, 0xffffffff); + pcicfgw32(pci, PciCBILR0, 0); + pcicfgw32(pci, PciCBIBR1, 0xffffffff); + pcicfgw32(pci, PciCBILR1, 0); + } + + // Set up PCI bus numbers if needed. + if (pcicfgr8(pci, PciSBN) == 0) { + static int busbase = 0x20; + + pcicfgw8(pci, PciSBN, busbase); + pcicfgw8(pci, PciUBN, busbase + 2); + busbase += 3; + } + + // Patch up intl if needed. + if ((pin = pcicfgr8(pci, PciINTP)) != 0 && + (pci->intl == 0xff || pci->intl == 0)) { + pci->intl = pciipin(nil, pin); + pcicfgw8(pci, PciINTL, pci->intl); + + if (pci->intl == 0xff || pci->intl == 0) + print("#Y%d: No interrupt?\n", (int)(cb - cbslots)); + } + + // Don't you love standards! + if (pci->vid == TI_vid) { + if (pci->did <= TI_1131_did) { + uchar cc; + + cc = pcicfgr8(pci, TI1131xCC); + cc &= ~(TI113X_CC_PCI_IRQ_ENA | + TI113X_CC_PCI_IREQ | + TI113X_CC_PCI_CSC | + TI113X_CC_ZVENABLE); + cc |= TI113X_CC_PCI_IRQ_ENA | + TI113X_CC_PCI_IREQ | + TI113X_CC_SPKROUTEN; + pcicfgw8(pci, TI1131xCC, cc); + + // PCI interrupts only + pcicfgw8(pci, TI1131xDC, + pcicfgr8(pci, TI1131xDC) & ~6); + + // CSC ints to PCI bus. + wrreg(cb, Rigc, rdreg(cb, Rigc) | 0x10); + } + else if (pci->did == TI_1250_did) { + print("No support yet for the TI_1250_did, prod pb\n"); + } + else if (pci->did == TI_1420_did) { + // Disable Vcc protection + pcicfgw32(cb->pci, 0x80, + pcicfgr32(cb->pci, 0x80) | (1 << 21)); + } + + pcicfgw16(cb->pci, PciPMC, pcicfgr16(cb->pci, PciPMC) & ~3); + } + + if (intl != -1 && intl != pci->intl) + intrenable(pci->intl, cbinterrupt, cb, pci->tbdf, "cardbus"); + intl = pci->intl; + + if ((baddr = pcicfgr32(cb->pci, PciBAR0)) == 0) { + int align = (pci->did == Ricoh_478_did)? 0x10000: 0x1000; + + baddr = upamalloc(baddr, align, align); + pcicfgw32(cb->pci, PciBAR0, baddr); + cb->regs = (ulong *)KADDR(baddr); + } + else + cb->regs = (ulong *)KADDR(upamalloc(baddr, 4096, 0)); + cb->state = SlotEmpty; + + /* Don't really know what to do with this... */ + i82365probe(cb, LegacyAddr, LegacyAddr + 1); + + print("#Y%ld: %s, %.8ulX intl %d\n", cb - cbslots, + variant[i].name, baddr, pci->intl); + } + + if (nslots == 0){ + iofree(LegacyAddr); + return; + } + + _pcmspecial = pccard_pcmspecial; + _pcmspecialclose = pccard_pcmspecialclose; + + for (i = 0; i != nslots; i++) { + Cardbus *cb = &cbslots[i]; + + if ((cb->regs[SocketState] & SE_CCD) == 0) + engine(cb, CardDetected); + } + + delay(500); /* Allow time for power up */ + + for (i = 0; i != nslots; i++) { + Cardbus *cb = &cbslots[i]; + + if (cb->regs[SocketState] & SE_POWER) + engine(cb, CardPowered); + + /* Ack and enable interrupts on all events */ + // cb->regs[SocketEvent] = cb->regs[SocketEvent]; + cb->regs[SocketMask] |= 0xF; + wrreg(cb, Rcscic, 0xC); + } +} + +static int +powerup(Cardbus *cb) +{ + ulong state; + ushort bcr; + + state = cb->regs[SocketState]; + if (state & SS_PC16) { + + // print("#Y%ld: Probed a PC16 card, powering up card\n", cb - cbslots); + cb->type = PC16; + memset(&cb->linfo, 0, sizeof(Pcminfo)); + + /* power up and unreset, wait's are empirical (???) */ + wrreg(cb, Rpc, Fautopower|Foutena|Fcardena); + delay(300); + wrreg(cb, Rigc, 0); + delay(100); + wrreg(cb, Rigc, Fnotreset); + delay(500); + + return 1; + } + + if (state & SS_CCD) + return 0; + + if (state & SS_NOTCARD) { + print("#Y%ld: Not a card inserted\n", cb - cbslots); + return 0; + } + + if ((state & SS_3V) == 0 && (state & SS_5V) == 0) { + print("#Y%ld: Unsupported voltage, powering down card!\n", + cb - cbslots); + cb->regs[SocketControl] = 0; + return 0; + } + + //print("#Y%ld: card %spowered at %d volt\n", cb - cbslots, + // (state & SS_POWER)? "": "not ", + // (state & SS_3V)? 3: (state & SS_5V)? 5: -1); + + /* Power up the card + * and make sure the secondary bus is not in reset. + */ + cb->regs[SocketControl] = (state & SS_5V)? SC_5V: SC_3V; + delay(50); + bcr = pcicfgr16(cb->pci, PciBCR); + bcr &= ~0x40; + pcicfgw16(cb->pci, PciBCR, bcr); + delay(100); + + cb->type = (state & SS_PC16)? PC16: PC32; + return 1; +} + +static void +powerdown(Cardbus *cb) +{ + ushort bcr; + + if (cb->type == PC16) { + + wrreg(cb, Rpc, 0); /* turn off card power */ + wrreg(cb, Rwe, 0); /* no windows */ + + cb->type = -1; + return; + } + + bcr = pcicfgr16(cb->pci, PciBCR); + bcr |= 0x40; + pcicfgw16(cb->pci, PciBCR, bcr); + cb->regs[SocketControl] = 0; + cb->type = -1; +} + +static void +configure(Cardbus *cb) +{ + int i; + Pcidev *pci; + + //print("configuring slot %d (%s)\n", (int)(cb - cbslots), states[cb->state]); + if (cb->state == SlotConfigured) + return; + engine(cb, CardConfigured); + + delay(50); /* Emperically established */ + + if (cb->type == PC16) { + i82365configure(cb); + return; + } + + /* Scan the CardBus for new PCI devices */ + pciscan(pcicfgr8(cb->pci, PciSBN), &cb->pci->bridge); + pci = cb->pci->bridge; + while (pci) { + ulong size, bar; + int memindex, ioindex; + + pcicfgw16(pci, PciPCR, + pcicfgr16(pci, PciPCR) & ~(PciPCR_IO|PciPCR_MEM)); + + /* Treat the found device as an ordinary PCI card. It seems that the + CIS is not always present in CardBus cards. XXX, need to support + multifunction cards */ + memindex = ioindex = 0; + for (i = 0; i != Nbars; i++) { + + if (pci->mem[i].size == 0) continue; + if (pci->mem[i].bar & 1) { + + // Allocate I/O space + if (ioindex > 1) { + print("#Y%ld: WARNING: Can only configure 2 I/O slots\n", cb - cbslots); + continue; + } + bar = ioreserve(-1, pci->mem[i].size, 0, "cardbus"); + + pci->mem[i].bar = bar | 1; + pcicfgw32(pci, PciBAR0 + i * sizeof(ulong), + pci->mem[i].bar); + pcicfgw16(cb->pci, PciCBIBR0 + ioindex * 8, bar); + pcicfgw16(cb->pci, PciCBILR0 + ioindex * 8, + bar + pci->mem[i].size - 1); + //print("ioindex[%d] %.8uX (%d)\n", + // ioindex, bar, pci->mem[i].size); + ioindex++; + continue; + } + + // Allocating memory space + if (memindex > 1) { + print("#Y%ld: WARNING: Can only configure 2 memory slots\n", cb - cbslots); + continue; + } + + bar = upamalloc(0, pci->mem[i].size, BY2PG); + pci->mem[i].bar = bar | (pci->mem[i].bar & 0x80); + pcicfgw32(pci, PciBAR0 + i * sizeof(ulong), pci->mem[i].bar); + pcicfgw32(cb->pci, PciCBMBR0 + memindex * 8, bar); + pcicfgw32(cb->pci, PciCBMLR0 + memindex * 8, + bar + pci->mem[i].size - 1); + + if (pci->mem[i].bar & 0x80) + /* Enable prefetch */ + pcicfgw16(cb->pci, PciBCR, + pcicfgr16(cb->pci, PciBCR) | + (1 << (8 + memindex))); + + //print("memindex[%d] %.8uX (%d)\n", + // memindex, bar, pci->mem[i].size); + memindex++; + } + + if ((size = pcibarsize(pci, PciEBAR0)) > 0) { + + if (memindex > 1) + print("#Y%ld: WARNING: Too many memory spaces, not mapping ROM space\n", + cb - cbslots); + else { + pci->rom.bar = upamalloc(0, size, BY2PG); + pci->rom.size = size; + + pcicfgw32(pci, PciEBAR0, pci->rom.bar); + pcicfgw32(cb->pci, PciCBMBR0 + memindex * 8, + pci->rom.bar); + pcicfgw32(cb->pci, PciCBMLR0 + memindex * 8, + pci->rom.bar + pci->rom.size - 1); + } + } + + /* Set the basic PCI registers for the device */ + pci->pcr = pcicfgr16(pci, PciPCR) | PciPCR_IO|PciPCR_MEM|PciPCR_Master; + pci->cls = 8; + pci->ltr = 64; + pcicfgw16(pci, PciPCR, pci->pcr); + pcicfgw8(pci, PciCLS, pci->cls); + pcicfgw8(pci, PciLTR, pci->ltr); + + if (pcicfgr8(pci, PciINTP)) { + pci->intl = pcicfgr8(cb->pci, PciINTL); + pcicfgw8(pci, PciINTL, pci->intl); + + /* Route interrupts to INTA#/B# */ + pcicfgw16(cb->pci, PciBCR, + pcicfgr16(cb->pci, PciBCR) & ~(1 << 7)); + } + + pci = pci->list; + } +} + +static void +unconfigure(Cardbus *cb) +{ + Pcidev *pci; + int i, ioindex, memindex; + + if (cb->type == PC16) { + print("#Y%d: Don't know how to unconfigure a PC16 card\n", + (int)(cb - cbslots)); + + memset(&cb->linfo, 0, sizeof(Pcminfo)); + return; + } + + pci = cb->pci->bridge; + if (pci == nil) + return; /* Not configured */ + cb->pci->bridge = nil; + + memindex = ioindex = 0; + while (pci) { + Pcidev *_pci; + + for (i = 0; i != Nbars; i++) { + if (pci->mem[i].size == 0) continue; + if (pci->mem[i].bar & 1) { + iofree(pci->mem[i].bar & ~1); + pcicfgw16(cb->pci, PciCBIBR0 + ioindex * 8, + (ushort)-1); + pcicfgw16(cb->pci, PciCBILR0 + ioindex * 8, 0); + ioindex++; + continue; + } + + upafree(pci->mem[i].bar & ~0xF, pci->mem[i].size); + pcicfgw32(cb->pci, PciCBMBR0 + memindex * 8, + (ulong)-1); + pcicfgw32(cb->pci, PciCBMLR0 + memindex * 8, 0); + pcicfgw16(cb->pci, PciBCR, + pcicfgr16(cb->pci, PciBCR) & + ~(1 << (8 + memindex))); + memindex++; + } + + if (pci->rom.bar && memindex < 2) { + upafree(pci->rom.bar & ~0xF, pci->rom.size); + pcicfgw32(cb->pci, PciCBMBR0 + memindex * 8, + (ulong)-1); + pcicfgw32(cb->pci, PciCBMLR0 + memindex * 8, 0); + memindex++; + } + + _pci = pci->list; + free(_pci); + pci = _pci; + } +} + +static void +i82365configure(Cardbus *cb) +{ + int this; + Cisdat cis; + PCMmap *m; + uchar type, link; + + /* + * Read all tuples in attribute space. + */ + m = isamap(cb, 0, 0, 1); + if(m == 0) + return; + + cis.cisbase = KADDR(m->isa); + cis.cispos = 0; + cis.cisskip = 2; + cis.cislen = m->len; + + /* loop through all the tuples */ + for(;;){ + this = cis.cispos; + if(readc(&cis, &type) != 1) + break; + if(type == 0xFF) + break; + if(readc(&cis, &link) != 1) + break; + + switch(type){ + default: + break; + case 0x15: + tvers1(cb, &cis, type); + break; + case 0x1A: + tcfig(cb, &cis, type); + break; + case 0x1B: + tentry(cb, &cis, type); + break; + } + + if(link == 0xFF) + break; + cis.cispos = this + (2+link); + } + isaunmap(m); +} + +/* + * look for a card whose version contains 'idstr' + */ +static int +pccard_pcmspecial(char *idstr, ISAConf *isa) +{ + int i, irq; + PCMconftab *ct, *et; + Pcminfo *pi; + Cardbus *cb; + uchar x, we, *p; + + cb = nil; + for (i = 0; i != nslots; i++) { + cb = &cbslots[i]; + + lock(cb); + if (cb->state == SlotConfigured && + cb->type == PC16 && + !cb->special && + strstr(cb->linfo.verstr, idstr)) + break; + unlock(cb); + } + + if (i == nslots) { + // print("#Y: %s not found\n", idstr); + return -1; + } + + pi = &cb->linfo; + + /* + * configure the PCMslot for IO. We assume very heavily that we can read + * configuration info from the CIS. If not, we won't set up correctly. + */ + irq = isa->irq; + if(irq == 2) + irq = 9; + + et = &pi->ctab[pi->nctab]; + ct = nil; + for(i = 0; i < isa->nopt; i++){ + int index; + char *cp; + + if(strncmp(isa->opt[i], "index=", 6)) + continue; + index = strtol(&isa->opt[i][6], &cp, 0); + if(cp == &isa->opt[i][6] || index >= pi->nctab) { + unlock(cb); + print("#Y%d: Cannot find index %d in conf table\n", + (int)(cb - cbslots), index); + return -1; + } + ct = &pi->ctab[index]; + } + + if(ct == nil){ + PCMconftab *t; + + /* assume default is right */ + if(pi->defctab) + ct = pi->defctab; + else + ct = pi->ctab; + + /* try for best match */ + if(ct->nio == 0 + || ct->io[0].start != isa->port || ((1<<irq) & ct->irqs) == 0){ + for(t = pi->ctab; t < et; t++) + if(t->nio + && t->io[0].start == isa->port + && ((1<<irq) & t->irqs)){ + ct = t; + break; + } + } + if(ct->nio == 0 || ((1<<irq) & ct->irqs) == 0){ + for(t = pi->ctab; t < et; t++) + if(t->nio && ((1<<irq) & t->irqs)){ + ct = t; + break; + } + } + if(ct->nio == 0){ + for(t = pi->ctab; t < et; t++) + if(t->nio){ + ct = t; + break; + } + } + } + + if(ct == et || ct->nio == 0) { + unlock(cb); + print("#Y%d: No configuration?\n", (int)(cb - cbslots)); + return -1; + } + if(isa->port == 0 && ct->io[0].start == 0) { + unlock(cb); + print("#Y%d: No part or start address\n", (int)(cb - cbslots)); + return -1; + } + + cb->special = 1; /* taken */ + + /* route interrupts */ + isa->irq = irq; + wrreg(cb, Rigc, irq | Fnotreset | Fiocard); + + /* set power and enable device */ + x = vcode(ct->vpp1); + wrreg(cb, Rpc, x|Fautopower|Foutena|Fcardena); + + /* 16-bit data path */ + if(ct->bit16) + x = Ftiming|Fiocs16|Fwidth16; + else + x = Ftiming; + if(ct->nio == 2 && ct->io[1].start) + x |= x<<4; + wrreg(cb, Rio, x); + + /* + * enable io port map 0 + * the 'top' register value includes the last valid address + */ + if(isa->port == 0) + isa->port = ct->io[0].start; + we = rdreg(cb, Rwe); + wrreg(cb, Riobtm0lo, isa->port); + wrreg(cb, Riobtm0hi, isa->port>>8); + i = isa->port+ct->io[0].len-1; + wrreg(cb, Riotop0lo, i); + wrreg(cb, Riotop0hi, i>>8); + we |= 1<<6; + if(ct->nio == 2 && ct->io[1].start){ + wrreg(cb, Riobtm1lo, ct->io[1].start); + wrreg(cb, Riobtm1hi, ct->io[1].start>>8); + i = ct->io[1].start+ct->io[1].len-1; + wrreg(cb, Riotop1lo, i); + wrreg(cb, Riotop1hi, i>>8); + we |= 1<<7; + } + wrreg(cb, Rwe, we); + + /* only touch Rconfig if it is present */ + if(pi->conf_present & (1<<Rconfig)){ + PCMmap *m; + + /* Reset adapter */ + m = isamap(cb, pi->conf_addr + Rconfig, 1, 1); + p = KADDR(m->isa + pi->conf_addr + Rconfig - m->ca); + + /* set configuration and interrupt type */ + x = ct->index; + if(ct->irqtype & 0x20) + x |= Clevel; + *p = x; + delay(5); + + isaunmap(m); + } + + pi->port = isa->port; + pi->irq = isa->irq; + unlock(cb); + + print("#Y%d: %s irq %d, port %lX\n", (int)(cb - cbslots), pi->verstr, isa->irq, isa->port); + return (int)(cb - cbslots); +} + +static void +pccard_pcmspecialclose(int slotno) +{ + Cardbus *cb = &cbslots[slotno]; + + wrreg(cb, Rwe, 0); /* no windows */ + cb->special = 0; +} + +static int +xcistuple(int slotno, int tuple, int subtuple, void *v, int nv, int attr) +{ + PCMmap *m; + Cisdat cis; + int i, l; + uchar *p; + uchar type, link, n, c; + int this, subtype; + Cardbus *cb = &cbslots[slotno]; + + m = isamap(cb, 0, 0, attr); + if(m == 0) + return -1; + + cis.cisbase = KADDR(m->isa); + cis.cispos = 0; + cis.cisskip = attr ? 2 : 1; + cis.cislen = m->len; + + /* loop through all the tuples */ + for(i = 0; i < 1000; i++){ + this = cis.cispos; + if(readc(&cis, &type) != 1) + break; + if(type == 0xFF) + break; + if(readc(&cis, &link) != 1) + break; + if(link == 0xFF) + break; + + n = link; + if (link > 1 && subtuple != -1) { + if (readc(&cis, &c) != 1) + break; + subtype = c; + n--; + } else + subtype = -1; + + if(type == tuple && subtype == subtuple) { + p = v; + for(l=0; l<nv && l<n; l++) + if(readc(&cis, p++) != 1) + break; + isaunmap(m); + return nv; + } + cis.cispos = this + (2+link); + } + isaunmap(m); + return -1; +} + +static Chan* +pccardattach(char *spec) +{ + if (!managerstarted) { + managerstarted = 1; + kproc("cardbus", processevents, nil); + } + return devattach('Y', spec); +} + +enum +{ + Qdir, + Qctl, + + Nents = 1, +}; + +#define SLOTNO(c) ((ulong)((c->qid.path>>8)&0xff)) +#define TYPE(c) ((ulong)(c->qid.path&0xff)) +#define QID(s,t) (((s)<<8)|(t)) + +static int +pccardgen(Chan *c, char*, Dirtab *, int , int i, Dir *dp) +{ + int slotno; + Qid qid; + long len; + int entry; + + if(i == DEVDOTDOT){ + mkqid(&qid, Qdir, 0, QTDIR); + devdir(c, qid, "#Y", 0, eve, 0555, dp); + return 1; + } + + len = 0; + if(i >= Nents * nslots) return -1; + slotno = i / Nents; + entry = i % Nents; + if (entry == 0) { + qid.path = QID(slotno, Qctl); + snprint(up->genbuf, sizeof up->genbuf, "cb%dctl", slotno); + } + else { + /* Entries for memory regions. I'll implement them when + needed. (pb) */ + } + qid.vers = 0; + qid.type = QTFILE; + devdir(c, qid, up->genbuf, len, eve, 0660, dp); + return 1; +} + +static Walkqid* +pccardwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, 0, 0, pccardgen); +} + +static int +pccardstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, 0, 0, pccardgen); +} + +static void +increfp(Cardbus *cb) +{ + lock(&cb->refslock); + cb->refs++; + unlock(&cb->refslock); +} + +static void +decrefp(Cardbus *cb) +{ + lock(&cb->refslock); + cb->refs--; + unlock(&cb->refslock); +} + +static Chan* +pccardopen(Chan *c, int omode) +{ + if (c->qid.type & QTDIR){ + if(omode != OREAD) + error(Eperm); + } else + increfp(&cbslots[SLOTNO(c)]); + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + return c; +} + +static void +pccardclose(Chan *c) +{ + if(c->flag & COPEN) + if((c->qid.type & QTDIR) == 0) + decrefp(&cbslots[SLOTNO(c)]); +} + +static long +pccardread(Chan *c, void *a, long n, vlong offset) +{ + Cardbus *cb; + char *buf, *p, *e; + int i; + + switch(TYPE(c)){ + case Qdir: + return devdirread(c, a, n, 0, 0, pccardgen); + + case Qctl: + buf = p = malloc(READSTR); + buf[0] = 0; + e = p + READSTR; + + cb = &cbslots[SLOTNO(c)]; + lock(cb); + p = seprint(p, e, "slot %ld: %s; ", cb - cbslots, states[cb->state]); + + switch (cb->type) { + case -1: + seprint(p, e, "\n"); + break; + + case PC32: + if (cb->pci->bridge) { + Pcidev *pci = cb->pci->bridge; + int i; + + while (pci) { + p = seprint(p, e, "%.4uX %.4uX; irq %d\n", + pci->vid, pci->did, pci->intl); + for (i = 0; i != Nbars; i++) + if (pci->mem[i].size) + p = seprint(p, e, + "\tmem[%d] %.8ulX (%.8uX)\n", + i, pci->mem[i].bar, + pci->mem[i].size); + if (pci->rom.size) + p = seprint(p, e, "\tROM %.8ulX (%.8uX)\n", + pci->rom.bar, pci->rom.size); + pci = pci->list; + } + } + break; + + case PC16: + if (cb->state == SlotConfigured) { + Pcminfo *pi = &cb->linfo; + + p = seprint(p, e, "%s port %X; irq %d;\n", + pi->verstr, pi->port, + pi->irq); + for (i = 0; i != pi->nctab; i++) { + PCMconftab *ct; + int j; + + ct = &pi->ctab[i]; + p = seprint(p, e, + "\tconfiguration[%d] irqs %.4uX; vpp %d, %d; %s\n", + i, ct->irqs, ct->vpp1, ct->vpp2, + (ct == pi->defctab)? "(default);": ""); + for (j = 0; j != ct->nio; j++) + if (ct->io[j].len > 0) + p = seprint(p, e, "\t\tio[%d] %.8ulX %uld\n", + j, ct->io[j].start, ct->io[j].len); + } + } + break; + } + unlock(cb); + + n = readstr(offset, a, n, buf); + free(buf); + return n; + } + return 0; +} + +static long +pccardwrite(Chan *c, void *v, long n, vlong) +{ + Rune r; + ulong n0; + char *device; + Cmdbuf *cbf; + Cmdtab *ct; + Cardbus *cb; + + n0 = n; + switch(TYPE(c)){ + case Qctl: + cb = &cbslots[SLOTNO(c)]; + + cbf = parsecmd(v, n); + if(waserror()){ + free(cbf); + nexterror(); + } + ct = lookupcmd(cbf, pccardctlmsg, nelem(pccardctlmsg)); + switch(ct->index){ + case CMdown: + device = cbf->f[1]; + device += chartorune(&r, device); + if ((n = devno(r, 1)) >= 0 && devtab[n]->config) + devtab[n]->config(0, device, nil); + qengine(cb, CardEjected); + break; + case CMpower: + if ((cb->regs[SocketState] & SS_CCD) == 0) + qengine(cb, CardDetected); + break; + } + poperror(); + free(cbf); + break; + } + return n0 - n; +} + +Dev pccarddevtab = { + 'Y', + "cardbus", + + devreset, + devinit, + devshutdown, + pccardattach, + pccardwalk, + pccardstat, + pccardopen, + devcreate, + pccardclose, + pccardread, + devbread, + pccardwrite, + devbwrite, + devremove, + devwstat, +}; + +static PCMmap * +isamap(Cardbus *cb, ulong offset, int len, int attr) +{ + uchar we, bit; + PCMmap *m, *nm; + Pcminfo *pi; + int i; + ulong e; + + pi = &cb->linfo; + + /* convert offset to granularity */ + if(len <= 0) + len = 1; + e = ROUND(offset+len, Mgran); + offset &= Mmask; + len = e - offset; + + /* look for a map that covers the right area */ + we = rdreg(cb, Rwe); + bit = 1; + nm = 0; + for(m = pi->mmap; m < &pi->mmap[nelem(pi->mmap)]; m++){ + if((we & bit)) + if(m->attr == attr) + if(offset >= m->ca && e <= m->cea){ + + m->ref++; + return m; + } + bit <<= 1; + if(nm == 0 && m->ref == 0) + nm = m; + } + m = nm; + if(m == 0) + return 0; + + /* if isa space isn't big enough, free it and get more */ + if(m->len < len){ + if(m->isa){ + umbfree(m->isa, m->len); + m->len = 0; + } + m->isa = PADDR(umbmalloc(0, len, Mgran)); + if(m->isa == 0){ + print("isamap: out of isa space\n"); + return 0; + } + m->len = len; + } + + /* set up new map */ + m->ca = offset; + m->cea = m->ca + m->len; + m->attr = attr; + i = m - pi->mmap; + bit = 1<<i; + wrreg(cb, Rwe, we & ~bit); /* disable map before changing it */ + wrreg(cb, MAP(i, Mbtmlo), m->isa>>Mshift); + wrreg(cb, MAP(i, Mbtmhi), (m->isa>>(Mshift+8)) | F16bit); + wrreg(cb, MAP(i, Mtoplo), (m->isa+m->len-1)>>Mshift); + wrreg(cb, MAP(i, Mtophi), ((m->isa+m->len-1)>>(Mshift+8))); + offset -= m->isa; + offset &= (1<<25)-1; + offset >>= Mshift; + wrreg(cb, MAP(i, Mofflo), offset); + wrreg(cb, MAP(i, Moffhi), (offset>>8) | (attr ? Fregactive : 0)); + wrreg(cb, Rwe, we | bit); /* enable map */ + m->ref = 1; + + return m; +} + +static void +isaunmap(PCMmap* m) +{ + m->ref--; +} + +/* + * reading and writing card registers + */ +static uchar +rdreg(Cardbus *cb, int index) +{ + outb(cb->lindex, cb->lbase + index); + return inb(cb->ldata); +} + +static void +wrreg(Cardbus *cb, int index, uchar val) +{ + outb(cb->lindex, cb->lbase + index); + outb(cb->ldata, val); +} + +static int +readc(Cisdat *cis, uchar *x) +{ + if(cis->cispos >= cis->cislen) + return 0; + *x = cis->cisbase[cis->cisskip*cis->cispos]; + cis->cispos++; + return 1; +} + +static ulong +getlong(Cisdat *cis, int size) +{ + uchar c; + int i; + ulong x; + + x = 0; + for(i = 0; i < size; i++){ + if(readc(cis, &c) != 1) + break; + x |= c<<(i*8); + } + return x; +} + +static void +tcfig(Cardbus *cb, Cisdat *cis, int ) +{ + uchar size, rasize, rmsize; + uchar last; + Pcminfo *pi; + + if(readc(cis, &size) != 1) + return; + rasize = (size&0x3) + 1; + rmsize = ((size>>2)&0xf) + 1; + if(readc(cis, &last) != 1) + return; + + pi = &cb->linfo; + pi->conf_addr = getlong(cis, rasize); + pi->conf_present = getlong(cis, rmsize); +} + +static void +tvers1(Cardbus *cb, Cisdat *cis, int ) +{ + uchar c, major, minor, last; + int i; + Pcminfo *pi; + + pi = &cb->linfo; + if(readc(cis, &major) != 1) + return; + if(readc(cis, &minor) != 1) + return; + last = 0; + for(i = 0; i < sizeof(pi->verstr) - 1; i++){ + if(readc(cis, &c) != 1) + return; + if(c == 0) + c = ';'; + if(c == '\n') + c = ';'; + if(c == 0xff) + break; + if(c == ';' && last == ';') + continue; + pi->verstr[i] = c; + last = c; + } + pi->verstr[i] = 0; +} + +static ulong +microvolt(Cisdat *cis) +{ + uchar c; + ulong microvolts; + ulong exp; + + if(readc(cis, &c) != 1) + return 0; + exp = exponent[c&0x7]; + microvolts = vmant[(c>>3)&0xf]*exp; + while(c & 0x80){ + if(readc(cis, &c) != 1) + return 0; + switch(c){ + case 0x7d: + break; /* high impedence when sleeping */ + case 0x7e: + case 0x7f: + microvolts = 0; /* no connection */ + break; + default: + exp /= 10; + microvolts += exp*(c&0x7f); + } + } + return microvolts; +} + +static ulong +nanoamps(Cisdat *cis) +{ + uchar c; + ulong nanoamps; + + if(readc(cis, &c) != 1) + return 0; + nanoamps = exponent[c&0x7]*vmant[(c>>3)&0xf]; + while(c & 0x80){ + if(readc(cis, &c) != 1) + return 0; + if(c == 0x7d || c == 0x7e || c == 0x7f) + nanoamps = 0; + } + return nanoamps; +} + +/* + * only nominal voltage (feature 1) is important for config, + * other features must read card to stay in sync. + */ +static ulong +power(Cisdat *cis) +{ + uchar feature; + ulong mv; + + mv = 0; + if(readc(cis, &feature) != 1) + return 0; + if(feature & 1) + mv = microvolt(cis); + if(feature & 2) + microvolt(cis); + if(feature & 4) + microvolt(cis); + if(feature & 8) + nanoamps(cis); + if(feature & 0x10) + nanoamps(cis); + if(feature & 0x20) + nanoamps(cis); + if(feature & 0x40) + nanoamps(cis); + return mv/1000000; +} + +static ulong +ttiming(Cisdat *cis, int scale) +{ + uchar unscaled; + ulong nanosecs; + + if(readc(cis, &unscaled) != 1) + return 0; + nanosecs = (mantissa[(unscaled>>3)&0xf]*exponent[unscaled&7])/10; + nanosecs = nanosecs * exponent[scale]; + return nanosecs; +} + +static void +timing(Cisdat *cis, PCMconftab *ct) +{ + uchar c, i; + + if(readc(cis, &c) != 1) + return; + i = c&0x3; + if(i != 3) + ct->maxwait = ttiming(cis, i); /* max wait */ + i = (c>>2)&0x7; + if(i != 7) + ct->readywait = ttiming(cis, i); /* max ready/busy wait */ + i = (c>>5)&0x7; + if(i != 7) + ct->otherwait = ttiming(cis, i); /* reserved wait */ +} + +static void +iospaces(Cisdat *cis, PCMconftab *ct) +{ + uchar c; + int i, nio; + + ct->nio = 0; + if(readc(cis, &c) != 1) + return; + + ct->bit16 = ((c>>5)&3) >= 2; + if(!(c & 0x80)){ + ct->io[0].start = 0; + ct->io[0].len = 1<<(c&0x1f); + ct->nio = 1; + return; + } + + if(readc(cis, &c) != 1) + return; + + /* + * For each of the range descriptions read the + * start address and the length (value is length-1). + */ + nio = (c&0xf)+1; + for(i = 0; i < nio; i++){ + ct->io[i].start = getlong(cis, (c>>4)&0x3); + ct->io[i].len = getlong(cis, (c>>6)&0x3)+1; + } + ct->nio = nio; +} + +static void +irq(Cisdat *cis, PCMconftab *ct) +{ + uchar c; + + if(readc(cis, &c) != 1) + return; + ct->irqtype = c & 0xe0; + if(c & 0x10) + ct->irqs = getlong(cis, 2); + else + ct->irqs = 1<<(c&0xf); + ct->irqs &= 0xDEB8; /* levels available to card */ +} + +static void +memspace(Cisdat *cis, int asize, int lsize, int host) +{ + ulong haddress, address, len; + + len = getlong(cis, lsize)*256; + address = getlong(cis, asize)*256; + USED(len, address); + if(host){ + haddress = getlong(cis, asize)*256; + USED(haddress); + } +} + +static void +tentry(Cardbus *cb, Cisdat *cis, int ) +{ + uchar c, i, feature; + PCMconftab *ct; + Pcminfo *pi; + + pi = &cb->linfo; + if(pi->nctab >= nelem(pi->ctab)) + return; + if(readc(cis, &c) != 1) + return; + ct = &pi->ctab[pi->nctab++]; + + /* copy from last default config */ + if(pi->defctab) + *ct = *pi->defctab; + + ct->index = c & 0x3f; + + /* is this the new default? */ + if(c & 0x40) + pi->defctab = ct; + + /* memory wait specified? */ + if(c & 0x80){ + if(readc(cis, &i) != 1) + return; + if(i&0x80) + ct->memwait = 1; + } + + if(readc(cis, &feature) != 1) + return; + switch(feature&0x3){ + case 1: + ct->vpp1 = ct->vpp2 = power(cis); + break; + case 2: + power(cis); + ct->vpp1 = ct->vpp2 = power(cis); + break; + case 3: + power(cis); + ct->vpp1 = power(cis); + ct->vpp2 = power(cis); + break; + default: + break; + } + if(feature&0x4) + timing(cis, ct); + if(feature&0x8) + iospaces(cis, ct); + if(feature&0x10) + irq(cis, ct); + switch((feature>>5)&0x3){ + case 1: + memspace(cis, 0, 2, 0); + break; + case 2: + memspace(cis, 2, 2, 0); + break; + case 3: + if(readc(cis, &c) != 1) + return; + for(i = 0; i <= (c&0x7); i++) + memspace(cis, (c>>5)&0x3, (c>>3)&0x3, c&0x80); + break; + } +} + +static void +i82365probe(Cardbus *cb, int lindex, int ldata) +{ + uchar c, id; + int dev = 0; /* According to the Ricoh spec 00->3F _and_ 80->BF seem + to be the same socket A (ditto for B). */ + + outb(lindex, Rid + (dev<<7)); + id = inb(ldata); + if((id & 0xf0) != 0x80) + return; /* not a memory & I/O card */ + if((id & 0x0f) == 0x00) + return; /* no revision number, not possible */ + + cb->lindex = lindex; + cb->ldata = ldata; + cb->ltype = Ti82365; + cb->lbase = (int)(cb - cbslots) * 0x40; + + switch(id){ + case 0x82: + case 0x83: + case 0x84: + /* could be a cirrus */ + outb(cb->lindex, Rchipinfo + (dev<<7)); + outb(cb->ldata, 0); + c = inb(cb->ldata); + if((c & 0xc0) != 0xc0) + break; + c = inb(cb->ldata); + if((c & 0xc0) != 0x00) + break; + if(c & 0x20){ + cb->ltype = Tpd6720; + } else { + cb->ltype = Tpd6710; + } + break; + } + + /* if it's not a Cirrus, it could be a Vadem... */ + if(cb->ltype == Ti82365){ + /* unlock the Vadem extended regs */ + outb(cb->lindex, 0x0E + (dev<<7)); + outb(cb->lindex, 0x37 + (dev<<7)); + + /* make the id register show the Vadem id */ + outb(cb->lindex, 0x3A + (dev<<7)); + c = inb(cb->ldata); + outb(cb->ldata, c|0xC0); + outb(cb->lindex, Rid + (dev<<7)); + c = inb(cb->ldata); + if(c & 0x08) + cb->ltype = Tvg46x; + + /* go back to Intel compatible id */ + outb(cb->lindex, 0x3A + (dev<<7)); + c = inb(cb->ldata); + outb(cb->ldata, c & ~0xC0); + } +} + +static int +vcode(int volt) +{ + switch(volt){ + case 5: + return 1; + case 12: + return 2; + default: + return 0; + } +} + diff --git a/os/pc/devpnp.c b/os/pc/devpnp.c new file mode 100644 index 00000000..fe4c05dd --- /dev/null +++ b/os/pc/devpnp.c @@ -0,0 +1,652 @@ +/* + * ISA PNP 1.0 support + access to PCI configuration space + * + * TODO + * - implement PNP card configuration (setting io bases etc) + * - write user program to drive PNP configuration... + * - extend PCI raw access to configuration space (writes, byte/short access?) + * - implement PCI access to memory/io space/BIOS ROM + * - use c->aux instead of performing lookup on each read/write? + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +typedef struct Pnp Pnp; +typedef struct Card Card; + +struct Pnp +{ + QLock; + int rddata; + int debug; + Card *cards; +}; + +struct Card +{ + int csn; + ulong id1; + ulong id2; + char *cfgstr; + int ncfg; + Card* next; +}; + +static Pnp pnp; + +#define DPRINT if(pnp.debug) print +#define XPRINT if(1) print + +enum { + Address = 0x279, + WriteData = 0xa79, + + Qtopdir = 0, + + Qpnpdir, + Qpnpctl, + Qcsnctl, + Qcsnraw, + + Qpcidir, + Qpcictl, + Qpciraw, +}; + +#define TYPE(q) ((ulong)(q).path & 0x0F) +#define CSN(q) (((ulong)(q).path>>4) & 0xFF) +#define QID(c, t) (((c)<<4)|(t)) + +static Dirtab topdir[] = { + ".", { Qtopdir, 0, QTDIR }, 0, 0555, + "pnp", { Qpnpdir, 0, QTDIR }, 0, 0555, + "pci", { Qpcidir, 0, QTDIR }, 0, 0555, +}; + +static Dirtab pnpdir[] = { + ".", { Qpnpdir, 0, QTDIR }, 0, 0555, + "ctl", { Qpnpctl, 0, 0 }, 0, 0666, +}; + +extern Dev pnpdevtab; +static int wrconfig(Card*, char*); + +static char key[32] = +{ + 0x6A, 0xB5, 0xDA, 0xED, 0xF6, 0xFB, 0x7D, 0xBE, + 0xDF, 0x6F, 0x37, 0x1B, 0x0D, 0x86, 0xC3, 0x61, + 0xB0, 0x58, 0x2C, 0x16, 0x8B, 0x45, 0xA2, 0xD1, + 0xE8, 0x74, 0x3A, 0x9D, 0xCE, 0xE7, 0x73, 0x39, +}; + +static void +cmd(int reg, int val) +{ + outb(Address, reg); + outb(WriteData, val); +} + +/* Send initiation key, putting each card in Sleep state */ +static void +initiation(void) +{ + int i; + + /* ensure each card's LFSR is reset */ + outb(Address, 0x00); + outb(Address, 0x00); + + /* send initiation key */ + for (i = 0; i < 32; i++) + outb(Address, key[i]); +} + +/* isolation protocol... */ +static int +readbit(int rddata) +{ + int r1, r2; + + r1 = inb(rddata); + r2 = inb(rddata); + microdelay(250); + return (r1 == 0x55) && (r2 == 0xaa); +} + +static int +isolate(int rddata, ulong *id1, ulong *id2) +{ + int i, csum, bit; + uchar *p, id[9]; + + outb(Address, 0x01); /* point to serial isolation register */ + delay(1); + csum = 0x6a; + for(i = 0; i < 64; i++){ + bit = readbit(rddata); + csum = (csum>>1) | (((csum&1) ^ ((csum>>1)&1) ^ bit)<<7); + p = &id[i>>3]; + *p = (*p>>1) | (bit<<7); + } + for(; i < 72; i++){ + p = &id[i>>3]; + *p = (*p>>1) | (readbit(rddata)<<7); + } + *id1 = (id[3]<<24)|(id[2]<<16)|(id[1]<<8)|id[0]; + *id2 = (id[7]<<24)|(id[6]<<16)|(id[5]<<8)|id[4]; + if(*id1 == 0) + return 0; + if(id[8] != csum) + DPRINT("pnp: bad checksum id1 %lux id2 %lux csum %x != %x\n", *id1, *id2, csum, id[8]); /**/ + return id[8] == csum; +} + +static int +getresbyte(int rddata) +{ + int tries = 0; + + outb(Address, 0x05); + while ((inb(rddata) & 1) == 0) + if (tries++ > 1000000) + error("pnp: timeout waiting for resource data\n"); + outb(Address, 0x04); + return inb(rddata); +} + +static char * +serial(ulong id1, ulong id2) +{ + int i1, i2, i3; + ulong x; + static char buf[20]; + + i1 = (id1>>2)&31; + i2 = ((id1<<3)&24)+((id1>>13)&7); + i3 = (id1>>8)&31; + x = (id1>>8)&0xff00|(id1>>24)&0x00ff; + if (i1 > 0 && i1 < 27 && i2 > 0 && i2 < 27 && i3 > 0 && i3 < 27 && (id1 & (1<<7)) == 0) + snprint(buf, sizeof(buf), "%c%c%c%.4lux.%lux", 'A'+i1-1, 'A'+i2-1, 'A'+i3-1, x, id2); + else + snprint(buf, sizeof(buf), "%.4lux%.4lux.%lux", (id1<<8)&0xff00|(id1>>8)&0x00ff, x, id2); + return buf; +} + +static Card * +findcsn(int csn, int create, int dolock) +{ + Card *c, *nc, **l; + + if(dolock) + qlock(&pnp); + l = &pnp.cards; + for(c = *l; c != nil; c = *l) { + if(c->csn == csn) + goto done; + if(c->csn > csn) + break; + l = &c->next; + } + if(create) { + *l = nc = malloc(sizeof(Card)); + nc->next = c; + nc->csn = csn; + c = nc; + } +done: + if(dolock) + qunlock(&pnp); + return c; +} + +static int +newcsn(void) +{ + int csn; + Card *c; + + csn = 1; + for(c = pnp.cards; c != nil; c = c->next) { + if(c->csn > csn) + break; + csn = c->csn+1; + } + return csn; +} + +static int +pnpncfg(int rddata) +{ + int i, n, x, ncfg, n1, n2; + + ncfg = 0; + for (;;) { + x = getresbyte(rddata); + if((x & 0x80) == 0) { + n = (x&7)+1; + for(i = 1; i < n; i++) + getresbyte(rddata); + } + else { + n1 = getresbyte(rddata); + n2 = getresbyte(rddata); + n = (n2<<8)|n1 + 3; + for (i = 3; i < n; i++) + getresbyte(rddata); + } + ncfg += n; + if((x>>3) == 0x0f) + break; + } + return ncfg; +} + +/* look for cards, and assign them CSNs */ +static int +pnpscan(int rddata, int dawn) +{ + Card *c; + int csn; + ulong id1, id2; + + initiation(); /* upsilon sigma */ + cmd(0x02, 0x04+0x01); /* reset CSN on all cards and reset logical devices */ + delay(1); /* delay after resetting cards */ + + cmd(0x03, 0); /* Wake all cards with a CSN of 0 */ + cmd(0x00, rddata>>2); /* Set the READ_DATA port on all cards */ + while(isolate(rddata, &id1, &id2)) { + for(c = pnp.cards; c != nil; c = c->next) + if(c->id1 == id1 && c->id2 == id2) + break; + if(c == nil) { + csn = newcsn(); + c = findcsn(csn, 1, 0); + c->id1 = id1; + c->id2 = id2; + } + else if(c->cfgstr != nil) { + if(!wrconfig(c, c->cfgstr)) + print("pnp%d: bad cfg: %s\n", c->csn, c->cfgstr); + c->cfgstr = nil; + } + cmd(0x06, c->csn); /* set the card's csn */ + if(dawn) + print("pnp%d: %s\n", c->csn, serial(id1, id2)); + c->ncfg = pnpncfg(rddata); + cmd(0x03, 0); /* Wake all cards with a CSN of 0, putting this card to sleep */ + } + cmd(0x02, 0x02); /* return cards to Wait for Key state */ + if(pnp.cards != 0) { + pnp.rddata = rddata; + return 1; + } + return 0; +} + +static void +pnpreset(void) +{ + Card *c; + ulong id1, id2; + int csn, i1, i2, i3, x; + char *s, *p, buf[20]; + ISAConf isa; + + memset(&isa, 0, sizeof(ISAConf)); + pnp.rddata = -1; + if (isaconfig("pnp", 0, &isa) == 0) + return; + if(isa.port < 0x203 || isa.port > 0x3ff) + return; + for(csn = 1; csn < 256; csn++) { + sprint(buf, "pnp%d", csn); + s = getconf(buf); + if(s == 0) + continue; + if(strlen(s) < 8 || s[7] != '.' || s[0] < 'A' || s[0] > 'Z' || s[1] < 'A' || s[1] > 'Z' || s[2] < 'A' || s[2] > 'Z') { +bad: + print("pnp%d: bad conf string %s\n", csn, s); + continue; + } + i1 = s[0]-'A'+1; + i2 = s[1]-'A'+1; + i3 = s[2]-'A'+1; + x = strtoul(&s[3], 0, 16); + id1 = (i1<<2)|((i2>>3)&3)|((i2&7)<<13)|(i3<<8)|((x&0xff)<<24)|((x&0xff00)<<8); + id2 = strtoul(&s[8], &p, 16); + if(*p == ' ') + p++; + else if(*p == '\0') + p = nil; + else + goto bad; + c = findcsn(csn, 1, 0); + c->id1 = id1; + c->id2 = id2; + c->cfgstr = p; + } + pnpscan(isa.port, 1); +} + +static int +csngen(Chan *c, int t, int csn, Card *cp, Dir *dp) +{ + Qid q; + + switch(t) { + case Qcsnctl: + q = (Qid){QID(csn, Qcsnctl), 0, 0}; + sprint(up->genbuf, "csn%dctl", csn); + devdir(c, q, up->genbuf, 0, eve, 0664, dp); + return 1; + case Qcsnraw: + q = (Qid){QID(csn, Qcsnraw), 0, 0}; + sprint(up->genbuf, "csn%draw", csn); + devdir(c, q, up->genbuf, cp->ncfg, eve, 0444, dp); + return 1; + } + return -1; +} + +static int +pcigen(Chan *c, int t, int tbdf, Dir *dp) +{ + Qid q; + + q = (Qid){BUSBDF(tbdf)|t, 0, 0}; + switch(t) { + case Qpcictl: + sprint(up->genbuf, "%d.%d.%dctl", BUSBNO(tbdf), BUSDNO(tbdf), BUSFNO(tbdf)); + devdir(c, q, up->genbuf, 0, eve, 0444, dp); + return 1; + case Qpciraw: + sprint(up->genbuf, "%d.%d.%draw", BUSBNO(tbdf), BUSDNO(tbdf), BUSFNO(tbdf)); + devdir(c, q, up->genbuf, 128, eve, 0444, dp); + return 1; + } + return -1; +} + +static int +pnpgen(Chan *c, char *, Dirtab*, int, int s, Dir *dp) +{ + Qid q; + Card *cp; + Pcidev *p; + int csn, tbdf; + + switch(TYPE(c->qid)){ + case Qtopdir: + if(s == DEVDOTDOT){ + q = (Qid){QID(0, Qtopdir), 0, QTDIR}; + sprint(up->genbuf, "#%C", pnpdevtab.dc); + devdir(c, q, up->genbuf, 0, eve, 0555, dp); + return 1; + } + return devgen(c, nil, topdir, nelem(topdir), s, dp); + case Qpnpdir: + if(s == DEVDOTDOT){ + q = (Qid){QID(0, Qtopdir), 0, QTDIR}; + sprint(up->genbuf, "#%C", pnpdevtab.dc); + devdir(c, q, up->genbuf, 0, eve, 0555, dp); + return 1; + } + if(s < nelem(pnpdir)-1) + return devgen(c, nil, pnpdir, nelem(pnpdir), s, dp); + s -= nelem(pnpdir)-1; + qlock(&pnp); + cp = pnp.cards; + while(s >= 2 && cp != nil) { + s -= 2; + cp = cp->next; + } + qunlock(&pnp); + if(cp == nil) + return -1; + return csngen(c, s+Qcsnctl, cp->csn, cp, dp); + case Qpnpctl: + return devgen(c, nil, pnpdir, nelem(pnpdir), s, dp); + case Qcsnctl: + case Qcsnraw: + csn = CSN(c->qid); + cp = findcsn(csn, 0, 1); + if(cp == nil) + return -1; + return csngen(c, TYPE(c->qid), csn, cp, dp); + case Qpcidir: + if(s == DEVDOTDOT){ + q = (Qid){QID(0, Qtopdir), 0, QTDIR}; + sprint(up->genbuf, "#%C", pnpdevtab.dc); + devdir(c, q, up->genbuf, 0, eve, 0555, dp); + return 1; + } + p = pcimatch(nil, 0, 0); + while(s >= 2 && p != nil) { + p = pcimatch(p, 0, 0); + s -= 2; + } + if(p == nil) + return -1; + return pcigen(c, s+Qpcictl, p->tbdf, dp); + case Qpcictl: + case Qpciraw: + tbdf = MKBUS(BusPCI, 0, 0, 0)|BUSBDF((ulong)c->qid.path); + p = pcimatchtbdf(tbdf); + if(p == nil) + return -1; + return pcigen(c, TYPE(c->qid), tbdf, dp); + default: + break; + } + return -1; +} + +static Chan* +pnpattach(char *spec) +{ + return devattach(pnpdevtab.dc, spec); +} + +Walkqid* +pnpwalk(Chan* c, Chan *nc, char** name, int nname) +{ + return devwalk(c, nc, name, nname, (Dirtab *)0, 0, pnpgen); +} + +static int +pnpstat(Chan* c, uchar* dp, int n) +{ + return devstat(c, dp, n, (Dirtab *)0, 0L, pnpgen); +} + +static Chan* +pnpopen(Chan *c, int omode) +{ + c = devopen(c, omode, (Dirtab*)0, 0, pnpgen); + switch(TYPE(c->qid)){ + default: + break; + } + return c; +} + +static void +pnpclose(Chan*) +{ +} + +static long +pnpread(Chan *c, void *va, long n, vlong offset) +{ + ulong x; + Card *cp; + Pcidev *p; + char buf[256], *ebuf, *w; + char *a = va; + int csn, i, tbdf, r; + + switch(TYPE(c->qid)){ + case Qtopdir: + case Qpnpdir: + case Qpcidir: + return devdirread(c, a, n, (Dirtab *)0, 0L, pnpgen); + case Qpnpctl: + if(pnp.rddata > 0) + sprint(up->genbuf, "enabled 0x%x\n", pnp.rddata); + else + sprint(up->genbuf, "disabled\n"); + return readstr(offset, a, n, up->genbuf); + case Qcsnraw: + csn = CSN(c->qid); + cp = findcsn(csn, 0, 1); + if(cp == nil) + error(Egreg); + if(offset+n > cp->ncfg) + n = cp->ncfg - offset; + qlock(&pnp); + initiation(); + cmd(0x03, csn); /* Wake up the card */ + for(i = 0; i < offset+9; i++) /* 9 == skip serial + csum */ + getresbyte(pnp.rddata); + for(i = 0; i < n; i++) + a[i] = getresbyte(pnp.rddata); + cmd(0x03, 0); /* Wake all cards with a CSN of 0, putting this card to sleep */ + cmd(0x02, 0x02); /* return cards to Wait for Key state */ + qunlock(&pnp); + break; + case Qcsnctl: + csn = CSN(c->qid); + cp = findcsn(csn, 0, 1); + if(cp == nil) + error(Egreg); + sprint(up->genbuf, "%s\n", serial(cp->id1, cp->id2)); + return readstr(offset, a, n, up->genbuf); + case Qpcictl: + tbdf = MKBUS(BusPCI, 0, 0, 0)|BUSBDF((ulong)c->qid.path); + p = pcimatchtbdf(tbdf); + if(p == nil) + error(Egreg); + ebuf = buf+sizeof buf-1; /* -1 for newline */ + w = seprint(buf, ebuf, "%.2x.%.2x.%.2x %.4x/%.4x %3d", + p->ccrb, p->ccru, p->ccrp, p->vid, p->did, p->intl); + for(i=0; i<nelem(p->mem); i++){ + if(p->mem[i].size == 0) + continue; + w = seprint(w, ebuf, " %d:%.8lux %d", i, p->mem[i].bar, p->mem[i].size); + } + *w++ = '\n'; + *w = '\0'; + return readstr(offset, a, n, buf); + case Qpciraw: + tbdf = MKBUS(BusPCI, 0, 0, 0)|BUSBDF((ulong)c->qid.path); + p = pcimatchtbdf(tbdf); + if(p == nil) + error(Egreg); + if(offset > 256) + return 0; + if(n+offset > 256) + n = 256-offset; + if(offset%4) + error(Ebadarg); + r = offset; + for(i = 0; i+4 <= n; i+=4) { + x = pcicfgr32(p, r); + a[0] = x; + a[1] = (x>>8); + a[2] = (x>>16); + a[3] = (x>>24); + a += 4; + r += 4; + } + return i; + default: + error(Egreg); + } + return n; +} + +static long +pnpwrite(Chan *c, void *a, long n, vlong) +{ + int csn; + Card *cp; + ulong port; + char buf[256]; + + if(n >= sizeof(buf)) + n = sizeof(buf)-1; + strncpy(buf, a, n); + buf[n] = 0; + + switch(TYPE(c->qid)){ + case Qpnpctl: + if(strncmp(buf, "port ", 5) == 0) { + port = strtoul(buf+5, 0, 0); + if(port < 0x203 || port > 0x3ff) + error("bad value for rddata port"); + qlock(&pnp); + if(waserror()) { + qunlock(&pnp); + nexterror(); + } + if(pnp.rddata > 0) + error("pnp port already set"); + if(!pnpscan(port, 0)) + error("no cards found"); + qunlock(&pnp); + poperror(); + } + else if(strncmp(buf, "debug ", 6) == 0) + pnp.debug = strtoul(buf+6, 0, 0); + else + error(Ebadctl); + break; + case Qcsnctl: + csn = CSN(c->qid); + cp = findcsn(csn, 0, 1); + if(cp == nil) + error(Egreg); + if(!wrconfig(cp, buf)) + error(Ebadctl); + break; + default: + error(Egreg); + } + return n; +} + +static int +wrconfig(Card *c, char *cmd) +{ + /* This should implement setting of I/O bases, etc */ + USED(c, cmd); + return 1; +} + + +Dev pnpdevtab = { + '$', + "pnp", + + pnpreset, + devinit, + devshutdown, + pnpattach, + pnpwalk, + pnpstat, + pnpopen, + devcreate, + pnpclose, + pnpread, + devbread, + pnpwrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/os/pc/devrtc.c b/os/pc/devrtc.c new file mode 100644 index 00000000..f6e1d911 --- /dev/null +++ b/os/pc/devrtc.c @@ -0,0 +1,461 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +/* + * real time clock and non-volatile ram + */ + +enum { + Paddr= 0x70, /* address port */ + Pdata= 0x71, /* data port */ + + Seconds= 0x00, + Minutes= 0x02, + Hours= 0x04, + Mday= 0x07, + Month= 0x08, + Year= 0x09, + Status= 0x0A, + + Nvoff= 128, /* where usable nvram lives */ + Nvsize= 256, + + Nbcd= 6, +}; + +typedef struct Rtc Rtc; +struct Rtc +{ + int sec; + int min; + int hour; + int mday; + int mon; + int year; +}; + + +enum{ + Qdir = 0, + Qrtc, + Qnvram, +}; + +Dirtab rtcdir[]={ + ".", {Qdir, 0, QTDIR}, 0, 0555, + "nvram", {Qnvram, 0}, Nvsize, 0664, + "rtc", {Qrtc, 0}, 0, 0664, +}; + +static ulong rtc2sec(Rtc*); +static void sec2rtc(ulong, Rtc*); + +void +rtcinit(void) +{ + if(ioalloc(Paddr, 2, 0, "rtc/nvr") < 0) + panic("rtcinit: ioalloc failed"); +} + +static Chan* +rtcattach(char* spec) +{ + return devattach('r', spec); +} + +static Walkqid* +rtcwalk(Chan* c, Chan *nc, char** name, int nname) +{ + return devwalk(c, nc, name, nname, rtcdir, nelem(rtcdir), devgen); +} + +static int +rtcstat(Chan* c, uchar* dp, int n) +{ + return devstat(c, dp, n, rtcdir, nelem(rtcdir), devgen); +} + +static Chan* +rtcopen(Chan* c, int omode) +{ + omode = openmode(omode); + switch((ulong)c->qid.path){ + case Qrtc: + if(strcmp(up->env->user, eve)!=0 && omode!=OREAD) + error(Eperm); + break; + case Qnvram: + if(strcmp(up->env->user, eve)!=0) + error(Eperm); + } + return devopen(c, omode, rtcdir, nelem(rtcdir), devgen); +} + +static void +rtcclose(Chan*) +{ +} + +#define GETBCD(o) ((bcdclock[o]&0xf) + 10*(bcdclock[o]>>4)) + +static long +_rtctime(void) +{ + uchar bcdclock[Nbcd]; + Rtc rtc; + int i; + + /* don't do the read until the clock is no longer busy */ + for(i = 0; i < 10000; i++){ + outb(Paddr, Status); + if(inb(Pdata) & 0x80) + continue; + + /* read clock values */ + outb(Paddr, Seconds); bcdclock[0] = inb(Pdata); + outb(Paddr, Minutes); bcdclock[1] = inb(Pdata); + outb(Paddr, Hours); bcdclock[2] = inb(Pdata); + outb(Paddr, Mday); bcdclock[3] = inb(Pdata); + outb(Paddr, Month); bcdclock[4] = inb(Pdata); + outb(Paddr, Year); bcdclock[5] = inb(Pdata); + + outb(Paddr, Status); + if((inb(Pdata) & 0x80) == 0) + break; + } + + /* + * convert from BCD + */ + rtc.sec = GETBCD(0); + rtc.min = GETBCD(1); + rtc.hour = GETBCD(2); + rtc.mday = GETBCD(3); + rtc.mon = GETBCD(4); + rtc.year = GETBCD(5); + + /* + * the world starts jan 1 1970 + */ + if(rtc.year < 70) + rtc.year += 2000; + else + rtc.year += 1900; + return rtc2sec(&rtc); +} + +static Lock nvrtlock; + +long +rtctime(void) +{ + int i; + long t, ot; + + ilock(&nvrtlock); + + /* loop till we get two reads in a row the same */ + t = _rtctime(); + for(i = 0; i < 100; i++){ + ot = t; + t = _rtctime(); + if(ot == t) + break; + } + if(i == 100) print("we are boofheads\n"); + + iunlock(&nvrtlock); + + return t; +} + +static long +rtcread(Chan* c, void* buf, long n, vlong off) +{ + ulong t; + char *a, *start; + ulong offset = off; + + if(c->qid.type & QTDIR) + return devdirread(c, buf, n, rtcdir, nelem(rtcdir), devgen); + + switch((ulong)c->qid.path){ + case Qrtc: + t = rtctime(); + n = readnum(offset, buf, n, t, 12); + return n; + case Qnvram: + if(n == 0) + return 0; + if(n > Nvsize) + n = Nvsize; + a = start = smalloc(n); + + ilock(&nvrtlock); + for(t = offset; t < offset + n; t++){ + if(t >= Nvsize) + break; + outb(Paddr, Nvoff+t); + *a++ = inb(Pdata); + } + iunlock(&nvrtlock); + + if(waserror()){ + free(start); + nexterror(); + } + memmove(buf, start, t - offset); + poperror(); + + free(start); + return t - offset; + } + error(Ebadarg); + return 0; +} + +#define PUTBCD(n,o) bcdclock[o] = (n % 10) | (((n / 10) % 10)<<4) + +static long +rtcwrite(Chan* c, void* buf, long n, vlong off) +{ + int t; + char *a, *start; + Rtc rtc; + ulong secs; + uchar bcdclock[Nbcd]; + char *cp, *ep; + ulong offset = off; + + if(offset!=0) + error(Ebadarg); + + + switch((ulong)c->qid.path){ + case Qrtc: + /* + * read the time + */ + cp = ep = buf; + ep += n; + while(cp < ep){ + if(*cp>='0' && *cp<='9') + break; + cp++; + } + secs = strtoul(cp, 0, 0); + + /* + * convert to bcd + */ + sec2rtc(secs, &rtc); + PUTBCD(rtc.sec, 0); + PUTBCD(rtc.min, 1); + PUTBCD(rtc.hour, 2); + PUTBCD(rtc.mday, 3); + PUTBCD(rtc.mon, 4); + PUTBCD(rtc.year, 5); + + /* + * write the clock + */ + ilock(&nvrtlock); + outb(Paddr, Seconds); outb(Pdata, bcdclock[0]); + outb(Paddr, Minutes); outb(Pdata, bcdclock[1]); + outb(Paddr, Hours); outb(Pdata, bcdclock[2]); + outb(Paddr, Mday); outb(Pdata, bcdclock[3]); + outb(Paddr, Month); outb(Pdata, bcdclock[4]); + outb(Paddr, Year); outb(Pdata, bcdclock[5]); + iunlock(&nvrtlock); + return n; + case Qnvram: + if(n == 0) + return 0; + if(n > Nvsize) + n = Nvsize; + + start = a = smalloc(n); + if(waserror()){ + free(start); + nexterror(); + } + memmove(a, buf, n); + poperror(); + + ilock(&nvrtlock); + for(t = offset; t < offset + n; t++){ + if(t >= Nvsize) + break; + outb(Paddr, Nvoff+t); + outb(Pdata, *a++); + } + iunlock(&nvrtlock); + + free(start); + return t - offset; + } + error(Ebadarg); + return 0; +} + +Dev rtcdevtab = { + 'r', + "rtc", + + devreset, + rtcinit, + devshutdown, + rtcattach, + rtcwalk, + rtcstat, + rtcopen, + devcreate, + rtcclose, + rtcread, + devbread, + rtcwrite, + devbwrite, + devremove, + devwstat, +}; + +#define SEC2MIN 60L +#define SEC2HOUR (60L*SEC2MIN) +#define SEC2DAY (24L*SEC2HOUR) + +/* + * days per month plus days/year + */ +static int dmsize[] = +{ + 365, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +}; +static int ldmsize[] = +{ + 366, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +}; + +/* + * return the days/month for the given year + */ +static int* +yrsize(int y) +{ + if((y%4) == 0 && ((y%100) != 0 || (y%400) == 0)) + return ldmsize; + else + return dmsize; +} + +/* + * compute seconds since Jan 1 1970 + */ +static ulong +rtc2sec(Rtc *rtc) +{ + ulong secs; + int i; + int *d2m; + + secs = 0; + + /* + * seconds per year + */ + for(i = 1970; i < rtc->year; i++){ + d2m = yrsize(i); + secs += d2m[0] * SEC2DAY; + } + + /* + * seconds per month + */ + d2m = yrsize(rtc->year); + for(i = 1; i < rtc->mon; i++) + secs += d2m[i] * SEC2DAY; + + secs += (rtc->mday-1) * SEC2DAY; + secs += rtc->hour * SEC2HOUR; + secs += rtc->min * SEC2MIN; + secs += rtc->sec; + + return secs; +} + +/* + * compute rtc from seconds since Jan 1 1970 + */ +static void +sec2rtc(ulong secs, Rtc *rtc) +{ + int d; + long hms, day; + int *d2m; + + /* + * break initial number into days + */ + hms = secs % SEC2DAY; + day = secs / SEC2DAY; + if(hms < 0) { + hms += SEC2DAY; + day -= 1; + } + + /* + * generate hours:minutes:seconds + */ + rtc->sec = hms % 60; + d = hms / 60; + rtc->min = d % 60; + d /= 60; + rtc->hour = d; + + /* + * year number + */ + if(day >= 0) + for(d = 1970; day >= *yrsize(d); d++) + day -= *yrsize(d); + else + for (d = 1970; day < 0; d--) + day += *yrsize(d-1); + rtc->year = d; + + /* + * generate month + */ + d2m = yrsize(rtc->year); + for(d = 1; day >= d2m[d]; d++) + day -= d2m[d]; + rtc->mday = day + 1; + rtc->mon = d; + + return; +} + +uchar +nvramread(int addr) +{ + uchar data; + + ilock(&nvrtlock); + outb(Paddr, addr); + data = inb(Pdata); + iunlock(&nvrtlock); + + return data; +} + +void +nvramwrite(int addr, uchar data) +{ + ilock(&nvrtlock); + outb(Paddr, addr); + outb(Pdata, data); + iunlock(&nvrtlock); +} diff --git a/os/pc/devtv.c b/os/pc/devtv.c new file mode 100644 index 00000000..5e45fa37 --- /dev/null +++ b/os/pc/devtv.c @@ -0,0 +1,1826 @@ +/* + * Driver for Hauppage TV board + * + * Control commands: + * + * init + * window %d %d %d %d + * colorkey %d %d %d %d %d %d + * capture %d %d %d %d + * capbrightness %d + * capcontrast %d + * capsaturation %d + * caphue %d + * capbw %d + * brightness %d + * contrast %d + * saturation %d + * source %d + * svideo %d + * format %d + * channel %d %d + * signal + * volume %d [ %d ] + * bass %d + * treble %d + * freeze %d + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "tv.h" + +#include <draw.h> + +enum { + MemSize= 1, + MemAddr= 0xB8000, + + CompressReg= -14, + + /* smart lock registers */ + SLReg1= -2, + SLReg2= -1, + + /* the Bt812 registers */ + Bt812Index= -5, + Bt812Data= -6, + + Bt2VideoPresent= 0x40, + Bt4ColorBars= 0x40, + Bt5YCFormat= 0x80, + Bt7TriState= 0x0C, + + /* VxP 500 registers */ + Vxp500Index= 0, + Vxp500Data= 1, + + /* video controller registers */ + MemoryWindowBaseAddrA= 0x14, + MemoryWindowBaseAddrB= 0x15, + MemoryPageReg= 0x16, + MemoryConfReg= 0x18, + ISAControl= 0x30, + I2CControl= 0x34, + InputVideoConfA= 0x38, + InputVideoConfB= 0x39, + ISASourceWindowWidthA= 0x3A, + ISASourceWindowWidthB= 0x3B, + ISASourceWindowHeightA= 0x3C, + ISASourceWindowHeightB= 0x3D, + InputHorzCropLeftA= 0x40, + InputHorzCropLeftB= 0x41, + InputHorzCropRightA= 0x44, + InputHorzCropRightB= 0x45, + InputHorzCropTopA= 0x48, + InputHorzCropTopB= 0x49, + InputHorzCropBottomA= 0x4C, + InputHorzCropBottomB= 0x4D, + InputHorzFilter= 0x50, + InputHorzScaleControlA= 0x54, + InputHorzScaleControlB= 0x55, + InputVertInterpolControl= 0x58, + InputVertScaleControlA= 0x5C, + InputVertScaleControlB= 0x5D, + InputFieldPixelBufStatus= 0x64, + VideoInputFrameBufDepthA= 0x68, + VideoInputFrameBufDepthB= 0x69, + AcquisitionControl= 0x6C, + AcquisitionAddrA= 0x70, + AcquisitionAddrB= 0x71, + AcquisitionAddrC= 0x72, + VideoBufferLayoutControl= 0x73, + CaptureControl= 0x80, + CaptureViewPortAddrA= 0x81, + CaptureViewPortAddrB= 0x82, + CaptureViewPortAddrC= 0x83, + CaptureViewPortWidthA= 0x84, + CaptureViewPortWidthB= 0x85, + CaptureViewPortHeightA= 0x86, + CaptureViewPortHeightB= 0x87, + CapturePixelBufLow= 0x88, + CapturePixelBufHigh= 0x89, + CaptureMultiBufDepthA= 0x8A, + CaptureMultiBufDepthB= 0x8B, + DisplayControl= 0x92, + VGAControl= 0x94, + OutputProcControlA= 0x96, + OutputProcControlB= 0x97, + DisplayViewPortStartAddrA= 0xA0, + DisplayViewPortStartAddrB= 0xA1, + DisplayViewPortStartAddrC= 0xA2, + DisplayViewPortWidthA= 0xA4, + DisplayViewPortWidthB= 0xA5, + DisplayViewPortHeightA= 0xA6, + DisplayViewPortHeightB= 0xA7, + DisplayViewPortOrigTopA= 0xA8, + DisplayViewPortOrigTopB= 0xA9, + DisplayViewPortOrigLeftA= 0xAA, + DisplayViewPortOrigLeftB= 0xAB, + DisplayWindowLeftA= 0xB0, + DisplayWindowLeftB= 0xB1, + DisplayWindowRightA= 0xB4, + DisplayWindowRightB= 0xB5, + DisplayWindowTopA= 0xB8, + DisplayWindowTopB= 0xB9, + DisplayWindowBottomA= 0xBC, + DisplayWindowBottomB= 0xBD, + OutputVertZoomControlA= 0xC0, + OutputVertZoomControlB= 0xC1, + OutputHorzZoomControlA= 0xC4, + OutputHorzZoomControlB= 0xC5, + BrightnessControl= 0xC8, + ContrastControl= 0xC9, + SaturationControl= 0xCA, + VideoOutIntrStatus= 0xD3, + + /* smart lock bits */ + PixelClk= 0x03, + SmartLock= 0x00, + FeatureConnector= 0x01, + Divider= 0x02, + Window= 0x08, + KeyWindow= 0x0C, + HSyncLow= 0x20, + VSyncLow= 0x40, + + ClkBit= 0x01, + DataBit= 0x02, + HoldBit= 0x04, + SelBit= 0x08, + DivControl= 0x40, + + /* i2c bus control bits */ + I2C_Clock= 0x02, + I2C_Data= 0x08, + I2C_RdClock= 0x10, + I2C_RdData= 0x20, + I2C_RdData_D= 0x40, + + /* I2C bus addresses */ + Adr5249= 0x22, /* teletext decoder */ + Adr8444= 0x48, /* 6-bit DAC (TDA 8444) */ + Adr6300= 0x80, /* sound fader (TEA 6300) */ + Adr6320= 0x80, /* sound fader (TEA 6320T) */ + AdrTuner= 0xC0, + + /* Philips audio chips */ + TEA6300= 0, + TEA6320T= 1, + + /* input formats */ + NTSC_M = 0, + NTSC_443 = 1, + External = 2, + + NTSCCropLeft= 36, /* NTSC 3.6 usec */ + NTSCCropRight= 558, /* NTSC 55.8 usec */ + + /* color control indices */ + Vxp500Brightness= 1, + Vxp500Contrast= 2, + Vxp500Saturation= 3, + Bt812Brightness= 4, + Bt812Contrast= 5, + Bt812Saturation= 6, + Bt812Hue= 7, + Bt812BW= 8, + + /* board revision numbers */ + RevisionPP= 0, + RevisionA= 1, + HighQ= 2, + + /* VGA controller registers */ + VGAMiscOut= 0x3CC, + VGAIndex= 0x3D4, + VGAData= 0x3D5, + VGAHorzTotal= 0x00, +}; + +enum { + Qdir, + Qdata, + Qctl, +}; + +static +Dirtab tvtab[]={ + ".", {Qdir, 0, QTDIR}, 0, 0555, + "tv", {Qdata, 0}, 0, 0666, + "tvctl", {Qctl, 0}, 0, 0666, +}; + +static +int ports[] = { /* board addresses */ + 0x51C, 0x53C, 0x55C, 0x57C, + 0x59C, 0x5BC, 0x5DC, 0x5FC +}; + +/* + * Default settings, settings between 0..100 + */ +static +int defaults[] = { + Vxp500Brightness, 0, + Vxp500Contrast, 54, + Vxp500Saturation, 54, + Bt812Brightness, 13, + Bt812Contrast, 57, + Bt812Saturation, 51, + Bt812Hue, 0, + Bt812BW, 0, +}; + +static int port; +static int soundchip; +static int boardrev; +static int left, right; +static int vsync, hsync; +static ulong xtalfreq; +static ushort cropleft, cropright; +static ushort cropbottom, croptop; +static Rectangle window, capwindow; + +static void setreg(int, int); +static void setbt812reg(int, int); +static void videoinit(void); +static void createwindow(Rectangle); +static void setcontrols(int, uchar); +static void setcolorkey(int, int, int, int, int, int); +static void soundinit(void); +static void setvolume(int, int); +static void setbass(int); +static void settreble(int); +static void setsoundsource(int); +static void tunerinit(void); +static void settuner(int, int); +static void setvideosource(int); +static int waitvideosignal(void); +static void freeze(int); +static void setsvideo(int); +static void setinputformat(int); +static void enablevideo(void); +static void *saveframe(int *); + +static int +min(int a, int b) +{ + return a < b ? a : b; +} + +static int +max(int a, int b) +{ + return a < b ? b : a; +} + +static int +present(int port) +{ + outb(port+Vxp500Index, 0xAA); + if (inb(port+Vxp500Index) != 0xAA) + return 0; + outb(port+Vxp500Index, 0x55); + outb(port+Vxp500Data, 0xAA); + if (inb(port+Vxp500Index) != 0x55) + return 0; + if (inb(port+Vxp500Data) != 0xAA) + return 0; + outb(port+Vxp500Data, 0x55); + if (inb(port+Vxp500Index) != 0x55) + return 0; + if (inb(port+Vxp500Data) != 0x55) + return 0; + return 1; +} + +static int +getvsync(void) +{ + int vslow, vshigh, s; + ushort timo; + + s = splhi(); + + outb(port+Vxp500Index, VideoOutIntrStatus); + + /* wait for VSync to go high then low */ + for (timo = ~0; timo; timo--) + if (inb(port+Vxp500Data) & 2) break; + for (timo = ~0; timo; timo--) + if ((inb(port+Vxp500Data) & 2) == 0) break; + + /* count how long it stays low and how long it stays high */ + for (vslow = 0, timo = ~0; timo; timo--, vslow++) + if (inb(port+Vxp500Data) & 2) break; + for (vshigh = 0, timo = ~0; timo; timo--, vshigh++) + if ((inb(port+Vxp500Data) & 2) == 0) break; + splx(s); + + return vslow < vshigh; +} + +static int +gethsync(void) +{ + int hslow, hshigh, s; + ushort timo; + + s = splhi(); + + outb(port+Vxp500Index, VideoOutIntrStatus); + + /* wait for HSync to go high then low */ + for (timo = ~0; timo; timo--) + if (inb(port+Vxp500Data) & 1) break; + for (timo = ~0; timo; timo--) + if ((inb(port+Vxp500Data) & 1) == 0) break; + + /* count how long it stays low and how long it stays high */ + for (hslow = 0, timo = ~0; timo; timo--, hslow++) + if (inb(port+Vxp500Data) & 1) break; + for (hshigh = 0, timo = ~0; timo; timo--, hshigh++) + if ((inb(port+Vxp500Data) & 1) == 0) break; + splx(s); + + return hslow < hshigh; +} + +static void +tvinit(void) +{ + int i; + + for (i = 0, port = 0; i < nelem(ports); i++) { + if (present(ports[i])) { + port = ports[i]; + break; + } + } + if (i == nelem(ports)) + return; + + /* + * the following routines are the prefered way to + * find out the sync polarities. Unfortunately, it + * doesn't always work. + */ +#ifndef VSync + vsync = getvsync(); + hsync = gethsync(); +#else + vsync = VSync; + hsync = HSync; +#endif + left = right = 80; + soundinit(); + tunerinit(); + videoinit(); +} + +static Chan* +tvattach(char *spec) +{ + if (port == 0) + error(Enonexist); + return devattach('V', spec); +} + +static Walkqid* +tvwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, tvtab, nelem(tvtab), devgen); +} + +static int +tvstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, tvtab, nelem(tvtab), devgen); +} + +static Chan* +tvopen(Chan *c, int omode) +{ + return devopen(c, omode, tvtab, nelem(tvtab), devgen); +} + +static void +tvclose(Chan *) +{ +} + +static long +tvread(Chan *c, void *a, long n, vlong offset) +{ + static void *frame; + static int size; + + USED(offset); + + switch((ulong)c->qid.path){ + case Qdir: + return devdirread(c, a, n, tvtab, nelem(tvtab), devgen); + case Qdata: + if (eqrect(capwindow, Rect(0, 0, 0, 0))) + error(Ebadarg); + if (offset == 0) + frame = saveframe(&size); + if (frame) { + if (n > size - offset) + n = size - offset; + memmove(a, (char *)frame + offset, n); + } else + error(Enovmem); + break; + default: + n=0; + break; + } + return n; +} + +static long +tvwrite(Chan *c, void *vp, long n, vlong offset) +{ + char buf[128], *field[10], *a; + int i, nf, source; + static Rectangle win; + static int hsize, size = 0; + static void *frame; + + USED(offset); + + a = vp; + switch((ulong)c->qid.path){ + case Qctl: + if (n > sizeof(buf)-1) + n = sizeof(buf)-1; + memmove(buf, a, n); + buf[n] = '\0'; + + nf = getfields(buf, field, nelem(field), 1, " \t"); + if (nf < 1) error(Ebadarg); + + if (strcmp(field[0], "init") == 0) { + window = Rect(0, 0, 0, 0); + capwindow = Rect(0, 0, 0, 0); + source = 0; /* video 0 input */ + setvideosource(source); + left = right = 80; + setsoundsource(source); + for (i = 0; i < nelem(defaults); i += 2) + setcontrols(defaults[i], defaults[i+1]); + } else if (strcmp(field[0], "colorkey") == 0) { + if (nf < 7) error(Ebadarg); + setcolorkey(strtoul(field[1], 0, 0), strtoul(field[2], 0, 0), + strtoul(field[3], 0, 0), strtoul(field[4], 0, 0), + strtoul(field[5], 0, 0), strtoul(field[6], 0, 0)); + } else if (strcmp(field[0], "window") == 0) { + if (nf < 5) error(Ebadarg); + createwindow(Rect(strtoul(field[1], 0, 0), strtoul(field[2], 0, 0), + strtoul(field[3], 0, 0), strtoul(field[4], 0, 0))); + setvolume(left, right); + } else if (strcmp(field[0], "capture") == 0) { + if (nf < 5) error(Ebadarg); + capwindow = Rect(strtoul(field[1], 0, 0), strtoul(field[2], 0, 0), + strtoul(field[3], 0, 0), strtoul(field[4], 0, 0)); + } else if (strcmp(field[0], "freeze") == 0) { + if (nf < 2) error(Ebadarg); + freeze(strtoul(field[1], 0, 0)); + } else if (strcmp(field[0], "capbrightness") == 0) { + if (nf < 2) error(Ebadarg); + setcontrols(Bt812Brightness, strtoul(field[1], 0, 0)); + } else if (strcmp(field[0], "capcontrast") == 0) { + if (nf < 2) error(Ebadarg); + setcontrols(Bt812Contrast, strtoul(field[1], 0, 0)); + } else if (strcmp(field[0], "capsaturation") == 0) { + if (nf < 2) error(Ebadarg); + setcontrols(Bt812Saturation, strtoul(field[1], 0, 0)); + } else if (strcmp(field[0], "caphue") == 0) { + if (nf < 2) error(Ebadarg); + setcontrols(Bt812Hue, strtoul(field[1], 0, 0)); + } else if (strcmp(field[0], "capbw") == 0) { + if (nf < 2) error(Ebadarg); + setcontrols(Bt812BW, strtoul(field[1], 0, 0)); + } else if (strcmp(field[0], "brightness") == 0) { + if (nf < 2) error(Ebadarg); + setcontrols(Vxp500Brightness, strtoul(field[1], 0, 0)); + } else if (strcmp(field[0], "contrast") == 0) { + if (nf < 2) error(Ebadarg); + setcontrols(Vxp500Contrast, strtoul(field[1], 0, 0)); + } else if (strcmp(field[0], "saturation") == 0) { + if (nf < 2) error(Ebadarg); + setcontrols(Vxp500Saturation, strtoul(field[1], 0, 0)); + } else if (strcmp(field[0], "source") == 0) { + if (nf < 2) error(Ebadarg); + source = strtoul(field[1], 0, 0); + setvideosource(source); + setsoundsource(source); + } else if (strcmp(field[0], "svideo") == 0) { + if (nf < 2) error(Ebadarg); + setsvideo(strtoul(field[1], 0, 0)); + } else if (strcmp(field[0], "format") == 0) { + if (nf < 2) error(Ebadarg); + setinputformat(strtoul(field[1], 0, 0)); + } else if (strcmp(field[0], "channel") == 0) { + if (nf < 3) error(Ebadarg); + setvolume(0, 0); + settuner(strtoul(field[1], 0, 0), strtoul(field[2], 0, 0)); + tsleep(&up->sleep, return0, 0, 300); + setvolume(left, right); + } else if (strcmp(field[0], "signal") == 0) { + if (!waitvideosignal()) + error(Etimedout); + } else if (strcmp(field[0], "volume") == 0) { + if (nf < 2) error(Ebadarg); + left = strtoul(field[1], 0, 0); + if (nf < 3) + right = left; + else + right = strtoul(field[2], 0, 0); + setvolume(left, right); + } else if (strcmp(field[0], "bass") == 0) { + if (nf < 2) error(Ebadarg); + setbass(strtoul(field[1], 0, 0)); + } else if (strcmp(field[0], "treble") == 0) { + if (nf < 2) error(Ebadarg); + settreble(strtoul(field[1], 0, 0)); + } else + error(Ebadctl); + break; + default: + error(Ebadusefd); + } + return n; +} + + +Dev tvdevtab = { + 'V', + "tv", + + devreset, + tvinit, + devshutdown, + tvattach, + tvwalk, + tvstat, + tvopen, + devcreate, + tvclose, + tvread, + devbread, + tvwrite, + devbwrite, + devremove, + devwstat, +}; + +static void +setreg(int index, int data) +{ + outb(port+Vxp500Index, index); + outb(port+Vxp500Data, data); +} + +static unsigned int +getreg(int index) +{ + outb(port+Vxp500Index, index); + return inb(port+Vxp500Data); +} + +/* + * I2C routines + */ +static void +delayi2c(void) +{ + int i, val; + + /* delay for 4.5 usec to guarantee clock time */ + for (i = 0; i < 75; i++) { /* was 50 */ + val = inb(port+Vxp500Data); + USED(val); + } +} + +static int +waitSDA(void) +{ + ushort timo; + + /* wait for i2c clock to float high */ + for (timo = ~0; timo; timo--) + if (inb(port+Vxp500Data) & I2C_RdData) + break; + if (!timo) print("devtv: waitSDA fell out of loop\n"); + return !timo; +} + +static int +waitSCL(void) +{ + ushort timo; + + /* wait for i2c clock to float high */ + for (timo = ~0; timo; timo--) + if (inb(port+Vxp500Data) & I2C_RdClock) + break; + delayi2c(); + if (!timo) print("devtv: waitSCL fell out of loop\n"); + return !timo; +} + +static int +seti2cdata(int data) +{ + int b, reg, val; + int error; + + error = 0; + reg = inb(port+Vxp500Data); + for (b = 0x80; b; b >>= 1) { + if (data & b) + reg |= I2C_Data; + else + reg &= ~I2C_Data; + outb(port+Vxp500Data, reg); + reg |= I2C_Clock; + outb(port+Vxp500Data, reg); + error |= waitSCL(); + reg &= ~I2C_Clock; + outb(port+Vxp500Data, reg); + delayi2c(); + } + reg |= I2C_Data; + outb(port+Vxp500Data, reg); + reg |= I2C_Clock; + outb(port+Vxp500Data, reg); + error |= waitSCL(); + val = inb(port+Vxp500Data); + USED(val); + reg &= ~I2C_Clock; + outb(port+Vxp500Data, reg); + delayi2c(); + return error; +} + +static int +seti2creg(int id, int index, int data) +{ + int reg, error; + + error = 0; + /* set i2c control register to enable i2c clock and data lines */ + setreg(I2CControl, I2C_Data|I2C_Clock); + error |= waitSDA(); + error |= waitSCL(); + outb(port+Vxp500Data, I2C_Clock); + delayi2c(); + outb(port+Vxp500Data, 0); + delayi2c(); + + error |= seti2cdata(id); + error |= seti2cdata(index); + error |= seti2cdata(data); + + reg = inb(port+Vxp500Data); + reg &= ~I2C_Data; + outb(port+Vxp500Data, reg); + reg |= I2C_Clock; + outb(port+Vxp500Data, reg); + error |= waitSCL(); + reg |= I2C_Data; + outb(port+Vxp500Data, reg); + error |= waitSDA(); + return error; +} + +static int +seti2cregs(int id, int index, int n, uchar *data) +{ + int reg, error; + + error = 0; + /* set i2c control register to enable i2c clock and data lines */ + setreg(I2CControl, I2C_Data|I2C_Clock); + error |= waitSDA(); + error |= waitSCL(); + outb(port+Vxp500Data, I2C_Clock); + delayi2c(); + outb(port+Vxp500Data, 0); + delayi2c(); + + /* send data */ + error |= seti2cdata(id); + error |= seti2cdata(index); + while (n--) + error |= seti2cdata(*data++); + + /* send stop */ + reg = inb(port+Vxp500Data); + reg &= ~I2C_Data; + outb(port+Vxp500Data, reg); + reg |= I2C_Clock; + outb(port+Vxp500Data, reg); + error |= waitSCL(); + reg |= I2C_Data; + outb(port+Vxp500Data, reg); + error |= waitSDA(); + return error; +} + +/* + * Audio routines + */ +static void +setvolume(int left, int right) +{ + int vol, loudness = 0; + + if (soundchip == TEA6300) { + seti2creg(Adr6300, 0, (63L * left) / 100); + seti2creg(Adr6300, 1, (63L * right) / 100); + vol = (15L * max(left, right)) / 100; + seti2creg(Adr6300, 4, 0x30 | vol); + } else { + vol = (63L * max(left, right)) / 100; + seti2creg(Adr6320, 0, vol | (loudness << 6)); + seti2creg(Adr6320, 1, (63L * right) / 100); + seti2creg(Adr6320, 2, (63L * left) / 100); + } +} + +static void +setbass(int bass) +{ + if (soundchip == TEA6300) + seti2creg(Adr6300, 2, (15L * bass) / 100); + else + seti2creg(Adr6320, 5, max((31L * bass) / 100, 4)); +} + +static void +settreble(int treble) +{ + if (soundchip == TEA6300) + seti2creg(Adr6300, 3, (15L * treble) / 100); + else + seti2creg(Adr6320, 6, max((31L * treble) / 100, 7)); + +} + +static void +setsoundsource(int source) +{ + if (soundchip == TEA6300) + seti2creg(Adr6300, 5, 1 << source); + else + seti2creg(Adr6320, 7, source); + setbass(50); + settreble(50); + setvolume(left, right); +} + +static void +soundinit(void) +{ + if (seti2creg(Adr6320, 7, 0) && seti2creg(Adr6300, 4, 0)) + print("devtv: Audio init failed\n"); + + soundchip = AudioChip; + setvolume(0, 0); +} + +/* + * Tuner routines + */ +static +long hrcfreq[] = { /* HRC CATV frequencies */ + 0, 7200, 5400, 6000, 6600, 7800, 8400, 17400, + 18000, 18600, 19200, 19800, 20400, 21000, 12000, 12600, + 13200, 13800, 14400, 15000, 15600, 16200, 16800, 21600, + 22200, 22800, 23400, 24000, 24600, 25200, 25800, 26400, + 27000, 27600, 28200, 28800, 29400, 30000, 30600, 31200, + 31800, 32400, 33000, 33600, 34200, 34800, 35400, 36000, + 36600, 37200, 37800, 38400, 39000, 39600, 40200, 40800, + 41400, 42000, 42600, 43200, 43800, 44400, 45000, 45600, + 46200, 46800, 47400, 48000, 48600, 49200, 49800, 50400, + 51000, 51600, 52200, 52800, 53400, 54000, 54600, 55200, + 55800, 56400, 57000, 57600, 58200, 58800, 59400, 60000, + 60600, 61200, 61800, 62400, 63000, 63600, 64200, 9000, + 9600, 10200, 10800, 11400, 64800, 65400, 66000, 66600, + 67200, 67800, 68400, 69000, 69600, 70200, 70800, 71400, + 72000, 72600, 73200, 73800, 74400, 75000, 75600, 76200, + 76800, 77400, 78000, 78600, 79200, 79800, +}; + +static void +settuner(int channel, int finetune) +{ + static long lastfreq; + uchar data[3]; + long freq; + int cw2, n, sa; + + if (channel < 0 || channel > nelem(hrcfreq)) + error(Ebadarg); + + freq = hrcfreq[channel]; + + /* these settings are all for (FS936E) USA Tuners */ + if (freq < 16025) /* low band */ + cw2 = 0xA4; + else if (freq < 45425) /* mid band */ + cw2 = 0x94; + else + cw2 = 0x34; + + /* + * Channels are stored are 1/100 MHz resolutions, but + * the tuner wants stuff in MHZ, so divide by 100, we + * then have to shift by 4 to get the prog. div. value + */ + n = ((freq + 4575L) * 16) / 100L + finetune; + + if (freq > lastfreq) { + sa = (n >> 8) & 0xFF; + data[0] = n & 0xFF; + data[1] = 0x8E; + data[2] = cw2; + } else { + sa = 0x8E; + data[0] = cw2; + data[1] = (n >> 8) & 0xFF; + data[2] = n & 0xFF; + } + lastfreq = freq; + seti2cregs(AdrTuner, sa, 3, data); +} + +static void +tunerinit(void) +{ + if (seti2creg(AdrTuner, 0, 0)) + print("devtv: Tuner init failed\n"); +} + +/* + * Video routines + */ +static int slreg1 = 0; +static int slreg2 = 0; +static int vcogain = 0; +static int phdetgain = 2; +static int plln1 = 2; +static int pllp2 = 1; + +static void +waitforretrace(void) +{ + ushort timo; + + for (timo = ~0; (getreg(VideoOutIntrStatus) & 2) == 0 && timo; timo--) + /* wait for VSync inactive */; + for (timo = ~0; (getreg(VideoOutIntrStatus) & 2) && timo; timo--) + /* wait for VSync active */; +} + +static void +updateshadowregs(void) +{ + int val; + + setreg(InputVideoConfA, getreg(InputVideoConfA) | 0x40); + val = getreg(OutputProcControlB); + setreg(OutputProcControlB, val & 0x7F); + setreg(OutputProcControlB, val | 0x80); +} + +static void +setvgareg(int data) +{ + /* set HSync & VSync first, to make sure VSync works properly */ + setreg(VGAControl, (getreg(VGAControl) & ~0x06) | (data & 0x06)); + + /* wait for VSync and set the whole register */ + waitforretrace(); + setreg(VGAControl, data); +} + +static void +setbt812reg(int index, int data) +{ + outb(port+Bt812Index, index); + outb(port+Bt812Data, data); +} + +static int +getbt812reg(int index) +{ + outb(port+Bt812Index, index); + return inb(port+Bt812Data); +} + +static void +setbt812regpair(int index, ushort data) +{ + outb(port+Bt812Index, index); + outb(port+Bt812Data, data); + outb(port+Bt812Data, data >> 8); +} + +static void +setvideosource(int source) +{ + int s; + + source &= 7; + s = source & 3; + setbt812reg(0, ((s << 2) | s) << 3); + s = (source & 4) << 4; + setbt812reg(4, (getbt812reg(4) & ~Bt4ColorBars) | s); +} + +static void +setsvideo(int enable) +{ + if (enable) + setbt812reg(5, getbt812reg(5) | Bt5YCFormat); + else + setbt812reg(5, getbt812reg(5) & ~Bt5YCFormat); +} + +static int +waitvideosignal(void) +{ + ushort timo; + + for (timo = ~0; timo; timo--) + if (getbt812reg(2) & Bt2VideoPresent) + return 1; + return 0; +} + +/* + * ICS1572 Programming Configuration + * + * R = 1 + * M = x + * A = x + * N1 = 4 + * N2 = internal divide ratio + */ +static +uchar ICSbits[7] = { + 0x01, /* bits 8 - 1 00000001 */ + 0x05, /* bits 16 - 9 00000101 */ + 0xFF, /* bits 24 - 17 11111111 */ + 0x8C, /* bits 32 - 25 10001100 */ + 0xBF, /* bits 40 - 33 10111111 */ + 0x00, /* bits 48 - 41 00000000 */ + 0x00, /* bits 56 - 49 00000000 */ +}; + +static void +sendbit(int val, int hold) +{ + slreg2 &= ~(HoldBit|DataBit|ClkBit); + if (val) slreg2 |= DataBit; + if (hold) slreg2 |= HoldBit; + outb(port+SLReg2, slreg2); + outb(port+SLReg2, slreg2|ClkBit); + outb(port+SLReg2, slreg2); +} + +static void +load1572(int select) +{ + int reg; + uchar mask; + + if (select) + slreg2 |= SelBit; + else + slreg2 &= ~SelBit; + outb(port+SLReg2, slreg2); + + for (reg = 0; reg < sizeof(ICSbits); reg++) { + for (mask = 1; mask != 0; mask <<= 1) { + if (reg == sizeof(ICSbits)-1 && mask == 0x80) { + sendbit(ICSbits[reg] & mask, 1); + } else + sendbit(ICSbits[reg] & mask, 0); + } + } +} + +static void +smartlockdiv(int count, int vcogain, int phdetgain, int n1, int p2) +{ + int extdiv, intdiv; + int nslreg2, external; + + nslreg2 = slreg2; + extdiv = ((count - 1) / 512) + 1; + intdiv = (count / extdiv); + nslreg2 &= ~0xC0; + switch (extdiv) { + case 1: external = 0; break; + case 2: external = 1; break; + case 3: external = 1; nslreg2 |= 0x40; break; + case 4: external = 1; nslreg2 |= 0x80; break; + default: return; + } + if ((slreg1 & PixelClk) == 0) { + slreg2 = nslreg2; + outb(port+SLReg2, slreg2); + } + + /* set PLL divider */ + ICSbits[0] &= ~0x07; + ICSbits[0] |= n1 & 0x07; + ICSbits[3] &= ~0xB7; + ICSbits[3] |= vcogain & 0x07; + ICSbits[3] |= (phdetgain & 0x03) << 4; + ICSbits[3] |= p2 << 7; + if (external) + ICSbits[1] |= 0x04; /* set EXTFBKEN */ + else + ICSbits[1] &= ~0x04; /* clear EXTFBKEN */ + intdiv--; + ICSbits[2] = intdiv; /* set N2 */ + ICSbits[3] &= ~ 0x08; + ICSbits[3] |= (intdiv >> 5) & 0x08; + load1572(1); +} + +static void +disablecolorkey(void) +{ + setreg(DisplayControl, getreg(DisplayControl) & 0xFE); + updateshadowregs(); +} + +static +uchar colorkeylimit[6] = { + 15, /* upper limit green */ + 255, /* lower limit green */ + 63, /* upper limit red */ + 63, /* upper limit blue */ + 15, /* lower limit red */ + 15, /* lower limit blue */ +}; + +static void +enablecolorkey(int enable) +{ + int i; + + if (enable) { + for (i = 0; i < 6; i++) + seti2creg(Adr8444, 0xF0 | i, colorkeylimit[i]); + slreg1 &= ~0x1C; + if (colorkeylimit[4] == 255) + slreg1 |= 0x04; /* disable red lower limit */ + if (colorkeylimit[1] == 255) + slreg1 |= 0x08; /* disable green lower limit */ + if (colorkeylimit[5] == 255) + slreg1 |= 0x10; /* disable blue lower limit */ + } else { + for (i = 0; i < 6; i++) + seti2creg(Adr8444, 0xF0 | i, 63); + slreg1 |= 0x1C; + } + outb(port+SLReg1, slreg1); + disablecolorkey(); +} + +static void +setcolorkey(int rl, int rh, int gl, int gh, int bl, int bh) +{ + colorkeylimit[0] = gh; + colorkeylimit[1] = gl; + colorkeylimit[2] = rh; + colorkeylimit[3] = bh; + colorkeylimit[4] = rl; + colorkeylimit[5] = bl; + enablecolorkey(1); +} + +static void +waitvideoframe(void) +{ + ushort timo; + int val; + + /* clear status bits and wait for start of an even field */ + val = getreg(InputFieldPixelBufStatus); + USED(val); + for (timo = ~0; timo; timo--) + if ((getreg(InputFieldPixelBufStatus) & 2) == 0) + break; + if (!timo) print("devtv: Wait for video frame failed\n"); +} + +static void +freeze(int enable) +{ + ushort timo; + int reg; + + if (enable) { + waitvideoframe(); + waitvideoframe(); + + setreg(InputVideoConfB, getreg(InputVideoConfB) | 0x08); + updateshadowregs(); + + for (timo = ~0; timo; timo--) + if (getreg(InputVideoConfB) & 0x80) break; + waitvideoframe(); + + reg = getreg(OutputProcControlB); + if ((reg & 0x20) == 0) { + setreg(ISAControl, 0x80); + setreg(OutputProcControlB, getreg(OutputProcControlB) | 0x20); + setreg(ISAControl, 0x42); + + reg = getreg(OutputProcControlB); + setreg(OutputProcControlB, reg & 0x7F); + setreg(OutputProcControlB, reg | 0x80); + } + } else { + setreg(InputVideoConfB, getreg(InputVideoConfB) & ~0x08); + updateshadowregs(); + + for (timo = ~0; timo; timo--) + if (getreg(InputVideoConfB) & 0x40) break; + waitvideoframe(); + reg = getreg(InputFieldPixelBufStatus); + USED(reg); + } +} + +static void +enablevideo(void) +{ + setreg(DisplayControl, 0x04); + updateshadowregs(); +} + +static void +disablevideo(void) +{ + setreg(DisplayControl, 0x18); + updateshadowregs(); +} + +static +uchar vxp500init[] = { /* video register initialization in (index,data) hex pairs */ + 0x30, 0x82, 0x39, 0x40, 0x58, 0x0C, 0x73, 0x02, 0x80, 0x00, 0x25, 0x0F, + 0x26, 0x0F, 0x38, 0x46, 0x30, 0x03, 0x12, 0x3B, 0x97, 0x20, 0x13, 0x00, + 0x14, 0x34, 0x15, 0x04, 0x16, 0x00, 0x17, 0x53, 0x18, 0x04, 0x19, 0x62, + 0x1C, 0x00, 0x1D, 0x00, 0x34, 0x3A, 0x38, 0x06, 0x3A, 0x00, 0x3B, 0x00, + 0x3C, 0x00, 0x3D, 0x00, 0x40, 0x40, 0x41, 0x40, 0x44, 0xFF, 0x45, 0xFF, + 0x48, 0x40, 0x49, 0x40, 0x4C, 0xFF, 0x4D, 0xFF, 0x50, 0xF0, 0x54, 0x30, + 0x55, 0x00, 0x5C, 0x04, 0x5D, 0x00, 0x60, 0x00, 0x68, 0x00, 0x69, 0x00, + 0x6C, 0x06, 0x6D, 0x00, 0x70, 0x00, 0x71, 0x00, 0x72, 0x00, 0x78, 0x01, + 0x79, 0x0C, 0x80, 0x10, 0x81, 0x00, 0x82, 0x00, 0x83, 0x00, 0x84, 0x00, + 0x85, 0x00, 0x86, 0x00, 0x87, 0x00, 0x88, 0x04, 0x89, 0x10, 0x8A, 0x00, + 0x8B, 0x00, 0x90, 0x05, 0x91, 0x0C, 0x92, 0x18, 0x93, 0x00, 0x96, 0x18, + 0x9A, 0x30, 0x9C, 0x2D, 0x9D, 0x00, 0xA0, 0x00, 0xA1, 0x00, 0xA2, 0x00, + 0xA4, 0x50, 0xA5, 0x50, 0xA6, 0xF0, 0xA7, 0xF0, 0xA8, 0x19, 0xA9, 0x18, + 0xAA, 0x64, 0xAB, 0x64, 0xB0, 0x64, 0xB1, 0x64, 0xB4, 0xA4, 0xB5, 0xA5, + 0xB8, 0x19, 0xB9, 0x18, 0xBC, 0x09, 0xBD, 0x09, 0xC0, 0x00, 0xC1, 0x02, + 0xC4, 0x00, 0xC5, 0x00, 0xC8, 0x00, 0xC9, 0x08, 0xCA, 0x08, 0xCE, 0x00, + 0xCF, 0x00, 0xD0, 0x00, 0xD1, 0x00, 0xD2, 0x00, 0xD8, 0x00, 0xD9, 0x00, + 0xDA, 0x00, 0xDB, 0x00, 0xDC, 0x00, 0xDD, 0x00, 0x38, 0x46, 0x97, 0xA0, + 0x97, 0x20, 0x97, 0xA0, +}; + +static +uchar bt812init[] = { /* bt812 initializations */ + 0xFF, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0xC0, + 0x04, 0x08, 0x05, 0x00, 0x06, 0x40, 0x07, 0x00, 0x08, 0x10, + 0x09, 0x90, 0x0A, 0x80, 0x0B, 0x00, 0x0C, 0x0C, 0x0D, 0x03, + 0x0E, 0x66, 0x0F, 0x00, 0x10, 0x80, 0x11, 0x02, 0x12, 0x16, + 0x13, 0x00, 0x14, 0xE5, 0x15, 0x01, 0x16, 0xAB, 0x17, 0xAA, + 0x18, 0x12, 0x19, 0x51, 0x1A, 0x46, 0x1B, 0x00, 0x1C, 0x00, + 0x1D, 0x37, +}; + +static ushort actpixs = 720; +static ulong Hdesired = 13500000L; + +/* NTSC-M NTSC-443 EXTERNAL */ +static ushort horzfreq[] = { 15734, 15625, 0 }; +static ushort Vdelay[] = { 22, 25, 25 }; +static ushort s2b[] = { 90, 90, 0 }; +static ushort actlines[] = { 485, 485, 575 }; +static ulong subcarfreq[] = { 3579545, 4433619, 4433619 }; + +static +unsigned int framewidth[5][4] = { + 1024, 512, 512, 512, /* mode 0 - single, double, single, quad */ + 1536, 768, 768, 384, /* mode 1 - single, double, single, quad */ + 2048, 1024, 1024, 512, /* mode 2 - single, double, single, quad */ + 1024, 512, 512, 512, /* mode 3 - single, double, single, quad */ + 1536, 768, 768, 384 /* mode 4 - single, double, single, quad */ +}; + +static +unsigned int frameheight[5][4] = { + 512, 512, 1024, 512, /* mode 0 - single, double, single, quad */ + 512, 512, 1024, 512, /* mode 1 - single, double, single, quad */ + 512, 512, 1024, 512, /* mode 2 - single, double, single, quad */ + 512, 512, 1024, 512, /* mode 3 - single, double, single, quad */ + 512, 512, 1024, 256 /* mode 4 - single, double, single, quad */ +}; + +static +uchar horzfilter[] = { 3, 3, 2, 2, 1, 1, 0, 0 }; + +static +uchar interleave[] = { 2, 3, 4, 2, 3 }; + +#define ADJUST(n) (((n) * hrsmult + hrsdiv - 1) / hrsdiv) + +static int q = 100; +static int ilv = 2; +static int hrsmult = 1; +static int hrsdiv = 2; + +static ushort panmask[] = { 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE }; + + +static void +cropwindow(int left, int right, int top, int bottom) +{ + top &= 0x3FE; + bottom &= 0x3FE; + setreg(InputHorzCropLeftA, left); + setreg(InputHorzCropLeftB, left >> 8); + setreg(InputHorzCropRightA, right); + setreg(InputHorzCropRightB, right >> 8); + setreg(InputHorzCropTopA, top); + setreg(InputHorzCropTopB, top >> 8); + setreg(InputHorzCropBottomA, bottom); + setreg(InputHorzCropBottomB, bottom >> 8); +} + +static void +setinputformat(int format) +{ + ushort hclock, hclockdesired; + ulong subcarrier; + int cr7; + + cr7 = getbt812reg(7) & ~Bt7TriState; + if (format == External) + cr7 |= Bt7TriState; + setbt812reg(7, cr7); + setbt812reg(5, getbt812reg(5) & 2); + + hclock = (xtalfreq >> 1) / horzfreq[format]; + setbt812regpair(0x0C, hclock); + setbt812regpair(0x0E, + (ushort)(s2b[format] * (Hdesired / 10) / 1000000L) | 1); + setbt812regpair(0x10, actpixs); + setbt812regpair(0x12, Vdelay[format]); + setbt812regpair(0x14, actlines[format]); + + subcarrier = (ulong) + ((((long long)subcarfreq[format] * 0x1000000) / xtalfreq + 1) / 2); + setbt812regpair(0x16, (int)(subcarrier & 0xFFFF)); /* subcarrier */ + setbt812reg(0x18, (int)(subcarrier >> 16)); + + setbt812reg(0x19, (uchar)(((xtalfreq / 200) * 675) / 1000000L + 8)); + setbt812reg(0x1A, (uchar)((xtalfreq * 65) / 20000000L - 10)); + hclockdesired = (ushort) (Hdesired / horzfreq[format]); + setbt812regpair(0x1B, + (ushort)(((hclock - hclockdesired) * 65536L) / hclockdesired)); +} + +static ushort +vgadivider(void) +{ + ushort horztotal; + + outb(VGAIndex, VGAHorzTotal); + horztotal = (inb(VGAData) << 3) + 40; + if (horztotal > ScreenWidth && horztotal < ((ScreenWidth * 3 ) / 2)) + return horztotal; + else + return (ScreenWidth * 5) / 4; +} + +static void +videoinit(void) +{ + int i, reg, width, tuner; + + /* early PLL smart lock initialization */ + if (ScreenWidth == 640) { + slreg1 = Window|HSyncLow|VSyncLow; + slreg2 = 0x0D; + } else { + slreg1 = Window; + slreg2 = 0x0C; + } + outb(port+CompressReg, 2); + outb(port+SLReg1, slreg1); + outb(port+SLReg2, slreg2); + smartlockdiv((vgadivider() * hrsmult)/hrsdiv, vcogain, phdetgain, 2, 1); + + /* program the VxP-500 chip (disables video) */ + waitforretrace(); + for (i = 0; i < sizeof(vxp500init); i += 2) + setreg(vxp500init[i], vxp500init[i+1]); + + /* set memory base for frame capture */ + setreg(MemoryWindowBaseAddrA, MemAddr >> 14); + setreg(MemoryWindowBaseAddrB, ((MemAddr >> 22) & 3) | (MemSize << 2)); + setreg(MemoryPageReg, 0); + + /* generic 422 decoder, mode 3 and 4 */ + setreg(MemoryConfReg, ilv+1); + + setreg(AcquisitionAddrA, 0); + setreg(AcquisitionAddrB, 0); + setreg(AcquisitionAddrC, 0); + + /* program VxP-500 for correct sync polarity */ + reg = ScreenWidth > 1023 ? 0x01 : 0x00; + reg |= (vsync << 1) | (hsync << 2); + setvgareg(reg); + setreg(VGAControl, reg); + + setreg(VideoBufferLayoutControl, 0); /* for ilv = 2 */ + + /* set sync polarities to get proper blanking */ + if (vsync) + slreg1 |= VSyncLow; + if (!hsync) { + slreg1 ^= HSyncLow; + setreg(VGAControl, reg | 4); + } + outb(port+SLReg1, slreg1); + + if ((slreg1 & PixelClk) == 0) { /* smart lock active */ + enablecolorkey(1); + setreg(VGAControl, getreg(VGAControl) & 6); + } else + enablecolorkey(0); + + /* color key initializations */ + if ((slreg1 & PixelClk) == 0) + setreg(VGAControl, getreg(VGAControl) & 7); + + /* initialize Bt812 */ + for (i = 0; i < sizeof(bt812init); i += 2) + setbt812reg(bt812init[i], bt812init[i+1]); + + /* figure out clock source (Xtal or Oscillator) and revision */ + setbt812reg(6, 0x40); + reg = getreg(InputFieldPixelBufStatus) & 3; + if ((getreg(InputFieldPixelBufStatus) & 3) == reg) { + /* crystal - could be revision PP if R34 is installed */ + setbt812reg(6, 0x00); + reg = inb(port+SLReg1); + if (reg & 0x20) { + if ((reg & 0xE0) == 0xE0) + boardrev = HighQ; + else + boardrev = RevisionA; + } else + boardrev = RevisionPP; + } else /* revision A or newer with 27 MHz oscillator */ + boardrev = RevisionA; + + /* figure out xtal frequency */ + if (xtalfreq == 0) { + if (boardrev == RevisionPP) { + tuner = (inb(port+SLReg1) >> 6) & 3; + if (tuner == 0) /* NTSC */ + xtalfreq = 24545400L; + else + xtalfreq = 29500000L; + } else if (boardrev == HighQ) + xtalfreq = 29500000L; + else + xtalfreq = 27000000L; + } + +// print("Hauppage revision %d (xtalfreq %ld)\n", boardrev, xtalfreq); + + /* on RevPP boards set early sync, on rev A and newer clear it */ + if (boardrev == RevisionPP) + setreg(InputVideoConfA, getreg(InputVideoConfA) | 4); + else + setreg(InputVideoConfA, getreg(InputVideoConfA) & ~4); + + switch (xtalfreq) { + case 24545400L: + actpixs = 640; + break; + case 29500000L: + actpixs = 768; + break; + default: + actpixs = 720; + break; + } + + /* set crop window (these values are for NTSC!) */ + if (boardrev == RevisionPP) { + Hdesired = xtalfreq / 2; + cropleft = (NTSCCropLeft * ((Hdesired / 10))) / 1000000L; + cropright = (NTSCCropRight * ((Hdesired / 10))) / 1000000L; + } else { + cropleft = actpixs / 100; + cropright = actpixs - cropleft; + } + width = ((cropright - cropleft + ilv) / ilv) * ilv; + cropright = cropleft + width + 1; + croptop = 26; + cropbottom = 505; + cropwindow(cropleft, cropright, croptop, cropbottom); + + /* set input format */ + setinputformat(NTSC_M); + setsvideo(0); +} + +static void +panwindow(Point p) +{ + int memmode, ilv, frw; + ulong pos; + + memmode = getreg(MemoryConfReg) & 7; + ilv = interleave[memmode]; + frw = framewidth[memmode][getreg(VideoBufferLayoutControl) & 3]; + + pos = (p.y * (frw/ilv)) + ((p.x/ilv) & panmask[memmode]); + setreg(DisplayViewPortStartAddrA, (uchar) pos); + setreg(DisplayViewPortStartAddrB, (uchar) (pos >> 8)); + setreg(DisplayViewPortStartAddrC, (uchar) (pos >> 16) & 0x03); + updateshadowregs(); +} + +static int +testqfactor(void) +{ + ulong timo; + int reg; + + waitvideoframe(); + for (reg = 0, timo = ~0; timo; timo--) { + reg |= getreg(InputFieldPixelBufStatus); + if (reg & 0xE) break; + } + if (reg & 0xC) return 0; + + waitvideoframe(); + for (reg = 0, timo = ~0; timo; timo--) { + reg |= getreg(InputFieldPixelBufStatus); + if (reg & 0xE) break; + } + return (reg & 0xC) == 0; +} + +static void +newwindow(Rectangle r) +{ + unsigned ww, wh, dx, dy, xs, ys, xe, ye; + unsigned scalex, scaley; + int frwidth, frheight, vidwidth, vidheight; + int memmode, layout; + int width, height; + int filter, changed, val; + + changed = r.min.x != window.min.x || r.min.y != window.min.y || + r.max.x != window.max.x || r.max.y != window.max.y; + if (changed) window = r; + + if (r.min.x < 0) r.min.x = 0; + if (r.max.x > ScreenWidth) r.max.x = ScreenWidth; + if (r.min.y < 0) r.min.y = 0; + if (r.max.y > ScreenHeight) r.max.y = ScreenHeight; + + if ((dx = r.max.x - r.min.x) <= 0) dx = 1; + if ((dy = r.max.y - r.min.y) <= 0) dy = 1; + + wh = dy; + ww = dx = ADJUST(dx); + r.min.x = (r.min.x * hrsmult) / hrsdiv; + + memmode = getreg(MemoryConfReg) & 7; + layout = getreg(VideoBufferLayoutControl) & 3; + vidwidth = cropright - cropleft + 1; + vidheight = (cropbottom & 0x3FE) - (croptop & 0x3FE) + 1; + frwidth = min(framewidth[memmode][layout], vidwidth); + frheight = min(frameheight[memmode][layout], vidheight); + + /* round up scale width to nearest multiple of interleave factor */ + dx = ((ulong)dx * q) / 100; + dx = ilv * ((dx + ilv - 1) / ilv); + + scalex = (((ulong)dx * 1024L) + vidwidth - 2) / (vidwidth - 1); + if (dy > frheight) dy = frheight - 1; + scaley = (((ulong)dy * 1024L) + vidheight - 2) / (vidheight - 1); + + setreg(InputHorzScaleControlA, (scalex << 6) & 0xC0); + setreg(InputHorzScaleControlB, (scalex >> 2) & 0xFF); + setreg(InputVertScaleControlA, (scaley << 6) & 0xC0); + setreg(InputVertScaleControlB, (scaley >> 2) & 0xFF); + + /* turn on horizontal filtering if we are scaling down */ + setreg(InputHorzFilter, horzfilter[((scalex - 1) >> 7) & 7]); + + /* set vertical interpolation */ + filter = scaley > 512 ? (ScreenWidth == 640 ? 0x44 : 0xC5) : 0x46; /* magic */ + if ((getreg(InputVertInterpolControl) & 0x1F) != (filter & 0x1F)) { + setreg(ISAControl, 0x80); + setreg(InputVertInterpolControl, filter & 0x1F); + setreg(ISAControl, 0x42); + } + setreg(AcquisitionControl, ((filter >> 6) ^ 3) | 0x04); + + /* set viewport position and size */ + width = ((ulong)ww * q) / 100; + if (width >= frwidth - ilv) + width = frwidth - ilv; + width = ((width + ilv - 1) / ilv) + 2; + + height = ((ulong)wh * dy + wh - 1) / wh; + if (height >= frheight) + height = frheight - 3; + height += 2; + + xs = r.min.x + XCorrection; + if (xs < 0) xs = 2; + ys = r.min.y + YCorrection; + if (ys < 0) ys = 2; + if (ScreenWidth > 1023) ys |= 1; + + setreg(DisplayViewPortWidthA, width); + setreg(DisplayViewPortWidthB, width >> 8); + setreg(DisplayViewPortHeightA, height); + setreg(DisplayViewPortHeightB, height >> 8); + setreg(DisplayViewPortOrigTopA, ys); + setreg(DisplayViewPortOrigTopB, ys >> 8); + setreg(DisplayViewPortOrigLeftA, xs); + setreg(DisplayViewPortOrigLeftB, xs >> 8); + + xe = r.min.x + ww - 1 + XCorrection; + if (xe < 0) xe = 2; + ye = r.min.y + wh - 1 + YCorrection; + if (ye < 0) ye = 2; + + setreg(DisplayWindowLeftA, xs); + setreg(DisplayWindowLeftB, xs >> 8); + setreg(DisplayWindowRightA, xe); + setreg(DisplayWindowRightB, xe >> 8); + setreg(DisplayWindowTopA, ys); + setreg(DisplayWindowTopB, ys >> 8); + setreg(DisplayWindowBottomA, ye); + setreg(DisplayWindowBottomB, ye >> 8); + + if (dx < ww) { /* horizontal zoom */ + int zoom = ((ulong) (dx - 1) * 2048) / ww; + setreg(OutputProcControlA, getreg(OutputProcControlA) | 6); + setreg(OutputHorzZoomControlA, zoom); + setreg(OutputHorzZoomControlB, zoom >> 8); + } else + setreg(OutputProcControlA, getreg(OutputProcControlA) & 0xF9); + + if (dy < wh) { /* vertical zoom */ + int zoom = ((ulong) (dy - 1) * 2048) / wh; + setreg(OutputProcControlB, getreg(OutputProcControlB) | 1); + setreg(OutputVertZoomControlA, zoom); + setreg(OutputVertZoomControlB, zoom >> 8); + } else + setreg(OutputProcControlB, getreg(OutputProcControlB) & 0xFE); + + setreg(OutputProcControlB, getreg(OutputProcControlB) | 0x20); + updateshadowregs(); + + if (changed) { + setreg(OutputProcControlA, getreg(OutputProcControlA) & 0xDF); + } else { + val = getreg(InputFieldPixelBufStatus); + USED(val); + } + + panwindow(Pt(0, 0)); +} + +static void +createwindow(Rectangle r) +{ + for (q = 100; q >= 30; q -= 10) { + newwindow(r); + if (testqfactor()) + break; + } + enablevideo(); +} + +static void +setcontrols(int index, uchar val) +{ + switch (index) { + case Vxp500Brightness: + setreg(BrightnessControl, (127L * val) / 100); + updateshadowregs(); + break; + case Vxp500Contrast: + setreg(ContrastControl, (15L * val) / 100); + updateshadowregs(); + break; + case Vxp500Saturation: + setreg(SaturationControl, (15L * val) / 100); + updateshadowregs(); + break; + case Bt812Brightness: + setbt812reg(0x08, ((126L * val) / 100) & 0xFE); + break; + case Bt812Contrast: + setbt812reg(0x09, ((254L * val) / 100) & 0xFE); + break; + case Bt812Saturation: + setbt812reg(0x0A, ((254L * val) / 100) & 0xFE); + break; + case Bt812Hue: + setbt812reg(0x0B, ((254L * val) / 100) & 0xFE); + break; + case Bt812BW: + setbt812reg(0x05, (getbt812reg(0x5) & ~2) | ((val << 1) & 2)); + break; + } +} + +static void +enablememwindow(void) +{ + setreg(MemoryWindowBaseAddrB, getreg(MemoryWindowBaseAddrB) | 0x20); +} + +static void +disablememwindow(void) +{ + setreg(MemoryWindowBaseAddrB, getreg(MemoryWindowBaseAddrB) & ~0x20); +} + +volatile static ushort *fb = (ushort *)EISA(MemAddr); +static uchar yuvpadbound[] = { 4, 12, 4, 2, 6 }; + +/* + * Capture a frame in UY0, VY1 format + */ +static void * +saveframe(int *nb) +{ + int memmode, layout, ilv; + int frwidth, frheight; + int bound, n, val, toggle; + unsigned save58, save6C; + int x, y, w, h, width, height; + ulong pos, size; + char *p; + static void *frame = 0; + static ulong framesize = 0; + + width = capwindow.max.x - capwindow.min.x; + height = capwindow.max.y - capwindow.min.y; + + memmode = getreg(MemoryConfReg) & 7; + if (memmode <= 2) { + print("devtv: cannot handle YUV411\n"); + error(Egreg); /* actually, Eleendert */ + } + layout = getreg(VideoBufferLayoutControl) & 3; + ilv = interleave[memmode]; + frwidth = framewidth[memmode][layout]; + frheight = frameheight[memmode][layout]; + + pos = getreg(AcquisitionAddrA) + + (getreg(AcquisitionAddrB) << 8) + (getreg(AcquisitionAddrC) & 3) << 16; + + x = capwindow.min.x + (pos % frwidth); + y = capwindow.min.y + (pos / frwidth); + if (x > frwidth || y > frheight) + return 0; + if (x + width > frwidth) + width = frwidth - x; + if (y + height > frheight) + height = frheight - y; + + pos = y * (frwidth / ilv) + (x / ilv); + + /* compute padding for each scan line */ + bound = yuvpadbound[memmode]; + switch (bound) { + case 2: + width = (width + 1) & ~1; + break; + case 4: + width = (width + 3) & ~3; + break; + default: + width = (width + (bound - 1)) / bound; + break; + } + + size = width * height * sizeof(ushort); + if (size != framesize) { + framesize = 0; + if (frame) + free(frame); + frame = malloc(size + 256); + } + if (frame == 0) + return 0; + + memset(frame, 0, size + 256); + + framesize = size; + p = (char *) frame + snprint(frame, 256, + "TYPE=ccir601\nWINDOW=%d %d %d %d\n\n", + capwindow.min.x, capwindow.min.y, + capwindow.min.x+width, capwindow.min.y+height); + + freeze(1); + + save58 = getreg(InputVertInterpolControl); + save6C = getreg(AcquisitionControl); + + waitforretrace(); + setreg(ISAControl, 0xC0); /* global reset */ + setreg(InputVertInterpolControl, 0x0D); + setreg(AcquisitionControl, 0x04); + setreg(CaptureControl, 0x80); /* set capture mode */ + setreg(VideoInputFrameBufDepthA, 0xFF); + setreg(VideoInputFrameBufDepthB, 0x03); + setreg(InputVideoConfA, getreg(InputVideoConfA) | 0x40); + setreg(ISAControl, 0x44); /* tight decode, global reset off */ + + setreg(CaptureViewPortAddrA, (int) pos & 0xFF); + setreg(CaptureViewPortAddrB, (int) (pos >> 8) & 0xFF); + setreg(CaptureViewPortAddrC, (int) (pos >> 16) & 0x03); + n = (width / ilv) - 1; + setreg(CaptureViewPortWidthA, n & 0xFF); + setreg(CaptureViewPortWidthB, n >> 8); + setreg(CaptureViewPortHeightA, (height-1) & 0xFF); + setreg(CaptureViewPortHeightB, (height-1) >> 8); + setreg(CapturePixelBufLow, 0x04); /* pix buffer low */ + setreg(CapturePixelBufHigh, 0x0E); /* pix buffer high */ + setreg(CaptureMultiBufDepthA, 0xFF); /* multi buffer depth maximum */ + setreg(CaptureMultiBufDepthB, 0x03); + updateshadowregs(); + + setreg(CaptureControl, 0x90); /* capture reset */ + val = getreg(InputFieldPixelBufStatus); /* clear read status */ + USED(val); + + toggle = !(getreg(OutputProcControlA) & 0x01) ? 0x8000 : 0x0000; + setreg(CaptureControl, 0xC0); /* capture enable, active */ + + while ((getreg(InputFieldPixelBufStatus) & 0x10) == 0) + /* wait for capture FIFO to become ready */; + + enablememwindow(); + for (h = height; h > 0; h--) { + for (w = width; w > 0; w -= 2) { + ushort uy0 = swab16(fb[0]) ^ toggle; + ushort vy1 = swab16(fb[1]) ^ toggle; + /* unfortunately p may not be properly aligned */ + *p++ = uy0 >> 8; + *p++ = uy0; + *p++ = vy1 >> 8; + *p++ = vy1; + } + } + disablememwindow(); + + waitforretrace(); + setreg(ISAControl, 0xC0); /* global reset */ + setreg(CaptureControl, 0); /* clear capture mode */ + setreg(InputVertInterpolControl, save58); + setreg(AcquisitionControl, save6C); + setreg(InputVideoConfA, getreg(InputVideoConfA) | 0x40); + setreg(ISAControl, 0x40); /* clear global reset */ + updateshadowregs(); + + freeze(0); + + *nb = p - (char *) frame; + return frame; +} diff --git a/os/pc/devusb.c b/os/pc/devusb.c new file mode 100644 index 00000000..56a3442a --- /dev/null +++ b/os/pc/devusb.c @@ -0,0 +1,951 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#include "usb.h" + +static int debug = 0; + +#define Chatty 1 +#define DPRINT if(Chatty)print +#define XPRINT if(debug)iprint + +Usbhost* usbhost[MaxUsb]; + +static char *devstates[] = { + [Disabled] "Disabled", + [Attached] "Attached", + [Enabled] "Enabled", + [Assigned] "Assigned", + [Configured] "Configured", +}; + +static char Ebadusbmsg[] = "invalid parameters to USB ctl message"; + +enum +{ + Qtopdir = 0, + Q2nd, + Qnew, + Qport, + Q3rd, + Qctl, + Qstatus, + Qep0, + /* other endpoint files */ +}; + +/* + * Qid path is: + * 8 bits of file type (qids above) + * 8 bits of slot number; default address 0 used for per-controller files + * 4 bits of controller number + */ +enum { + TYPEBITS = 8, + SLOTBITS = 8, + CTLRBITS = 4, + + SLOTSHIFT = TYPEBITS, + CTLRSHIFT = SLOTSHIFT+SLOTBITS, + + TYPEMASK = (1<<TYPEBITS)-1, + SLOTMASK = (1<<SLOTBITS)-1, + CTLRMASK = (1<<CTLRBITS)-1, +}; + +#define TYPE(q) (((ulong)(q).path)&TYPEMASK) +#define SLOT(q) ((((ulong)(q).path)>>SLOTSHIFT)&SLOTMASK) +#define CTLR(q) ((((ulong)(q).path)>>CTLRSHIFT)&CTLRMASK) +#define PATH(t, s, c) ((t)|((s)<<SLOTSHIFT)|((c)<<CTLRSHIFT)) + +static Dirtab usbdir2[] = { + "new", {Qnew}, 0, 0666, + "port", {Qport}, 0, 0666, +}; + +static Dirtab usbdir3[]={ + "ctl", {Qctl}, 0, 0666, + "status", {Qstatus}, 0, 0444, + "setup", {Qep0}, 0, 0666, + /* epNdata names are generated on demand */ +}; + +enum +{ + PMdisable, + PMenable, + PMreset, +}; + +enum +{ + CMclass, + CMdata, + CMdebug, + CMep, + CMmaxpkt, + CMadjust, + CMspeed, + CMunstall, +}; + +static Cmdtab usbportmsg[] = +{ + PMdisable, "disable", 2, + PMenable, "enable", 2, + PMreset, "reset", 2, +}; + +static Cmdtab usbctlmsg[] = +{ + CMclass, "class", 0, + CMdata, "data", 3, + CMdebug, "debug", 3, + CMep, "ep", 6, + CMmaxpkt, "maxpkt", 3, + CMadjust, "adjust", 3, + CMspeed, "speed", 2, + CMunstall, "unstall", 2, +}; + +static struct +{ + char* type; + int (*reset)(Usbhost*); +} usbtypes[MaxUsb+1]; + +void +addusbtype(char* t, int (*r)(Usbhost*)) +{ + static int ntype; + + if(ntype == MaxUsb) + panic("too many USB host interface types"); + usbtypes[ntype].type = t; + usbtypes[ntype].reset = r; + ntype++; +} + +static Udev* +usbdeviceofslot(Usbhost *uh, int s) +{ + if(s < 0 || s > nelem(uh->dev)) + return nil; + return uh->dev[s]; +} + +static Udev* +usbdevice(Chan *c) +{ + int bus; + Udev *d; + Usbhost *uh; + + bus = CTLR(c->qid); + if(bus > nelem(usbhost) || (uh = usbhost[bus]) == nil) { + error(Egreg); + return nil; /* for compiler */ + } + d = usbdeviceofslot(uh, SLOT(c->qid)); + if(d == nil || d->id != c->qid.vers || d->state == Disabled) + error(Ehungup); + return d; +} + +static Endpt * +devendpt(Udev *d, int id, int add) +{ + Usbhost *uh; + Endpt *e, **p; + + p = &d->ep[id&0xF]; + lock(d); + e = *p; + if(e != nil){ + incref(e); + XPRINT("incref(0x%p) in devendpt, new value %ld\n", e, e->ref); + unlock(d); + return e; + } + unlock(d); + if(!add) + return nil; + + e = mallocz(sizeof(*e), 1); + e->ref = 1; + e->x = id&0xF; + e->id = id; + e->sched = -1; + e->maxpkt = 8; + e->nbuf = 1; + e->dev = d; + e->active = 0; + + uh = d->uh; + uh->epalloc(uh, e); + + lock(d); + if(*p != nil){ + incref(*p); + XPRINT("incref(0x%p) in devendpt, new value %ld\n", *p, (*p)->ref); + unlock(d); + uh->epfree(uh, e); + free(e); + return *p; + } + *p = e; + unlock(d); + e->rq = qopen(8*1024, 0, nil, e); + e->wq = qopen(8*1024, 0, nil, e); + return e; +} + +static void +freept(Endpt *e) +{ + Usbhost *uh; + + if(e != nil && decref(e) == 0){ + XPRINT("freept(%d,%d)\n", e->dev->x, e->x); + uh = e->dev->uh; + uh->epclose(uh, e); + e->dev->ep[e->x] = nil; + uh->epfree(uh, e); + free(e); + } +} + +static Udev* +usbnewdevice(Usbhost *uh) +{ + int i; + Udev *d; + Endpt *e; + + d = nil; + qlock(uh); + if(waserror()){ + qunlock(uh); + nexterror(); + } + for(i=0; i<nelem(uh->dev); i++) + if(uh->dev[i] == nil){ + uh->idgen++; + d = mallocz(sizeof(*d), 1); + d->uh = uh; + d->ref = 1; + d->x = i; + d->id = (uh->idgen << 8) | i; + d->state = Enabled; + XPRINT("calling devendpt in usbnewdevice\n"); + e = devendpt(d, 0, 1); /* always provide control endpoint 0 */ + e->mode = ORDWR; + e->iso = 0; + e->sched = -1; + uh->dev[i] = d; + break; + } + poperror(); + qunlock(uh); + return d; +} + +static void +freedev(Udev *d, int ept) +{ + int i; + Endpt *e; + Usbhost *uh; + + uh = d->uh; + if(decref(d) == 0){ + XPRINT("freedev 0x%p, 0\n", d); + for(i=0; i<nelem(d->ep); i++) + freept(d->ep[i]); + if(d->x >= 0) + uh->dev[d->x] = nil; + free(d); + } else { + if(ept >= 0 && ept < nelem(d->ep)){ + e = d->ep[ept]; + XPRINT("freedev, freept 0x%p\n", e); + if(e != nil) + uh->epclose(uh, e); + } + } +} + +static int +usbgen(Chan *c, char *, Dirtab*, int, int s, Dir *dp) +{ + Qid q; + Udev *d; + Endpt *e; + Dirtab *tab; + Usbhost *uh; + int t, bus, slot, perm; + + /* + * Top level directory contains the controller names. + */ + if(c->qid.path == Qtopdir){ + if(s == DEVDOTDOT){ + mkqid(&q, Qtopdir, 0, QTDIR); + devdir(c, q, "#U", 0, eve, 0555, dp); + return 1; + } + if(s >= nelem(usbhost) || usbhost[s] == nil) + return -1; + mkqid(&q, PATH(Q2nd, 0, s), 0, QTDIR); + snprint(up->genbuf, sizeof up->genbuf, "usb%d", s); + devdir(c, q, up->genbuf, 0, eve, 0555, dp); + return 1; + } + bus = CTLR(c->qid); + if(bus >= nelem(usbhost) || (uh = usbhost[bus]) == nil) + return -1; + + /* + * Second level contains "new", "port", and a numbered + * directory for each enumerated device on the bus. + */ + t = TYPE(c->qid); + if(t < Q3rd){ + if(s == DEVDOTDOT){ + mkqid(&q, Qtopdir, 0, QTDIR); + devdir(c, q, "#U", 0, eve, 0555, dp); + return 1; + } + if(s < nelem(usbdir2)){ + d = uh->dev[0]; + if(d == nil) + return -1; + tab = &usbdir2[s]; + mkqid(&q, PATH(tab->qid.path, 0, bus), d->id, QTFILE); + devdir(c, q, tab->name, tab->length, eve, tab->perm, dp); + return 1; + } + s -= nelem(usbdir2); + if(s >= 0 && s < nelem(uh->dev)) { + d = uh->dev[s]; + if(d == nil) + return 0; + sprint(up->genbuf, "%d", s); + mkqid(&q, PATH(Q3rd, s, bus), d->id, QTDIR); + devdir(c, q, up->genbuf, 0, eve, 0555, dp); + return 1; + } + return -1; + } + + /* + * Third level. + */ + slot = SLOT(c->qid); + if(s == DEVDOTDOT) { + mkqid(&q, PATH(Q2nd, 0, bus), c->qid.vers, QTDIR); + snprint(up->genbuf, sizeof up->genbuf, "usb%d", bus); + devdir(c, q, up->genbuf, 0, eve, 0555, dp); + return 1; + } + if(s < nelem(usbdir3)) { + tab = &usbdir3[s]; + mkqid(&q, PATH(tab->qid.path, slot, bus), c->qid.vers, QTFILE); + devdir(c, q, tab->name, tab->length, eve, tab->perm, dp); + return 1; + } + s -= nelem(usbdir3); + + /* active endpoints */ + d = usbdeviceofslot(uh, slot); + if(d == nil || s >= nelem(d->ep)) + return -1; + if(s == 0 || (e = d->ep[s]) == nil) /* ep0data is called "setup" */ + return 0; + sprint(up->genbuf, "ep%ddata", s); + mkqid(&q, PATH(Qep0+s, slot, bus), c->qid.vers, QTFILE); + switch(e->mode) { + case OREAD: + perm = 0444; + break; + case OWRITE: + perm = 0222; + break; + default: + perm = 0666; + break; + } + devdir(c, q, up->genbuf, e->buffered, eve, perm, dp); + return 1; +} + +static Usbhost* +usbprobe(int cardno, int ctlrno) +{ + Usbhost *uh; + char buf[128], *ebuf, name[64], *p, *type; + + uh = malloc(sizeof(Usbhost)); + memset(uh, 0, sizeof(Usbhost)); + uh->tbdf = BUSUNKNOWN; + + if(cardno < 0){ + if(isaconfig("usb", ctlrno, uh) == 0){ + free(uh); + return nil; + } + for(cardno = 0; usbtypes[cardno].type; cardno++){ + type = uh->type; + if(type==nil || *type==0) + type = "uhci"; + if(cistrcmp(usbtypes[cardno].type, type)) + continue; + break; + } + } + + if(cardno >= MaxUsb || usbtypes[cardno].type == nil){ + free(uh); + return nil; + } + if(usbtypes[cardno].reset(uh) < 0){ + free(uh); + return nil; + } + + /* + * IRQ2 doesn't really exist, it's used to gang the interrupt + * controllers together. A device set to IRQ2 will appear on + * the second interrupt controller as IRQ9. + */ + if(uh->irq == 2) + uh->irq = 9; + snprint(name, sizeof(name), "usb%d", ctlrno); + intrenable(uh->irq, uh->interrupt, uh, uh->tbdf, name); + + ebuf = buf + sizeof buf; + p = seprint(buf, ebuf, "#U/usb%d: %s: port 0x%luX irq %d", ctlrno, usbtypes[cardno].type, uh->port, uh->irq); + if(uh->mem) + p = seprint(p, ebuf, " addr 0x%luX", PADDR(uh->mem)); + if(uh->size) + seprint(p, ebuf, " size 0x%luX", uh->size); + print("%s\n", buf); + + return uh; +} + +static void +usbreset(void) +{ + int cardno, ctlrno; + Usbhost *uh; + + for(ctlrno = 0; ctlrno < MaxUsb; ctlrno++){ + if((uh = usbprobe(-1, ctlrno)) == nil) + continue; + usbhost[ctlrno] = uh; + } + + if(getconf("*nousbprobe")) + return; + + cardno = ctlrno = 0; + while(usbtypes[cardno].type != nil && ctlrno < MaxUsb){ + if(usbhost[ctlrno] != nil){ + ctlrno++; + continue; + } + if((uh = usbprobe(cardno, ctlrno)) == nil){ + cardno++; + continue; + } + usbhost[ctlrno] = uh; + ctlrno++; + } +} + +void +usbinit(void) +{ + Udev *d; + int ctlrno; + Usbhost *uh; + + for(ctlrno = 0; ctlrno < MaxUsb; ctlrno++){ + uh = usbhost[ctlrno]; + if(uh == nil) + continue; + if(uh->init != 0) + uh->init(uh); + + /* reserve device for configuration */ + d = usbnewdevice(uh); + incref(d); + d->state = Attached; + } +} + +Chan * +usbattach(char *spec) +{ + return devattach('U', spec); +} + +static Walkqid* +usbwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, nil, 0, usbgen); +} + +static int +usbstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, nil, 0, usbgen); +} + +Chan* +usbopen(Chan *c, int omode) +{ + Udev *d; + Endpt *e; + int f, s, type; + Usbhost *uh; + + if(c->qid.type == QTDIR) + return devopen(c, omode, nil, 0, usbgen); + + f = 0; + type = TYPE(c->qid); + if(type == Qnew){ + d = usbdevice(c); + d = usbnewdevice(d->uh); + XPRINT("usbopen, new dev 0x%p\n", d); + if(d == nil) { + XPRINT("usbopen failed (usbnewdevice)\n"); + error(Enodev); + } + type = Qctl; + mkqid(&c->qid, PATH(type, d->x, CTLR(c->qid)), d->id, QTFILE); + f = 1; + } + + if(type < Q3rd){ + XPRINT("usbopen, devopen < Q3rd\n"); + return devopen(c, omode, nil, 0, usbgen); + } + + d = usbdevice(c); + uh = d->uh; + qlock(uh); + if(waserror()){ + qunlock(uh); + nexterror(); + } + + switch(type){ + case Qctl: + if(0&&d->busy) + error(Einuse); + d->busy = 1; + if(!f) + incref(d); + XPRINT("usbopen, Qctl 0x%p\n", d); + break; + + default: + s = type - Qep0; + XPRINT("usbopen, default 0x%p, %d\n", d, s); + if(s >= 0 && s < nelem(d->ep)){ + if((e = d->ep[s]) == nil) { + XPRINT("usbopen failed (endpoint)\n"); + error(Enodev); + } + XPRINT("usbopen: dev 0x%p, ept 0x%p\n", d, e); + uh->epopen(uh, e); + e->foffset = 0; + e->toffset = 0; + e->poffset = 0; + e->buffered = 0; + } + incref(d); + break; + } + poperror(); + qunlock(uh); + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + return c; +} + +void +usbclose(Chan *c) +{ + Udev *d; + int ept, type; + Usbhost *uh; + + type = TYPE(c->qid); + if(c->qid.type == QTDIR || type < Q3rd) + return; + d = usbdevice(c); + uh = d->uh; + qlock(uh); + if(waserror()){ + qunlock(uh); + nexterror(); + } + if(type == Qctl) + d->busy = 0; + XPRINT("usbclose: dev 0x%p\n", d); + if(c->flag & COPEN){ + ept = (type != Qctl) ? type - Qep0 : -1; + XPRINT("usbclose: freedev 0x%p\n", d); + freedev(d, ept); + } + poperror(); + qunlock(uh); +} + +static char * +epstatus(char *s, char *se, Endpt *e, int i) +{ + char *p; + + p = seprint(s, se, "%2d %#6.6lux %10lud bytes %10lud blocks\n", i, e->csp, e->nbytes, e->nblocks); + if(e->iso){ + p = seprint(p, se, "bufsize %6d buffered %6d", e->maxpkt, e->buffered); + if(e->toffset) + p = seprint(p, se, " offset %10lud time %19lld\n", e->toffset, e->time); + p = seprint(p, se, "\n"); + } + return p; +} + +long +usbread(Chan *c, void *a, long n, vlong offset) +{ + int t, i; + Udev *d; + Endpt *e; + Usbhost *uh; + char *s, *se, *p; + + if(c->qid.type == QTDIR) + return devdirread(c, a, n, nil, 0, usbgen); + + d = usbdevice(c); + uh = d->uh; + t = TYPE(c->qid); + + if(t >= Qep0) { + t -= Qep0; + if(t >= nelem(d->ep)) + error(Eio); + e = d->ep[t]; + if(e == nil || e->mode == OWRITE) + error(Egreg); + if(t == 0) { + if(e->iso) + error(Egreg); + e->data01 = 1; + n = uh->read(uh, e, a, n, 0LL); + if(e->setin){ + e->setin = 0; + e->data01 = 1; + uh->write(uh, e, "", 0, 0LL, TokOUT); + } + return n; + } + return uh->read(uh, e, a, n, offset); + } + + s = smalloc(READSTR); + se = s+READSTR; + if(waserror()){ + free(s); + nexterror(); + } + switch(t){ + case Qport: + uh->portinfo(uh, s, se); + break; + + case Qctl: + seprint(s, se, "%11d %11d\n", d->x, d->id); + break; + + case Qstatus: + if (d->did || d->vid) + p = seprint(s, se, "%s %#6.6lux %#4.4ux %#4.4ux\n", devstates[d->state], d->csp, d->vid, d->did); + else + p = seprint(s, se, "%s %#6.6lux\n", devstates[d->state], d->csp); + for(i=0; i<nelem(d->ep); i++) { + e = d->ep[i]; + if(e == nil) + continue; + /* TO DO: freeze e */ + p = epstatus(p, se, e, i); + } + } + n = readstr(offset, a, n, s); + poperror(); + free(s); + return n; +} + +long +usbwrite(Chan *c, void *a, long n, vlong offset) +{ + Udev *d; + Endpt *e; + Cmdtab *ct; + Cmdbuf *cb; + Usbhost *uh; + int id, nw, t, i; + char cmd[50]; + + if(c->qid.type == QTDIR) + error(Egreg); + d = usbdevice(c); + uh = d->uh; + t = TYPE(c->qid); + switch(t){ + case Qport: + cb = parsecmd(a, n); + if(waserror()){ + free(cb); + nexterror(); + } + + ct = lookupcmd(cb, usbportmsg, nelem(usbportmsg)); + id = strtol(cb->f[1], nil, 0); + switch(ct->index){ + case PMdisable: + uh->portenable(uh, id, 0); + break; + case PMenable: + uh->portenable(uh, id, 1); + break; + case PMreset: + uh->portreset(uh, id); + break; + } + + poperror(); + free(cb); + return n; + case Qctl: + cb = parsecmd(a, n); + if(waserror()){ + free(cb); + nexterror(); + } + + ct = lookupcmd(cb, usbctlmsg, nelem(usbctlmsg)); + switch(ct->index){ + case CMspeed: + d->ls = strtoul(cb->f[1], nil, 0) == 0; + break; + case CMclass: + if (cb->nf != 4 && cb->nf != 6) + cmderror(cb, Ebadusbmsg); + /* class #ifc ept csp ( == class subclass proto) [vendor product] */ + d->npt = strtoul(cb->f[1], nil, 0); /* # of interfaces */ + i = strtoul(cb->f[2], nil, 0); /* endpoint */ + if (i < 0 || i >= nelem(d->ep) + || d->npt > nelem(d->ep) || i >= d->npt) + cmderror(cb, Ebadusbmsg); + if (cb->nf == 6) { + d->vid = strtoul(cb->f[4], nil, 0); + d->did = strtoul(cb->f[5], nil, 0); + } + if (i == 0) + d->csp = strtoul(cb->f[3], nil, 0); + if(d->ep[i] == nil){ + XPRINT("calling devendpt in usbwrite (CMclass)\n"); + d->ep[i] = devendpt(d, i, 1); + } + d->ep[i]->csp = strtoul(cb->f[3], nil, 0); + break; + case CMdata: + i = strtoul(cb->f[1], nil, 0); + if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil) + error(Ebadusbmsg); + e = d->ep[i]; + e->data01 = strtoul(cb->f[2], nil, 0) != 0; + break; + case CMmaxpkt: + i = strtoul(cb->f[1], nil, 0); + if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil) + error(Ebadusbmsg); + e = d->ep[i]; + e->maxpkt = strtoul(cb->f[2], nil, 0); + if(e->maxpkt > 1500) + e->maxpkt = 1500; + break; + case CMadjust: + i = strtoul(cb->f[1], nil, 0); + if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil) + error(Ebadusbmsg); + e = d->ep[i]; + if (e->iso == 0) + error(Eperm); + i = strtoul(cb->f[2], nil, 0); + /* speed may not result in change of maxpkt */ + if (i < (e->maxpkt-1)/e->samplesz * 1000/e->pollms + || i > e->maxpkt/e->samplesz * 1000/e->pollms){ + snprint(cmd, sizeof(cmd), "%d < %d < %d?", + (e->maxpkt-1)/e->samplesz * 1000/e->pollms, + i, + e->maxpkt/e->samplesz * 1000/e->pollms); + error(cmd); + } + e->hz = i; + break; + case CMdebug: + i = strtoul(cb->f[1], nil, 0); + if(i < -1 || i >= nelem(d->ep) || d->ep[i] == nil) + error(Ebadusbmsg); + if (i == -1) + debug = 0; + else { + debug = 1; + e = d->ep[i]; + e->debug = strtoul(cb->f[2], nil, 0); + } + break; + case CMunstall: + i = strtoul(cb->f[1], nil, 0); + if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil) + error(Ebadusbmsg); + e = d->ep[i]; + e->err = nil; + break; + case CMep: + /* ep n `bulk' mode maxpkt nbuf OR + * ep n period mode samplesize Hz + */ + i = strtoul(cb->f[1], nil, 0); + if(i < 0 || i >= nelem(d->ep)) { + XPRINT("field 1: 0 <= %d < %d\n", i, nelem(d->ep)); + error(Ebadarg); + } + if((e = d->ep[i]) == nil){ + XPRINT("calling devendpt in usbwrite (CMep)\n"); + e = devendpt(d, i, 1); + } + qlock(uh); + if(waserror()){ + freept(e); + qunlock(uh); + nexterror(); + } + if(e->active) + error(Eperm); + if(strcmp(cb->f[2], "bulk") == 0){ + /* ep n `bulk' mode maxpkt nbuf */ + e->iso = 0; + i = strtoul(cb->f[4], nil, 0); + if(i < 8 || i > 1023) + i = 8; + e->maxpkt = i; + i = strtoul(cb->f[5], nil, 0); + if(i >= 1 && i <= 32) + e->nbuf = i; + } else { + /* ep n period mode samplesize Hz */ + i = strtoul(cb->f[2], nil, 0); + if(i > 0 && i <= 1000){ + e->pollms = i; + }else { + XPRINT("field 4: 0 <= %d <= 1000\n", i); + error(Ebadarg); + } + i = strtoul(cb->f[4], nil, 0); + if(i >= 1 && i <= 8){ + e->samplesz = i; + }else { + XPRINT("field 4: 0 < %d <= 8\n", i); + error(Ebadarg); + } + i = strtoul(cb->f[5], nil, 0); + if(i >= 1 && i*e->samplesz <= 12*1000*1000){ + /* Hz */ + e->hz = i; + e->remain = 0; + }else { + XPRINT("field 5: 1 < %d <= 100000 Hz\n", i); + error(Ebadarg); + } + e->maxpkt = (e->hz * e->pollms + 999)/1000 * e->samplesz; + e->iso = 1; + } + e->mode = strcmp(cb->f[3],"r") == 0? OREAD : + strcmp(cb->f[3],"w") == 0? OWRITE : ORDWR; + uh->epmode(uh, e); + poperror(); + qunlock(uh); + } + + poperror(); + free(cb); + return n; + + case Qep0: /* SETUP endpoint 0 */ + /* should canqlock etc */ + e = d->ep[0]; + if(e == nil || e->iso) + error(Egreg); + if(n < 8) + error(Eio); + nw = *(uchar*)a & RD2H; + e->data01 = 0; + n = uh->write(uh, e, a, n, 0LL, TokSETUP); + if(nw == 0) { /* host to device: use IN[DATA1] to ack */ + e->data01 = 1; + nw = uh->read(uh, e, cmd, 0LL, 8); + if(nw != 0) + error(Eio); /* could provide more status */ + }else + e->setin = 1; /* two-phase */ + break; + + default: /* sends DATA[01] */ + t -= Qep0; + if(t < 0 || t >= nelem(d->ep)) + error(Egreg); + e = d->ep[t]; + if(e == nil || e->mode == OREAD) + error(Egreg); + n = uh->write(uh, e, a, n, offset, TokOUT); + break; + } + return n; +} + +Dev usbdevtab = { + 'U', + "usb", + + usbreset, + usbinit, + devshutdown, + usbattach, + usbwalk, + usbstat, + usbopen, + devcreate, + usbclose, + usbread, + devbread, + usbwrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/os/pc/devvga.c b/os/pc/devvga.c new file mode 100644 index 00000000..0d7a9f0d --- /dev/null +++ b/os/pc/devvga.c @@ -0,0 +1,620 @@ +/* + * VGA controller + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#define Image IMAGE +#include <draw.h> +#include <memdraw.h> +#include <cursor.h> +#include "screen.h" + +typedef struct Vgaseg Vgaseg; +struct Vgaseg { + QLock; + ulong pa; + ulong len; + void* va; +}; + +enum { + Nvgaseg = 4, + + Qdir = 0, + Qvgactl, + Qvgaovl, + Qvgaovlctl, + + Qsegs, + Qmax = Qsegs+Nvgaseg +}; + +static Dirtab vgadir[Qmax] = { + ".", { Qdir, 0, QTDIR }, 0, 0550, + "vgactl", { Qvgactl, 0 }, 0, 0660, + "vgaovl", { Qvgaovl, 0 }, 0, 0660, + "vgaovlctl", { Qvgaovlctl, 0 }, 0, 0660, + /* dynamically-created memory segments are added here */ +}; + +static Vgaseg vgasegs[Nvgaseg]; +static Lock vgadirlock; +static int nvgadir = Qsegs; + +enum { + CMactualsize, + CMblank, + CMblanktime, + CMdrawinit, + CMhwaccel, + CMhwblank, + CMhwgc, + CMlinear, + CMpalettedepth, + CMpanning, + CMsize, + CMtype, + CMunblank, +}; + +static Cmdtab vgactlmsg[] = { + CMactualsize, "actualsize", 2, + CMblank, "blank", 1, + CMblanktime, "blanktime", 2, + CMdrawinit, "drawinit", 1, + CMhwaccel, "hwaccel", 2, + CMhwblank, "hwblank", 2, + CMhwgc, "hwgc", 2, + CMlinear, "linear", 0, + CMpalettedepth, "palettedepth", 2, + CMpanning, "panning", 2, + CMsize, "size", 3, + CMtype, "type", 2, + CMunblank, "unblank", 1, +}; + +static void +vgareset(void) +{ + /* reserve the 'standard' vga registers */ + if(ioalloc(0x2b0, 0x2df-0x2b0+1, 0, "vga") < 0) + panic("vga ports already allocated"); + if(ioalloc(0x3c0, 0x3da-0x3c0+1, 0, "vga") < 0) + panic("vga ports already allocated"); + conf.monitor = 1; +} + +void +addvgaseg(char *name, ulong pa, ulong size) +{ + int i; + Dirtab d; + Vgaseg *s; + ulong va; + + va = mmukmap(pa, 0, size); + if(va == 0) + return; + memset(&d, 0, sizeof(d)); + strecpy(d.name, d.name+sizeof(name), name); + lock(&vgadirlock); + for(i=0; i<nvgadir; i++) + if(strcmp(vgadir[i].name, name) == 0){ + unlock(&vgadirlock); + print("devvga: duplicate segment %s\n", name); + return; + } + if(nvgadir >= nelem(vgadir)){ + unlock(&vgadirlock); + print("devvga: segment %s: too many segments\n", name); + return; + } + d.qid.path = nvgadir; + d.perm = 0660; + d.length = size; + s = &vgasegs[nvgadir-Qsegs]; + s->pa = pa; + s->len = size; + s->va = (void*)va; + vgadir[nvgadir] = d; + nvgadir++; + unlock(&vgadirlock); +} + +static long +vgasegrd(Vgaseg *s, uchar *buf, long n, ulong offset) +{ + int i; + uchar *a, *d; + ulong v; + + if(offset >= s->len) + return 0; + if(offset+n > s->len) + n = s->len - offset; + d = (uchar*)s->va + offset; + qlock(s); + if(waserror()){ + qunlock(s); + nexterror(); + } + a = buf; + while(n > 0){ + i = 4 - ((ulong)d & 3); + if(i > n) + i = n; + if(i == 3) + i = 2; + switch(i){ + case 4: + v = (a[3]<<24) | (a[2]<<16) | (a[1]<<8) | a[0]; + *(ulong*)d = v; + break; + case 2: + v = (a[1]<<8) | a[0]; + *(ushort*)d = v; + break; + case 1: + *d = *a; + break; + } + d += i; + a += i; + n -= i; + } + poperror(); + qunlock(s); + return a-buf; +} + +static long +vgasegwr(Vgaseg *s, uchar *buf, long n, ulong offset) +{ + int i; + uchar *a, *r; + ulong v; + + if(offset >= s->len) + return 0; + if(offset+n > s->len) + n = s->len - offset; + r = (uchar*)s->va + offset; + qlock(s); + if(waserror()){ + qunlock(s); + nexterror(); + } + a = buf; + while(n > 0){ + i = 4 - ((ulong)r & 3); + if(i > n) + i = n; + if(i == 3) + i = 2; + switch(i){ + case 4: + v = *(ulong*)r; + a[0] = v; + a[1] = v>>8; + a[2] = v>>16; + a[3] = v>>24; + break; + case 2: + v = *(ushort*)r; + a[0] = v; + a[1] = v>>8; + break; + case 1: + *a = *r; + break; + } + r += i; + a += i; + n -= i; + } + poperror(); + qunlock(s); + return a-buf; +} + +static Chan* +vgaattach(char* spec) +{ + if(*spec && strcmp(spec, "0")) + error(Eio); + return devattach('v', spec); +} + +Walkqid* +vgawalk(Chan* c, Chan *nc, char** name, int nname) +{ + return devwalk(c, nc, name, nname, vgadir, nvgadir, devgen); +} + +static int +vgastat(Chan* c, uchar* dp, int n) +{ + return devstat(c, dp, n, vgadir, nvgadir, devgen); +} + +static Chan* +vgaopen(Chan* c, int omode) +{ + VGAscr *scr; + static char *openctl = "openctl\n"; + + scr = &vgascreen[0]; + if ((ulong)c->qid.path == Qvgaovlctl) { + if (scr->dev && scr->dev->ovlctl) + scr->dev->ovlctl(scr, c, openctl, strlen(openctl)); + else + error(Enonexist); + } + return devopen(c, omode, vgadir, nvgadir, devgen); +} + +static void +vgaclose(Chan* c) +{ + VGAscr *scr; + static char *closectl = "closectl\n"; + + scr = &vgascreen[0]; + if((ulong)c->qid.path == Qvgaovlctl) + if(scr->dev && scr->dev->ovlctl){ + if(waserror()){ + print("ovlctl error: %s\n", up->env->errstr); + return; + } + scr->dev->ovlctl(scr, c, closectl, strlen(closectl)); + poperror(); + } +} + +static void +checkport(int start, int end) +{ + /* standard vga regs are OK */ + if(start >= 0x2b0 && end <= 0x2df+1) + return; + if(start >= 0x3c0 && end <= 0x3da+1) + return; + + if(iounused(start, end)) + return; + error(Eperm); +} + +static long +vgaread(Chan* c, void* a, long n, vlong off) +{ + int len; + char *p, *s; + VGAscr *scr; + ulong offset = off; + char chbuf[30]; + + switch((ulong)c->qid.path){ + + case Qdir: + return devdirread(c, a, n, vgadir, nvgadir, devgen); + + case Qvgactl: + scr = &vgascreen[0]; + + p = malloc(READSTR); + if(waserror()){ + free(p); + nexterror(); + } + + len = 0; + + if(scr->dev) + s = scr->dev->name; + else + s = "cga"; + len += snprint(p+len, READSTR-len, "type %s\n", s); + + if(scr->gscreen) { + len += snprint(p+len, READSTR-len, "size %dx%dx%d %s\n", + scr->gscreen->r.max.x, scr->gscreen->r.max.y, + scr->gscreen->depth, chantostr(chbuf, scr->gscreen->chan)); + + if(Dx(scr->gscreen->r) != Dx(physgscreenr) + || Dy(scr->gscreen->r) != Dy(physgscreenr)) + len += snprint(p+len, READSTR-len, "actualsize %dx%d\n", + physgscreenr.max.x, physgscreenr.max.y); + } + + len += snprint(p+len, READSTR-len, "blank time %lud idle %d state %s\n", + blanktime, drawidletime(), scr->isblank ? "off" : "on"); + len += snprint(p+len, READSTR-len, "hwaccel %s\n", hwaccel ? "on" : "off"); + len += snprint(p+len, READSTR-len, "hwblank %s\n", hwblank ? "on" : "off"); + len += snprint(p+len, READSTR-len, "panning %s\n", panning ? "on" : "off"); + snprint(p+len, READSTR-len, "addr 0x%lux\n", scr->aperture); + n = readstr(offset, a, n, p); + poperror(); + free(p); + + return n; + + case Qvgaovl: + case Qvgaovlctl: + error(Ebadusefd); + break; + + default: + if(c->qid.path < nvgadir) + return vgasegrd(&vgasegs[c->qid.path], a, n, offset); + error(Egreg); + break; + } + + return 0; +} + +static char Ebusy[] = "vga already configured"; + +static void +vgactl(Cmdbuf *cb) +{ + int align, i, size, x, y, z; + char *chanstr, *p; + ulong chan; + Cmdtab *ct; + VGAscr *scr; + extern VGAdev *vgadev[]; + extern VGAcur *vgacur[]; + + scr = &vgascreen[0]; + ct = lookupcmd(cb, vgactlmsg, nelem(vgactlmsg)); + switch(ct->index){ + case CMhwgc: + if(strcmp(cb->f[1], "off") == 0){ + lock(&cursor); + if(scr->cur){ + if(scr->cur->disable) + scr->cur->disable(scr); + scr->cur = nil; + } + unlock(&cursor); + return; + } + + for(i = 0; vgacur[i]; i++){ + if(strcmp(cb->f[1], vgacur[i]->name)) + continue; + lock(&cursor); + if(scr->cur && scr->cur->disable) + scr->cur->disable(scr); + scr->cur = vgacur[i]; + if(scr->cur->enable) + scr->cur->enable(scr); + unlock(&cursor); + return; + } + break; + + case CMtype: + for(i = 0; vgadev[i]; i++){ + if(strcmp(cb->f[1], vgadev[i]->name)) + continue; + if(scr->dev && scr->dev->disable) + scr->dev->disable(scr); + scr->dev = vgadev[i]; + if(scr->dev->enable) + scr->dev->enable(scr); + return; + } + break; + + case CMsize: + if(drawhasclients()) + error(Ebusy); + + x = strtoul(cb->f[1], &p, 0); + if(x == 0 || x > 2048) + error(Ebadarg); + if(*p) + p++; + + y = strtoul(p, &p, 0); + if(y == 0 || y > 2048) + error(Ebadarg); + if(*p) + p++; + + z = strtoul(p, &p, 0); + + chanstr = cb->f[2]; + if((chan = strtochan(chanstr)) == 0) + error("bad channel"); + + if(chantodepth(chan) != z) + error("depth, channel do not match"); + + cursoroff(1); + deletescreenimage(); + if(screensize(x, y, z, chan)) + error(Egreg); + vgascreenwin(scr); + cursoron(1); + return; + + case CMactualsize: + if(scr->gscreen == nil) + error("set the screen size first"); + + x = strtoul(cb->f[1], &p, 0); + if(x == 0 || x > 2048) + error(Ebadarg); + if(*p) + p++; + + y = strtoul(p, nil, 0); + if(y == 0 || y > 2048) + error(Ebadarg); + + if(x > scr->gscreen->r.max.x || y > scr->gscreen->r.max.y) + error("physical screen bigger than virtual"); + + physgscreenr = Rect(0,0,x,y); + scr->gscreen->clipr = physgscreenr; + return; + + case CMpalettedepth: + x = strtoul(cb->f[1], &p, 0); + if(x != 8 && x != 6) + error(Ebadarg); + + scr->palettedepth = x; + return; + + case CMdrawinit: + memimagedraw(scr->gscreen, scr->gscreen->r, memblack, ZP, nil, ZP, S); + if(scr && scr->dev && scr->dev->drawinit) + scr->dev->drawinit(scr); + return; + + case CMlinear: + if(cb->nf!=2 && cb->nf!=3) + error(Ebadarg); + size = strtoul(cb->f[1], 0, 0); + if(cb->nf == 2) + align = 0; + else + align = strtoul(cb->f[2], 0, 0); + if(screenaperture(size, align)) + error("not enough free address space"); + return; + + case CMblank: + drawblankscreen(1); + return; + + case CMunblank: + drawblankscreen(0); + return; + + case CMblanktime: + blanktime = strtoul(cb->f[1], 0, 0); + return; + + case CMpanning: + if(strcmp(cb->f[1], "on") == 0){ + if(scr == nil || scr->cur == nil) + error("set screen first"); + if(!scr->cur->doespanning) + error("panning not supported"); + scr->gscreen->clipr = scr->gscreen->r; + panning = 1; + } + else if(strcmp(cb->f[1], "off") == 0){ + scr->gscreen->clipr = physgscreenr; + panning = 0; + }else + break; + return; + + case CMhwaccel: + if(strcmp(cb->f[1], "on") == 0) + hwaccel = 1; + else if(strcmp(cb->f[1], "off") == 0) + hwaccel = 0; + else + break; + return; + + case CMhwblank: + if(strcmp(cb->f[1], "on") == 0) + hwblank = 1; + else if(strcmp(cb->f[1], "off") == 0) + hwblank = 0; + else + break; + return; + } + + cmderror(cb, "bad VGA control message"); +} + +char Enooverlay[] = "No overlay support"; + +static long +vgawrite(Chan* c, void* a, long n, vlong off) +{ + ulong offset = off; + Cmdbuf *cb; + VGAscr *scr; + + switch((ulong)c->qid.path){ + + case Qdir: + error(Eperm); + + case Qvgactl: + if(offset || n >= READSTR) + error(Ebadarg); + cb = parsecmd(a, n); + if(waserror()){ + free(cb); + nexterror(); + } + vgactl(cb); + poperror(); + free(cb); + return n; + + case Qvgaovl: + scr = &vgascreen[0]; + if (scr->dev == nil || scr->dev->ovlwrite == nil) { + error(Enooverlay); + break; + } + return scr->dev->ovlwrite(scr, a, n, off); + + case Qvgaovlctl: + scr = &vgascreen[0]; + if (scr->dev == nil || scr->dev->ovlctl == nil) { + error(Enooverlay); + break; + } + scr->dev->ovlctl(scr, c, a, n); + return n; + + default: + if(c->qid.path < nvgadir) + return vgasegwr(&vgasegs[c->qid.path], a, n, offset); + error(Egreg); + break; + } + + return 0; +} + +Dev vgadevtab = { + 'v', + "vga", + + vgareset, + devinit, + devshutdown, + vgaattach, + vgawalk, + vgastat, + vgaopen, + devcreate, + vgaclose, + vgaread, + devbread, + vgawrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/os/pc/devzt5512.c b/os/pc/devzt5512.c new file mode 100644 index 00000000..af189fff --- /dev/null +++ b/os/pc/devzt5512.c @@ -0,0 +1,308 @@ +/* + * Namespace Interface for Ziatech 5512 System Registers + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + + +enum{ + Qdir, + Qsysid, + Qwatchdog, + Qledctl, + Qpower, + Qswitch, + Qstat, +}; + +static +Dirtab zttab[]={ + ".", {Qdir,0,QTDIR}, 0, 0555, + "id", {Qsysid, 0}, 0, 0444, + "watchdog", {Qwatchdog, 0}, 0, 0600, + "ledctl", {Qledctl, 0}, 0, 0666, + "powerstat", {Qpower, 0}, 0, 0444, + "switch", {Qswitch, 0}, 0, 0444, + "stat", {Qstat, 0}, 0, 0444, +}; + +extern int watchdog; +void +watchdog_strobe(void) +{ + uchar sysreg; + + sysreg = inb(0x78); + sysreg &= (~1); + outb(0x78, sysreg); /* disable/strobe watchdog */ + sysreg |= 1; + outb(0x78, sysreg); /* enable watchdog */ +} + +static void +ztreset(void) /* default in dev.c */ +{ + uchar sysreg; + + if(watchdog) + addclock0link(watchdog_strobe); + /* clear status LEDs */ + sysreg = inb(0xe2); + sysreg &= ~3; /* clear usr1 */ + sysreg &= ~(3 << 2); /* clear usr2 */ + outb(0xe2, sysreg); +} + +static Chan* +ztattach(char* spec) +{ + return devattach('Z', spec); +} + +static int +ztwalk(Chan* c, char* name) +{ + return devwalk(c, name, zttab, nelem(zttab), devgen); +} + +static void +ztstat(Chan* c, char* db) +{ + devstat(c, db, zttab, nelem(zttab), devgen); +} + +static Chan* +ztopen(Chan* c, int omode) +{ + return devopen(c, omode, zttab, nelem(zttab), devgen); +} + +static void +ztclose(Chan* c) +{ + USED(c); +} + +static long +ztread(Chan* c, void* a, long n, vlong offset) +{ + uchar sysreg; + char buf[256]; + + USED(offset); + + switch(c->qid.path & ~CHDIR) { + case Qdir: + return devdirread(c, a, n, zttab, nelem(zttab), devgen); + case Qsysid: { + ulong rev; + sysreg = inb(0xe3); + rev = (ulong) (sysreg & 0x7f); + sysreg = inb(0xe2); + sprint(buf, "Board Rev: %lud\nSerial #: %lud\n", rev, (ulong)(sysreg >> 4)); + return readstr(offset, a, n, buf); + }; + case Qwatchdog: + sysreg = inb(0x78); + if((sysreg & 1) == 1) { + n = readstr(offset, a, n, "enabled"); + } else { + n = readstr(offset, a, n, "disabled"); + } + return n; + case Qledctl: + { + char usr1[6], usr2[6]; + sysreg = inb(0xe2); + switch( sysreg & 3 ) { + case 0: + case 3: + sprint(usr1, "off"); + break; + case 1: + sprint(usr1, "red"); + break; + case 2: + sprint(usr1, "green"); + }; + switch( (sysreg >> 2) & 3) { + case 0: + case 3: + sprint(usr2, "off"); + break; + case 1: + sprint(usr2, "red"); + break; + case 2: + sprint(usr2, "green"); + }; + sprint(buf, "usr1: %s\nusr2: %s\n",usr1, usr2); + return readstr(offset, a, n, buf); + }; + case Qpower: + sysreg = inb(0xe4); + sprint(buf, "DEG#: %d\nFAL#: %d\n", (sysreg & 2), (sysreg & 1)); + return readstr(offset, a, n, buf); + case Qswitch: + sysreg = inb(0xe4); + sprint(buf, "%d %d %d %d", (sysreg & (1<<6)), (sysreg & (1<<5)), (sysreg & (1<<4)), (sysreg & (1<<3))); + return readstr(offset, a, n, buf); + case Qstat: { + char bus[10],cpu[20], mode[20], boot[20]; + + sysreg = inb(0xe5); + switch (sysreg & 0x7) { + case 1: + sprint(bus, "66 MHz"); + break; + case 2: + sprint(bus, "60 MHz"); + break; + case 3: + sprint(bus, "50 MHz"); + break; + default: + sprint(bus, "unknown"); + }; + switch ((sysreg>>3)&0x7) { + case 0: + sprint(cpu, "75, 90, 100 MHz"); + break; + case 1: + sprint(cpu, "120, 133 MHz"); + break; + case 2: + sprint(cpu, "180, 200 MHz"); + break; + case 3: + sprint(cpu, "150, 166 MHz"); + default: + sprint(cpu, "unknown"); + }; + if(sysreg & (1<<6)) + sprint(mode, "Port 80 test mode"); + else + sprint(mode, "Normal decode"); + if(sysreg & (1<<7)) + sprint(boot,"EEPROM"); + else + sprint(boot,"Flash"); + sprint(buf,"Bus Frequency: %s\nPentium: %s\nTest Mode Status: %s\nBIOS Boot ROM: %s\n", + bus, cpu, mode, boot); + return readstr(offset, a, n, buf); + }; + default: + n=0; + break; + } + return n; +} + + + +static long +ztwrite(Chan* c, void *vp, long n, vlong offset) +{ + uchar sysreg; + char buf[256]; + char *a; + int nf; + char *fields[3]; + + a = vp; + if(n >= sizeof(buf)) + n = sizeof(buf)-1; + strncpy(buf, a, n); + buf[n] = 0; + + USED(a, offset); + + switch(c->qid.path & ~CHDIR){ + case Qwatchdog: + sysreg = inb(0x78); + + if(strncmp(buf, "enable", 6) == 0) { + if((sysreg & 1) != 1) + addclock0link(watchdog_strobe); + break; + } + n = 0; + error(Ebadarg); + case Qledctl: + nf = getfields(buf, fields, 3, 1, " \t\n"); + if(nf < 2) { + error(Ebadarg); + n = 0; + break; + } + sysreg = inb(0xe2); + USED(sysreg); + if(strncmp(fields[0],"usr1", 4)==0) { + sysreg &= ~3; + if(strncmp(fields[1], "off", 3)==0) { + outb(0xe2, sysreg); + break; + } + if(strncmp(fields[1], "red", 3)==0) { + sysreg |= 1; + outb(0xe2, sysreg); + break; + } + if(strncmp(fields[1], "green", 5)==0) { + sysreg |= 2; + outb(0xe2, sysreg); + break; + } + } + if(strncmp(fields[0],"usr2", 4)==0) { + sysreg &= ~(3 << 2); + if(strncmp(fields[1], "off", 3)==0) { + outb(0xe2, sysreg); + break; + } + if(strncmp(fields[1], "red", 3)==0) { + sysreg |= (1 << 2); + outb(0xe2, sysreg); + break; + } + if(strncmp(fields[1], "green", 5)==0) { + sysreg |= (2 << 2); + outb(0xe2, sysreg); + break; + } + } + n = 0; + error(Ebadarg); + default: + error(Ebadusefd); + } + return n; +} + + + +Dev zt5512devtab = { /* defaults in dev.c */ + 'Z', + "Ziatech5512", + + ztreset, /* devreset */ + devinit, /* devinit */ + ztattach, + devdetach, + devclone, /* devclone */ + ztwalk, + ztstat, + ztopen, + devcreate, /* devcreate */ + ztclose, + ztread, + devbread, /* devbread */ + ztwrite, + devbwrite, /* devbwrite */ + devremove, /* devremove */ + devwstat, /* devwstat */ +}; diff --git a/os/pc/dma.c b/os/pc/dma.c new file mode 100644 index 00000000..9da7bddd --- /dev/null +++ b/os/pc/dma.c @@ -0,0 +1,237 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" + +typedef struct DMAport DMAport; +typedef struct DMA DMA; +typedef struct DMAxfer DMAxfer; + +/* + * state of a dma transfer + */ +struct DMAxfer +{ + ulong bpa; /* bounce buffer physical address */ + void* bva; /* bounce buffer virtual address */ + int blen; /* bounce buffer length */ + void* va; /* virtual address destination/src */ + long len; /* bytes to be transferred */ + int isread; +}; + +/* + * the dma controllers. the first half of this structure specifies + * the I/O ports used by the DMA controllers. + */ +struct DMAport +{ + uchar addr[4]; /* current address (4 channels) */ + uchar count[4]; /* current count (4 channels) */ + uchar page[4]; /* page registers (4 channels) */ + uchar cmd; /* command status register */ + uchar req; /* request registers */ + uchar sbm; /* single bit mask register */ + uchar mode; /* mode register */ + uchar cbp; /* clear byte pointer */ + uchar mc; /* master clear */ + uchar cmask; /* clear mask register */ + uchar wam; /* write all mask register bit */ +}; + +struct DMA +{ + DMAport; + int shift; + Lock; + DMAxfer x[4]; +}; + +DMA dma[2] = { + { 0x00, 0x02, 0x04, 0x06, + 0x01, 0x03, 0x05, 0x07, + 0x87, 0x83, 0x81, 0x82, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0 }, + + { 0xc0, 0xc4, 0xc8, 0xcc, + 0xc2, 0xc6, 0xca, 0xce, + 0x8f, 0x8b, 0x89, 0x8a, + 0xd0, 0xd2, 0xd4, 0xd6, 0xd8, 0xda, 0xdc, 0xde, + 1 }, +}; + +/* + * DMA must be in the first 16MB. This gets called early by the + * initialisation routines of any devices which require DMA to ensure + * the allocated bounce buffers are below the 16MB limit. + */ +int +dmainit(int chan, int maxtransfer) +{ + DMA *dp; + DMAxfer *xp; + static int once; + + if(once == 0){ + if(ioalloc(0x00, 0x10, 0, "dma") < 0 + || ioalloc(0x80, 0x10, 0, "dma") < 0 + || ioalloc(0xd0, 0x10, 0, "dma") < 0) + panic("dmainit"); + once = 1; + } + + if(maxtransfer > 64*1024) + maxtransfer = 64*1024; + + dp = &dma[(chan>>2)&1]; + chan = chan & 3; + xp = &dp->x[chan]; + if(xp->bva != nil){ + if(xp->blen < maxtransfer) + return 1; + return 0; + } + + xp->bva = xspanalloc(maxtransfer, BY2PG, 64*1024); + if(xp->bva == nil) + return 1; + xp->bpa = PADDR(xp->bva); + if(xp->bpa >= 16*MB){ + /* + * This will panic with the current + * implementation of xspanalloc(). + xfree(xp->bva); + */ + xp->bva = nil; + return 1; + } + xp->blen = maxtransfer; + xp->len = 0; + xp->isread = 0; + + return 0; +} + +/* + * setup a dma transfer. if the destination is not in kernel + * memory, allocate a page for the transfer. + * + * we assume BIOS has set up the command register before we + * are booted. + * + * return the updated transfer length (we can't transfer across 64k + * boundaries) + */ +long +dmasetup(int chan, void *va, long len, int isread) +{ + DMA *dp; + ulong pa; + uchar mode; + DMAxfer *xp; + + dp = &dma[(chan>>2)&1]; + chan = chan & 3; + xp = &dp->x[chan]; + + /* + * if this isn't kernel memory or crossing 64k boundary or above 16 meg + * use the bounce buffer. + */ + pa = PADDR(va); + if((((ulong)va)&0xF0000000) != KZERO + || (pa&0xFFFF0000) != ((pa+len)&0xFFFF0000) + || pa >= 16*MB) { + if(xp->bva == nil) + return -1; + if(len > xp->blen) + len = xp->blen; + if(!isread) + memmove(xp->bva, va, len); + xp->va = va; + xp->len = len; + xp->isread = isread; + pa = xp->bpa; + } + else + xp->len = 0; + + /* + * this setup must be atomic + */ + ilock(dp); + mode = (isread ? 0x44 : 0x48) | chan; + outb(dp->mode, mode); /* single mode dma (give CPU a chance at mem) */ + outb(dp->page[chan], pa>>16); + outb(dp->cbp, 0); /* set count & address to their first byte */ + outb(dp->addr[chan], pa>>dp->shift); /* set address */ + outb(dp->addr[chan], pa>>(8+dp->shift)); + outb(dp->count[chan], (len>>dp->shift)-1); /* set count */ + outb(dp->count[chan], ((len>>dp->shift)-1)>>8); + outb(dp->sbm, chan); /* enable the channel */ + iunlock(dp); + + return len; +} + +int +dmadone(int chan) +{ + DMA *dp; + + dp = &dma[(chan>>2)&1]; + chan = chan & 3; + + return inb(dp->cmd) & (1<<chan); +} + +/* + * this must be called after a dma has been completed. + * + * if a page has been allocated for the dma, + * copy the data into the actual destination + * and free the page. + */ +void +dmaend(int chan) +{ + DMA *dp; + DMAxfer *xp; + + dp = &dma[(chan>>2)&1]; + chan = chan & 3; + + /* + * disable the channel + */ + ilock(dp); + outb(dp->sbm, 4|chan); + iunlock(dp); + + xp = &dp->x[chan]; + if(xp->len == 0 || !xp->isread) + return; + + /* + * copy out of temporary page + */ + memmove(xp->va, xp->bva, xp->len); + xp->len = 0; +} + +/* +int +dmacount(int chan) +{ + int retval; + DMA *dp; + + dp = &dma[(chan>>2)&1]; + outb(dp->cbp, 0); + retval = inb(dp->count[chan]); + retval |= inb(dp->count[chan]) << 8; + return((retval<<dp->shift)+1); +} + */ diff --git a/os/pc/ether2000.c b/os/pc/ether2000.c new file mode 100644 index 00000000..4ea8fd4f --- /dev/null +++ b/os/pc/ether2000.c @@ -0,0 +1,230 @@ +#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/netif.h" + +#include "etherif.h" +#include "ether8390.h" + +/* + * Driver written for the 'Notebook Computer Ethernet LAN Adapter', + * a plug-in to the bus-slot on the rear of the Gateway NOMAD 425DXL + * laptop. The manual says NE2000 compatible. + * The interface appears to be pretty well described in the National + * Semiconductor Local Area Network Databook (1992) as one of the + * AT evaluation cards. + * + * The NE2000 is really just a DP8390[12] plus a data port + * and a reset port. + */ +enum { + Data = 0x10, /* offset from I/O base of data port */ + Reset = 0x1F, /* offset from I/O base of reset port */ +}; + +typedef struct Ctlr Ctlr; +typedef struct Ctlr { + Pcidev* pcidev; + Ctlr* next; + int active; +} Ctlr; + +static Ctlr* ctlrhead; +static Ctlr* ctlrtail; + +static struct { + char* name; + int id; +} ne2000pci[] = { + { "Realtek 8029", (0x8029<<16)|0x10EC, }, + { "Winbond 89C940", (0x0940<<16)|0x1050, }, + { nil }, +}; + +static Ctlr* +ne2000match(Ether* edev, int id) +{ + int port; + Pcidev *p; + Ctlr *ctlr; + + /* + * Any adapter matches if no edev->port is supplied, + * otherwise the ports must match. + */ + for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){ + if(ctlr->active) + continue; + p = ctlr->pcidev; + if(((p->did<<16)|p->vid) != id) + continue; + port = p->mem[0].bar & ~0x01; + if(edev->port != 0 && edev->port != port) + continue; + + /* + * It suffices to fill these in, + * the rest is gleaned from the card. + */ + edev->port = port; + edev->irq = p->intl; + + ctlr->active = 1; + + return ctlr; + } + + return nil; +} + +static void +ne2000pnp(Ether* edev) +{ + int i, id; + Pcidev *p; + Ctlr *ctlr; + + /* + * Make a list of all ethernet controllers + * if not already done. + */ + if(ctlrhead == nil){ + p = nil; + while(p = pcimatch(p, 0, 0)){ + if(p->ccrb != 0x02 || p->ccru != 0) + continue; + ctlr = malloc(sizeof(Ctlr)); + ctlr->pcidev = p; + + if(ctlrhead != nil) + ctlrtail->next = ctlr; + else + ctlrhead = ctlr; + ctlrtail = ctlr; + } + } + + /* + * Is it a card with an unrecognised vid+did? + * Normally a search is made through all the found controllers + * for one which matches any of the known vid+did pairs. + * If a vid+did pair is specified a search is made for that + * specific controller only. + */ + id = 0; + for(i = 0; i < edev->nopt; i++){ + if(cistrncmp(edev->opt[i], "id=", 3) == 0) + id = strtol(&edev->opt[i][3], nil, 0); + } + + if(id != 0) + ne2000match(edev, id); + else for(i = 0; ne2000pci[i].name; i++){ + if(ne2000match(edev, ne2000pci[i].id) != nil) + break; + } +} + +static int +ne2000reset(Ether* edev) +{ + static int first; + ushort buf[16]; + ulong port; + Dp8390 *dp8390; + int i; + uchar ea[Eaddrlen]; + + if(edev->port == 0) + ne2000pnp(edev); + + /* + * Set up the software configuration. + * Use defaults for irq, mem and size + * if not specified. + * Must have a port, no more default. + */ + if(edev->port == 0) + return -1; + if(edev->irq == 0) + edev->irq = 2; + if(edev->mem == 0) + edev->mem = 0x4000; + if(edev->size == 0) + edev->size = 16*1024; + port = edev->port; + + if(ioalloc(edev->port, 0x20, 0, "ne2000") < 0) + return -1; + + edev->ctlr = malloc(sizeof(Dp8390)); + dp8390 = edev->ctlr; + dp8390->width = 2; + dp8390->ram = 0; + + dp8390->port = port; + dp8390->data = port+Data; + + dp8390->tstart = HOWMANY(edev->mem, Dp8390BufSz); + dp8390->pstart = dp8390->tstart + HOWMANY(sizeof(Etherpkt), Dp8390BufSz); + dp8390->pstop = dp8390->tstart + HOWMANY(edev->size, Dp8390BufSz); + + dp8390->dummyrr = 1; + for(i = 0; i < edev->nopt; i++){ + if(strcmp(edev->opt[i], "nodummyrr")) + continue; + dp8390->dummyrr = 0; + break; + } + + /* + * Reset the board. This is done by doing a read + * followed by a write to the Reset address. + */ + buf[0] = inb(port+Reset); + delay(2); + outb(port+Reset, buf[0]); + delay(2); + + /* + * Init the (possible) chip, then use the (possible) + * chip to read the (possible) PROM for ethernet address + * and a marker byte. + * Could just look at the DP8390 command register after + * initialisation has been tried, but that wouldn't be + * enough, there are other ethernet boards which could + * match. + */ + dp8390reset(edev); + memset(buf, 0, sizeof(buf)); + dp8390read(dp8390, buf, 0, sizeof(buf)); + if((buf[0x0E] & 0xFF) != 0x57 || (buf[0x0F] & 0xFF) != 0x57){ + iofree(edev->port); + free(edev->ctlr); + return -1; + } + + /* + * Stupid machine. Shorts were asked for, + * shorts were delivered, although the PROM is a byte array. + * Set the ethernet address. + */ + memset(ea, 0, Eaddrlen); + if(memcmp(ea, edev->ea, Eaddrlen) == 0){ + for(i = 0; i < sizeof(edev->ea); i++) + edev->ea[i] = buf[i]; + } + dp8390setea(edev); + + return 0; +} + +void +ether2000link(void) +{ + addethercard("NE2000", ne2000reset); +} diff --git a/os/pc/ether2114x.c b/os/pc/ether2114x.c new file mode 100644 index 00000000..43bae880 --- /dev/null +++ b/os/pc/ether2114x.c @@ -0,0 +1,1828 @@ +/* + * Digital Semiconductor DECchip 2114x PCI Fast Ethernet LAN Controller. + * To do: + * thresholds; + * ring sizing; + * handle more error conditions; + * tidy setup packet mess; + * push initialisation back to attach; + * full SROM decoding. + */ +#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/netif.h" + +#include "etherif.h" + +#define DEBUG (0) +#define debug if(DEBUG)print + +enum { + Nrde = 64, + Ntde = 64, +}; + +#define Rbsz ROUNDUP(sizeof(Etherpkt)+4, 4) + +enum { /* CRS0 - Bus Mode */ + Swr = 0x00000001, /* Software Reset */ + Bar = 0x00000002, /* Bus Arbitration */ + Dsl = 0x0000007C, /* Descriptor Skip Length (field) */ + Ble = 0x00000080, /* Big/Little Endian */ + Pbl = 0x00003F00, /* Programmable Burst Length (field) */ + Cal = 0x0000C000, /* Cache Alignment (field) */ + Cal8 = 0x00004000, /* 8 longword boundary alignment */ + Cal16 = 0x00008000, /* 16 longword boundary alignment */ + Cal32 = 0x0000C000, /* 32 longword boundary alignment */ + Tap = 0x000E0000, /* Transmit Automatic Polling (field) */ + Dbo = 0x00100000, /* Descriptor Byte Ordering Mode */ + Rml = 0x00200000, /* Read Multiple */ +}; + +enum { /* CSR[57] - Status and Interrupt Enable */ + Ti = 0x00000001, /* Transmit Interrupt */ + Tps = 0x00000002, /* Transmit Process Stopped */ + Tu = 0x00000004, /* Transmit buffer Unavailable */ + Tjt = 0x00000008, /* Transmit Jabber Timeout */ + Unf = 0x00000020, /* transmit UNderFlow */ + Ri = 0x00000040, /* Receive Interrupt */ + Ru = 0x00000080, /* Receive buffer Unavailable */ + Rps = 0x00000100, /* Receive Process Stopped */ + Rwt = 0x00000200, /* Receive Watchdog Timeout */ + Eti = 0x00000400, /* Early Transmit Interrupt */ + Gte = 0x00000800, /* General purpose Timer Expired */ + Fbe = 0x00002000, /* Fatal Bit Error */ + Ais = 0x00008000, /* Abnormal Interrupt Summary */ + Nis = 0x00010000, /* Normal Interrupt Summary */ + Rs = 0x000E0000, /* Receive process State (field) */ + Ts = 0x00700000, /* Transmit process State (field) */ + Eb = 0x03800000, /* Error bits */ +}; + +enum { /* CSR6 - Operating Mode */ + Hp = 0x00000001, /* Hash/Perfect receive filtering mode */ + Sr = 0x00000002, /* Start/stop Receive */ + Ho = 0x00000004, /* Hash-Only filtering mode */ + Pb = 0x00000008, /* Pass Bad frames */ + If = 0x00000010, /* Inverse Filtering */ + Sb = 0x00000020, /* Start/stop Backoff counter */ + Pr = 0x00000040, /* Promiscuous Mode */ + Pm = 0x00000080, /* Pass all Multicast */ + Fd = 0x00000200, /* Full Duplex mode */ + Om = 0x00000C00, /* Operating Mode (field) */ + Fc = 0x00001000, /* Force Collision */ + St = 0x00002000, /* Start/stop Transmission Command */ + Tr = 0x0000C000, /* ThReshold control bits (field) */ + Tr128 = 0x00000000, + Tr256 = 0x00004000, + Tr512 = 0x00008000, + Tr1024 = 0x0000C000, + Ca = 0x00020000, /* CApture effect enable */ + Ps = 0x00040000, /* Port Select */ + Hbd = 0x00080000, /* HeartBeat Disable */ + Imm = 0x00100000, /* IMMediate mode */ + Sf = 0x00200000, /* Store and Forward */ + Ttm = 0x00400000, /* Transmit Threshold Mode */ + Pcs = 0x00800000, /* PCS function */ + Scr = 0x01000000, /* SCRambler mode */ + Mbo = 0x02000000, /* Must Be One */ + Ra = 0x40000000, /* Receive All */ + Sc = 0x80000000, /* Special Capture effect enable */ + + TrMODE = Tr512, /* default transmission threshold */ +}; + +enum { /* CSR9 - ROM and MII Management */ + Scs = 0x00000001, /* serial ROM chip select */ + Sclk = 0x00000002, /* serial ROM clock */ + Sdi = 0x00000004, /* serial ROM data in */ + Sdo = 0x00000008, /* serial ROM data out */ + Ss = 0x00000800, /* serial ROM select */ + Wr = 0x00002000, /* write */ + Rd = 0x00004000, /* read */ + + Mdc = 0x00010000, /* MII management clock */ + Mdo = 0x00020000, /* MII management write data */ + Mii = 0x00040000, /* MII management operation mode (W) */ + Mdi = 0x00080000, /* MII management data in */ +}; + +enum { /* CSR12 - General-Purpose Port */ + Gpc = 0x00000100, /* General Purpose Control */ +}; + +typedef struct Des { + int status; + int control; + ulong addr; + Block* bp; +} Des; + +enum { /* status */ + Of = 0x00000001, /* Rx: OverFlow */ + Ce = 0x00000002, /* Rx: CRC Error */ + Db = 0x00000004, /* Rx: Dribbling Bit */ + Re = 0x00000008, /* Rx: Report on MII Error */ + Rw = 0x00000010, /* Rx: Receive Watchdog */ + Ft = 0x00000020, /* Rx: Frame Type */ + Cs = 0x00000040, /* Rx: Collision Seen */ + Tl = 0x00000080, /* Rx: Frame too Long */ + Ls = 0x00000100, /* Rx: Last deScriptor */ + Fs = 0x00000200, /* Rx: First deScriptor */ + Mf = 0x00000400, /* Rx: Multicast Frame */ + Rf = 0x00000800, /* Rx: Runt Frame */ + Dt = 0x00003000, /* Rx: Data Type (field) */ + De = 0x00004000, /* Rx: Descriptor Error */ + Fl = 0x3FFF0000, /* Rx: Frame Length (field) */ + Ff = 0x40000000, /* Rx: Filtering Fail */ + + Def = 0x00000001, /* Tx: DEFerred */ + Uf = 0x00000002, /* Tx: UnderFlow error */ + Lf = 0x00000004, /* Tx: Link Fail report */ + Cc = 0x00000078, /* Tx: Collision Count (field) */ + Hf = 0x00000080, /* Tx: Heartbeat Fail */ + Ec = 0x00000100, /* Tx: Excessive Collisions */ + Lc = 0x00000200, /* Tx: Late Collision */ + Nc = 0x00000400, /* Tx: No Carrier */ + Lo = 0x00000800, /* Tx: LOss of carrier */ + To = 0x00004000, /* Tx: Transmission jabber timeOut */ + + Es = 0x00008000, /* [RT]x: Error Summary */ + Own = 0x80000000, /* [RT]x: OWN bit */ +}; + +enum { /* control */ + Bs1 = 0x000007FF, /* [RT]x: Buffer 1 Size */ + Bs2 = 0x003FF800, /* [RT]x: Buffer 2 Size */ + + Ch = 0x01000000, /* [RT]x: second address CHained */ + Er = 0x02000000, /* [RT]x: End of Ring */ + + Ft0 = 0x00400000, /* Tx: Filtering Type 0 */ + Dpd = 0x00800000, /* Tx: Disabled PaDding */ + Ac = 0x04000000, /* Tx: Add CRC disable */ + Set = 0x08000000, /* Tx: SETup packet */ + Ft1 = 0x10000000, /* Tx: Filtering Type 1 */ + Fseg = 0x20000000, /* Tx: First SEGment */ + Lseg = 0x40000000, /* Tx: Last SEGment */ + Ic = 0x80000000, /* Tx: Interrupt on Completion */ +}; + +enum { /* PHY registers */ + Bmcr = 0, /* Basic Mode Control */ + Bmsr = 1, /* Basic Mode Status */ + Phyidr1 = 2, /* PHY Identifier #1 */ + Phyidr2 = 3, /* PHY Identifier #2 */ + Anar = 4, /* Auto-Negotiation Advertisment */ + Anlpar = 5, /* Auto-Negotiation Link Partner Ability */ + Aner = 6, /* Auto-Negotiation Expansion */ +}; + +enum { /* Variants */ + Tulip0 = (0x0009<<16)|0x1011, + Tulip1 = (0x0014<<16)|0x1011, + Tulip3 = (0x0019<<16)|0x1011, + Pnic = (0x0002<<16)|0x11AD, + Pnic2 = (0xC115<<16)|0x11AD, + CentaurP = (0x0985<<16)|0x1317, +}; + +typedef struct Ctlr Ctlr; +typedef struct Ctlr { + int port; + Pcidev* pcidev; + Ctlr* next; + int active; + int id; /* (pcidev->did<<16)|pcidev->vid */ + + uchar* srom; + int sromsz; /* address size in bits */ + uchar* sromea; /* MAC address */ + uchar* leaf; + int sct; /* selected connection type */ + int k; /* info block count */ + uchar* infoblock[16]; + int sctk; /* sct block index */ + int curk; /* current block index */ + uchar* type5block; + + int phy[32]; /* logical to physical map */ + int phyreset; /* reset bitmap */ + int curphyad; + int fdx; + int ttm; + + uchar fd; /* option */ + int medium; /* option */ + + int csr6; /* CSR6 - operating mode */ + int mask; /* CSR[57] - interrupt mask */ + int mbps; + + Lock lock; + + Des* rdr; /* receive descriptor ring */ + int nrdr; /* size of rdr */ + int rdrx; /* index into rdr */ + + Lock tlock; + Des* tdr; /* transmit descriptor ring */ + int ntdr; /* size of tdr */ + int tdrh; /* host index into tdr */ + int tdri; /* interface index into tdr */ + int ntq; /* descriptors active */ + int ntqmax; + Block* setupbp; + + ulong of; /* receive statistics */ + ulong ce; + ulong cs; + ulong tl; + ulong rf; + ulong de; + + ulong ru; + ulong rps; + ulong rwt; + + ulong uf; /* transmit statistics */ + ulong ec; + ulong lc; + ulong nc; + ulong lo; + ulong to; + + ulong tps; + ulong tu; + ulong tjt; + ulong unf; +} Ctlr; + +static Ctlr* ctlrhead; +static Ctlr* ctlrtail; + +#define csr32r(c, r) (inl((c)->port+((r)*8))) +#define csr32w(c, r, l) (outl((c)->port+((r)*8), (ulong)(l))) + +static void +promiscuous(void* arg, int on) +{ + Ctlr *ctlr; + + ctlr = ((Ether*)arg)->ctlr; + ilock(&ctlr->lock); + if(on) + ctlr->csr6 |= Pr; + else + ctlr->csr6 &= ~Pr; + csr32w(ctlr, 6, ctlr->csr6); + iunlock(&ctlr->lock); +} + +/* multicast already on, don't need to do anything */ +static void +multicast(void*, uchar*, int) +{ +} + +static void +attach(Ether* ether) +{ + Ctlr *ctlr; + + ctlr = ether->ctlr; + ilock(&ctlr->lock); + if(!(ctlr->csr6 & Sr)){ + ctlr->csr6 |= Sr; + csr32w(ctlr, 6, ctlr->csr6); + } + iunlock(&ctlr->lock); +} + +static long +ifstat(Ether* ether, void* a, long n, ulong offset) +{ + Ctlr *ctlr; + char *buf, *p; + int i, l, len; + + ctlr = ether->ctlr; + + ether->crcs = ctlr->ce; + ether->frames = ctlr->rf+ctlr->cs; + ether->buffs = ctlr->de+ctlr->tl; + ether->overflows = ctlr->of; + + if(n == 0) + return 0; + + p = malloc(READSTR); + l = snprint(p, READSTR, "Overflow: %lud\n", ctlr->of); + l += snprint(p+l, READSTR-l, "Ru: %lud\n", ctlr->ru); + l += snprint(p+l, READSTR-l, "Rps: %lud\n", ctlr->rps); + l += snprint(p+l, READSTR-l, "Rwt: %lud\n", ctlr->rwt); + l += snprint(p+l, READSTR-l, "Tps: %lud\n", ctlr->tps); + l += snprint(p+l, READSTR-l, "Tu: %lud\n", ctlr->tu); + l += snprint(p+l, READSTR-l, "Tjt: %lud\n", ctlr->tjt); + l += snprint(p+l, READSTR-l, "Unf: %lud\n", ctlr->unf); + l += snprint(p+l, READSTR-l, "CRC Error: %lud\n", ctlr->ce); + l += snprint(p+l, READSTR-l, "Collision Seen: %lud\n", ctlr->cs); + l += snprint(p+l, READSTR-l, "Frame Too Long: %lud\n", ctlr->tl); + l += snprint(p+l, READSTR-l, "Runt Frame: %lud\n", ctlr->rf); + l += snprint(p+l, READSTR-l, "Descriptor Error: %lud\n", ctlr->de); + l += snprint(p+l, READSTR-l, "Underflow Error: %lud\n", ctlr->uf); + l += snprint(p+l, READSTR-l, "Excessive Collisions: %lud\n", ctlr->ec); + l += snprint(p+l, READSTR-l, "Late Collision: %lud\n", ctlr->lc); + l += snprint(p+l, READSTR-l, "No Carrier: %lud\n", ctlr->nc); + l += snprint(p+l, READSTR-l, "Loss of Carrier: %lud\n", ctlr->lo); + l += snprint(p+l, READSTR-l, "Transmit Jabber Timeout: %lud\n", + ctlr->to); + l += snprint(p+l, READSTR-l, "csr6: %luX %uX\n", csr32r(ctlr, 6), + ctlr->csr6); + snprint(p+l, READSTR-l, "ntqmax: %d\n", ctlr->ntqmax); + ctlr->ntqmax = 0; + buf = a; + len = readstr(offset, buf, n, p); + if(offset > l) + offset -= l; + else + offset = 0; + buf += len; + n -= len; + + l = snprint(p, READSTR, "srom:"); + for(i = 0; i < (1<<(ctlr->sromsz)*sizeof(ushort)); i++){ + if(i && ((i & 0x0F) == 0)) + l += snprint(p+l, READSTR-l, "\n "); + l += snprint(p+l, READSTR-l, " %2.2uX", ctlr->srom[i]); + } + + snprint(p+l, READSTR-l, "\n"); + len += readstr(offset, buf, n, p); + free(p); + + return len; +} + +static void +txstart(Ether* ether) +{ + Ctlr *ctlr; + Block *bp; + Des *des; + int control; + + ctlr = ether->ctlr; + while(ctlr->ntq < (ctlr->ntdr-1)){ + if(ctlr->setupbp){ + bp = ctlr->setupbp; + ctlr->setupbp = 0; + control = Ic|Set|BLEN(bp); + } + else{ + bp = qget(ether->oq); + if(bp == nil) + break; + control = Ic|Lseg|Fseg|BLEN(bp); + } + + ctlr->tdr[PREV(ctlr->tdrh, ctlr->ntdr)].control &= ~Ic; + des = &ctlr->tdr[ctlr->tdrh]; + des->bp = bp; + des->addr = PCIWADDR(bp->rp); + des->control |= control; + ctlr->ntq++; + coherence(); + des->status = Own; + csr32w(ctlr, 1, 0); + ctlr->tdrh = NEXT(ctlr->tdrh, ctlr->ntdr); + } + + if(ctlr->ntq > ctlr->ntqmax) + ctlr->ntqmax = ctlr->ntq; +} + +static void +transmit(Ether* ether) +{ + Ctlr *ctlr; + + ctlr = ether->ctlr; + ilock(&ctlr->tlock); + txstart(ether); + iunlock(&ctlr->tlock); +} + +static void +interrupt(Ureg*, void* arg) +{ + Ctlr *ctlr; + Ether *ether; + int len, status; + Des *des; + Block *bp; + + ether = arg; + ctlr = ether->ctlr; + + while((status = csr32r(ctlr, 5)) & (Nis|Ais)){ + /* + * Acknowledge the interrupts and mask-out + * the ones that are implicitly handled. + */ + csr32w(ctlr, 5, status); + status &= (ctlr->mask & ~(Nis|Ti)); + + if(status & Ais){ + if(status & Tps) + ctlr->tps++; + if(status & Tu) + ctlr->tu++; + if(status & Tjt) + ctlr->tjt++; + if(status & Ru) + ctlr->ru++; + if(status & Rps) + ctlr->rps++; + if(status & Rwt) + ctlr->rwt++; + status &= ~(Ais|Rwt|Rps|Ru|Tjt|Tu|Tps); + } + + /* + * Received packets. + */ + if(status & Ri){ + des = &ctlr->rdr[ctlr->rdrx]; + while(!(des->status & Own)){ + if(des->status & Es){ + if(des->status & Of) + ctlr->of++; + if(des->status & Ce) + ctlr->ce++; + if(des->status & Cs) + ctlr->cs++; + if(des->status & Tl) + ctlr->tl++; + if(des->status & Rf) + ctlr->rf++; + if(des->status & De) + ctlr->de++; + } + else if(bp = iallocb(Rbsz)){ + len = ((des->status & Fl)>>16)-4; + des->bp->wp = des->bp->rp+len; + etheriq(ether, des->bp, 1); + des->bp = bp; + des->addr = PCIWADDR(bp->rp); + } + + des->control &= Er; + des->control |= Rbsz; + coherence(); + des->status = Own; + + ctlr->rdrx = NEXT(ctlr->rdrx, ctlr->nrdr); + des = &ctlr->rdr[ctlr->rdrx]; + } + status &= ~Ri; + } + + /* + * Check the transmit side: + * check for Transmit Underflow and Adjust + * the threshold upwards; + * free any transmitted buffers and try to + * top-up the ring. + */ + if(status & Unf){ + ctlr->unf++; + ilock(&ctlr->lock); + csr32w(ctlr, 6, ctlr->csr6 & ~St); + switch(ctlr->csr6 & Tr){ + case Tr128: + len = Tr256; + break; + case Tr256: + len = Tr512; + break; + case Tr512: + len = Tr1024; + break; + default: + case Tr1024: + len = Sf; + break; + } + ctlr->csr6 = (ctlr->csr6 & ~Tr)|len; + csr32w(ctlr, 6, ctlr->csr6); + iunlock(&ctlr->lock); + csr32w(ctlr, 5, Tps); + status &= ~(Unf|Tps); + } + + ilock(&ctlr->tlock); + while(ctlr->ntq){ + des = &ctlr->tdr[ctlr->tdri]; + if(des->status & Own) + break; + + if(des->status & Es){ + if(des->status & Uf) + ctlr->uf++; + if(des->status & Ec) + ctlr->ec++; + if(des->status & Lc) + ctlr->lc++; + if(des->status & Nc) + ctlr->nc++; + if(des->status & Lo) + ctlr->lo++; + if(des->status & To) + ctlr->to++; + ether->oerrs++; + } + + freeb(des->bp); + des->control &= Er; + + ctlr->ntq--; + ctlr->tdri = NEXT(ctlr->tdri, ctlr->ntdr); + } + txstart(ether); + iunlock(&ctlr->tlock); + + /* + * Anything left not catered for? + */ + if(status) + panic("#l%d: status %8.8uX\n", ether->ctlrno, status); + } +} + +static void +ctlrinit(Ether* ether) +{ + Ctlr *ctlr; + Des *des; + Block *bp; + int i; + uchar bi[Eaddrlen*2]; + + ctlr = ether->ctlr; + + /* + * Allocate and initialise the receive ring; + * allocate and initialise the transmit ring; + * unmask interrupts and start the transmit side; + * create and post a setup packet to initialise + * the physical ethernet address. + */ + ctlr->rdr = xspanalloc(ctlr->nrdr*sizeof(Des), 8*sizeof(ulong), 0); + for(des = ctlr->rdr; des < &ctlr->rdr[ctlr->nrdr]; des++){ + des->bp = iallocb(Rbsz); + if(des->bp == nil) + panic("can't allocate ethernet receive ring\n"); + des->status = Own; + des->control = Rbsz; + des->addr = PCIWADDR(des->bp->rp); + } + ctlr->rdr[ctlr->nrdr-1].control |= Er; + ctlr->rdrx = 0; + csr32w(ctlr, 3, PCIWADDR(ctlr->rdr)); + + ctlr->tdr = xspanalloc(ctlr->ntdr*sizeof(Des), 8*sizeof(ulong), 0); + ctlr->tdr[ctlr->ntdr-1].control |= Er; + ctlr->tdrh = 0; + ctlr->tdri = 0; + csr32w(ctlr, 4, PCIWADDR(ctlr->tdr)); + + /* + * Clear any bits in the Status Register (CSR5) as + * the PNIC has a different reset value from a true 2114x. + */ + ctlr->mask = Nis|Ais|Fbe|Rwt|Rps|Ru|Ri|Unf|Tjt|Tps|Ti; + csr32w(ctlr, 5, ctlr->mask); + csr32w(ctlr, 7, ctlr->mask); + ctlr->csr6 |= St|Pm; + csr32w(ctlr, 6, ctlr->csr6); + + for(i = 0; i < Eaddrlen/2; i++){ + bi[i*4] = ether->ea[i*2]; + bi[i*4+1] = ether->ea[i*2+1]; + bi[i*4+2] = ether->ea[i*2+1]; + bi[i*4+3] = ether->ea[i*2]; + } + bp = iallocb(Eaddrlen*2*16); + if(bp == nil) + panic("can't allocate ethernet setup buffer\n"); + memset(bp->rp, 0xFF, sizeof(bi)); + for(i = sizeof(bi); i < sizeof(bi)*16; i += sizeof(bi)) + memmove(bp->rp+i, bi, sizeof(bi)); + bp->wp += sizeof(bi)*16; + + ctlr->setupbp = bp; + ether->oq = qopen(256*1024, Qmsg, 0, 0); + transmit(ether); +} + +static void +csr9w(Ctlr* ctlr, int data) +{ + csr32w(ctlr, 9, data); + microdelay(1); +} + +static int +miimdi(Ctlr* ctlr, int n) +{ + int data, i; + + /* + * Read n bits from the MII Management Register. + */ + data = 0; + for(i = n-1; i >= 0; i--){ + if(csr32r(ctlr, 9) & Mdi) + data |= (1<<i); + csr9w(ctlr, Mii|Mdc); + csr9w(ctlr, Mii); + } + csr9w(ctlr, 0); + + return data; +} + +static void +miimdo(Ctlr* ctlr, int bits, int n) +{ + int i, mdo; + + /* + * Write n bits to the MII Management Register. + */ + for(i = n-1; i >= 0; i--){ + if(bits & (1<<i)) + mdo = Mdo; + else + mdo = 0; + csr9w(ctlr, mdo); + csr9w(ctlr, mdo|Mdc); + csr9w(ctlr, mdo); + } +} + +static int +miir(Ctlr* ctlr, int phyad, int regad) +{ + int data, i; + + if(ctlr->id == Pnic){ + i = 1000; + csr32w(ctlr, 20, 0x60020000|(phyad<<23)|(regad<<18)); + do{ + microdelay(1); + data = csr32r(ctlr, 20); + }while((data & 0x80000000) && --i); + + if(i == 0) + return -1; + return data & 0xFFFF; + } + + /* + * Preamble; + * ST+OP+PHYAD+REGAD; + * TA + 16 data bits. + */ + miimdo(ctlr, 0xFFFFFFFF, 32); + miimdo(ctlr, 0x1800|(phyad<<5)|regad, 14); + data = miimdi(ctlr, 18); + + if(data & 0x10000) + return -1; + + return data & 0xFFFF; +} + +static void +miiw(Ctlr* ctlr, int phyad, int regad, int data) +{ + /* + * Preamble; + * ST+OP+PHYAD+REGAD+TA + 16 data bits; + * Z. + */ + miimdo(ctlr, 0xFFFFFFFF, 32); + data &= 0xFFFF; + data |= (0x05<<(5+5+2+16))|(phyad<<(5+2+16))|(regad<<(2+16))|(0x02<<16); + miimdo(ctlr, data, 32); + csr9w(ctlr, Mdc); + csr9w(ctlr, 0); +} + +static int +sromr(Ctlr* ctlr, int r) +{ + int i, op, data, size; + + if(ctlr->id == Pnic){ + i = 1000; + csr32w(ctlr, 19, 0x600|r); + do{ + microdelay(1); + data = csr32r(ctlr, 19); + }while((data & 0x80000000) && --i); + + if(ctlr->sromsz == 0) + ctlr->sromsz = 6; + + return csr32r(ctlr, 9) & 0xFFFF; + } + + /* + * This sequence for reading a 16-bit register 'r' + * in the EEPROM is taken (pretty much) straight from Section + * 7.4 of the 21140 Hardware Reference Manual. + */ +reread: + csr9w(ctlr, Rd|Ss); + csr9w(ctlr, Rd|Ss|Scs); + csr9w(ctlr, Rd|Ss|Sclk|Scs); + csr9w(ctlr, Rd|Ss); + + op = 0x06; + for(i = 3-1; i >= 0; i--){ + data = Rd|Ss|(((op>>i) & 0x01)<<2)|Scs; + csr9w(ctlr, data); + csr9w(ctlr, data|Sclk); + csr9w(ctlr, data); + } + + /* + * First time through must work out the EEPROM size. + * This doesn't seem to work on the 21041 as implemented + * in Virtual PC for the Mac, so wire any 21041 to 6, + * it's the only 21041 this code will ever likely see. + */ + if((size = ctlr->sromsz) == 0){ + if(ctlr->id == Tulip1) + ctlr->sromsz = size = 6; + else + size = 8; + } + + for(size = size-1; size >= 0; size--){ + data = Rd|Ss|(((r>>size) & 0x01)<<2)|Scs; + csr9w(ctlr, data); + csr9w(ctlr, data|Sclk); + csr9w(ctlr, data); + microdelay(1); + if(ctlr->sromsz == 0 && !(csr32r(ctlr, 9) & Sdo)) + break; + } + + data = 0; + for(i = 16-1; i >= 0; i--){ + csr9w(ctlr, Rd|Ss|Sclk|Scs); + if(csr32r(ctlr, 9) & Sdo) + data |= (1<<i); + csr9w(ctlr, Rd|Ss|Scs); + } + + csr9w(ctlr, 0); + + if(ctlr->sromsz == 0){ + ctlr->sromsz = 8-size; + goto reread; + } + + return data & 0xFFFF; +} + +static void +shutdown(Ether* ether) +{ + Ctlr *ctlr = ether->ctlr; + +print("ether2114x shutting down\n"); + csr32w(ctlr, 0, Swr); +} + +static void +softreset(Ctlr* ctlr) +{ + /* + * Soft-reset the controller and initialise bus mode. + * Delay should be >= 50 PCI cycles (2×S @ 25MHz). + */ + csr32w(ctlr, 0, Swr); + microdelay(10); + csr32w(ctlr, 0, Rml|Cal16); + delay(1); +} + +static int +type5block(Ctlr* ctlr, uchar* block) +{ + int csr15, i, len; + + /* + * Reset or GPR sequence. Reset should be once only, + * before the GPR sequence. + * Note 'block' is not a pointer to the block head but + * a pointer to the data in the block starting at the + * reset length value so type5block can be used for the + * sequences contained in type 1 and type 3 blocks. + * The SROM docs state the 21140 type 5 block is the + * same as that for the 21143, but the two controllers + * use different registers and sequence-element lengths + * so the 21140 code here is a guess for a real type 5 + * sequence. + */ + len = *block++; + if(ctlr->id != Tulip3){ + for(i = 0; i < len; i++){ + csr32w(ctlr, 12, *block); + block++; + } + return len; + } + + for(i = 0; i < len; i++){ + csr15 = *block++<<16; + csr15 |= *block++<<24; + csr32w(ctlr, 15, csr15); + debug("%8.8uX ", csr15); + } + return 2*len; +} + +static int +typephylink(Ctlr* ctlr, uchar*) +{ + int an, bmcr, bmsr, csr6, x; + + /* + * Fail if + * auto-negotiataion enabled but not complete; + * no valid link established. + */ + bmcr = miir(ctlr, ctlr->curphyad, Bmcr); + miir(ctlr, ctlr->curphyad, Bmsr); + bmsr = miir(ctlr, ctlr->curphyad, Bmsr); + debug("bmcr 0x%2.2uX bmsr 0x%2.2uX\n", bmcr, bmsr); + if(((bmcr & 0x1000) && !(bmsr & 0x0020)) || !(bmsr & 0x0004)) + return 0; + + if(bmcr & 0x1000){ + an = miir(ctlr, ctlr->curphyad, Anar); + an &= miir(ctlr, ctlr->curphyad, Anlpar) & 0x3E0; + debug("an 0x%2.uX 0x%2.2uX 0x%2.2uX\n", + miir(ctlr, ctlr->curphyad, Anar), + miir(ctlr, ctlr->curphyad, Anlpar), + an); + + if(an & 0x0100) + x = 0x4000; + else if(an & 0x0080) + x = 0x2000; + else if(an & 0x0040) + x = 0x1000; + else if(an & 0x0020) + x = 0x0800; + else + x = 0; + } + else if((bmcr & 0x2100) == 0x2100) + x = 0x4000; + else if(bmcr & 0x2000){ + /* + * If FD capable, force it if necessary. + */ + if((bmsr & 0x4000) && ctlr->fd){ + miiw(ctlr, ctlr->curphyad, Bmcr, 0x2100); + x = 0x4000; + } + else + x = 0x2000; + } + else if(bmcr & 0x0100) + x = 0x1000; + else + x = 0x0800; + + csr6 = Sc|Mbo|Hbd|Ps|Ca|TrMODE|Sb; + if(ctlr->fdx & x) + csr6 |= Fd; + if(ctlr->ttm & x) + csr6 |= Ttm; + debug("csr6 0x%8.8uX 0x%8.8uX 0x%8.8luX\n", + csr6, ctlr->csr6, csr32r(ctlr, 6)); + if(csr6 != ctlr->csr6){ + ctlr->csr6 = csr6; + csr32w(ctlr, 6, csr6); + } + + return 1; +} + +static int +typephymode(Ctlr* ctlr, uchar* block, int wait) +{ + uchar *p; + int len, mc, nway, phyx, timeo; + + if(DEBUG){ + int i; + + len = (block[0] & ~0x80)+1; + for(i = 0; i < len; i++) + debug("%2.2uX ", block[i]); + debug("\n"); + } + + if(block[1] == 1) + len = 1; + else if(block[1] == 3) + len = 2; + else + return -1; + + /* + * Snarf the media capabilities, nway advertisment, + * FDX and TTM bitmaps. + */ + p = &block[5+len*block[3]+len*block[4+len*block[3]]]; + mc = *p++; + mc |= *p++<<8; + nway = *p++; + nway |= *p++<<8; + ctlr->fdx = *p++; + ctlr->fdx |= *p++<<8; + ctlr->ttm = *p++; + ctlr->ttm |= *p<<8; + debug("mc %4.4uX nway %4.4uX fdx %4.4uX ttm %4.4uX\n", + mc, nway, ctlr->fdx, ctlr->ttm); + USED(mc); + + phyx = block[2]; + ctlr->curphyad = ctlr->phy[phyx]; + + ctlr->csr6 = 0;//Sc|Mbo|Hbd|Ps|Ca|TrMODE|Sb; + //csr32w(ctlr, 6, ctlr->csr6); + if(typephylink(ctlr, block)) + return 0; + + if(!(ctlr->phyreset & (1<<phyx))){ + debug("reset seq: len %d: ", block[3]); + if(ctlr->type5block) + type5block(ctlr, &ctlr->type5block[2]); + else + type5block(ctlr, &block[4+len*block[3]]); + debug("\n"); + ctlr->phyreset |= (1<<phyx); + } + + /* + * GPR sequence. + */ + debug("gpr seq: len %d: ", block[3]); + type5block(ctlr, &block[3]); + debug("\n"); + + ctlr->csr6 = 0;//Sc|Mbo|Hbd|Ps|Ca|TrMODE|Sb; + //csr32w(ctlr, 6, ctlr->csr6); + if(typephylink(ctlr, block)) + return 0; + + /* + * Turn off auto-negotiation, set the auto-negotiation + * advertisment register then start the auto-negotiation + * process again. + */ + miiw(ctlr, ctlr->curphyad, Bmcr, 0); + miiw(ctlr, ctlr->curphyad, Anar, nway|1); + miiw(ctlr, ctlr->curphyad, Bmcr, 0x1000); + + if(!wait) + return 0; + + for(timeo = 0; timeo < 45; timeo++){ + if(typephylink(ctlr, block)) + return 0; + delay(100); + } + + return -1; +} + +static int +typesymmode(Ctlr *ctlr, uchar *block, int wait) +{ + uint gpmode, gpdata, command; + + USED(wait); + gpmode = block[3] | ((uint) block[4] << 8); + gpdata = block[5] | ((uint) block[6] << 8); + command = (block[7] | ((uint) block[8] << 8)) & 0x71; + if (command & 0x8000) { + print("ether2114x.c: FIXME: handle type 4 mode blocks where cmd.active_invalid != 0\n"); + return -1; + } + csr32w(ctlr, 15, gpmode); + csr32w(ctlr, 15, gpdata); + ctlr->csr6 = (command & 0x71) << 18; + csr32w(ctlr, 6, ctlr->csr6); + return 0; +} + +static int +type2mode(Ctlr* ctlr, uchar* block, int) +{ + uchar *p; + int csr6, csr13, csr14, csr15, gpc, gpd; + + csr6 = Sc|Mbo|Ca|TrMODE|Sb; + debug("type2mode: medium 0x%2.2uX\n", block[2]); + + /* + * Don't attempt full-duplex + * unless explicitly requested. + */ + if((block[2] & 0x3F) == 0x04){ /* 10BASE-TFD */ + if(!ctlr->fd) + return -1; + csr6 |= Fd; + } + + /* + * Operating mode programming values from the datasheet + * unless media specific data is explicitly given. + */ + p = &block[3]; + if(block[2] & 0x40){ + csr13 = (block[4]<<8)|block[3]; + csr14 = (block[6]<<8)|block[5]; + csr15 = (block[8]<<8)|block[7]; + p += 6; + } + else switch(block[2] & 0x3F){ + default: + return -1; + case 0x00: /* 10BASE-T */ + csr13 = 0x00000001; + csr14 = 0x00007F3F; + csr15 = 0x00000008; + break; + case 0x01: /* 10BASE-2 */ + csr13 = 0x00000009; + csr14 = 0x00000705; + csr15 = 0x00000006; + break; + case 0x02: /* 10BASE-5 (AUI) */ + csr13 = 0x00000009; + csr14 = 0x00000705; + csr15 = 0x0000000E; + break; + case 0x04: /* 10BASE-TFD */ + csr13 = 0x00000001; + csr14 = 0x00007F3D; + csr15 = 0x00000008; + break; + } + gpc = *p++<<16; + gpc |= *p++<<24; + gpd = *p++<<16; + gpd |= *p<<24; + + csr32w(ctlr, 13, 0); + csr32w(ctlr, 14, csr14); + csr32w(ctlr, 15, gpc|csr15); + delay(10); + csr32w(ctlr, 15, gpd|csr15); + csr32w(ctlr, 13, csr13); + + ctlr->csr6 = csr6; + csr32w(ctlr, 6, ctlr->csr6); + + debug("type2mode: csr13 %8.8uX csr14 %8.8uX csr15 %8.8uX\n", + csr13, csr14, csr15); + debug("type2mode: gpc %8.8uX gpd %8.8uX csr6 %8.8uX\n", + gpc, gpd, csr6); + + return 0; +} + +static int +type0link(Ctlr* ctlr, uchar* block) +{ + int m, polarity, sense; + + m = (block[3]<<8)|block[2]; + sense = 1<<((m & 0x000E)>>1); + if(m & 0x0080) + polarity = sense; + else + polarity = 0; + + return (csr32r(ctlr, 12) & sense)^polarity; +} + +static int +type0mode(Ctlr* ctlr, uchar* block, int wait) +{ + int csr6, m, timeo; + + csr6 = Sc|Mbo|Hbd|Ca|TrMODE|Sb; +debug("type0: medium 0x%uX, fd %d: 0x%2.2uX 0x%2.2uX 0x%2.2uX 0x%2.2uX\n", + ctlr->medium, ctlr->fd, block[0], block[1], block[2], block[3]); + switch(block[0]){ + default: + break; + + case 0x04: /* 10BASE-TFD */ + case 0x05: /* 100BASE-TXFD */ + case 0x08: /* 100BASE-FXFD */ + /* + * Don't attempt full-duplex + * unless explicitly requested. + */ + if(!ctlr->fd) + return -1; + csr6 |= Fd; + break; + } + + m = (block[3]<<8)|block[2]; + if(m & 0x0001) + csr6 |= Ps; + if(m & 0x0010) + csr6 |= Ttm; + if(m & 0x0020) + csr6 |= Pcs; + if(m & 0x0040) + csr6 |= Scr; + + csr32w(ctlr, 12, block[1]); + microdelay(10); + csr32w(ctlr, 6, csr6); + ctlr->csr6 = csr6; + + if(!wait) + return 0; + + for(timeo = 0; timeo < 30; timeo++){ + if(type0link(ctlr, block)) + return 0; + delay(100); + } + + return -1; +} + +static int +media21041(Ether* ether, int wait) +{ + Ctlr* ctlr; + uchar *block; + int csr6, csr13, csr14, csr15, medium, timeo; + + ctlr = ether->ctlr; + block = ctlr->infoblock[ctlr->curk]; + debug("media21041: block[0] %2.2uX, medium %4.4uX sct %4.4uX\n", + block[0], ctlr->medium, ctlr->sct); + + medium = block[0] & 0x3F; + if(ctlr->medium >= 0 && medium != ctlr->medium) + return 0; + if(ctlr->sct != 0x0800 && (ctlr->sct & 0x3F) != medium) + return 0; + + csr6 = Sc|Mbo|Ca|TrMODE|Sb; + if(block[0] & 0x40){ + csr13 = (block[2]<<8)|block[1]; + csr14 = (block[4]<<8)|block[3]; + csr15 = (block[6]<<8)|block[5]; + } + else switch(medium){ + default: + return -1; + case 0x00: /* 10BASE-T */ + csr13 = 0xEF01; + csr14 = 0xFF3F; + csr15 = 0x0008; + break; + case 0x01: /* 10BASE-2 */ + csr13 = 0xEF09; + csr14 = 0xF73D; + csr15 = 0x0006; + break; + case 0x02: /* 10BASE-5 */ + csr13 = 0xEF09; + csr14 = 0xF73D; + csr15 = 0x000E; + break; + case 0x04: /* 10BASE-TFD */ + csr13 = 0xEF01; + csr14 = 0xFF3D; + csr15 = 0x0008; + break; + } + + csr32w(ctlr, 13, 0); + csr32w(ctlr, 14, csr14); + csr32w(ctlr, 15, csr15); + csr32w(ctlr, 13, csr13); + delay(10); + + if(medium == 0x04) + csr6 |= Fd; + ctlr->csr6 = csr6; + csr32w(ctlr, 6, ctlr->csr6); + + debug("media21041: csr6 %8.8uX csr13 %4.4uX csr14 %4.4uX csr15 %4.4uX\n", + csr6, csr13, csr14, csr15); + + if(!wait) + return 0; + + for(timeo = 0; timeo < 30; timeo++){ + if(!(csr32r(ctlr, 12) & 0x0002)){ + debug("media21041: ok: csr12 %4.4luX timeo %d\n", + csr32r(ctlr, 12), timeo); + return 10; + } + delay(100); + } + debug("media21041: !ok: csr12 %4.4luX\n", csr32r(ctlr, 12)); + + return -1; +} + +static int +mediaxx(Ether* ether, int wait) +{ + Ctlr* ctlr; + uchar *block; + + ctlr = ether->ctlr; + block = ctlr->infoblock[ctlr->curk]; + if(block[0] & 0x80){ + switch(block[1]){ + default: + return -1; + case 0: + if(ctlr->medium >= 0 && block[2] != ctlr->medium) + return 0; +/* need this test? */ if(ctlr->sct != 0x0800 && (ctlr->sct & 0x3F) != block[2]) + return 0; + if(type0mode(ctlr, block+2, wait)) + return 0; + break; + case 1: + if(typephymode(ctlr, block, wait)) + return 0; + break; + case 2: + debug("type2: medium %d block[2] %d\n", + ctlr->medium, block[2]); + if(ctlr->medium >= 0 && ((block[2] & 0x3F) != ctlr->medium)) + return 0; + if(type2mode(ctlr, block, wait)) + return 0; + break; + case 3: + if(typephymode(ctlr, block, wait)) + return 0; + break; + case 4: + debug("type4: medium %d block[2] %d\n", + ctlr->medium, block[2]); + if(ctlr->medium >= 0 && ((block[2] & 0x3F) != ctlr->medium)) + return 0; + if(typesymmode(ctlr, block, wait)) + return 0; + break; + } + } + else{ + if(ctlr->medium >= 0 && block[0] != ctlr->medium) + return 0; +/* need this test? */if(ctlr->sct != 0x0800 && (ctlr->sct & 0x3F) != block[0]) + return 0; + if(type0mode(ctlr, block, wait)) + return 0; + } + + if(ctlr->csr6){ + if(!(ctlr->csr6 & Ps) || (ctlr->csr6 & Ttm)) + return 10; + return 100; + } + + return 0; +} + +static int +media(Ether* ether, int wait) +{ + Ctlr* ctlr; + int k, mbps; + + ctlr = ether->ctlr; + for(k = 0; k < ctlr->k; k++){ + switch(ctlr->id){ + default: + mbps = mediaxx(ether, wait); + break; + case Tulip1: /* 21041 */ + mbps = media21041(ether, wait); + break; + } + if(mbps > 0) + return mbps; + if(ctlr->curk == 0) + ctlr->curk = ctlr->k-1; + else + ctlr->curk--; + } + + return 0; +} + +static char* mediatable[9] = { + "10BASE-T", /* TP */ + "10BASE-2", /* BNC */ + "10BASE-5", /* AUI */ + "100BASE-TX", + "10BASE-TFD", + "100BASE-TXFD", + "100BASE-T4", + "100BASE-FX", + "100BASE-FXFD", +}; + +static uchar en1207[] = { /* Accton EN1207-COMBO */ + 0x00, 0x00, 0xE8, /* [0] vendor ethernet code */ + 0x00, /* [3] spare */ + + 0x00, 0x08, /* [4] connection (LSB+MSB = 0x0800) */ + 0x1F, /* [6] general purpose control */ + 2, /* [7] block count */ + + 0x00, /* [8] media code (10BASE-TX) */ + 0x0B, /* [9] general purpose port data */ + 0x9E, 0x00, /* [10] command (LSB+MSB = 0x009E) */ + + 0x03, /* [8] media code (100BASE-TX) */ + 0x1B, /* [9] general purpose port data */ + 0x6D, 0x00, /* [10] command (LSB+MSB = 0x006D) */ + + /* There is 10BASE-2 as well, but... */ +}; + +static uchar ana6910fx[] = { /* Adaptec (Cogent) ANA-6910FX */ + 0x00, 0x00, 0x92, /* [0] vendor ethernet code */ + 0x00, /* [3] spare */ + + 0x00, 0x08, /* [4] connection (LSB+MSB = 0x0800) */ + 0x3F, /* [6] general purpose control */ + 1, /* [7] block count */ + + 0x07, /* [8] media code (100BASE-FX) */ + 0x03, /* [9] general purpose port data */ + 0x2D, 0x00 /* [10] command (LSB+MSB = 0x000D) */ +}; + +static uchar smc9332[] = { /* SMC 9332 */ + 0x00, 0x00, 0xC0, /* [0] vendor ethernet code */ + 0x00, /* [3] spare */ + + 0x00, 0x08, /* [4] connection (LSB+MSB = 0x0800) */ + 0x1F, /* [6] general purpose control */ + 2, /* [7] block count */ + + 0x00, /* [8] media code (10BASE-TX) */ + 0x00, /* [9] general purpose port data */ + 0x9E, 0x00, /* [10] command (LSB+MSB = 0x009E) */ + + 0x03, /* [8] media code (100BASE-TX) */ + 0x09, /* [9] general purpose port data */ + 0x6D, 0x00, /* [10] command (LSB+MSB = 0x006D) */ +}; + +static uchar* leaf21140[] = { + en1207, /* Accton EN1207-COMBO */ + ana6910fx, /* Adaptec (Cogent) ANA-6910FX */ + smc9332, /* SMC 9332 */ + nil, +}; + +/* + * Copied to ctlr->srom at offset 20. + */ +static uchar leafpnic[] = { + 0x00, 0x00, 0x00, 0x00, /* MAC address */ + 0x00, 0x00, + 0x00, /* controller 0 device number */ + 0x1E, 0x00, /* controller 0 info leaf offset */ + 0x00, /* reserved */ + 0x00, 0x08, /* selected connection type */ + 0x00, /* general purpose control */ + 0x01, /* block count */ + + 0x8C, /* format indicator and count */ + 0x01, /* block type */ + 0x00, /* PHY number */ + 0x00, /* GPR sequence length */ + 0x00, /* reset sequence length */ + 0x00, 0x78, /* media capabilities */ + 0xE0, 0x01, /* Nway advertisment */ + 0x00, 0x50, /* FDX bitmap */ + 0x00, 0x18, /* TTM bitmap */ +}; + +static int +srom(Ctlr* ctlr) +{ + int i, k, oui, phy, x; + uchar *p; + + /* + * This is a partial decoding of the SROM format described in + * 'Digital Semiconductor 21X4 Serial ROM Format, Version 4.05, + * 2-Mar-98'. Only the 2114[03] are handled, support for other + * controllers can be added as needed. + * Do a dummy read first to get the size and allocate ctlr->srom. + */ + sromr(ctlr, 0); + if(ctlr->srom == nil) + ctlr->srom = malloc((1<<ctlr->sromsz)*sizeof(ushort)); + for(i = 0; i < (1<<ctlr->sromsz); i++){ + x = sromr(ctlr, i); + ctlr->srom[2*i] = x; + ctlr->srom[2*i+1] = x>>8; + } + + if(DEBUG){ + print("srom:"); + for(i = 0; i < ((1<<ctlr->sromsz)*sizeof(ushort)); i++){ + if(i && ((i & 0x0F) == 0)) + print("\n "); + print(" %2.2uX", ctlr->srom[i]); + } + print("\n"); + } + + /* + * There are at least 2 SROM layouts: + * e.g. Digital EtherWORKS station address at offset 20; + * this complies with the 21140A SROM + * application note from Digital; + * e.g. SMC9332 station address at offset 0 followed by + * 2 additional bytes, repeated at offset + * 6; the 8 bytes are also repeated in + * reverse order at offset 8. + * To check which it is, read the SROM and check for the repeating + * patterns of the non-compliant cards; if that fails use the one at + * offset 20. + */ + ctlr->sromea = ctlr->srom; + for(i = 0; i < 8; i++){ + x = ctlr->srom[i]; + if(x != ctlr->srom[15-i] || x != ctlr->srom[16+i]){ + ctlr->sromea = &ctlr->srom[20]; + break; + } + } + + /* + * Fake up the SROM for the PNIC and AMDtek. + * They look like a 21140 with a PHY. + * The MAC address is byte-swapped in the orginal + * PNIC SROM data. + */ + if(ctlr->id == Pnic){ + memmove(&ctlr->srom[20], leafpnic, sizeof(leafpnic)); + for(i = 0; i < Eaddrlen; i += 2){ + ctlr->srom[20+i] = ctlr->srom[i+1]; + ctlr->srom[20+i+1] = ctlr->srom[i]; + } + } + if(ctlr->id == CentaurP){ + memmove(&ctlr->srom[20], leafpnic, sizeof(leafpnic)); + for(i = 0; i < Eaddrlen; i += 2){ + ctlr->srom[20+i] = ctlr->srom[8+i]; + ctlr->srom[20+i+1] = ctlr->srom[8+i+1]; + } + } + + /* + * Next, try to find the info leaf in the SROM for media detection. + * If it's a non-conforming card try to match the vendor ethernet code + * and point p at a fake info leaf with compact 21140 entries. + */ + if(ctlr->sromea == ctlr->srom){ + p = nil; + for(i = 0; leaf21140[i] != nil; i++){ + if(memcmp(leaf21140[i], ctlr->sromea, 3) == 0){ + p = &leaf21140[i][4]; + break; + } + } + if(p == nil) + return -1; + } + else + p = &ctlr->srom[(ctlr->srom[28]<<8)|ctlr->srom[27]]; + + /* + * Set up the info needed for later media detection. + * For the 21140, set the general-purpose mask in CSR12. + * The info block entries are stored in order of increasing + * precedence, so detection will work backwards through the + * stored indexes into ctlr->srom. + * If an entry is found which matches the selected connection + * type, save the index. Otherwise, start at the last entry. + * If any MII entries are found (type 1 and 3 blocks), scan + * for PHYs. + */ + ctlr->leaf = p; + ctlr->sct = *p++; + ctlr->sct |= *p++<<8; + if(ctlr->id != Tulip3 && ctlr->id != Tulip1){ + csr32w(ctlr, 12, Gpc|*p++); + delay(200); + } + ctlr->k = *p++; + if(ctlr->k >= nelem(ctlr->infoblock)) + ctlr->k = nelem(ctlr->infoblock)-1; + ctlr->sctk = ctlr->k-1; + phy = 0; + for(k = 0; k < ctlr->k; k++){ + ctlr->infoblock[k] = p; + if(ctlr->id == Tulip1){ + debug("type21041: 0x%2.2uX\n", p[0]); + if(ctlr->sct != 0x0800 && *p == (ctlr->sct & 0xFF)) + ctlr->sctk = k; + if(*p & 0x40) + p += 7; + else + p += 1; + } + /* + * The RAMIX PMC665 has a badly-coded SROM, + * hence the test for 21143 and type 3. + */ + else if((*p & 0x80) || (ctlr->id == Tulip3 && *(p+1) == 3)){ + *p |= 0x80; + if(*(p+1) == 1 || *(p+1) == 3) + phy = 1; + if(*(p+1) == 5) + ctlr->type5block = p; + p += (*p & ~0x80)+1; + } + else{ + debug("type0: 0x%2.2uX 0x%2.2uX 0x%2.2uX 0x%2.2uX\n", + p[0], p[1], p[2], p[3]); + if(ctlr->sct != 0x0800 && *p == (ctlr->sct & 0xFF)) + ctlr->sctk = k; + p += 4; + } + } + ctlr->curk = ctlr->sctk; + debug("sct 0x%uX medium 0x%uX k %d curk %d phy %d\n", + ctlr->sct, ctlr->medium, ctlr->k, ctlr->curk, phy); + + if(phy){ + x = 0; + for(k = 0; k < nelem(ctlr->phy); k++){ + if(ctlr->id == CentaurP && k != 1) + continue; + if((oui = miir(ctlr, k, 2)) == -1 || oui == 0) + continue; + debug("phy reg 2 %4.4uX\n", oui); + if(DEBUG){ + oui = (oui & 0x3FF)<<6; + oui |= miir(ctlr, k, 3)>>10; + miir(ctlr, k, 1); + debug("phy%d: index %d oui %uX reg1 %uX\n", + x, k, oui, miir(ctlr, k, 1)); + USED(oui); + } + ctlr->phy[x] = k; + } + } + + ctlr->fd = 0; + ctlr->medium = -1; + + return 0; +} + +static void +dec2114xpci(void) +{ + Ctlr *ctlr; + Pcidev *p; + int x; + + p = nil; + while(p = pcimatch(p, 0, 0)){ + if(p->ccrb != 0x02 || p->ccru != 0) + continue; + switch((p->did<<16)|p->vid){ + default: + continue; + + case Tulip3: /* 21143 */ + /* + * Exit sleep mode. + */ + x = pcicfgr32(p, 0x40); + x &= ~0xC0000000; + pcicfgw32(p, 0x40, x); + /*FALLTHROUGH*/ + + case Tulip0: /* 21140 */ + case Tulip1: /* 21041 */ + case Pnic: /* PNIC */ + case Pnic2: /* PNIC-II */ + case CentaurP: /* ADMtek */ + break; + } + + /* + * bar[0] is the I/O port register address and + * bar[1] is the memory-mapped register address. + */ + ctlr = malloc(sizeof(Ctlr)); + ctlr->port = p->mem[0].bar & ~0x01; + ctlr->pcidev = p; + ctlr->id = (p->did<<16)|p->vid; + + if(ioalloc(ctlr->port, p->mem[0].size, 0, "dec2114x") < 0){ + print("dec2114x: port 0x%uX in use\n", ctlr->port); + free(ctlr); + continue; + } + + /* + * Some cards (e.g. ANA-6910FX) seem to need the Ps bit + * set or they don't always work right after a hardware + * reset. + */ + csr32w(ctlr, 6, Mbo|Ps); + softreset(ctlr); + + if(srom(ctlr)){ + iofree(ctlr->port); + free(ctlr); + continue; + } + + switch(ctlr->id){ + default: + break; + + case Pnic: /* PNIC */ + /* + * Turn off the jabber timer. + */ + csr32w(ctlr, 15, 0x00000001); + break; + case CentaurP: + /* + * Nice - the register offsets change from *8 to *4 + * for CSR16 and up... + * CSR25/26 give the MAC address read from the SROM. + * Don't really need to use this other than as a check, + * the SROM will be read in anyway so the value there + * can be used directly. + */ + debug("csr25 %8.8luX csr26 %8.8luX\n", + inl(ctlr->port+0xA4), inl(ctlr->port+0xA8)); + debug("phyidr1 %4.4luX phyidr2 %4.4luX\n", + inl(ctlr->port+0xBC), inl(ctlr->port+0xC0)); + break; + } + + if(ctlrhead != nil) + ctlrtail->next = ctlr; + else + ctlrhead = ctlr; + ctlrtail = ctlr; + } +} + +static int +reset(Ether* ether) +{ + Ctlr *ctlr; + int i, x; + uchar ea[Eaddrlen]; + static int scandone; + + if(scandone == 0){ + dec2114xpci(); + scandone = 1; + } + + /* + * Any adapter matches if no ether->port is supplied, + * otherwise the ports must match. + */ + for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){ + if(ctlr->active) + continue; + if(ether->port == 0 || ether->port == ctlr->port){ + ctlr->active = 1; + break; + } + } + if(ctlr == nil) + return -1; + + ether->ctlr = ctlr; + ether->port = ctlr->port; + ether->irq = ctlr->pcidev->intl; + ether->tbdf = ctlr->pcidev->tbdf; + + /* + * Check if the adapter's station address is to be overridden. + * If not, read it from the EEPROM and set in ether->ea prior to + * loading the station address in the hardware. + */ + memset(ea, 0, Eaddrlen); + if(memcmp(ea, ether->ea, Eaddrlen) == 0) + memmove(ether->ea, ctlr->sromea, Eaddrlen); + + /* + * Look for a medium override in case there's no autonegotiation + * (no MII) or the autonegotiation fails. + */ + for(i = 0; i < ether->nopt; i++){ + if(cistrcmp(ether->opt[i], "FD") == 0){ + ctlr->fd = 1; + continue; + } + for(x = 0; x < nelem(mediatable); x++){ + debug("compare <%s> <%s>\n", mediatable[x], + ether->opt[i]); + if(cistrcmp(mediatable[x], ether->opt[i])) + continue; + ctlr->medium = x; + + switch(ctlr->medium){ + default: + ctlr->fd = 0; + break; + + case 0x04: /* 10BASE-TFD */ + case 0x05: /* 100BASE-TXFD */ + case 0x08: /* 100BASE-FXFD */ + ctlr->fd = 1; + break; + } + break; + } + } + + ether->mbps = media(ether, 1); + + /* + * Initialise descriptor rings, ethernet address. + */ + ctlr->nrdr = Nrde; + ctlr->ntdr = Ntde; + pcisetbme(ctlr->pcidev); + ctlrinit(ether); + + /* + * Linkage to the generic ethernet driver. + */ + ether->attach = attach; + ether->transmit = transmit; + ether->interrupt = interrupt; + ether->ifstat = ifstat; + + ether->arg = ether; + ether->shutdown = shutdown; + ether->multicast = multicast; + ether->promiscuous = promiscuous; + + return 0; +} + +void +ether2114xlink(void) +{ + addethercard("21140", reset); + addethercard("2114x", reset); +} diff --git a/os/pc/ether589.c b/os/pc/ether589.c new file mode 100644 index 00000000..ef19093f --- /dev/null +++ b/os/pc/ether589.c @@ -0,0 +1,214 @@ +/* + * 3C589 and 3C562. + * To do: + * check xcvr10Base2 still works (is GlobalReset necessary?). + */ +#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/netif.h" + +#include "etherif.h" + +enum { /* all windows */ + CommandR = 0x000E, + IntStatusR = 0x000E, +}; + +enum { /* Commands */ + GlobalReset = 0x0000, + SelectRegisterWindow = 0x0001, + RxReset = 0x0005, + TxReset = 0x000B, + AcknowledgeInterrupt = 0x000D, +}; + +enum { /* IntStatus bits */ + commandInProgress = 0x1000, +}; + +#define COMMAND(port, cmd, a) outs((port)+CommandR, ((cmd)<<11)|(a)) +#define STATUS(port) ins((port)+IntStatusR) + +enum { /* Window 0 - setup */ + Wsetup = 0x0000, + /* registers */ + ManufacturerID = 0x0000, /* 3C5[08]*, 3C59[27] */ + ProductID = 0x0002, /* 3C5[08]*, 3C59[27] */ + ConfigControl = 0x0004, /* 3C5[08]*, 3C59[27] */ + AddressConfig = 0x0006, /* 3C5[08]*, 3C59[27] */ + ResourceConfig = 0x0008, /* 3C5[08]*, 3C59[27] */ + EepromCommand = 0x000A, + EepromData = 0x000C, + /* AddressConfig Bits */ + autoSelect9 = 0x0080, + xcvrMask9 = 0xC000, + /* ConfigControl bits */ + Ena = 0x0001, + base10TAvailable9 = 0x0200, + coaxAvailable9 = 0x1000, + auiAvailable9 = 0x2000, + /* EepromCommand bits */ + EepromReadRegister = 0x0080, + EepromBusy = 0x8000, +}; + +enum { /* Window 1 - operating set */ + Wop = 0x0001, +}; + +enum { /* Window 3 - FIFO management */ + Wfifo = 0x0003, + /* registers */ + InternalConfig = 0x0000, /* 3C509B, 3C589, 3C59[0257] */ + /* InternalConfig bits */ + xcvr10BaseT = 0x00000000, + xcvr10Base2 = 0x00300000, +}; + +enum { /* Window 4 - diagnostic */ + Wdiagnostic = 0x0004, + /* registers */ + MediaStatus = 0x000A, + /* MediaStatus bits */ + linkBeatDetect = 0x0800, +}; + +extern int etherelnk3reset(Ether*); + +static char *tcmpcmcia[] = { + "3C589", /* 3COM 589[ABCD] */ + "3C562", /* 3COM 562 */ + "589E", /* 3COM Megahertz 589E */ + nil, +}; + +static int +configASIC(Ether* ether, int port, int xcvr) +{ + int x; + + /* set Window 0 configuration registers */ + COMMAND(port, SelectRegisterWindow, Wsetup); + outs(port+ConfigControl, Ena); + + /* IRQ must be 3 on 3C589/3C562 */ + outs(port + ResourceConfig, 0x3F00); + + x = ins(port+AddressConfig) & ~xcvrMask9; + x |= (xcvr>>20)<<14; + outs(port+AddressConfig, x); + + COMMAND(port, TxReset, 0); + while(STATUS(port) & commandInProgress) + ; + COMMAND(port, RxReset, 0); + while(STATUS(port) & commandInProgress) + ; + + return etherelnk3reset(ether); +} + +static int +reset(Ether* ether) +{ + int i, t, slot; + char *type; + int port; + enum { WantAny, Want10BT, Want10B2 }; + int want; + uchar ea[6]; + char *p; + + if(ether->irq == 0) + ether->irq = 10; + if(ether->port == 0) + ether->port = 0x240; + port = ether->port; + + if(ioalloc(port, 0x10, 0, "3C589") < 0) + return -1; + + type = nil; + slot = -1; + for(i = 0; tcmpcmcia[i] != nil; i++){ + type = tcmpcmcia[i]; + if((slot = pcmspecial(type, ether)) >= 0) + break; + } + if(slot < 0){ + iofree(port); + return -1; + } + + /* + * Read Ethernet address from card memory + * on 3C562, but only if the user has not + * overridden it. + */ + memset(ea, 0, sizeof ea); + if(memcmp(ea, ether->ea, 6) == 0 && strcmp(type, "3C562") == 0) { + if(pcmcistuple(slot, 0x88, -1, ea, 6) == 6) { + for(i = 0; i < 6; i += 2){ + t = ea[i]; + ea[i] = ea[i+1]; + ea[i+1] = t; + } + memmove(ether->ea, ea, 6); + } + } + /* + * Allow user to specify desired media in plan9.ini + */ + want = WantAny; + for(i = 0; i < ether->nopt; i++){ + if(cistrncmp(ether->opt[i], "media=", 6) != 0) + continue; + p = ether->opt[i]+6; + if(cistrcmp(p, "10base2") == 0) + want = Want10B2; + else if(cistrcmp(p, "10baseT") == 0) + want = Want10BT; + } + + /* try configuring as a 10BaseT */ + if(want==WantAny || want==Want10BT){ + if(configASIC(ether, port, xcvr10BaseT) < 0){ + pcmspecialclose(slot); + iofree(port); + return -1; + } + delay(100); + COMMAND(port, SelectRegisterWindow, Wdiagnostic); + if((ins(port+MediaStatus)&linkBeatDetect) || want==Want10BT){ + COMMAND(port, SelectRegisterWindow, Wop); + print("#l%d: xcvr10BaseT %s\n", ether->ctlrno, type); + return 0; + } + } + + /* try configuring as a 10base2 */ + if(want==WantAny || want==Want10B2){ + COMMAND(port, GlobalReset, 0); + if(configASIC(ether, port, xcvr10Base2) < 0){ + pcmspecialclose(slot); + iofree(port); + return -1; + } + print("#l%d: xcvr10Base2 %s\n", ether->ctlrno, type); + return 0; + } + return -1; /* not reached */ +} + +void +ether589link(void) +{ + addethercard("3C589", reset); + addethercard("3C562", reset); + addethercard("589E", reset); +} diff --git a/os/pc/ether79c960.c b/os/pc/ether79c960.c new file mode 100644 index 00000000..f74574cd --- /dev/null +++ b/os/pc/ether79c960.c @@ -0,0 +1,523 @@ +/* + * AM79C960 + * PCnet Single-Chip Ethernet Controller for ISA Bus + * To do: + * only issue transmit interrupt if necessary? + * dynamically increase rings as necessary? + * use Blocks as receive buffers? + * currently hardwires 10Base-T + */ +#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/netif.h" + +#include "etherif.h" + +#define chatty 1 +#define DPRINT if(chatty)print + +enum { + Lognrdre = 6, + Nrdre = (1<<Lognrdre), /* receive descriptor ring entries */ + Logntdre = 4, + Ntdre = (1<<Logntdre), /* transmit descriptor ring entries */ + + Rbsize = ETHERMAXTU+4, /* ring buffer size (+4 for CRC) */ +}; + +enum { /* I/O resource map */ + Aprom = 0x0000, /* physical address */ + Rdp = 0x0010, /* register data port */ + Rap = 0x0012, /* register address port */ + Sreset = 0x0014, /* software reset */ + /*Bdp = 0x001C, /* bus configuration register data port */ + Idp = 0x0016, /* ISA data port */ +}; + +enum { /* ISACSR2 */ + Isa10 = 0x0001, /* 10base-T */ + Isamedia = 0x0003, /* media selection mask */ + Isaawake = 0x0004, /* Auto-Wake */ +}; + +enum { /* CSR0 */ + Init = 0x0001, /* begin initialisation */ + Strt = 0x0002, /* enable chip */ + Stop = 0x0004, /* disable chip */ + Tdmd = 0x0008, /* transmit demand */ + Txon = 0x0010, /* transmitter on */ + Rxon = 0x0020, /* receiver on */ + Iena = 0x0040, /* interrupt enable */ + Intr = 0x0080, /* interrupt flag */ + Idon = 0x0100, /* initialisation done */ + Tint = 0x0200, /* transmit interrupt */ + Rint = 0x0400, /* receive interrupt */ + Merr = 0x0800, /* memory error */ + Miss = 0x1000, /* missed frame */ + Cerr = 0x2000, /* collision */ + Babl = 0x4000, /* transmitter timeout */ + Err = 0x8000, /* Babl|Cerr|Miss|Merr */ +}; + +enum { /* CSR3 */ + Emba = 0x0008, /* enable modified back-off algorithm */ + Dxmt2pd = 0x0010, /* disable transmit two part deferral */ + Lappen = 0x0020, /* look-ahead packet processing enable */ + Idonm = 0x0100, /* initialisation done mask */ + Tintm = 0x0200, /* transmit interrupt mask */ + Rintm = 0x0400, /* receive interrupt mask */ + Merrm = 0x0800, /* memory error mask */ + Missm = 0x1000, /* missed frame mask */ + Bablm = 0x4000, /* babl mask */ +}; + +enum { /* CSR4 */ + ApadXmt = 0x0800, /* auto pad transmit */ +}; + +enum { /* CSR15 */ + Prom = 0x8000, /* promiscuous mode */ + TenBaseT = 0x0080, /* 10Base-T */ +}; + +typedef struct { /* Initialisation Block */ + ushort mode; + uchar padr[6]; + uchar ladr[8]; + ushort rdra0; /* bits 0-15 */ + uchar rdra16; /* bits 16-23 */ + uchar rlen; /* upper 3 bits */ + ushort tdra0; /* bits 0-15 */ + uchar tdra16; /* bits 16-23 */ + uchar tlen; /* upper 3 bits */ +} Iblock; + +typedef struct { /* receive descriptor ring entry */ + ushort rbadr; /* buffer address 0-15 */ + ushort rmd1; /* status|buffer address 16-23 */ + ushort rmd2; /* bcnt */ + ushort rmd3; /* mcnt */ +} Rdre; + +typedef struct { /* transmit descriptor ring entry */ + ushort tbadr; /* buffer address 0-15 */ + ushort tmd1; /* status|buffer address 16-23 */ + ushort tmd2; /* bcnt */ + ushort tmd3; /* errors */ +} Tdre; + +enum { /* [RT]dre status bits */ + Enp = 0x0100, /* end of packet */ + Stp = 0x0200, /* start of packet */ + RxBuff = 0x0400, /* buffer error */ + TxDef = 0x0400, /* deferred */ + RxCrc = 0x0800, /* CRC error */ + TxOne = 0x0800, /* one retry needed */ + RxOflo = 0x1000, /* overflow error */ + TxMore = 0x1000, /* more than one retry needed */ + Fram = 0x2000, /* framing error */ + RxErr = 0x4000, /* Fram|Oflo|Crc|RxBuff */ + TxErr = 0x4000, /* Uflo|Lcol|Lcar|Rtry */ + Own = 0x8000, +}; + +typedef struct { + Lock; + + int init; /* initialisation in progress */ + Iblock iblock; + + Rdre* rdr; /* receive descriptor ring */ + void* rrb; /* receive ring buffers */ + int rdrx; /* index into rdr */ + + Tdre* tdr; /* transmit descriptor ring */ + void* trb; /* transmit ring buffers */ + int tdrx; /* index into tdr */ +} Ctlr; + +static void +attach(Ether* ether) +{ + Ctlr *ctlr; + int port; + + ctlr = ether->ctlr; + ilock(ctlr); + if(ctlr->init){ + iunlock(ctlr); + return; + } + port = ether->port; + outs(port+Rdp, Iena|Strt); + iunlock(ctlr); +} + +static void +ringinit(Ctlr* ctlr) +{ + int i, x; + + /* + * Initialise the receive and transmit buffer rings. The ring + * entries must be aligned on 16-byte boundaries. + * + * This routine is protected by ctlr->init. + */ + if(ctlr->rdr == 0) + ctlr->rdr = xspanalloc(Nrdre*sizeof(Rdre), 0x10, 0); + if(ctlr->rrb == 0) + ctlr->rrb = xalloc(Nrdre*Rbsize); + + x = PADDR(ctlr->rrb); + if ((x >> 24)&0xFF) + panic("ether79c960: address>24bit"); + for(i = 0; i < Nrdre; i++){ + ctlr->rdr[i].rbadr = x&0xFFFF; + ctlr->rdr[i].rmd1 = Own|(x>>16)&0xFF; + x += Rbsize; + ctlr->rdr[i].rmd2 = 0xF000|-Rbsize&0x0FFF; + ctlr->rdr[i].rmd3 = 0; + } + ctlr->rdrx = 0; + + if(ctlr->tdr == 0) + ctlr->tdr = xspanalloc(Ntdre*sizeof(Tdre), 0x10, 0); + if(ctlr->trb == 0) + ctlr->trb = xalloc(Ntdre*Rbsize); + + x = PADDR(ctlr->trb); + if ((x >> 24)&0xFF) + panic("ether79c960: address>24bit"); + for(i = 0; i < Ntdre; i++){ + ctlr->tdr[i].tbadr = x&0xFFFF; + ctlr->tdr[i].tmd1 = (x>>16)&0xFF; + x += Rbsize; + ctlr->tdr[i].tmd2 = 0xF000|-Rbsize&0x0FFF; + } + ctlr->tdrx = 0; +} + +static void +promiscuous(void* arg, int on) +{ + Ether *ether; + int port, x; + Ctlr *ctlr; + + ether = arg; + port = ether->port; + ctlr = ether->ctlr; + + /* + * Put the chip into promiscuous mode. First we must wait until + * anyone transmitting is done, then we can stop the chip and put + * it in promiscuous mode. Restarting is made harder by the chip + * reloading the transmit and receive descriptor pointers with their + * base addresses when Strt is set (unlike the older Lance chip), + * so the rings must be re-initialised. + */ + ilock(ctlr); + if(ctlr->init){ + iunlock(ctlr); + return; + } + ctlr->init = 1; + iunlock(ctlr); + + outs(port+Rdp, Stop); + + outs(port+Rap, 15); + x = ins(port+Rdp) & ~Prom; + if(on) + x |= Prom; /* BUG: multicast ... */ + outs(port+Rdp, x); + outs(port+Rap, 0); + + ringinit(ctlr); + + ilock(ctlr); + ctlr->init = 0; + outs(port+Rdp, Iena|Strt); + iunlock(ctlr); +} + +static int +owntdre(void* arg) +{ + return (((Tdre*)arg)->tmd1 & Own) == 0; +} + +static void +txstart(Ether *ether) +{ + int port; + Ctlr *ctlr; + Tdre *tdre; + Etherpkt *pkt; + Block *bp; + int n; + + port = ether->port; + ctlr = ether->ctlr; + + if(ctlr->init) + return; + + /* + * Take the next transmit buffer, if it is free. + */ + tdre = &ctlr->tdr[ctlr->tdrx]; + if(owntdre(tdre) == 0) + return; + bp = qget(ether->oq); + if(bp == nil) + return; + + /* + * Copy the packet to the transmit buffer and fill in our + * source ethernet address. There's no need to pad to ETHERMINTU + * here as we set ApadXmit in CSR4. + */ + n = BLEN(bp); + pkt = KADDR(tdre->tbadr|(tdre->tmd1&0xFF)<<16); + memmove(pkt->d, bp->rp, n); + memmove(pkt->s, ether->ea, sizeof(pkt->s)); + freeb(bp); + + /* + * Give ownership of the descriptor to the chip, increment the + * software ring descriptor pointer and tell the chip to poll. + */ + tdre->tmd3 = 0; + tdre->tmd2 = 0xF000|(-n)&0x0FFF; + tdre->tmd1 |= Own|Stp|Enp; + ctlr->tdrx = NEXT(ctlr->tdrx, Ntdre); + outs(port+Rdp, Iena|Tdmd); + + ether->outpackets++; +} + +static void +transmit(Ether *ether) +{ + Ctlr *ctlr; + + ctlr = ether->ctlr; + + ilock(ctlr); + txstart(ether); + iunlock(ctlr); +} + +static void +interrupt(Ureg*, void* arg) +{ + Ether *ether; + int port, csr0, status; + Ctlr *ctlr; + Rdre *rdre; + Etherpkt *pkt; + Block *bp; + int len; + + ether = arg; + port = ether->port; + ctlr = ether->ctlr; + + /* + * Acknowledge all interrupts and whine about those that shouldn't + * happen. + */ + csr0 = ins(port+Rdp); + outs(port+Rdp, Babl|Cerr|Miss|Merr|Rint|Tint|Iena); + if(csr0 & (Babl|Miss|Merr)) + print("AMD70C960#%d: csr0 = 0x%uX\n", ether->ctlrno, csr0); + + /* + * Receiver interrupt: run round the descriptor ring logging + * errors and passing valid receive data up to the higher levels + * until we encounter a descriptor still owned by the chip. + */ + if(csr0 & Rint){ + rdre = &ctlr->rdr[ctlr->rdrx]; + while(((status = rdre->rmd1) & Own) == 0){ + if(status & RxErr){ + if(status & RxBuff) + ether->buffs++; + if(status & RxCrc) + ether->crcs++; + if(status & RxOflo) + ether->overflows++; + } + else { + len = (rdre->rmd3 & 0x0FFF)-4; + if((bp = iallocb(len)) != nil){ + ether->inpackets++; + pkt = KADDR(rdre->rbadr|(rdre->rmd1&0xFF)<<16); + memmove(bp->wp, pkt, len); + bp->wp += len; + etheriq(ether, bp, 1); + } + } + + /* + * Finished with this descriptor, reinitialise it, + * give it back to the chip, then on to the next... + */ + rdre->rmd3 = 0; + rdre->rmd2 = 0xF000|-Rbsize&0x0FFF; + rdre->rmd1 |= Own; + + ctlr->rdrx = NEXT(ctlr->rdrx, Nrdre); + rdre = &ctlr->rdr[ctlr->rdrx]; + } + } + + /* + * Transmitter interrupt: start next block if waiting for free descriptor. + */ + if(csr0 & Tint){ + lock(ctlr); + txstart(ether); + unlock(ctlr); + } +} + +static int +reset(Ether* ether) +{ + int port, x, i; + uchar ea[Eaddrlen]; + Ctlr *ctlr; + + if(ether->port == 0) + ether->port = 0x300; + if(ether->irq == 0) + ether->irq = 10; + if(ether->irq == 2) + ether->irq = 9; + if(ether->dma == 0) + ether->dma = 5; + port = ether->port; + + if(port == 0 || ether->dma == 0) + return -1; + + /* + * Allocate a controller structure and start to fill in the + * initialisation block (must be DWORD aligned). + */ + ether->ctlr = malloc(sizeof(Ctlr)); + ctlr = ether->ctlr; + + ilock(ctlr); + ctlr->init = 1; + + /* + * Set the auto pad transmit in CSR4. + */ + /*outs(port+Rdp, 0x00);/**/ + ins(port+Sreset); /**/ + delay(1); + outs(port+Rap, 0); + outs(port+Rdp, Stop); + + outs(port+Rap, 4); + x = ins(port+Rdp) & 0xFFFF; + outs(port+Rdp, ApadXmt|x); + + outs(port+Rap, 0); + + /* + * Check if we are going to override the adapter's station address. + * If not, read it from the I/O-space and set in ether->ea prior to loading the + * station address in the initialisation block. + */ + memset(ea, 0, Eaddrlen); + if(memcmp(ea, ether->ea, Eaddrlen) == 0){ + for(i=0; i<6; i++) + ether->ea[i] = inb(port + Aprom + i); + } + + ctlr->iblock.rlen = Lognrdre<<5; + ctlr->iblock.tlen = Logntdre<<5; + memmove(ctlr->iblock.padr, ether->ea, sizeof(ctlr->iblock.padr)); + + ringinit(ctlr); + + x = PADDR(ctlr->rdr); + ctlr->iblock.rdra0 = x&0xFFFF; + ctlr->iblock.rdra16 = (x >> 16)&0xFF; + x = PADDR(ctlr->tdr); + ctlr->iblock.tdra0 = x&0xFFFF; + ctlr->iblock.tdra16 = (x >> 16)&0xFF; + + /* + * set the DMA controller to cascade mode for bus master + */ + switch(ether->dma){ + case 5: + outb(0xd6, 0xc1); outb(0xd4, 1); break; + case 6: + outb(0xd6, 0xc2); outb(0xd4, 2); break; + case 7: + outb(0xd6, 0xc3); outb(0xd4, 3); break; + } + + /* + * Ensure 10Base-T (for now) + */ + ctlr->iblock.mode = TenBaseT; + outs(port+Rap, 2); + x = ins(port+Idp); + x &= ~Isamedia; + x |= Isa10; + x |= Isaawake; + outs(port+Idp, x); + + /* + * Point the chip at the initialisation block and tell it to go. + * Mask the Idon interrupt and poll for completion. Strt and interrupt + * enables will be set later when we're ready to attach to the network. + */ + x = PADDR(&ctlr->iblock); + if((x>>24)&0xFF) + panic("ether79c960: address>24bit"); + outs(port+Rap, 1); + outs(port+Rdp, x & 0xFFFF); + outs(port+Rap, 2); + outs(port+Rdp, (x>>16) & 0xFF); + outs(port+Rap, 3); + outs(port+Rdp, Idonm); + outs(port+Rap, 0); + outs(port+Rdp, Init); + + while((ins(port+Rdp) & Idon) == 0) + ; + outs(port+Rdp, Idon|Stop); + ctlr->init = 0; + iunlock(ctlr); + + ether->port = port; + ether->attach = attach; + ether->transmit = transmit; + ether->interrupt = interrupt; + ether->ifstat = 0; + + ether->promiscuous = promiscuous; + ether->arg = ether; + + return 0; +} + +void +ether79c960link(void) +{ + addethercard("AMD79C960", reset); +} diff --git a/os/pc/ether79c970.c b/os/pc/ether79c970.c new file mode 100644 index 00000000..25bf4185 --- /dev/null +++ b/os/pc/ether79c970.c @@ -0,0 +1,640 @@ +/* + * AMD79C970 + * PCnet-PCI Single-Chip Ethernet Controller for PCI Local Bus + * To do: + * finish this rewrite + */ +#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/netif.h" + +#include "etherif.h" + +enum { + Lognrdre = 6, + Nrdre = (1<<Lognrdre),/* receive descriptor ring entries */ + Logntdre = 4, + Ntdre = (1<<Logntdre),/* transmit descriptor ring entries */ + + Rbsize = ETHERMAXTU+4, /* ring buffer size (+4 for CRC) */ +}; + +enum { /* DWIO I/O resource map */ + Aprom = 0x0000, /* physical address */ + Rdp = 0x0010, /* register data port */ + Rap = 0x0014, /* register address port */ + Sreset = 0x0018, /* software reset */ + Bdp = 0x001C, /* bus configuration register data port */ +}; + +enum { /* CSR0 */ + Init = 0x0001, /* begin initialisation */ + Strt = 0x0002, /* enable chip */ + Stop = 0x0004, /* disable chip */ + Tdmd = 0x0008, /* transmit demand */ + Txon = 0x0010, /* transmitter on */ + Rxon = 0x0020, /* receiver on */ + Iena = 0x0040, /* interrupt enable */ + Intr = 0x0080, /* interrupt flag */ + Idon = 0x0100, /* initialisation done */ + Tint = 0x0200, /* transmit interrupt */ + Rint = 0x0400, /* receive interrupt */ + Merr = 0x0800, /* memory error */ + Miss = 0x1000, /* missed frame */ + Cerr = 0x2000, /* collision */ + Babl = 0x4000, /* transmitter timeout */ + Err = 0x8000, /* Babl|Cerr|Miss|Merr */ +}; + +enum { /* CSR3 */ + Bswp = 0x0004, /* byte swap */ + Emba = 0x0008, /* enable modified back-off algorithm */ + Dxmt2pd = 0x0010, /* disable transmit two part deferral */ + Lappen = 0x0020, /* look-ahead packet processing enable */ +}; + +enum { /* CSR4 */ + ApadXmt = 0x0800, /* auto pad transmit */ +}; + +enum { /* CSR15 */ + Prom = 0x8000, /* promiscuous mode */ +}; + +typedef struct Iblock Iblock; +struct Iblock { /* Initialisation Block */ + ushort mode; + uchar rlen; /* upper 4 bits */ + uchar tlen; /* upper 4 bits */ + uchar padr[6]; + uchar res[2]; + uchar ladr[8]; + ulong rdra; + ulong tdra; +}; + +typedef struct Dre Dre; +struct Dre { /* descriptor ring entry */ + ulong addr; + ulong md1; /* status|bcnt */ + ulong md2; /* rcc|rpc|mcnt */ + Block* bp; +}; + +enum { /* md1 */ + Enp = 0x01000000, /* end of packet */ + Stp = 0x02000000, /* start of packet */ + RxBuff = 0x04000000, /* buffer error */ + Def = 0x04000000, /* deferred */ + Crc = 0x08000000, /* CRC error */ + One = 0x08000000, /* one retry needed */ + Oflo = 0x10000000, /* overflow error */ + More = 0x10000000, /* more than one retry needed */ + Fram = 0x20000000, /* framing error */ + RxErr = 0x40000000, /* Fram|Oflo|Crc|RxBuff */ + TxErr = 0x40000000, /* Uflo|Lcol|Lcar|Rtry */ + Own = 0x80000000, +}; + +enum { /* md2 */ + Rtry = 0x04000000, /* failed after repeated retries */ + Lcar = 0x08000000, /* loss of carrier */ + Lcol = 0x10000000, /* late collision */ + Uflo = 0x40000000, /* underflow error */ + TxBuff = 0x80000000, /* buffer error */ +}; + +typedef struct Ctlr Ctlr; +struct Ctlr { + Lock; + int port; + Pcidev* pcidev; + Ctlr* next; + int active; + + int init; /* initialisation in progress */ + Iblock iblock; + + Dre* rdr; /* receive descriptor ring */ + int rdrx; + + Dre* tdr; /* transmit descriptor ring */ + int tdrh; /* host index into tdr */ + int tdri; /* interface index into tdr */ + int ntq; /* descriptors active */ + + ulong rxbuff; /* receive statistics */ + ulong crc; + ulong oflo; + ulong fram; + + ulong rtry; /* transmit statistics */ + ulong lcar; + ulong lcol; + ulong uflo; + ulong txbuff; + + ulong merr; /* bobf is such a whiner */ + ulong miss; + ulong babl; + + int (*ior)(Ctlr*, int); + void (*iow)(Ctlr*, int, int); +}; + +static Ctlr* ctlrhead; +static Ctlr* ctlrtail; + +/* + * The Rdp, Rap, Sreset, Bdp ports are 32-bit port offset in the enumeration above. + * To get to 16-bit offsets, scale down with 0x10 staying the same. + */ +static int +io16r(Ctlr *c, int r) +{ + if(r >= Rdp) + r = (r-Rdp)/2+Rdp; + return ins(c->port+r); +} + +static void +io16w(Ctlr *c, int r, int v) +{ + if(r >= Rdp) + r = (r-Rdp)/2+Rdp; + outs(c->port+r, v); +} + +static int +io32r(Ctlr *c, int r) +{ + return inl(c->port+r); +} + +static void +io32w(Ctlr *c, int r, int v) +{ + outl(c->port+r, v); +} + +static void +attach(Ether*) +{ +} + +static long +ifstat(Ether* ether, void* a, long n, ulong offset) +{ + char *p; + int len; + Ctlr *ctlr; + + ctlr = ether->ctlr; + + ether->crcs = ctlr->crc; + ether->frames = ctlr->fram; + ether->buffs = ctlr->rxbuff+ctlr->txbuff; + ether->overflows = ctlr->oflo; + + if(n == 0) + return 0; + + p = malloc(READSTR); + len = snprint(p, READSTR, "Rxbuff: %ld\n", ctlr->rxbuff); + len += snprint(p+len, READSTR-len, "Crc: %ld\n", ctlr->crc); + len += snprint(p+len, READSTR-len, "Oflo: %ld\n", ctlr->oflo); + len += snprint(p+len, READSTR-len, "Fram: %ld\n", ctlr->fram); + len += snprint(p+len, READSTR-len, "Rtry: %ld\n", ctlr->rtry); + len += snprint(p+len, READSTR-len, "Lcar: %ld\n", ctlr->lcar); + len += snprint(p+len, READSTR-len, "Lcol: %ld\n", ctlr->lcol); + len += snprint(p+len, READSTR-len, "Uflo: %ld\n", ctlr->uflo); + len += snprint(p+len, READSTR-len, "Txbuff: %ld\n", ctlr->txbuff); + len += snprint(p+len, READSTR-len, "Merr: %ld\n", ctlr->merr); + len += snprint(p+len, READSTR-len, "Miss: %ld\n", ctlr->miss); + snprint(p+len, READSTR-len, "Babl: %ld\n", ctlr->babl); + + n = readstr(offset, a, n, p); + free(p); + + return n; +} + +static void +ringinit(Ctlr* ctlr) +{ + Dre *dre; + + /* + * Initialise the receive and transmit buffer rings. + * The ring entries must be aligned on 16-byte boundaries. + * + * This routine is protected by ctlr->init. + */ + if(ctlr->rdr == 0){ + ctlr->rdr = xspanalloc(Nrdre*sizeof(Dre), 0x10, 0); + for(dre = ctlr->rdr; dre < &ctlr->rdr[Nrdre]; dre++){ + dre->bp = iallocb(Rbsize); + if(dre->bp == nil) + panic("can't allocate ethernet receive ring\n"); + dre->addr = PADDR(dre->bp->rp); + dre->md2 = 0; + dre->md1 = Own|(-Rbsize & 0xFFFF); + } + } + ctlr->rdrx = 0; + + if(ctlr->tdr == 0) + ctlr->tdr = xspanalloc(Ntdre*sizeof(Dre), 0x10, 0); + memset(ctlr->tdr, 0, Ntdre*sizeof(Dre)); + ctlr->tdrh = ctlr->tdri = 0; +} + +static void +promiscuous(void* arg, int on) +{ + Ether *ether; + int x; + Ctlr *ctlr; + + ether = arg; + ctlr = ether->ctlr; + + /* + * Put the chip into promiscuous mode. First must wait until + * anyone transmitting is done, then stop the chip and put + * it in promiscuous mode. Restarting is made harder by the chip + * reloading the transmit and receive descriptor pointers with their + * base addresses when Strt is set (unlike the older Lance chip), + * so the rings must be re-initialised. + */ + ilock(ctlr); + if(ctlr->init){ + iunlock(ctlr); + return; + } + ctlr->init = 1; + iunlock(ctlr); + + while(ctlr->ntq) + ; + + ctlr->iow(ctlr, Rdp, Stop); + + ctlr->iow(ctlr, Rap, 15); + x = ctlr->ior(ctlr, Rdp) & ~Prom; + if(on) + x |= Prom; + ctlr->iow(ctlr, Rdp, x); + ctlr->iow(ctlr, Rap, 0); + + ringinit(ctlr); + + ilock(ctlr); + ctlr->init = 0; + ctlr->iow(ctlr, Rdp, Iena|Strt); + iunlock(ctlr); +} + +static void +txstart(Ether* ether) +{ + Ctlr *ctlr; + Block *bp; + Dre *dre; + + ctlr = ether->ctlr; + + if(ctlr->init) + return; + + while(ctlr->ntq < (Ntdre-1)){ + bp = qget(ether->oq); + if(bp == nil) + break; + + /* + * Give ownership of the descriptor to the chip, + * increment the software ring descriptor pointer + * and tell the chip to poll. + * There's no need to pad to ETHERMINTU + * here as ApadXmt is set in CSR4. + */ + dre = &ctlr->tdr[ctlr->tdrh]; + dre->bp = bp; + dre->addr = PADDR(bp->rp); + dre->md2 = 0; + dre->md1 = Own|Stp|Enp|(-BLEN(bp) & 0xFFFF); + ctlr->ntq++; + ctlr->iow(ctlr, Rdp, Iena|Tdmd); + ctlr->tdrh = NEXT(ctlr->tdrh, Ntdre); + } +} + +static void +transmit(Ether* ether) +{ + Ctlr *ctlr; + + ctlr = ether->ctlr; + ilock(ctlr); + txstart(ether); + iunlock(ctlr); +} + +static void +interrupt(Ureg*, void* arg) +{ + Ctlr *ctlr; + Ether *ether; + int csr0, len; + Dre *dre; + Block *bp; + + ether = arg; + ctlr = ether->ctlr; + + /* + * Acknowledge all interrupts and whine about those that shouldn't + * happen. + */ +intrloop: + csr0 = ctlr->ior(ctlr, Rdp) & 0xFFFF; + ctlr->iow(ctlr, Rdp, Babl|Cerr|Miss|Merr|Rint|Tint|Iena); + if(csr0 & Merr) + ctlr->merr++; + if(csr0 & Miss) + ctlr->miss++; + if(csr0 & Babl) + ctlr->babl++; + //if(csr0 & (Babl|Miss|Merr)) + // print("#l%d: csr0 = 0x%uX\n", ether->ctlrno, csr0); + if(!(csr0 & (Rint|Tint))) + return; + + /* + * Receiver interrupt: run round the descriptor ring logging + * errors and passing valid receive data up to the higher levels + * until a descriptor is encountered still owned by the chip. + */ + if(csr0 & Rint){ + dre = &ctlr->rdr[ctlr->rdrx]; + while(!(dre->md1 & Own)){ + if(dre->md1 & RxErr){ + if(dre->md1 & RxBuff) + ctlr->rxbuff++; + if(dre->md1 & Crc) + ctlr->crc++; + if(dre->md1 & Oflo) + ctlr->oflo++; + if(dre->md1 & Fram) + ctlr->fram++; + } + else if(bp = iallocb(Rbsize)){ + len = (dre->md2 & 0x0FFF)-4; + dre->bp->wp = dre->bp->rp+len; + etheriq(ether, dre->bp, 1); + dre->bp = bp; + dre->addr = PADDR(bp->rp); + } + + /* + * Finished with this descriptor, reinitialise it, + * give it back to the chip, then on to the next... + */ + dre->md2 = 0; + dre->md1 = Own|(-Rbsize & 0xFFFF); + + ctlr->rdrx = NEXT(ctlr->rdrx, Nrdre); + dre = &ctlr->rdr[ctlr->rdrx]; + } + } + + /* + * Transmitter interrupt: wakeup anyone waiting for a free descriptor. + */ + if(csr0 & Tint){ + lock(ctlr); + while(ctlr->ntq){ + dre = &ctlr->tdr[ctlr->tdri]; + if(dre->md1 & Own) + break; + + if(dre->md1 & TxErr){ + if(dre->md2 & Rtry) + ctlr->rtry++; + if(dre->md2 & Lcar) + ctlr->lcar++; + if(dre->md2 & Lcol) + ctlr->lcol++; + if(dre->md2 & Uflo) + ctlr->uflo++; + if(dre->md2 & TxBuff) + ctlr->txbuff++; + ether->oerrs++; + } + + freeb(dre->bp); + + ctlr->ntq--; + ctlr->tdri = NEXT(ctlr->tdri, Ntdre); + } + txstart(ether); + unlock(ctlr); + } + goto intrloop; +} + +static void +amd79c970pci(void) +{ + int port; + Ctlr *ctlr; + Pcidev *p; + + p = nil; + while(p = pcimatch(p, 0x1022, 0x2000)){ + port = p->mem[0].bar & ~0x01; + if(ioalloc(port, p->mem[0].size, 0, "amd79c970") < 0){ + print("amd79c970: port 0x%uX in use\n", port); + continue; + } + ctlr = malloc(sizeof(Ctlr)); + ctlr->port = p->mem[0].bar & ~0x01; + ctlr->pcidev = p; + + if(ctlrhead != nil) + ctlrtail->next = ctlr; + else + ctlrhead = ctlr; + ctlrtail = ctlr; + } +} + +static int +reset(Ether* ether) +{ + int x; + uchar ea[Eaddrlen]; + Ctlr *ctlr; + + if(ctlrhead == nil) + amd79c970pci(); + + /* + * Any adapter matches if no port is supplied, + * otherwise the ports must match. + */ + for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){ + if(ctlr->active) + continue; + if(ether->port == 0 || ether->port == ctlr->port){ + ctlr->active = 1; + break; + } + } + if(ctlr == nil) + return -1; + + /* + * Allocate a controller structure and start to initialise it. + */ + ether->ctlr = ctlr; + ether->port = ctlr->port; + ether->irq = ctlr->pcidev->intl; + ether->tbdf = ctlr->pcidev->tbdf; + pcisetbme(ctlr->pcidev); + ilock(ctlr); + ctlr->init = 1; + + io32r(ctlr, Sreset); + io16r(ctlr, Sreset); + + if(io16w(ctlr, Rap, 0), io16r(ctlr, Rdp) == 4){ + ctlr->ior = io16r; + ctlr->iow = io16w; + }else if(io32w(ctlr, Rap, 0), io32r(ctlr, Rdp) == 4){ + ctlr->ior = io32r; + ctlr->iow = io32w; + }else{ + print("#l%d: card doesn't talk right\n", ether->ctlrno); +iprint("#l%d: card doesn't talk right\n", ether->ctlrno); + iunlock(ctlr); + return -1; + } + + ctlr->iow(ctlr, Rap, 88); + x = ctlr->ior(ctlr, Rdp); + ctlr->iow(ctlr, Rap, 89); + x |= ctlr->ior(ctlr, Rdp)<<16; + + switch(x&0xFFFFFFF){ + case 0x2420003: /* PCnet/PCI 79C970 */ + case 0x2621003: /* PCnet/PCI II 79C970A */ + break; + default: + print("#l%d: unknown PCnet card version %.7ux\n", + ether->ctlrno, x&0xFFFFFFF); +iprint("#l%d: unknown PCnet card version %.7ux\n", + ether->ctlrno, x&0xFFFFFFF); + iunlock(ctlr); + return -1; + } + + /* + * Set the software style in BCR20 to be PCnet-PCI to ensure 32-bit access. + * Set the auto pad transmit in CSR4. + */ + ctlr->iow(ctlr, Rap, 20); + ctlr->iow(ctlr, Bdp, 0x0002); + + ctlr->iow(ctlr, Rap, 4); + x = ctlr->ior(ctlr, Rdp) & 0xFFFF; + ctlr->iow(ctlr, Rdp, ApadXmt|x); + + ctlr->iow(ctlr, Rap, 0); + + /* + * Check if the adapter's station address is to be overridden. + * If not, read it from the I/O-space and set in ether->ea prior to + * loading the station address in the initialisation block. + */ + memset(ea, 0, Eaddrlen); + if(!memcmp(ea, ether->ea, Eaddrlen)){ + x = ctlr->ior(ctlr, Aprom); + ether->ea[0] = x; + ether->ea[1] = x>>8; + if(ctlr->ior == io16r) + x = ctlr->ior(ctlr, Aprom+2); + else + x >>= 16; + ether->ea[2] = x; + ether->ea[3] = x>>8; + x = ctlr->ior(ctlr, Aprom+4); + ether->ea[4] = x; + ether->ea[5] = x>>8; + } + + /* + * Start to fill in the initialisation block + * (must be DWORD aligned). + */ + ctlr->iblock.rlen = Lognrdre<<4; + ctlr->iblock.tlen = Logntdre<<4; + memmove(ctlr->iblock.padr, ether->ea, sizeof(ctlr->iblock.padr)); + + ringinit(ctlr); + ctlr->iblock.rdra = PADDR(ctlr->rdr); + ctlr->iblock.tdra = PADDR(ctlr->tdr); + + /* + * Point the chip at the initialisation block and tell it to go. + * Mask the Idon interrupt and poll for completion. Strt and interrupt + * enables will be set later when attaching to the network. + */ + x = PADDR(&ctlr->iblock); + ctlr->iow(ctlr, Rap, 1); + ctlr->iow(ctlr, Rdp, x & 0xFFFF); + ctlr->iow(ctlr, Rap, 2); + ctlr->iow(ctlr, Rdp, (x>>16) & 0xFFFF); + ctlr->iow(ctlr, Rap, 3); + ctlr->iow(ctlr, Rdp, Idon); + ctlr->iow(ctlr, Rap, 0); + ctlr->iow(ctlr, Rdp, Init); + + while(!(ctlr->ior(ctlr, Rdp) & Idon)) + ; + + /* + * We used to set CSR0 to Idon|Stop here, and then + * in attach change it to Iena|Strt. Apparently the simulated + * 79C970 in VMware never enables after a write of Idon|Stop, + * so we enable the device here now. + */ + ctlr->iow(ctlr, Rdp, Iena|Strt); + ctlr->init = 0; + iunlock(ctlr); + + /* + * Linkage to the generic ethernet driver. + */ + ether->attach = attach; + ether->transmit = transmit; + ether->interrupt = interrupt; + ether->ifstat = ifstat; + + ether->arg = ether; + ether->promiscuous = promiscuous; + + return 0; +} + +void +ether79c970link(void) +{ + addethercard("AMD79C970", reset); +} diff --git a/os/pc/ether8003.c b/os/pc/ether8003.c new file mode 100644 index 00000000..41244c09 --- /dev/null +++ b/os/pc/ether8003.c @@ -0,0 +1,271 @@ +#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/netif.h" + +#include "etherif.h" +#include "ether8390.h" + +/* + * Western Digital/Standard Microsystems Corporation cards (WD80[01]3). + * Also handles 8216 cards (Elite Ultra). + * Configuration code based on that provided by SMC a long time ago. + */ +enum { /* 83C584 Bus Interface Controller */ + Msr = 0x00, /* Memory Select Register */ + Icr = 0x01, /* Interface Configuration Register */ + Iar = 0x02, /* I/O Address Register */ + Bio = 0x03, /* BIOS ROM Address Register */ + Ear = 0x03, /* EEROM Address Register (shared with Bio) */ + Irr = 0x04, /* Interrupt Request Register */ + Hcr = 0x04, /* 8216 hardware control */ + Laar = 0x05, /* LA Address Register */ + Ijr = 0x06, /* Initialisation Jumpers */ + Gp2 = 0x07, /* General Purpose Data Register */ + Lar = 0x08, /* LAN Address Registers */ + Id = 0x0E, /* Card ID byte */ + Cksum = 0x0F, /* Checksum */ +}; + +enum { /* Msr */ + Rst = 0x80, /* software reset */ + Menb = 0x40, /* memory enable */ +}; + +enum { /* Icr */ + Bit16 = 0x01, /* 16-bit bus */ + Other = 0x02, /* other register access */ + Ir2 = 0x04, /* IR2 */ + Msz = 0x08, /* SRAM size */ + Rla = 0x10, /* recall LAN address */ + Rx7 = 0x20, /* recall all but I/O and LAN address */ + Rio = 0x40, /* recall I/O address from EEROM */ + Sto = 0x80, /* non-volatile EEROM store */ +}; + +enum { /* Laar */ + ZeroWS16 = 0x20, /* zero wait states for 16-bit ops */ + L16en = 0x40, /* enable 16-bit LAN operation */ + M16en = 0x80, /* enable 16-bit memory access */ +}; + +enum { /* Ijr */ + Ienable = 0x01, /* 8216 interrupt enable */ +}; + +/* + * Mapping from configuration bits to interrupt level. + */ +static int irq8003[8] = { + 9, 3, 5, 7, 10, 11, 15, 4, +}; + +static int irq8216[8] = { + 0, 9, 3, 5, 7, 10, 11, 15, +}; + +static void +reset8003(Ether* ether, uchar ea[Eaddrlen], uchar ic[8]) +{ + Dp8390 *ctlr; + ulong port; + + ctlr = ether->ctlr; + port = ether->port; + + /* + * Check for old, dumb 8003E, which doesn't have an interface + * chip. Only Msr exists out of the 1st eight registers, reads + * of the others just alias the 2nd eight registers, the LAN + * address ROM. Can check Icr, Irr and Laar against the ethernet + * address read above and if they match it's an 8003E (or an + * 8003EBT, 8003S, 8003SH or 8003WT, doesn't matter), in which + * case the default irq gets used. + */ + if(memcmp(&ea[1], &ic[1], 5) == 0){ + memset(ic, 0, sizeof(ic)); + ic[Msr] = (((ulong)ether->mem)>>13) & 0x3F; + } + else{ + /* + * As a final sanity check for the 8013EBT, which doesn't have + * the 83C584 interface chip, but has 2 real registers, write Gp2 + * and if it reads back the same, it's not an 8013EBT. + */ + outb(port+Gp2, 0xAA); + inb(port+Msr); /* wiggle bus */ + if(inb(port+Gp2) != 0xAA){ + memset(ic, 0, sizeof(ic)); + ic[Msr] = (((ulong)ether->mem)>>13) & 0x3F; + } + else + ether->irq = irq8003[((ic[Irr]>>5) & 0x3)|(ic[Icr] & 0x4)]; + + /* + * Check if 16-bit card. + * If Bit16 is read/write, then it's an 8-bit card. + * If Bit16 is set, it's in a 16-bit slot. + */ + outb(port+Icr, ic[Icr]^Bit16); + inb(port+Msr); /* wiggle bus */ + if((inb(port+Icr) & Bit16) == (ic[Icr] & Bit16)){ + ctlr->width = 2; + ic[Icr] &= ~Bit16; + } + outb(port+Icr, ic[Icr]); + + if(ctlr->width == 2 && (inb(port+Icr) & Bit16) == 0) + ctlr->width = 1; + } + + ether->mem = (ulong)KADDR((ic[Msr] & 0x3F)<<13); + if(ctlr->width == 2) + ether->mem |= (ic[Laar] & 0x1F)<<19; + else + ether->mem |= 0x80000; + + if(ic[Icr] & (1<<3)) + ether->size = 32*1024; + if(ctlr->width == 2) + ether->size <<= 1; + + /* + * Enable interface RAM, set interface width. + */ + outb(port+Msr, ic[Msr]|Menb); + if(ctlr->width == 2) + outb(port+Laar, ic[Laar]|L16en|M16en|ZeroWS16); +} + +static void +reset8216(Ether* ether, uchar[8]) +{ + uchar hcr, irq, x; + ulong addr, port; + Dp8390 *ctlr; + + ctlr = ether->ctlr; + port = ether->port; + + ctlr->width = 2; + + /* + * Switch to the alternate register set and retrieve the memory + * and irq information. + */ + hcr = inb(port+Hcr); + outb(port+Hcr, 0x80|hcr); + addr = inb(port+0x0B) & 0xFF; + irq = inb(port+0x0D); + outb(port+Hcr, hcr); + + ether->mem = (ulong)KADDR(0xC0000+((((addr>>2) & 0x30)|(addr & 0x0F))<<13)); + ether->size = 8192*(1<<((addr>>4) & 0x03)); + ether->irq = irq8216[((irq>>4) & 0x04)|((irq>>2) & 0x03)]; + + /* + * Enable interface RAM, set interface width, and enable interrupts. + */ + x = inb(port+Msr) & ~Rst; + outb(port+Msr, Menb|x); + x = inb(port+Laar); + outb(port+Laar, M16en|x); + outb(port+Ijr, Ienable); +} + +/* + * Get configuration parameters, enable memory. + * There are opportunities here for buckets of code, try to resist. + */ +static int +reset(Ether* ether) +{ + int i; + uchar ea[Eaddrlen], ic[8], id, nullea[Eaddrlen], sum; + ulong port; + Dp8390 *ctlr; + + /* + * Set up the software configuration. + * Use defaults for port, irq, mem and size if not specified. + * Defaults are set for the dumb 8003E which can't be + * autoconfigured. + */ + if(ether->port == 0) + ether->port = 0x280; + if(ether->irq == 0) + ether->irq = 3; + if(ether->mem == 0) + ether->mem = 0xD0000; + if(ether->size == 0) + ether->size = 8*1024; + if(ioalloc(ether->port, 0x20, 0, "wd8003") < 0) + return -1; + + /* + * Look for the interface. Read the LAN address ROM + * and validate the checksum - the sum of all 8 bytes + * should be 0xFF. + * At the same time, get the (possible) interface chip + * registers, they'll be used later to check for aliasing. + */ + port = ether->port; + sum = 0; + for(i = 0; i < sizeof(ea); i++){ + ea[i] = inb(port+Lar+i); + sum += ea[i]; + ic[i] = inb(port+i); + } + id = inb(port+Id); + sum += id; + sum += inb(port+Cksum); + if(sum != 0xFF){ + iofree(ether->port); + return -1; + } + + ether->ctlr = malloc(sizeof(Dp8390)); + ctlr = ether->ctlr; + ctlr->ram = 1; + + if((id & 0xFE) == 0x2A) + reset8216(ether, ic); + else + reset8003(ether, ea, ic); + + /* + * Set the DP8390 ring addresses. + */ + ctlr->port = port+0x10; + ctlr->tstart = 0; + ctlr->pstart = HOWMANY(sizeof(Etherpkt), Dp8390BufSz); + ctlr->pstop = HOWMANY(ether->size, Dp8390BufSz); + + /* + * Finally, init the 8390, set the ethernet address + * and claim the memory used. + */ + dp8390reset(ether); + memset(nullea, 0, Eaddrlen); + if(memcmp(nullea, ether->ea, Eaddrlen) == 0){ + for(i = 0; i < sizeof(ether->ea); i++) + ether->ea[i] = ea[i]; + } + dp8390setea(ether); + + if(umbrwmalloc(PADDR(ether->mem), ether->size, 0) == 0) + print("ether8003: warning - 0x%luX unavailable\n", + PADDR(ether->mem)); + + return 0; +} + +void +ether8003link(void) +{ + addethercard("WD8003", reset); +} diff --git a/os/pc/ether8139.c b/os/pc/ether8139.c new file mode 100644 index 00000000..12deba1b --- /dev/null +++ b/os/pc/ether8139.c @@ -0,0 +1,755 @@ +/* + * Realtek 8139 (but not the 8129). + * Error recovery for the various over/under -flow conditions + * may need work. + */ +#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/netif.h" + +#include "etherif.h" + +enum { /* registers */ + Idr0 = 0x0000, /* MAC address */ + Mar0 = 0x0008, /* Multicast address */ + Tsd0 = 0x0010, /* Transmit Status Descriptor0 */ + Tsad0 = 0x0020, /* Transmit Start Address Descriptor0 */ + Rbstart = 0x0030, /* Receive Buffer Start Address */ + Erbcr = 0x0034, /* Early Receive Byte Count */ + Ersr = 0x0036, /* Early Receive Status */ + Cr = 0x0037, /* Command Register */ + Capr = 0x0038, /* Current Address of Packet Read */ + Cbr = 0x003A, /* Current Buffer Address */ + Imr = 0x003C, /* Interrupt Mask */ + Isr = 0x003E, /* Interrupt Status */ + Tcr = 0x0040, /* Transmit Configuration */ + Rcr = 0x0044, /* Receive Configuration */ + Tctr = 0x0048, /* Timer Count */ + Mpc = 0x004C, /* Missed Packet Counter */ + Cr9346 = 0x0050, /* 9346 Command Register */ + Config0 = 0x0051, /* Configuration Register 0 */ + Config1 = 0x0052, /* Configuration Register 1 */ + TimerInt = 0x0054, /* Timer Interrupt */ + Msr = 0x0058, /* Media Status */ + Config3 = 0x0059, /* Configuration Register 3 */ + Config4 = 0x005A, /* Configuration Register 4 */ + Mulint = 0x005C, /* Multiple Interrupt Select */ + RerID = 0x005E, /* PCI Revision ID */ + Tsad = 0x0060, /* Transmit Status of all Descriptors */ + + Bmcr = 0x0062, /* Basic Mode Control */ + Bmsr = 0x0064, /* Basic Mode Status */ + Anar = 0x0066, /* Auto-Negotiation Advertisment */ + Anlpar = 0x0068, /* Auto-Negotiation Link Partner */ + Aner = 0x006A, /* Auto-Negotiation Expansion */ + Dis = 0x006C, /* Disconnect Counter */ + Fcsc = 0x006E, /* False Carrier Sense Counter */ + Nwaytr = 0x0070, /* N-way Test */ + Rec = 0x0072, /* RX_ER Counter */ + Cscr = 0x0074, /* CS Configuration */ + Phy1parm = 0x0078, /* PHY Parameter 1 */ + Twparm = 0x007C, /* Twister Parameter */ + Phy2parm = 0x0080, /* PHY Parameter 2 */ +}; + +enum { /* Cr */ + Bufe = 0x01, /* Rx Buffer Empty */ + Te = 0x04, /* Transmitter Enable */ + Re = 0x08, /* Receiver Enable */ + Rst = 0x10, /* Software Reset */ +}; + +enum { /* Imr/Isr */ + Rok = 0x0001, /* Receive OK */ + Rer = 0x0002, /* Receive Error */ + Tok = 0x0004, /* Transmit OK */ + Ter = 0x0008, /* Transmit Error */ + Rxovw = 0x0010, /* Receive Buffer Overflow */ + PunLc = 0x0020, /* Packet Underrun or Link Change */ + Fovw = 0x0040, /* Receive FIFO Overflow */ + Clc = 0x2000, /* Cable Length Change */ + Timerbit = 0x4000, /* Timer */ + Serr = 0x8000, /* System Error */ +}; + +enum { /* Tcr */ + Clrabt = 0x00000001, /* Clear Abort */ + TxrrSHIFT = 4, /* Transmit Retry Count */ + TxrrMASK = 0x000000F0, + MtxdmaSHIFT = 8, /* Max. DMA Burst Size */ + MtxdmaMASK = 0x00000700, + Mtxdma2048 = 0x00000700, + Acrc = 0x00010000, /* Append CRC (not) */ + LbkSHIFT = 17, /* Loopback Test */ + LbkMASK = 0x00060000, + Rtl8139ArevG = 0x00800000, /* RTL8139A Rev. G ID */ + IfgSHIFT = 24, /* Interframe Gap */ + IfgMASK = 0x03000000, + HwveridSHIFT = 26, /* Hardware Version ID */ + HwveridMASK = 0x7C000000, +}; + +enum { /* Rcr */ + Aap = 0x00000001, /* Accept All Packets */ + Apm = 0x00000002, /* Accept Physical Match */ + Am = 0x00000004, /* Accept Multicast */ + Ab = 0x00000008, /* Accept Broadcast */ + Ar = 0x00000010, /* Accept Runt */ + Aer = 0x00000020, /* Accept Error */ + Sel9356 = 0x00000040, /* 9356 EEPROM used */ + Wrap = 0x00000080, /* Rx Buffer Wrap Control */ + MrxdmaSHIFT = 8, /* Max. DMA Burst Size */ + MrxdmaMASK = 0x00000700, + Mrxdmaunlimited = 0x00000700, + RblenSHIFT = 11, /* Receive Buffer Length */ + RblenMASK = 0x00001800, + Rblen8K = 0x00000000, /* 8KB+16 */ + Rblen16K = 0x00000800, /* 16KB+16 */ + Rblen32K = 0x00001000, /* 32KB+16 */ + Rblen64K = 0x00001800, /* 64KB+16 */ + RxfthSHIFT = 13, /* Receive Buffer Length */ + RxfthMASK = 0x0000E000, + Rxfth256 = 0x00008000, + Rxfthnone = 0x0000E000, + Rer8 = 0x00010000, /* Accept Error Packets > 8 bytes */ + MulERINT = 0x00020000, /* Multiple Early Interrupt Select */ + ErxthSHIFT = 24, /* Early Rx Threshold */ + ErxthMASK = 0x0F000000, + Erxthnone = 0x00000000, +}; + +enum { /* Received Packet Status */ + Rcok = 0x0001, /* Receive Completed OK */ + Fae = 0x0002, /* Frame Alignment Error */ + Crc = 0x0004, /* CRC Error */ + Long = 0x0008, /* Long Packet */ + Runt = 0x0010, /* Runt Packet Received */ + Ise = 0x0020, /* Invalid Symbol Error */ + Bar = 0x2000, /* Broadcast Address Received */ + Pam = 0x4000, /* Physical Address Matched */ + Mar = 0x8000, /* Multicast Address Received */ +}; + +enum { /* Media Status Register */ + Rxpf = 0x01, /* Pause Flag */ + Txpf = 0x02, /* Pause Flag */ + Linkb = 0x04, /* Inverse of Link Status */ + Speed10 = 0x08, /* 10Mbps */ + Auxstatus = 0x10, /* Aux. Power Present Status */ + Rxfce = 0x40, /* Receive Flow Control Enable */ + Txfce = 0x80, /* Transmit Flow Control Enable */ +}; + +typedef struct Td Td; +struct Td { /* Soft Transmit Descriptor */ + int tsd; + int tsad; + uchar* data; + Block* bp; +}; + +enum { /* Tsd0 */ + SizeSHIFT = 0, /* Descriptor Size */ + SizeMASK = 0x00001FFF, + Own = 0x00002000, + Tun = 0x00004000, /* Transmit FIFO Underrun */ + Tcok = 0x00008000, /* Transmit COmpleted OK */ + EtxthSHIFT = 16, /* Early Tx Threshold */ + EtxthMASK = 0x001F0000, + NccSHIFT = 24, /* Number of Collisions Count */ + NccMASK = 0x0F000000, + Cdh = 0x10000000, /* CD Heartbeat */ + Owc = 0x20000000, /* Out of Window Collision */ + Tabt = 0x40000000, /* Transmit Abort */ + Crs = 0x80000000, /* Carrier Sense Lost */ +}; + +enum { + Rblen = Rblen64K, /* Receive Buffer Length */ + Ntd = 4, /* Number of Transmit Descriptors */ + Tdbsz = ROUNDUP(sizeof(Etherpkt), 4), +}; + +typedef struct Ctlr Ctlr; +typedef struct Ctlr { + int port; + Pcidev* pcidev; + Ctlr* next; + int active; + int id; + + QLock alock; /* attach */ + Lock ilock; /* init */ + void* alloc; /* base of per-Ctlr allocated data */ + + int rcr; /* receive configuration register */ + uchar* rbstart; /* receive buffer */ + int rblen; /* receive buffer length */ + int ierrs; /* receive errors */ + + Lock tlock; /* transmit */ + Td td[Ntd]; + int ntd; /* descriptors active */ + int tdh; /* host index into td */ + int tdi; /* interface index into td */ + int etxth; /* early transmit threshold */ + int taligned; /* packet required no alignment */ + int tunaligned; /* packet required alignment */ + + int dis; /* disconnect counter */ + int fcsc; /* false carrier sense counter */ + int rec; /* RX_ER counter */ +} Ctlr; + +static Ctlr* ctlrhead; +static Ctlr* ctlrtail; + +#define csr8r(c, r) (inb((c)->port+(r))) +#define csr16r(c, r) (ins((c)->port+(r))) +#define csr32r(c, r) (inl((c)->port+(r))) +#define csr8w(c, r, b) (outb((c)->port+(r), (int)(b))) +#define csr16w(c, r, w) (outs((c)->port+(r), (ushort)(w))) +#define csr32w(c, r, l) (outl((c)->port+(r), (ulong)(l))) + +static void +rtl8139promiscuous(void* arg, int on) +{ + Ether *edev; + Ctlr * ctlr; + + edev = arg; + ctlr = edev->ctlr; + ilock(&ctlr->ilock); + + if(on) + ctlr->rcr |= Aap; + else + ctlr->rcr &= ~Aap; + csr32w(ctlr, Rcr, ctlr->rcr); + iunlock(&ctlr->ilock); +} + +static long +rtl8139ifstat(Ether* edev, void* a, long n, ulong offset) +{ + int l; + char *p; + Ctlr *ctlr; + + ctlr = edev->ctlr; + p = malloc(READSTR); + l = snprint(p, READSTR, "rcr %#8.8ux\n", ctlr->rcr); + l += snprint(p+l, READSTR-l, "ierrs %d\n", ctlr->ierrs); + l += snprint(p+l, READSTR-l, "etxth %d\n", ctlr->etxth); + l += snprint(p+l, READSTR-l, "taligned %d\n", ctlr->taligned); + l += snprint(p+l, READSTR-l, "tunaligned %d\n", ctlr->tunaligned); + ctlr->dis += csr16r(ctlr, Dis); + l += snprint(p+l, READSTR-l, "dis %d\n", ctlr->dis); + ctlr->fcsc += csr16r(ctlr, Fcsc); + l += snprint(p+l, READSTR-l, "fcscnt %d\n", ctlr->fcsc); + ctlr->rec += csr16r(ctlr, Rec); + l += snprint(p+l, READSTR-l, "rec %d\n", ctlr->rec); + + l += snprint(p+l, READSTR-l, "Tcr %#8.8lux\n", csr32r(ctlr, Tcr)); + l += snprint(p+l, READSTR-l, "Config0 %#2.2ux\n", csr8r(ctlr, Config0)); + l += snprint(p+l, READSTR-l, "Config1 %#2.2ux\n", csr8r(ctlr, Config1)); + l += snprint(p+l, READSTR-l, "Msr %#2.2ux\n", csr8r(ctlr, Msr)); + l += snprint(p+l, READSTR-l, "Config3 %#2.2ux\n", csr8r(ctlr, Config3)); + l += snprint(p+l, READSTR-l, "Config4 %#2.2ux\n", csr8r(ctlr, Config4)); + + l += snprint(p+l, READSTR-l, "Bmcr %#4.4ux\n", csr16r(ctlr, Bmcr)); + l += snprint(p+l, READSTR-l, "Bmsr %#4.4ux\n", csr16r(ctlr, Bmsr)); + l += snprint(p+l, READSTR-l, "Anar %#4.4ux\n", csr16r(ctlr, Anar)); + l += snprint(p+l, READSTR-l, "Anlpar %#4.4ux\n", csr16r(ctlr, Anlpar)); + l += snprint(p+l, READSTR-l, "Aner %#4.4ux\n", csr16r(ctlr, Aner)); + l += snprint(p+l, READSTR-l, "Nwaytr %#4.4ux\n", csr16r(ctlr, Nwaytr)); + snprint(p+l, READSTR-l, "Cscr %#4.4ux\n", csr16r(ctlr, Cscr)); + n = readstr(offset, a, n, p); + free(p); + + return n; +} + +static int +rtl8139reset(Ctlr* ctlr) +{ + int timeo; + + /* + * Soft reset the controller. + */ + csr8w(ctlr, Cr, Rst); + for(timeo = 0; timeo < 1000; timeo++){ + if(!(csr8r(ctlr, Cr) & Rst)) + return 0; + delay(1); + } + + return -1; +} + +static void +rtl8139halt(Ctlr* ctlr) +{ + int i; + + csr8w(ctlr, Cr, 0); + csr16w(ctlr, Imr, 0); + csr16w(ctlr, Isr, ~0); + + for(i = 0; i < Ntd; i++){ + if(ctlr->td[i].bp == nil) + continue; + freeb(ctlr->td[i].bp); + ctlr->td[i].bp = nil; + } +} + +static void +rtl8139init(Ether* edev) +{ + int i; + ulong r; + Ctlr *ctlr; + uchar *alloc; + + ctlr = edev->ctlr; + ilock(&ctlr->ilock); + + rtl8139halt(ctlr); + + /* + * MAC Address. + */ + r = (edev->ea[3]<<24)|(edev->ea[2]<<16)|(edev->ea[1]<<8)|edev->ea[0]; + csr32w(ctlr, Idr0, r); + r = (edev->ea[5]<<8)|edev->ea[4]; + csr32w(ctlr, Idr0+4, r); + + /* + * Receiver + */ + alloc = (uchar*)ROUNDUP((ulong)ctlr->alloc, 32); + ctlr->rbstart = alloc; + alloc += ctlr->rblen+16; + memset(ctlr->rbstart, 0, ctlr->rblen+16); + csr32w(ctlr, Rbstart, PCIWADDR(ctlr->rbstart)); + ctlr->rcr = Rxfth256|Rblen|Mrxdmaunlimited|Ab|Apm; + + /* + * Transmitter. + */ + for(i = 0; i < Ntd; i++){ + ctlr->td[i].tsd = Tsd0+i*4; + ctlr->td[i].tsad = Tsad0+i*4; + ctlr->td[i].data = alloc; + alloc += Tdbsz; + ctlr->td[i].bp = nil; + } + ctlr->ntd = ctlr->tdh = ctlr->tdi = 0; + ctlr->etxth = 128/32; + + /* + * Interrupts. + */ + csr32w(ctlr, TimerInt, 0); + csr16w(ctlr, Imr, Serr|Timerbit|Fovw|PunLc|Rxovw|Ter|Tok|Rer|Rok); + csr32w(ctlr, Mpc, 0); + + /* + * Enable receiver/transmitter. + * Need to enable before writing the Rcr or it won't take. + */ + csr8w(ctlr, Cr, Te|Re); + csr32w(ctlr, Tcr, Mtxdma2048); + csr32w(ctlr, Rcr, ctlr->rcr); + + iunlock(&ctlr->ilock); +} + +static void +rtl8139attach(Ether* edev) +{ + Ctlr *ctlr; + + ctlr = edev->ctlr; + qlock(&ctlr->alock); + if(ctlr->alloc == nil){ + ctlr->rblen = 1<<((Rblen>>RblenSHIFT)+13); + ctlr->alloc = mallocz(ctlr->rblen+16 + Ntd*Tdbsz + 32, 0); + rtl8139init(edev); + } + qunlock(&ctlr->alock); +} + +static void +rtl8139txstart(Ether* edev) +{ + Td *td; + int size; + Block *bp; + Ctlr *ctlr; + + ctlr = edev->ctlr; + while(ctlr->ntd < Ntd){ + bp = qget(edev->oq); + if(bp == nil) + break; + size = BLEN(bp); + + td = &ctlr->td[ctlr->tdh]; + if(((int)bp->rp) & 0x03){ + memmove(td->data, bp->rp, size); + freeb(bp); + csr32w(ctlr, td->tsad, PCIWADDR(td->data)); + ctlr->tunaligned++; + } + else{ + td->bp = bp; + csr32w(ctlr, td->tsad, PCIWADDR(bp->rp)); + ctlr->taligned++; + } + csr32w(ctlr, td->tsd, (ctlr->etxth<<EtxthSHIFT)|size); + + ctlr->ntd++; + ctlr->tdh = NEXT(ctlr->tdh, Ntd); + } +} + +static void +rtl8139transmit(Ether* edev) +{ + Ctlr *ctlr; + + ctlr = edev->ctlr; + ilock(&ctlr->tlock); + rtl8139txstart(edev); + iunlock(&ctlr->tlock); +} + +static void +rtl8139receive(Ether* edev) +{ + Block *bp; + Ctlr *ctlr; + ushort capr; + uchar cr, *p; + int l, length, status; + + ctlr = edev->ctlr; + + /* + * Capr is where the host is reading from, + * Cbr is where the NIC is currently writing. + */ + capr = (csr16r(ctlr, Capr)+16) % ctlr->rblen; + while(!(csr8r(ctlr, Cr) & Bufe)){ + p = ctlr->rbstart+capr; + + /* + * Apparently the packet length may be 0xFFF0 if + * the NIC is still copying the packet into memory. + */ + length = (*(p+3)<<8)|*(p+2); + if(length == 0xFFF0) + break; + status = (*(p+1)<<8)|*p; + + if(!(status & Rcok)){ + if(status & (Ise|Fae)) + edev->frames++; + if(status & Crc) + edev->crcs++; + if(status & (Runt|Long)) + edev->buffs++; + + /* + * Reset the receiver. + * Also may have to restore the multicast list + * here too if it ever gets used. + */ + cr = csr8r(ctlr, Cr); + csr8w(ctlr, Cr, cr & ~Re); + csr32w(ctlr, Rbstart, PCIWADDR(ctlr->rbstart)); + csr8w(ctlr, Cr, cr); + csr32w(ctlr, Rcr, ctlr->rcr); + + continue; + } + + /* + * Receive Completed OK. + * Very simplistic; there are ways this could be done + * without copying, but the juice probably isn't worth + * the squeeze. + * The packet length includes a 4 byte CRC on the end. + */ + capr = (capr+4) % ctlr->rblen; + p = ctlr->rbstart+capr; + capr = (capr+length) % ctlr->rblen; + + if((bp = iallocb(length)) != nil){ + if(p+length >= ctlr->rbstart+ctlr->rblen){ + l = ctlr->rbstart+ctlr->rblen - p; + memmove(bp->wp, p, l); + bp->wp += l; + length -= l; + p = ctlr->rbstart; + } + if(length > 0){ + memmove(bp->wp, p, length); + bp->wp += length; + } + bp->wp -= 4; + etheriq(edev, bp, 1); + } + + capr = ROUNDUP(capr, 4); + csr16w(ctlr, Capr, capr-16); + } +} + +static void +rtl8139interrupt(Ureg*, void* arg) +{ + Td *td; + Ctlr *ctlr; + Ether *edev; + int isr, msr, tsd; + + edev = arg; + ctlr = edev->ctlr; + + while((isr = csr16r(ctlr, Isr)) != 0){ + csr16w(ctlr, Isr, isr); + if(isr & (Fovw|PunLc|Rxovw|Rer|Rok)){ + rtl8139receive(edev); + if(!(isr & Rok)) + ctlr->ierrs++; + isr &= ~(Fovw|Rxovw|Rer|Rok); + } + + if(isr & (Ter|Tok)){ + ilock(&ctlr->tlock); + while(ctlr->ntd){ + td = &ctlr->td[ctlr->tdi]; + tsd = csr32r(ctlr, td->tsd); + if(!(tsd & (Tabt|Tun|Tcok))) + break; + + if(!(tsd & Tcok)){ + if(tsd & Tun){ + if(ctlr->etxth < ETHERMAXTU/32) + ctlr->etxth++; + } + edev->oerrs++; + } + + if(td->bp != nil){ + freeb(td->bp); + td->bp = nil; + } + + ctlr->ntd--; + ctlr->tdi = NEXT(ctlr->tdi, Ntd); + } + rtl8139txstart(edev); + iunlock(&ctlr->tlock); + isr &= ~(Ter|Tok); + } + + if(isr & PunLc){ + /* + * Maybe the link changed - do we care very much? + */ + msr = csr8r(ctlr, Msr); + if(!(msr & Linkb)){ + if(!(msr & Speed10) && edev->mbps != 100){ + edev->mbps = 100; + qsetlimit(edev->oq, 256*1024); + } + else if((msr & Speed10) && edev->mbps != 10){ + edev->mbps = 10; + qsetlimit(edev->oq, 65*1024); + } + } + isr &= ~(Clc|PunLc); + } + + /* + * Only Serr|Timerbit should be left by now. + * Should anything be done to tidy up? TimerInt isn't + * used so that can be cleared. A PCI bus error is indicated + * by Serr, that's pretty serious; is there anyhing to do + * other than try to reinitialise the chip? + */ + if((isr & (Serr|Timerbit)) != 0){ + iprint("rtl8139interrupt: imr %#4.4ux isr %#4.4ux\n", + csr16r(ctlr, Imr), isr); + if(isr & Timerbit) + csr32w(ctlr, TimerInt, 0); + if(isr & Serr) + rtl8139init(edev); + } + } +} + +static Ctlr* +rtl8139match(Ether* edev, int id) +{ + Pcidev *p; + Ctlr *ctlr; + int i, port; + + /* + * Any adapter matches if no edev->port is supplied, + * otherwise the ports must match. + */ + for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){ + if(ctlr->active) + continue; + p = ctlr->pcidev; + if(((p->did<<16)|p->vid) != id) + continue; + port = p->mem[0].bar & ~0x01; + if(edev->port != 0 && edev->port != port) + continue; + + if(ioalloc(port, p->mem[0].size, 0, "rtl8139") < 0){ + print("rtl8139: port %#ux in use\n", port); + continue; + } + + if(pcigetpms(p) > 0){ + pcisetpms(p, 0); + + for(i = 0; i < 6; i++) + pcicfgw32(p, PciBAR0+i*4, p->mem[i].bar); + pcicfgw8(p, PciINTL, p->intl); + pcicfgw8(p, PciLTR, p->ltr); + pcicfgw8(p, PciCLS, p->cls); + pcicfgw16(p, PciPCR, p->pcr); + } + + ctlr->port = port; + if(rtl8139reset(ctlr)) + continue; + pcisetbme(p); + + ctlr->active = 1; + return ctlr; + } + return nil; +} + +static struct { + char* name; + int id; +} rtl8139pci[] = { + { "rtl8139", (0x8139<<16)|0x10EC, }, /* generic */ + { "smc1211", (0x1211<<16)|0x1113, }, /* SMC EZ-Card */ + { "dfe-538tx", (0x1300<<16)|0x1186, }, /* D-Link DFE-538TX */ + { "dfe-560txd", (0x1340<<16)|0x1186, }, /* D-Link DFE-560TXD */ + { nil }, +}; + +static int +rtl8139pnp(Ether* edev) +{ + int i, id; + Pcidev *p; + Ctlr *ctlr; + uchar ea[Eaddrlen]; + + /* + * Make a list of all ethernet controllers + * if not already done. + */ + if(ctlrhead == nil){ + p = nil; + while(p = pcimatch(p, 0, 0)){ + if(p->ccrb != 0x02 || p->ccru != 0) + continue; + ctlr = malloc(sizeof(Ctlr)); + ctlr->pcidev = p; + ctlr->id = (p->did<<16)|p->vid; + + if(ctlrhead != nil) + ctlrtail->next = ctlr; + else + ctlrhead = ctlr; + ctlrtail = ctlr; + } + } + + /* + * Is it an RTL8139 under a different name? + * Normally a search is made through all the found controllers + * for one which matches any of the known vid+did pairs. + * If a vid+did pair is specified a search is made for that + * specific controller only. + */ + id = 0; + for(i = 0; i < edev->nopt; i++){ + if(cistrncmp(edev->opt[i], "id=", 3) == 0) + id = strtol(&edev->opt[i][3], nil, 0); + } + + ctlr = nil; + if(id != 0) + ctlr = rtl8139match(edev, id); + else for(i = 0; rtl8139pci[i].name; i++){ + if((ctlr = rtl8139match(edev, rtl8139pci[i].id)) != nil) + break; + } + if(ctlr == nil) + return -1; + + edev->ctlr = ctlr; + edev->port = ctlr->port; + edev->irq = ctlr->pcidev->intl; + edev->tbdf = ctlr->pcidev->tbdf; + + /* + * Check if the adapter's station address is to be overridden. + * If not, read it from the device and set in edev->ea. + */ + memset(ea, 0, Eaddrlen); + if(memcmp(ea, edev->ea, Eaddrlen) == 0){ + i = csr32r(ctlr, Idr0); + edev->ea[0] = i; + edev->ea[1] = i>>8; + edev->ea[2] = i>>16; + edev->ea[3] = i>>24; + i = csr32r(ctlr, Idr0+4); + edev->ea[4] = i; + edev->ea[5] = i>>8; + } + + edev->attach = rtl8139attach; + edev->transmit = rtl8139transmit; + edev->interrupt = rtl8139interrupt; + edev->ifstat = rtl8139ifstat; + + edev->arg = edev; + edev->promiscuous = rtl8139promiscuous; + + /* + * This should be much more dynamic but will do for now. + */ + if((csr8r(ctlr, Msr) & (Speed10|Linkb)) == 0) + edev->mbps = 100; + + return 0; +} + +void +ether8139link(void) +{ + addethercard("rtl8139", rtl8139pnp); +} diff --git a/os/pc/ether82543gc.c b/os/pc/ether82543gc.c new file mode 100644 index 00000000..6277b10b --- /dev/null +++ b/os/pc/ether82543gc.c @@ -0,0 +1,1367 @@ +/* + * Intel RS-82543GC Gigabit Ethernet Controller + * as found on the Intel PRO/1000[FT] Server Adapter. + * The older non-[FT] cards use the 82542 (LSI L2A1157) chip; no attempt + * is made to handle the older chip although it should be possible. + * The datasheet is not very clear about running on a big-endian system + * and this driver assumes little-endian throughout. + * To do: + * GMII/MII + * receive tuning + * transmit tuning + */ +#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/netif.h" + +#include "etherif.h" + +enum { + Ctrl = 0x00000000, /* Device Control */ + Status = 0x00000008, /* Device Status */ + Eecd = 0x00000010, /* EEPROM/Flash Control/Data */ + Ctrlext = 0x00000018, /* Extended Device Control */ + Mdic = 0x00000020, /* MDI Control */ + Fcal = 0x00000028, /* Flow Control Address Low */ + Fcah = 0x0000002C, /* Flow Control Address High */ + Fct = 0x00000030, /* Flow Control Type */ + Icr = 0x000000C0, /* Interrupt Cause Read */ + Ics = 0x000000C8, /* Interrupt Cause Set */ + Ims = 0x000000D0, /* Interrupt Mask Set/Read */ + Imc = 0x000000D8, /* Interrupt mask Clear */ + Rctl = 0x00000100, /* Receive Control */ + Fcttv = 0x00000170, /* Flow Control Transmit Timer Value */ + Txcw = 0x00000178, /* Transmit configuration word reg. */ + Rxcw = 0x00000180, /* Receive configuration word reg. */ + Tctl = 0x00000400, /* Transmit Control */ + Tipg = 0x00000410, /* Transmit IPG */ + Tbt = 0x00000448, /* Transmit Burst Timer */ + Ait = 0x00000458, /* Adaptive IFS Throttle */ + Fcrtl = 0x00002160, /* Flow Control RX Threshold Low */ + Fcrth = 0x00002168, /* Flow Control Rx Threshold High */ + Rdfh = 0x00002410, /* Receive data fifo head */ + Rdft = 0x00002418, /* Receive data fifo tail */ + Rdfhs = 0x00002420, /* Receive data fifo head saved */ + Rdfts = 0x00002428, /* Receive data fifo tail saved */ + Rdfpc = 0x00002430, /* Receive data fifo packet count */ + Rdbal = 0x00002800, /* Rdesc Base Address Low */ + Rdbah = 0x00002804, /* Rdesc Base Address High */ + Rdlen = 0x00002808, /* Receive Descriptor Length */ + Rdh = 0x00002810, /* Receive Descriptor Head */ + Rdt = 0x00002818, /* Receive Descriptor Tail */ + Rdtr = 0x00002820, /* Receive Descriptor Timer Ring */ + Rxdctl = 0x00002828, /* Receive Descriptor Control */ + Txdmac = 0x00003000, /* Transfer DMA Control */ + Ett = 0x00003008, /* Early Transmit Control */ + Tdfh = 0x00003410, /* Transmit data fifo head */ + Tdft = 0x00003418, /* Transmit data fifo tail */ + Tdfhs = 0x00003420, /* Transmit data Fifo Head saved */ + Tdfts = 0x00003428, /* Transmit data fifo tail saved */ + Tdfpc = 0x00003430, /* Trasnmit data Fifo packet count */ + Tdbal = 0x00003800, /* Tdesc Base Address Low */ + Tdbah = 0x00003804, /* Tdesc Base Address High */ + Tdlen = 0x00003808, /* Transmit Descriptor Length */ + Tdh = 0x00003810, /* Transmit Descriptor Head */ + Tdt = 0x00003818, /* Transmit Descriptor Tail */ + Tidv = 0x00003820, /* Transmit Interrupt Delay Value */ + Txdctl = 0x00003828, /* Transmit Descriptor Control */ + + Statistics = 0x00004000, /* Start of Statistics Area */ + Gorcl = 0x88/4, /* Good Octets Received Count */ + Gotcl = 0x90/4, /* Good Octets Transmitted Count */ + Torl = 0xC0/4, /* Total Octets Received */ + Totl = 0xC8/4, /* Total Octets Transmitted */ + Nstatistics = 64, + + Rxcsum = 0x00005000, /* Receive Checksum Control */ + Mta = 0x00005200, /* Multicast Table Array */ + Ral = 0x00005400, /* Receive Address Low */ + Rah = 0x00005404, /* Receive Address High */ +}; + +enum { /* Ctrl */ + Bem = 0x00000002, /* Big Endian Mode */ + Prior = 0x00000004, /* Priority on the PCI bus */ + Lrst = 0x00000008, /* Link Reset */ + Asde = 0x00000020, /* Auto-Speed Detection Enable */ + Slu = 0x00000040, /* Set Link Up */ + Ilos = 0x00000080, /* Invert Loss of Signal (LOS) */ + Frcspd = 0x00000800, /* Force Speed */ + Frcdplx = 0x00001000, /* Force Duplex */ + Swdpinslo = 0x003C0000, /* Software Defined Pins - lo nibble */ + Swdpin0 = 0x00040000, + Swdpin1 = 0x00080000, + Swdpin2 = 0x00100000, + Swdpin3 = 0x00200000, + Swdpiolo = 0x03C00000, /* Software Defined I/O Pins */ + Swdpio0 = 0x00400000, + Swdpio1 = 0x00800000, + Swdpio2 = 0x01000000, + Swdpio3 = 0x02000000, + Devrst = 0x04000000, /* Device Reset */ + Rfce = 0x08000000, /* Receive Flow Control Enable */ + Tfce = 0x10000000, /* Transmit Flow Control Enable */ + Vme = 0x40000000, /* VLAN Mode Enable */ +}; + +enum { /* Status */ + Lu = 0x00000002, /* Link Up */ + Tckok = 0x00000004, /* Transmit clock is running */ + Rbcok = 0x00000008, /* Receive clock is running */ + Txoff = 0x00000010, /* Transmission Paused */ + Tbimode = 0x00000020, /* TBI Mode Indication */ + SpeedMASK = 0x000000C0, + Speed10 = 0x00000000, /* 10Mb/s */ + Speed100 = 0x00000040, /* 100Mb/s */ + Speed1000 = 0x00000080, /* 1000Mb/s */ + Mtxckok = 0x00000400, /* MTX clock is running */ + Pci66 = 0x00000800, /* PCI Bus speed indication */ + Bus64 = 0x00001000, /* PCI Bus width indication */ +}; + +enum { /* Ctrl and Status */ + Fd = 0x00000001, /* Full-Duplex */ + AsdvMASK = 0x00000300, + Asdv10 = 0x00000000, /* 10Mb/s */ + Asdv100 = 0x00000100, /* 100Mb/s */ + Asdv1000 = 0x00000200, /* 1000Mb/s */ +}; + +enum { /* Eecd */ + Sk = 0x00000001, /* Clock input to the EEPROM */ + Cs = 0x00000002, /* Chip Select */ + Di = 0x00000004, /* Data Input to the EEPROM */ + Do = 0x00000008, /* Data Output from the EEPROM */ +}; + +enum { /* Ctrlext */ + Gpien = 0x0000000F, /* General Purpose Interrupt Enables */ + Swdpinshi = 0x000000F0, /* Software Defined Pins - hi nibble */ + Swdpiohi = 0x00000F00, /* Software Defined Pins - I or O */ + Asdchk = 0x00001000, /* ASD Check */ + Eerst = 0x00002000, /* EEPROM Reset */ + Ips = 0x00004000, /* Invert Power State */ + Spdbyps = 0x00008000, /* Speed Select Bypass */ +}; + +enum { /* EEPROM content offsets */ + Ea = 0x00, /* Ethernet Address */ + Cf = 0x03, /* Compatibility Field */ + Pba = 0x08, /* Printed Board Assembly number */ + Icw1 = 0x0A, /* Initialization Control Word 1 */ + Sid = 0x0B, /* Subsystem ID */ + Svid = 0x0C, /* Subsystem Vendor ID */ + Did = 0x0D, /* Device ID */ + Vid = 0x0E, /* Vendor ID */ + Icw2 = 0x0F, /* Initialization Control Word 2 */ +}; + +enum { /* Mdic */ + MDIdMASK = 0x0000FFFF, /* Data */ + MDIdSHIFT = 0, + MDIrMASK = 0x001F0000, /* PHY Register Address */ + MDIrSHIFT = 16, + MDIpMASK = 0x03E00000, /* PHY Address */ + MDIpSHIFT = 21, + MDIwop = 0x04000000, /* Write Operation */ + MDIrop = 0x08000000, /* Read Operation */ + MDIready = 0x10000000, /* End of Transaction */ + MDIie = 0x20000000, /* Interrupt Enable */ + MDIe = 0x40000000, /* Error */ +}; + +enum { /* Icr, Ics, Ims, Imc */ + Txdw = 0x00000001, /* Transmit Descriptor Written Back */ + Txqe = 0x00000002, /* Transmit Queue Empty */ + Lsc = 0x00000004, /* Link Status Change */ + Rxseq = 0x00000008, /* Receive Sequence Error */ + Rxdmt0 = 0x00000010, /* Rdesc Minimum Threshold Reached */ + Rxo = 0x00000040, /* Receiver Overrun */ + Rxt0 = 0x00000080, /* Receiver Timer Interrupt */ + Mdac = 0x00000200, /* MDIO Access Completed */ + Rxcfg = 0x00000400, /* Receiving /C/ ordered sets */ + Gpi0 = 0x00000800, /* General Purpose Interrupts */ + Gpi1 = 0x00001000, + Gpi2 = 0x00002000, + Gpi3 = 0x00004000, +}; + +enum { /* Txcw */ + Ane = 0x80000000, /* Autonegotiate enable */ + Np = 0x00008000, /* Next Page */ + As = 0x00000100, /* Asymmetric Flow control desired */ + Ps = 0x00000080, /* Pause supported */ + Hd = 0x00000040, /* Half duplex supported */ + TxcwFd = 0x00000020, /* Full Duplex supported */ +}; + +enum { /* Rxcw */ + Rxword = 0x0000FFFF, /* Data from auto-negotiation process */ + Rxnocarrier = 0x04000000, /* Carrier Sense indication */ + Rxinvalid = 0x08000000, /* Invalid Symbol during configuration */ + Rxchange = 0x10000000, /* Change to the Rxword indication */ + Rxconfig = 0x20000000, /* /C/ order set reception indication */ + Rxsync = 0x40000000, /* Lost bit synchronization indication */ + Anc = 0x80000000, /* Auto Negotiation Complete */ +}; + +enum { /* Rctl */ + Rrst = 0x00000001, /* Receiver Software Reset */ + Ren = 0x00000002, /* Receiver Enable */ + Sbp = 0x00000004, /* Store Bad Packets */ + Upe = 0x00000008, /* Unicast Promiscuous Enable */ + Mpe = 0x00000010, /* Multicast Promiscuous Enable */ + Lpe = 0x00000020, /* Long Packet Reception Enable */ + LbmMASK = 0x000000C0, /* Loopback Mode */ + LbmOFF = 0x00000000, /* No Loopback */ + LbmTBI = 0x00000040, /* TBI Loopback */ + LbmMII = 0x00000080, /* GMII/MII Loopback */ + LbmXCVR = 0x000000C0, /* Transceiver Loopback */ + RdtmsMASK = 0x00000300, /* Rdesc Minimum Threshold Size */ + RdtmsHALF = 0x00000000, /* Threshold is 1/2 Rdlen */ + RdtmsQUARTER = 0x00000100, /* Threshold is 1/4 Rdlen */ + RdtmsEIGHTH = 0x00000200, /* Threshold is 1/8 Rdlen */ + MoMASK = 0x00003000, /* Multicast Offset */ + Bam = 0x00008000, /* Broadcast Accept Mode */ + BsizeMASK = 0x00030000, /* Receive Buffer Size */ + Bsize2048 = 0x00000000, /* Bsex = 0 */ + Bsize1024 = 0x00010000, /* Bsex = 0 */ + Bsize512 = 0x00020000, /* Bsex = 0 */ + Bsize256 = 0x00030000, /* Bsex = 0 */ + Bsize16384 = 0x00010000, /* Bsex = 1 */ + Vfe = 0x00040000, /* VLAN Filter Enable */ + Cfien = 0x00080000, /* Canonical Form Indicator Enable */ + Cfi = 0x00100000, /* Canonical Form Indicator value */ + Dpf = 0x00400000, /* Discard Pause Frames */ + Pmcf = 0x00800000, /* Pass MAC Control Frames */ + Bsex = 0x02000000, /* Buffer Size Extension */ + Secrc = 0x04000000, /* Strip CRC from incoming packet */ +}; + +enum { /* Tctl */ + Trst = 0x00000001, /* Transmitter Software Reset */ + Ten = 0x00000002, /* Transmit Enable */ + Psp = 0x00000008, /* Pad Short Packets */ + CtMASK = 0x00000FF0, /* Collision Threshold */ + CtSHIFT = 4, + ColdMASK = 0x003FF000, /* Collision Distance */ + ColdSHIFT = 12, + Swxoff = 0x00400000, /* Sofware XOFF Transmission */ + Pbe = 0x00800000, /* Packet Burst Enable */ + Rtlc = 0x01000000, /* Re-transmit on Late Collision */ + Nrtu = 0x02000000, /* No Re-transmit on Underrrun */ +}; + +enum { /* [RT]xdctl */ + PthreshMASK = 0x0000003F, /* Prefetch Threshold */ + PthreshSHIFT = 0, + HthreshMASK = 0x00003F00, /* Host Threshold */ + HthreshSHIFT = 8, + WthreshMASK = 0x003F0000, /* Writeback Threshold */ + WthreshSHIFT = 16, + Gran = 0x00000000, /* Granularity */ + RxGran = 0x01000000, /* Granularity */ +}; + +enum { /* Rxcsum */ + PcssMASK = 0x000000FF, /* Packet Checksum Start */ + PcssSHIFT = 0, + Ipofl = 0x00000100, /* IP Checksum Off-load Enable */ + Tuofl = 0x00000200, /* TCP/UDP Checksum Off-load Enable */ +}; + +enum { /* Receive Delay Timer Ring */ + Fpd = 0x80000000, /* Flush partial Descriptor Block */ +}; + +typedef struct Rdesc { /* Receive Descriptor */ + uint addr[2]; + ushort length; + ushort checksum; + uchar status; + uchar errors; + ushort special; +} Rdesc; + +enum { /* Rdesc status */ + Rdd = 0x01, /* Descriptor Done */ + Reop = 0x02, /* End of Packet */ + Ixsm = 0x04, /* Ignore Checksum Indication */ + Vp = 0x08, /* Packet is 802.1Q (matched VET) */ + Tcpcs = 0x20, /* TCP Checksum Calculated on Packet */ + Ipcs = 0x40, /* IP Checksum Calculated on Packet */ + Pif = 0x80, /* Passed in-exact filter */ +}; + +enum { /* Rdesc errors */ + Ce = 0x01, /* CRC Error or Alignment Error */ + Se = 0x02, /* Symbol Error */ + Seq = 0x04, /* Sequence Error */ + Cxe = 0x10, /* Carrier Extension Error */ + Tcpe = 0x20, /* TCP/UDP Checksum Error */ + Ipe = 0x40, /* IP Checksum Error */ + Rxe = 0x80, /* RX Data Error */ +}; + +typedef struct Tdesc { /* Legacy+Normal Transmit Descriptor */ + uint addr[2]; + uint control; /* varies with descriptor type */ + uint status; /* varies with descriptor type */ +} Tdesc; + +enum { /* Tdesc control */ + CsoMASK = 0x00000F00, /* Checksum Offset */ + CsoSHIFT = 16, + Teop = 0x01000000, /* End of Packet */ + Ifcs = 0x02000000, /* Insert FCS */ + Ic = 0x04000000, /* Insert Checksum (Dext == 0) */ + Tse = 0x04000000, /* TCP Segmentaion Enable (Dext == 1) */ + Rs = 0x08000000, /* Report Status */ + Rps = 0x10000000, /* Report Status Sent */ + Dext = 0x20000000, /* Extension (!legacy) */ + Vle = 0x40000000, /* VLAN Packet Enable */ + Ide = 0x80000000, /* Interrupt Delay Enable */ +}; + +enum { /* Tdesc status */ + Tdd = 0x00000001, /* Descriptor Done */ + Ec = 0x00000002, /* Excess Collisions */ + Lc = 0x00000004, /* Late Collision */ + Tu = 0x00000008, /* Transmit Underrun */ + CssMASK = 0x0000FF00, /* Checksum Start Field */ + CssSHIFT = 8, +}; + +enum { + Nrdesc = 256, /* multiple of 8 */ + Ntdesc = 256, /* multiple of 8 */ + Nblocks = 4098, /* total number of blocks to use */ + + SBLOCKSIZE = 2048, + JBLOCKSIZE = 16384, + + NORMAL = 1, + JUMBO = 2, +}; + +typedef struct Ctlr Ctlr; +typedef struct Ctlr { + int port; + Pcidev* pcidev; + Ctlr* next; + int active; + int started; + int id; + ushort eeprom[0x40]; + + int* nic; + int im; /* interrupt mask */ + + Lock slock; + uint statistics[Nstatistics]; + + Lock rdlock; + Rdesc* rdba; /* receive descriptor base address */ + Block* rb[Nrdesc]; /* receive buffers */ + int rdh; /* receive descriptor head */ + int rdt; /* receive descriptor tail */ + Block** freehead; /* points to long or short head */ + + Lock tdlock; + Tdesc* tdba; /* transmit descriptor base address */ + Block* tb[Ntdesc]; /* transmit buffers */ + int tdh; /* transmit descriptor head */ + int tdt; /* transmit descriptor tail */ + int txstalled; /* count of times unable to send */ + + int txcw; + int fcrtl; + int fcrth; + + ulong multimask[128]; /* bit mask for multicast addresses */ +} Ctlr; + +static Ctlr* gc82543ctlrhead; +static Ctlr* gc82543ctlrtail; + +static Lock freelistlock; +static Block* freeShortHead; +static Block* freeJumboHead; + +#define csr32r(c, r) (*((c)->nic+((r)/4))) +#define csr32w(c, r, v) (*((c)->nic+((r)/4)) = (v)) + +static void gc82543watchdog(void* arg); + +static void +gc82543attach(Ether* edev) +{ + int ctl; + Ctlr *ctlr; + char name[KNAMELEN]; + + /* + * To do here: + * one-time stuff; + * adjust queue length depending on speed; + * flow control. + * more needed here... + */ + ctlr = edev->ctlr; + lock(&ctlr->slock); + if(ctlr->started == 0){ + ctlr->started = 1; + snprint(name, KNAMELEN, "#l%d82543", edev->ctlrno); + kproc(name, gc82543watchdog, edev, 0); + } + unlock(&ctlr->slock); + + ctl = csr32r(ctlr, Rctl)|Ren; + csr32w(ctlr, Rctl, ctl); + ctl = csr32r(ctlr, Tctl)|Ten; + csr32w(ctlr, Tctl, ctl); + + csr32w(ctlr, Ims, ctlr->im); +} + +static char* statistics[Nstatistics] = { + "CRC Error", + "Alignment Error", + "Symbol Error", + "RX Error", + "Missed Packets", + "Single Collision", + "Excessive Collisions", + "Multiple Collision", + "Late Collisions", + nil, + "Collision", + "Transmit Underrun", + "Defer", + "Transmit - No CRS", + "Sequence Error", + "Carrier Extension Error", + "Receive Error Length", + nil, + "XON Received", + "XON Transmitted", + "XOFF Received", + "XOFF Transmitted", + "FC Received Unsupported", + "Packets Received (64 Bytes)", + "Packets Received (65-127 Bytes)", + "Packets Received (128-255 Bytes)", + "Packets Received (256-511 Bytes)", + "Packets Received (512-1023 Bytes)", + "Packets Received (1024-1522 Bytes)", + "Good Packets Received", + "Broadcast Packets Received", + "Multicast Packets Received", + "Good Packets Transmitted", + nil, + "Good Octets Received", + nil, + "Good Octets Transmitted", + nil, + nil, + nil, + "Receive No Buffers", + "Receive Undersize", + "Receive Fragment", + "Receive Oversize", + "Receive Jabber", + nil, + nil, + nil, + "Total Octets Received", + nil, + "Total Octets Transmitted", + nil, + "Total Packets Received", + "Total Packets Transmitted", + "Packets Transmitted (64 Bytes)", + "Packets Transmitted (65-127 Bytes)", + "Packets Transmitted (128-255 Bytes)", + "Packets Transmitted (256-511 Bytes)", + "Packets Transmitted (512-1023 Bytes)", + "Packets Transmitted (1024-1522 Bytes)", + "Multicast Packets Transmitted", + "Broadcast Packets Transmitted", + "TCP Segmentation Context Transmitted", + "TCP Segmentation Context Fail", +}; + +static long +gc82543ifstat(Ether* edev, void* a, long n, ulong offset) +{ + Ctlr *ctlr; + char *p, *s; + int i, l, r; + uvlong tuvl, ruvl; + + ctlr = edev->ctlr; + lock(&ctlr->slock); + p = malloc(2*READSTR); + l = 0; + for(i = 0; i < Nstatistics; i++){ + r = csr32r(ctlr, Statistics+i*4); + if((s = statistics[i]) == nil) + continue; + switch(i){ + case Gorcl: + case Gotcl: + case Torl: + case Totl: + ruvl = r; + ruvl += ((uvlong)csr32r(ctlr, Statistics+(i+1)*4))<<32; + tuvl = ruvl; + tuvl += ctlr->statistics[i]; + tuvl += ((uvlong)ctlr->statistics[i+1])<<32; + if(tuvl == 0) + continue; + ctlr->statistics[i] = tuvl; + ctlr->statistics[i+1] = tuvl>>32; + l += snprint(p+l, 2*READSTR-l, "%s: %llud %llud\n", + s, tuvl, ruvl); + i++; + break; + + default: + ctlr->statistics[i] += r; + if(ctlr->statistics[i] == 0) + continue; + l += snprint(p+l, 2*READSTR-l, "%s: %ud %ud\n", + s, ctlr->statistics[i], r); + break; + } + } + + l += snprint(p+l, 2*READSTR-l, "eeprom:"); + for(i = 0; i < 0x40; i++){ + if(i && ((i & 0x07) == 0)) + l += snprint(p+l, 2*READSTR-l, "\n "); + l += snprint(p+l, 2*READSTR-l, " %4.4uX", ctlr->eeprom[i]); + } + + snprint(p+l, 2*READSTR-l, "\ntxstalled %d\n", ctlr->txstalled); + n = readstr(offset, a, n, p); + free(p); + unlock(&ctlr->slock); + + return n; +} + +static void +gc82543promiscuous(void* arg, int on) +{ + int rctl; + Ctlr *ctlr; + Ether *edev; + + edev = arg; + ctlr = edev->ctlr; + + rctl = csr32r(ctlr, Rctl); + rctl &= ~MoMASK; /* make sure we're using bits 47:36 */ + if(on) + rctl |= Upe|Mpe; + else + rctl &= ~(Upe|Mpe); + csr32w(ctlr, Rctl, rctl); +} + +static void +gc82543multicast(void* arg, uchar* addr, int on) +{ + int bit, x; + Ctlr *ctlr; + Ether *edev; + + edev = arg; + ctlr = edev->ctlr; + x = addr[5]>>1; + bit = ((addr[5] & 1)<<4)|(addr[4]>>4); + if(on) + ctlr->multimask[x] |= 1<<bit; + else + ctlr->multimask[x] &= ~(1<<bit); + + csr32w(ctlr, Mta+x*4, ctlr->multimask[x]); +} + +static long +gc82543ctl(Ether* edev, void* buf, long n) +{ + Cmdbuf *cb; + Ctlr *ctlr; + int ctrl, i, r; + + ctlr = edev->ctlr; + if(ctlr == nil) + error(Enonexist); + + lock(&ctlr->slock); + r = 0; + cb = parsecmd(buf, n); + if(cb->nf < 2) + r = -1; + else if(cistrcmp(cb->f[0], "auto") == 0){ + ctrl = csr32r(ctlr, Ctrl); + if(cistrcmp(cb->f[1], "off") == 0){ + csr32w(ctlr, Txcw, ctlr->txcw & ~Ane); + ctrl |= (Slu|Fd); + if(ctlr->txcw & As) + ctrl |= Rfce; + if(ctlr->txcw & Ps) + ctrl |= Tfce; + csr32w(ctlr, Ctrl, ctrl); + } + else if(cistrcmp(cb->f[1], "on") == 0){ + csr32w(ctlr, Txcw, ctlr->txcw); + ctrl &= ~(Slu|Fd); + csr32w(ctlr, Ctrl, ctrl); + } + else + r = -1; + } + else if(cistrcmp(cb->f[0], "clear") == 0){ + if(cistrcmp(cb->f[1], "stats") == 0){ + for(i = 0; i < Nstatistics; i++) + ctlr->statistics[i] = 0; + } + else + r = -1; + } + else + r = -1; + unlock(&ctlr->slock); + + free(cb); + return (r == 0) ? n : r; +} + +static void +gc82543txinit(Ctlr* ctlr) +{ + int i; + int tdsize; + Block *bp, **bpp; + + tdsize = ROUND(Ntdesc*sizeof(Tdesc), 4096); + + if(ctlr->tdba == nil) + ctlr->tdba = xspanalloc(tdsize, 32, 0); + + for(i = 0; i < Ntdesc; i++){ + bpp = &ctlr->tb[i]; + bp = *bpp; + if(bp != nil){ + *bpp = nil; + freeb(bp); + } + memset(&ctlr->tdba[i], 0, sizeof(Tdesc)); + } + + csr32w(ctlr, Tdbal, PCIWADDR(ctlr->tdba)); + csr32w(ctlr, Tdbah, 0); + csr32w(ctlr, Tdlen, Ntdesc*sizeof(Tdesc)); + + /* + * set the ring head and tail pointers. + */ + ctlr->tdh = 0; + csr32w(ctlr, Tdh, ctlr->tdh); + ctlr->tdt = 0; + csr32w(ctlr, Tdt, ctlr->tdt); + + csr32w(ctlr, Tipg, (6<<20)|(8<<10)|6); + csr32w(ctlr, Tidv, 128); + csr32w(ctlr, Ait, 0); + csr32w(ctlr, Txdmac, 0); + csr32w(ctlr, Txdctl, Gran|(4<<WthreshSHIFT)|(1<<HthreshSHIFT)|16); + csr32w(ctlr, Tctl, (0x0F<<CtSHIFT)|Psp|(6<<ColdSHIFT)); + + ctlr->im |= Txdw; +} + +static void +gc82543transmit(Ether* edev) +{ + Block *bp, **bpp; + Ctlr *ctlr; + Tdesc *tdesc; + int tdh, tdt, s; + + ctlr = edev->ctlr; + + ilock(&ctlr->tdlock); + tdh = ctlr->tdh; + for(;;){ + /* + * Free any completed packets + */ + tdesc = &ctlr->tdba[tdh]; + if(!(tdesc->status & Tdd)) + break; + memset(tdesc, 0, sizeof(Tdesc)); + bpp = &ctlr->tb[tdh]; + bp = *bpp; + if(bp != nil){ + *bpp = nil; + freeb(bp); + } + tdh = NEXT(tdh, Ntdesc); + } + ctlr->tdh = tdh; + s = csr32r(ctlr, Status); + + /* + * Try to fill the ring back up + * but only if link is up and transmission isn't paused. + */ + if((s & (Txoff|Lu)) == Lu){ + tdt = ctlr->tdt; + while(NEXT(tdt, Ntdesc) != tdh){ + if((bp = qget(edev->oq)) == nil) + break; + + tdesc = &ctlr->tdba[tdt]; + tdesc->addr[0] = PCIWADDR(bp->rp); + tdesc->control = Ide|Rs|Ifcs|Teop|BLEN(bp); + ctlr->tb[tdt] = bp; + tdt = NEXT(tdt, Ntdesc); + } + + if(tdt != ctlr->tdt){ + ctlr->tdt = tdt; + csr32w(ctlr, Tdt, tdt); + } + } + else + ctlr->txstalled++; + + iunlock(&ctlr->tdlock); +} + +static Block * +gc82543allocb(Ctlr* ctlr) +{ + Block *bp; + + ilock(&freelistlock); + if((bp = *(ctlr->freehead)) != nil){ + *(ctlr->freehead) = bp->next; + bp->next = nil; + } + iunlock(&freelistlock); + return bp; +} + +static void +gc82543replenish(Ctlr* ctlr) +{ + int rdt; + Block *bp; + Rdesc *rdesc; + + ilock(&ctlr->rdlock); + rdt = ctlr->rdt; + while(NEXT(rdt, Nrdesc) != ctlr->rdh){ + rdesc = &ctlr->rdba[rdt]; + if(ctlr->rb[rdt] == nil){ + bp = gc82543allocb(ctlr); + if(bp == nil){ + iprint("no available buffers\n"); + break; + } + ctlr->rb[rdt] = bp; + rdesc->addr[0] = PCIWADDR(bp->rp); + rdesc->addr[1] = 0; + } + coherence(); + rdesc->status = 0; + rdt = NEXT(rdt, Nrdesc); + } + ctlr->rdt = rdt; + csr32w(ctlr, Rdt, rdt); + iunlock(&ctlr->rdlock); +} + +static void +gc82543rxinit(Ctlr* ctlr) +{ + int rdsize, i; + + csr32w(ctlr, Rctl, Dpf|Bsize2048|Bam|RdtmsHALF); + + /* + * Allocate the descriptor ring and load its + * address and length into the NIC. + */ + rdsize = ROUND(Nrdesc*sizeof(Rdesc), 4096); + if(ctlr->rdba == nil) + ctlr->rdba = xspanalloc(rdsize, 32, 0); + memset(ctlr->rdba, 0, rdsize); + + ctlr->rdh = 0; + ctlr->rdt = 0; + + csr32w(ctlr, Rdtr, Fpd|64); + csr32w(ctlr, Rdbal, PCIWADDR(ctlr->rdba)); + csr32w(ctlr, Rdbah, 0); + csr32w(ctlr, Rdlen, Nrdesc*sizeof(Rdesc)); + csr32w(ctlr, Rdh, 0); + csr32w(ctlr, Rdt, 0); + for(i = 0; i < Nrdesc; i++){ + if(ctlr->rb[i] != nil){ + freeb(ctlr->rb[i]); + ctlr->rb[i] = nil; + } + } + gc82543replenish(ctlr); + + csr32w(ctlr, Rxdctl, RxGran|(8<<WthreshSHIFT)|(4<<HthreshSHIFT)|1); + ctlr->im |= Rxt0|Rxo|Rxdmt0|Rxseq; +} + +static void +gc82543recv(Ether* edev, int icr) +{ + Block *bp; + Ctlr *ctlr; + Rdesc *rdesc; + int rdh; + + ctlr = edev->ctlr; + + rdh = ctlr->rdh; + for(;;){ + rdesc = &ctlr->rdba[rdh]; + + if(!(rdesc->status & Rdd)) + break; + + if((rdesc->status & Reop) && rdesc->errors == 0){ + bp = ctlr->rb[rdh]; + ctlr->rb[rdh] = nil; + bp->wp += rdesc->length; + bp->next = nil; + etheriq(edev, bp, 1); + } + + if(ctlr->rb[rdh] != nil){ + /* either non eop packet, or error */ + freeb(ctlr->rb[rdh]); + ctlr->rb[rdh] = nil; + } + memset(rdesc, 0, sizeof(Rdesc)); + coherence(); + rdh = NEXT(rdh, Nrdesc); + } + ctlr->rdh = rdh; + + if(icr & Rxdmt0) + gc82543replenish(ctlr); +} + +static void +freegc82543short(Block *bp) +{ + ilock(&freelistlock); + /* reset read/write pointer to proper positions */ + bp->rp = bp->lim - ROUND(SBLOCKSIZE, BLOCKALIGN); + bp->wp = bp->rp; + bp->next = freeShortHead; + freeShortHead = bp; + iunlock(&freelistlock); +} + +static void +freegc82532jumbo(Block *bp) +{ + ilock(&freelistlock); + /* reset read/write pointer to proper positions */ + bp->rp = bp->lim - ROUND(JBLOCKSIZE, BLOCKALIGN); + bp->wp = bp->rp; + bp->next = freeJumboHead; + freeJumboHead = bp; + iunlock(&freelistlock); +} + +static void +linkintr(Ctlr* ctlr) +{ + int ctrl; + + ctrl = csr32r(ctlr, Ctrl); + + if((ctrl & Swdpin1) || + ((csr32r(ctlr, Rxcw) & Rxconfig) && !(csr32r(ctlr, Txcw) & Ane))){ + csr32w(ctlr, Txcw, ctlr->txcw); + ctrl &= ~(Slu|Fd|Frcdplx); + csr32w(ctlr, Ctrl, ctrl); + } +} + +static void +gc82543interrupt(Ureg*, void* arg) +{ + Ctlr *ctlr; + Ether *edev; + int icr; + + edev = arg; + ctlr = edev->ctlr; + + while((icr = csr32r(ctlr, Icr) & ctlr->im) != 0){ + /* + * Link status changed. + */ + if(icr & (Lsc|Rxseq)) + linkintr(ctlr); + + /* + * Process recv buffers. + */ + gc82543recv(edev, icr); + + /* + * Refill transmit ring and free packets. + */ + gc82543transmit(edev); + } +} + +static int +gc82543init(Ether* edev) +{ + int csr, i; + Block *bp; + Ctlr *ctlr; + + ctlr = edev->ctlr; + + /* + * Allocate private buffer pool to use for receiving packets. + */ + ilock(&freelistlock); + if (ctlr->freehead == nil){ + for(i = 0; i < Nblocks; i++){ + bp = iallocb(SBLOCKSIZE); + if(bp != nil){ + bp->next = freeShortHead; + bp->free = freegc82543short; + freeShortHead = bp; + } + else{ + print("82543gc: no memory\n"); + break; + } + } + ctlr->freehead = &freeShortHead; + } + iunlock(&freelistlock); + + /* + * Set up the receive addresses. + * There are 16 addresses. The first should be the MAC address. + * The others are cleared and not marked valid (MS bit of Rah). + */ + csr = (edev->ea[3]<<24)|(edev->ea[2]<<16)|(edev->ea[1]<<8)|edev->ea[0]; + csr32w(ctlr, Ral, csr); + csr = 0x80000000|(edev->ea[5]<<8)|edev->ea[4]; + csr32w(ctlr, Rah, csr); + for(i = 1; i < 16; i++){ + csr32w(ctlr, Ral+i*8, 0); + csr32w(ctlr, Rah+i*8, 0); + } + + /* + * Clear the Multicast Table Array. + * It's a 4096 bit vector accessed as 128 32-bit registers. + */ + for(i = 0; i < 128; i++) + csr32w(ctlr, Mta+i*4, 0); + + gc82543txinit(ctlr); + gc82543rxinit(ctlr); + + return 0; +} + +static int +at93c46io(Ctlr* ctlr, char* op, int data) +{ + char *lp, *p; + int i, loop, eecd, r; + + eecd = csr32r(ctlr, Eecd); + + r = 0; + loop = -1; + lp = nil; + for(p = op; *p != '\0'; p++){ + switch(*p){ + default: + return -1; + case ' ': + continue; + case ':': /* start of loop */ + if(lp != nil){ + if(p != (lp+1) || loop != 7) + return -1; + lp = p; + loop = 15; + continue; + } + lp = p; + loop = 7; + continue; + case ';': /* end of loop */ + if(lp == nil) + return -1; + loop--; + if(loop >= 0) + p = lp; + else + lp = nil; + continue; + case 'C': /* assert clock */ + eecd |= Sk; + break; + case 'c': /* deassert clock */ + eecd &= ~Sk; + break; + case 'D': /* next bit in 'data' byte */ + if(loop < 0) + return -1; + if(data & (1<<loop)) + eecd |= Di; + else + eecd &= ~Di; + break; + case 'O': /* collect data output */ + i = (csr32r(ctlr, Eecd) & Do) != 0; + if(loop >= 0) + r |= (i<<loop); + else + r = i; + continue; + case 'I': /* assert data input */ + eecd |= Di; + break; + case 'i': /* deassert data input */ + eecd &= ~Di; + break; + case 'S': /* enable chip select */ + eecd |= Cs; + break; + case 's': /* disable chip select */ + eecd &= ~Cs; + break; + } + csr32w(ctlr, Eecd, eecd); + microdelay(1); + } + if(loop >= 0) + return -1; + return r; +} + +static int +at93c46r(Ctlr* ctlr) +{ + ushort sum; + int addr, data; + + sum = 0; + for(addr = 0; addr < 0x40; addr++){ + /* + * Read a word at address 'addr' from the Atmel AT93C46 + * 3-Wire Serial EEPROM or compatible. The EEPROM access is + * controlled by 4 bits in Eecd. See the AT93C46 datasheet + * for protocol details. + */ + if(at93c46io(ctlr, "S ICc :DCc;", (0x02<<6)|addr) != 0) + break; + data = at93c46io(ctlr, "::COc;", 0); + at93c46io(ctlr, "sic", 0); + ctlr->eeprom[addr] = data; + sum += data; + } + + return sum; +} + +static void +gc82543detach(Ctlr* ctlr) +{ + /* + * Perform a device reset to get the chip back to the + * power-on state, followed by an EEPROM reset to read + * the defaults for some internal registers. + */ + csr32w(ctlr, Imc, ~0); + csr32w(ctlr, Rctl, 0); + csr32w(ctlr, Tctl, 0); + + delay(10); + + csr32w(ctlr, Ctrl, Devrst); + while(csr32r(ctlr, Ctrl) & Devrst) + ; + + csr32w(ctlr, Ctrlext, Eerst); + while(csr32r(ctlr, Ctrlext) & Eerst) + ; + + csr32w(ctlr, Imc, ~0); + while(csr32r(ctlr, Icr)) + ; +} + +static void +gc82543checklink(Ctlr* ctlr) +{ + int ctrl, status, rxcw; + + ctrl = csr32r(ctlr, Ctrl); + status = csr32r(ctlr, Status); + rxcw = csr32r(ctlr, Rxcw); + + if(!(status & Lu)){ + if(!(ctrl & (Swdpin1|Slu)) && !(rxcw & Rxconfig)){ + csr32w(ctlr, Txcw, ctlr->txcw & ~Ane); + ctrl |= (Slu|Fd); + if(ctlr->txcw & As) + ctrl |= Rfce; + if(ctlr->txcw & Ps) + ctrl |= Tfce; + csr32w(ctlr, Ctrl, ctrl); + } + } + else if((ctrl & Slu) && (rxcw & Rxconfig)){ + csr32w(ctlr, Txcw, ctlr->txcw); + ctrl &= ~(Slu|Fd); + csr32w(ctlr, Ctrl, ctrl); + } +} + +static void +gc82543shutdown(Ether* ether) +{ + gc82543detach(ether->ctlr); +} + +static int +gc82543reset(Ctlr* ctlr) +{ + int ctl; + int te; + + /* + * Read the EEPROM, validate the checksum + * then get the device back to a power-on state. + */ + if(at93c46r(ctlr) != 0xBABA) + return -1; + + gc82543detach(ctlr); + + te = ctlr->eeprom[Icw2]; + if((te & 0x3000) == 0){ + ctlr->fcrtl = 0x00002000; + ctlr->fcrth = 0x00004000; + ctlr->txcw = Ane|TxcwFd; + } + else if((te & 0x3000) == 0x2000){ + ctlr->fcrtl = 0; + ctlr->fcrth = 0; + ctlr->txcw = Ane|TxcwFd|As; + } + else{ + ctlr->fcrtl = 0x00002000; + ctlr->fcrth = 0x00004000; + ctlr->txcw = Ane|TxcwFd|As|Ps; + } + + csr32w(ctlr, Txcw, ctlr->txcw); + + csr32w(ctlr, Ctrlext, (te & 0x00f0)<<4); + + csr32w(ctlr, Tctl, csr32r(ctlr, Tctl)|(64<<ColdSHIFT)); + + te = ctlr->eeprom[Icw1]; + ctl = ((te & 0x01E0)<<17)|(te & 0x0010)<<3; + csr32w(ctlr, Ctrl, ctl); + + delay(10); + + /* + * Flow control - values from the datasheet. + */ + csr32w(ctlr, Fcal, 0x00C28001); + csr32w(ctlr, Fcah, 0x00000100); + csr32w(ctlr, Fct, 0x00008808); + csr32w(ctlr, Fcttv, 0x00000100); + + csr32w(ctlr, Fcrtl, ctlr->fcrtl); + csr32w(ctlr, Fcrth, ctlr->fcrth); + + ctlr->im = Lsc; + gc82543checklink(ctlr); + + return 0; +} + +static void +gc82543watchdog(void* arg) +{ + Ether *edev; + Ctlr *ctlr; + + edev = arg; + for(;;){ + tsleep(&up->sleep, return0, 0, 1000); + + ctlr = edev->ctlr; + if(ctlr == nil){ + print("%s: exiting\n", up->text); + pexit("disabled", 0); + } + + gc82543checklink(ctlr); + gc82543replenish(ctlr); + } +} + +static void +gc82543pci(void) +{ + int port, cls; + Pcidev *p; + Ctlr *ctlr; + + p = nil; + while(p = pcimatch(p, 0, 0)){ + if(p->ccrb != 0x02 || p->ccru != 0) + continue; + + switch((p->did<<16)|p->vid){ + case (0x1000<<16)|0x8086: /* LSI L2A1157 (82542) */ + case (0x1004<<16)|0x8086: /* Intel PRO/1000 T */ + case (0x1008<<16)|0x8086: /* Intel PRO/1000 XT */ + default: + continue; + case (0x1001<<16)|0x8086: /* Intel PRO/1000 F */ + break; + } + + port = upamalloc(p->mem[0].bar & ~0x0F, p->mem[0].size, 0); + if(port == 0){ + print("gc82543: can't map %8.8luX\n", p->mem[0].bar); + continue; + } + cls = pcicfgr8(p, PciCLS); + switch(cls){ + case 0x00: + case 0xFF: + print("82543gc: unusable cache line size\n"); + continue; + case 0x08: + break; + default: + print("82543gc: cache line size %d, expected 32\n", + cls*4); + } + ctlr = malloc(sizeof(Ctlr)); + ctlr->port = port; + ctlr->pcidev = p; + ctlr->id = (p->did<<16)|p->vid; + + ctlr->nic = KADDR(ctlr->port); + + if(gc82543reset(ctlr)){ + free(ctlr); + continue; + } + + if(gc82543ctlrhead != nil) + gc82543ctlrtail->next = ctlr; + else + gc82543ctlrhead = ctlr; + gc82543ctlrtail = ctlr; + } +} + +static int +gc82543pnp(Ether* edev) +{ + int i; + Ctlr *ctlr; + uchar ea[Eaddrlen]; + + if(gc82543ctlrhead == nil) + gc82543pci(); + + /* + * Any adapter matches if no edev->port is supplied, + * otherwise the ports must match. + */ + for(ctlr = gc82543ctlrhead; ctlr != nil; ctlr = ctlr->next){ + if(ctlr->active) + continue; + if(edev->port == 0 || edev->port == ctlr->port){ + ctlr->active = 1; + break; + } + } + if(ctlr == nil) + return -1; + + edev->ctlr = ctlr; + edev->port = ctlr->port; + edev->irq = ctlr->pcidev->intl; + edev->tbdf = ctlr->pcidev->tbdf; + edev->mbps = 1000; + + /* + * Check if the adapter's station address is to be overridden. + * If not, read it from the EEPROM and set in ether->ea prior to + * loading the station address in the hardware. + */ + memset(ea, 0, Eaddrlen); + if(memcmp(ea, edev->ea, Eaddrlen) == 0){ + for(i = Ea; i < Eaddrlen/2; i++){ + edev->ea[2*i] = ctlr->eeprom[i]; + edev->ea[2*i+1] = ctlr->eeprom[i]>>8; + } + } + gc82543init(edev); + + /* + * Linkage to the generic ethernet driver. + */ + edev->attach = gc82543attach; + edev->transmit = gc82543transmit; + edev->interrupt = gc82543interrupt; + edev->ifstat = gc82543ifstat; + edev->shutdown = gc82543shutdown; + edev->ctl = gc82543ctl; + edev->arg = edev; + edev->promiscuous = gc82543promiscuous; + edev->multicast = gc82543multicast; + + return 0; +} + +void +ether82543gclink(void) +{ + addethercard("82543GC", gc82543pnp); +} diff --git a/os/pc/ether82557.c b/os/pc/ether82557.c new file mode 100644 index 00000000..b61e3e1c --- /dev/null +++ b/os/pc/ether82557.c @@ -0,0 +1,1325 @@ +/* + * Intel 82557 Fast Ethernet PCI Bus LAN Controller + * as found on the Intel EtherExpress PRO/100B. This chip is full + * of smarts, unfortunately they're not all in the right place. + * To do: + * the PCI scanning code could be made common to other adapters; + * auto-negotiation, full-duplex; + * optionally use memory-mapped registers; + * detach for PCI reset problems (also towards loadable drivers). + */ +#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/netif.h" + +#include "etherif.h" + +enum { + Nrfd = 64, /* receive frame area */ + Ncb = 64, /* maximum control blocks queued */ + + NullPointer = 0xFFFFFFFF, /* 82557 NULL pointer */ +}; + +enum { /* CSR */ + Status = 0x00, /* byte or word (word includes Ack) */ + Ack = 0x01, /* byte */ + CommandR = 0x02, /* byte or word (word includes Interrupt) */ + Interrupt = 0x03, /* byte */ + General = 0x04, /* dword */ + Port = 0x08, /* dword */ + Fcr = 0x0C, /* Flash control register */ + Ecr = 0x0E, /* EEPROM control register */ + Mcr = 0x10, /* MDI control register */ + Gstatus = 0x1D, /* General status register */ +}; + +enum { /* Status */ + RUidle = 0x0000, + RUsuspended = 0x0004, + RUnoresources = 0x0008, + RUready = 0x0010, + RUrbd = 0x0020, /* bit */ + RUstatus = 0x003F, /* mask */ + + CUidle = 0x0000, + CUsuspended = 0x0040, + CUactive = 0x0080, + CUstatus = 0x00C0, /* mask */ + + StatSWI = 0x0400, /* SoftWare generated Interrupt */ + StatMDI = 0x0800, /* MDI r/w done */ + StatRNR = 0x1000, /* Receive unit Not Ready */ + StatCNA = 0x2000, /* Command unit Not Active (Active->Idle) */ + StatFR = 0x4000, /* Finished Receiving */ + StatCX = 0x8000, /* Command eXecuted */ + StatTNO = 0x8000, /* Transmit NOT OK */ +}; + +enum { /* Command (byte) */ + CUnop = 0x00, + CUstart = 0x10, + CUresume = 0x20, + LoadDCA = 0x40, /* Load Dump Counters Address */ + DumpSC = 0x50, /* Dump Statistical Counters */ + LoadCUB = 0x60, /* Load CU Base */ + ResetSA = 0x70, /* Dump and Reset Statistical Counters */ + + RUstart = 0x01, + RUresume = 0x02, + RUabort = 0x04, + LoadHDS = 0x05, /* Load Header Data Size */ + LoadRUB = 0x06, /* Load RU Base */ + RBDresume = 0x07, /* Resume frame reception */ +}; + +enum { /* Interrupt (byte) */ + InterruptM = 0x01, /* interrupt Mask */ + InterruptSI = 0x02, /* Software generated Interrupt */ +}; + +enum { /* Ecr */ + EEsk = 0x01, /* serial clock */ + EEcs = 0x02, /* chip select */ + EEdi = 0x04, /* serial data in */ + EEdo = 0x08, /* serial data out */ + + EEstart = 0x04, /* start bit */ + EEread = 0x02, /* read opcode */ +}; + +enum { /* Mcr */ + MDIread = 0x08000000, /* read opcode */ + MDIwrite = 0x04000000, /* write opcode */ + MDIready = 0x10000000, /* ready bit */ + MDIie = 0x20000000, /* interrupt enable */ +}; + +typedef struct Rfd { + int field; + ulong link; + ulong rbd; + ushort count; + ushort size; + + uchar data[1700]; +} Rfd; + +enum { /* field */ + RfdCollision = 0x00000001, + RfdIA = 0x00000002, /* IA match */ + RfdRxerr = 0x00000010, /* PHY character error */ + RfdType = 0x00000020, /* Type frame */ + RfdRunt = 0x00000080, + RfdOverrun = 0x00000100, + RfdBuffer = 0x00000200, + RfdAlignment = 0x00000400, + RfdCRC = 0x00000800, + + RfdOK = 0x00002000, /* frame received OK */ + RfdC = 0x00008000, /* reception Complete */ + RfdSF = 0x00080000, /* Simplified or Flexible (1) Rfd */ + RfdH = 0x00100000, /* Header RFD */ + + RfdI = 0x20000000, /* Interrupt after completion */ + RfdS = 0x40000000, /* Suspend after completion */ + RfdEL = 0x80000000, /* End of List */ +}; + +enum { /* count */ + RfdF = 0x4000, + RfdEOF = 0x8000, +}; + +typedef struct Cb Cb; +typedef struct Cb { + ushort status; + ushort command; + ulong link; + union { + uchar data[24]; /* CbIAS + CbConfigure */ + struct { + ulong tbd; + ushort count; + uchar threshold; + uchar number; + + ulong tba; + ushort tbasz; + ushort pad; + }; + }; + + Block* bp; + Cb* next; +} Cb; + +enum { /* action command */ + CbU = 0x1000, /* transmit underrun */ + CbOK = 0x2000, /* DMA completed OK */ + CbC = 0x8000, /* execution Complete */ + + CbNOP = 0x0000, + CbIAS = 0x0001, /* Individual Address Setup */ + CbConfigure = 0x0002, + CbMAS = 0x0003, /* Multicast Address Setup */ + CbTransmit = 0x0004, + CbDump = 0x0006, + CbDiagnose = 0x0007, + CbCommand = 0x0007, /* mask */ + + CbSF = 0x0008, /* Flexible-mode CbTransmit */ + + CbI = 0x2000, /* Interrupt after completion */ + CbS = 0x4000, /* Suspend after completion */ + CbEL = 0x8000, /* End of List */ +}; + +enum { /* CbTransmit count */ + CbEOF = 0x8000, +}; + +typedef struct Ctlr Ctlr; +typedef struct Ctlr { + Lock slock; /* attach */ + int state; + + int port; + Pcidev* pcidev; + Ctlr* next; + int active; + + int eepromsz; /* address size in bits */ + ushort* eeprom; + + Lock miilock; + + int tick; + + Lock rlock; /* registers */ + int command; /* last command issued */ + + Block* rfdhead; /* receive side */ + Block* rfdtail; + int nrfd; + + Lock cblock; /* transmit side */ + int action; + int nop; + uchar configdata[24]; + int threshold; + int ncb; + Cb* cbr; + Cb* cbhead; + Cb* cbtail; + int cbq; + int cbqmax; + int cbqmaxhw; + + Lock dlock; /* dump statistical counters */ + ulong dump[17]; +} Ctlr; + +static Ctlr* ctlrhead; +static Ctlr* ctlrtail; + +static uchar configdata[24] = { + 0x16, /* byte count */ + 0x08, /* Rx/Tx FIFO limit */ + 0x00, /* adaptive IFS */ + 0x00, + 0x00, /* Rx DMA maximum byte count */ +// 0x80, /* Tx DMA maximum byte count */ + 0x00, /* Tx DMA maximum byte count */ + 0x32, /* !late SCB, CNA interrupts */ + 0x03, /* discard short Rx frames */ + 0x00, /* 503/MII */ + + 0x00, + 0x2E, /* normal operation, NSAI */ + 0x00, /* linear priority */ + 0x60, /* inter-frame spacing */ + 0x00, + 0xF2, + 0xC8, /* 503, promiscuous mode off */ + 0x00, + 0x40, + 0xF3, /* transmit padding enable */ + 0x80, /* full duplex pin enable */ + 0x3F, /* no Multi IA */ + 0x05, /* no Multi Cast ALL */ +}; + +#define csr8r(c, r) (inb((c)->port+(r))) +#define csr16r(c, r) (ins((c)->port+(r))) +#define csr32r(c, r) (inl((c)->port+(r))) +#define csr8w(c, r, b) (outb((c)->port+(r), (int)(b))) +#define csr16w(c, r, w) (outs((c)->port+(r), (ushort)(w))) +#define csr32w(c, r, l) (outl((c)->port+(r), (ulong)(l))) + +static void +command(Ctlr* ctlr, int c, int v) +{ + int timeo; + + ilock(&ctlr->rlock); + + /* + * Only back-to-back CUresume can be done + * without waiting for any previous command to complete. + * This should be the common case. + * Unfortunately there's a chip errata where back-to-back + * CUresumes can be lost, the fix is to always wait. + if(c == CUresume && ctlr->command == CUresume){ + csr8w(ctlr, CommandR, c); + iunlock(&ctlr->rlock); + return; + } + */ + + for(timeo = 0; timeo < 100; timeo++){ + if(!csr8r(ctlr, CommandR)) + break; + microdelay(1); + } + if(timeo >= 100){ + ctlr->command = -1; + iunlock(&ctlr->rlock); + iprint("i82557: command 0x%uX %uX timeout\n", c, v); + return; + } + + switch(c){ + + case CUstart: + case LoadDCA: + case LoadCUB: + case RUstart: + case LoadHDS: + case LoadRUB: + csr32w(ctlr, General, v); + break; + + /* + case CUnop: + case CUresume: + case DumpSC: + case ResetSA: + case RUresume: + case RUabort: + */ + default: + break; + } + csr8w(ctlr, CommandR, c); + ctlr->command = c; + + iunlock(&ctlr->rlock); +} + +static Block* +rfdalloc(ulong link) +{ + Block *bp; + Rfd *rfd; + + if(bp = iallocb(sizeof(Rfd))){ + rfd = (Rfd*)bp->rp; + rfd->field = 0; + rfd->link = link; + rfd->rbd = NullPointer; + rfd->count = 0; + rfd->size = sizeof(Etherpkt); + } + + return bp; +} + +static void +watchdog(void* arg) +{ + Ether *ether; + Ctlr *ctlr; + static void txstart(Ether*); + + ether = arg; + for(;;){ + tsleep(&up->sleep, return0, 0, 4000); + + /* + * Hmmm. This doesn't seem right. Currently + * the device can't be disabled but it may be in + * the future. + */ + ctlr = ether->ctlr; + if(ctlr == nil || ctlr->state == 0){ + print("%s: exiting\n", up->text); + pexit("disabled", 0); + } + + ilock(&ctlr->cblock); + if(ctlr->tick++){ + ctlr->action = CbMAS; + txstart(ether); + } + iunlock(&ctlr->cblock); + } +} + +static void +attach(Ether* ether) +{ + Ctlr *ctlr; + char name[KNAMELEN]; + + ctlr = ether->ctlr; + lock(&ctlr->slock); + if(ctlr->state == 0){ + ilock(&ctlr->rlock); + csr8w(ctlr, Interrupt, 0); + iunlock(&ctlr->rlock); + command(ctlr, RUstart, PADDR(ctlr->rfdhead->rp)); + ctlr->state = 1; + + /* + * Start the watchdog timer for the receive lockup errata + * unless the EEPROM compatibility word indicates it may be + * omitted. + */ + if((ctlr->eeprom[0x03] & 0x0003) != 0x0003){ + snprint(name, KNAMELEN, "#l%dwatchdog", ether->ctlrno); + kproc(name, watchdog, ether, 0); + } + } + unlock(&ctlr->slock); +} + +static long +ifstat(Ether* ether, void* a, long n, ulong offset) +{ + char *p; + int i, len, phyaddr; + Ctlr *ctlr; + ulong dump[17]; + + ctlr = ether->ctlr; + lock(&ctlr->dlock); + + /* + * Start the command then + * wait for completion status, + * should be 0xA005. + */ + ctlr->dump[16] = 0; + command(ctlr, DumpSC, 0); + while(ctlr->dump[16] == 0) + ; + + ether->oerrs = ctlr->dump[1]+ctlr->dump[2]+ctlr->dump[3]; + ether->crcs = ctlr->dump[10]; + ether->frames = ctlr->dump[11]; + ether->buffs = ctlr->dump[12]+ctlr->dump[15]; + ether->overflows = ctlr->dump[13]; + + if(n == 0){ + unlock(&ctlr->dlock); + return 0; + } + + memmove(dump, ctlr->dump, sizeof(dump)); + unlock(&ctlr->dlock); + + p = malloc(READSTR); + len = snprint(p, READSTR, "transmit good frames: %lud\n", dump[0]); + len += snprint(p+len, READSTR-len, "transmit maximum collisions errors: %lud\n", dump[1]); + len += snprint(p+len, READSTR-len, "transmit late collisions errors: %lud\n", dump[2]); + len += snprint(p+len, READSTR-len, "transmit underrun errors: %lud\n", dump[3]); + len += snprint(p+len, READSTR-len, "transmit lost carrier sense: %lud\n", dump[4]); + len += snprint(p+len, READSTR-len, "transmit deferred: %lud\n", dump[5]); + len += snprint(p+len, READSTR-len, "transmit single collisions: %lud\n", dump[6]); + len += snprint(p+len, READSTR-len, "transmit multiple collisions: %lud\n", dump[7]); + len += snprint(p+len, READSTR-len, "transmit total collisions: %lud\n", dump[8]); + len += snprint(p+len, READSTR-len, "receive good frames: %lud\n", dump[9]); + len += snprint(p+len, READSTR-len, "receive CRC errors: %lud\n", dump[10]); + len += snprint(p+len, READSTR-len, "receive alignment errors: %lud\n", dump[11]); + len += snprint(p+len, READSTR-len, "receive resource errors: %lud\n", dump[12]); + len += snprint(p+len, READSTR-len, "receive overrun errors: %lud\n", dump[13]); + len += snprint(p+len, READSTR-len, "receive collision detect errors: %lud\n", dump[14]); + len += snprint(p+len, READSTR-len, "receive short frame errors: %lud\n", dump[15]); + len += snprint(p+len, READSTR-len, "nop: %d\n", ctlr->nop); + if(ctlr->cbqmax > ctlr->cbqmaxhw) + ctlr->cbqmaxhw = ctlr->cbqmax; + len += snprint(p+len, READSTR-len, "cbqmax: %d\n", ctlr->cbqmax); + ctlr->cbqmax = 0; + len += snprint(p+len, READSTR-len, "threshold: %d\n", ctlr->threshold); + + len += snprint(p+len, READSTR-len, "eeprom:"); + for(i = 0; i < (1<<ctlr->eepromsz); i++){ + if(i && ((i & 0x07) == 0)) + len += snprint(p+len, READSTR-len, "\n "); + len += snprint(p+len, READSTR-len, " %4.4uX", ctlr->eeprom[i]); + } + + if((ctlr->eeprom[6] & 0x1F00) && !(ctlr->eeprom[6] & 0x8000)){ + phyaddr = ctlr->eeprom[6] & 0x00FF; + len += snprint(p+len, READSTR-len, "\nphy %2d:", phyaddr); + for(i = 0; i < 6; i++){ + static int miir(Ctlr*, int, int); + + len += snprint(p+len, READSTR-len, " %4.4uX", + miir(ctlr, phyaddr, i)); + } + } + + snprint(p+len, READSTR-len, "\n"); + n = readstr(offset, a, n, p); + free(p); + + return n; +} + +static void +txstart(Ether* ether) +{ + Ctlr *ctlr; + Block *bp; + Cb *cb; + + ctlr = ether->ctlr; + while(ctlr->cbq < (ctlr->ncb-1)){ + cb = ctlr->cbhead->next; + if(ctlr->action == 0){ + bp = qget(ether->oq); + if(bp == nil) + break; + + cb->command = CbS|CbSF|CbTransmit; + cb->tbd = PADDR(&cb->tba); + cb->count = 0; + cb->threshold = ctlr->threshold; + cb->number = 1; + cb->tba = PADDR(bp->rp); + cb->bp = bp; + cb->tbasz = BLEN(bp); + } + else if(ctlr->action == CbConfigure){ + cb->command = CbS|CbConfigure; + memmove(cb->data, ctlr->configdata, sizeof(ctlr->configdata)); + ctlr->action = 0; + } + else if(ctlr->action == CbIAS){ + cb->command = CbS|CbIAS; + memmove(cb->data, ether->ea, Eaddrlen); + ctlr->action = 0; + } + else if(ctlr->action == CbMAS){ + cb->command = CbS|CbMAS; + memset(cb->data, 0, sizeof(cb->data)); + ctlr->action = 0; + } + else{ + print("#l%d: action 0x%uX\n", ether->ctlrno, ctlr->action); + ctlr->action = 0; + break; + } + cb->status = 0; + + coherence(); + ctlr->cbhead->command &= ~CbS; + ctlr->cbhead = cb; + ctlr->cbq++; + } + + /* + * Workaround for some broken HUB chips + * when connected at 10Mb/s half-duplex. + */ + if(ctlr->nop){ + command(ctlr, CUnop, 0); + microdelay(1); + } + command(ctlr, CUresume, 0); + + if(ctlr->cbq > ctlr->cbqmax) + ctlr->cbqmax = ctlr->cbq; +} + +static void +configure(Ether* ether, int promiscuous) +{ + Ctlr *ctlr; + + ctlr = ether->ctlr; + ilock(&ctlr->cblock); + if(promiscuous){ + ctlr->configdata[6] |= 0x80; /* Save Bad Frames */ + //ctlr->configdata[6] &= ~0x40; /* !Discard Overrun Rx Frames */ + ctlr->configdata[7] &= ~0x01; /* !Discard Short Rx Frames */ + ctlr->configdata[15] |= 0x01; /* Promiscuous mode */ + ctlr->configdata[18] &= ~0x01; /* (!Padding enable?), !stripping enable */ + ctlr->configdata[21] |= 0x08; /* Multi Cast ALL */ + } + else{ + ctlr->configdata[6] &= ~0x80; + //ctlr->configdata[6] |= 0x40; + ctlr->configdata[7] |= 0x01; + ctlr->configdata[15] &= ~0x01; + ctlr->configdata[18] |= 0x01; /* 0x03? */ + ctlr->configdata[21] &= ~0x08; + } + ctlr->action = CbConfigure; + txstart(ether); + iunlock(&ctlr->cblock); +} + +static void +promiscuous(void* arg, int on) +{ + configure(arg, on); +} + +static void +multicast(void* arg, uchar *addr, int on) +{ + USED(addr, on); + configure(arg, 1); +} + +static void +transmit(Ether* ether) +{ + Ctlr *ctlr; + + ctlr = ether->ctlr; + ilock(&ctlr->cblock); + txstart(ether); + iunlock(&ctlr->cblock); +} + +static void +receive(Ether* ether) +{ + Rfd *rfd; + Ctlr *ctlr; + int count; + Block *bp, *pbp, *xbp; + + ctlr = ether->ctlr; + bp = ctlr->rfdhead; + for(rfd = (Rfd*)bp->rp; rfd->field & RfdC; rfd = (Rfd*)bp->rp){ + /* + * If it's an OK receive frame + * 1) save the count + * 2) if it's small, try to allocate a block and copy + * the data, then adjust the necessary fields for reuse; + * 3) if it's big, try to allocate a new Rfd and if + * successful + * adjust the received buffer pointers for the + * actual data received; + * initialise the replacement buffer to point to + * the next in the ring; + * initialise bp to point to the replacement; + * 4) if there's a good packet, pass it on for disposal. + */ + if(rfd->field & RfdOK){ + pbp = nil; + count = rfd->count & 0x3FFF; + if((count < ETHERMAXTU/4) && (pbp = iallocb(count))){ + memmove(pbp->rp, bp->rp+sizeof(Rfd)-sizeof(rfd->data), count); + pbp->wp = pbp->rp + count; + + rfd->count = 0; + rfd->field = 0; + } + else if(xbp = rfdalloc(rfd->link)){ + bp->rp += sizeof(Rfd)-sizeof(rfd->data); + bp->wp = bp->rp + count; + + xbp->next = bp->next; + bp->next = 0; + + pbp = bp; + bp = xbp; + } + if(pbp != nil) + etheriq(ether, pbp, 1); + } + else{ + rfd->count = 0; + rfd->field = 0; + } + + /* + * The ring tail pointer follows the head with with one + * unused buffer in between to defeat hardware prefetch; + * once the tail pointer has been bumped on to the next + * and the new tail has the Suspend bit set, it can be + * removed from the old tail buffer. + * As a replacement for the current head buffer may have + * been allocated above, ensure that the new tail points + * to it (next and link). + */ + rfd = (Rfd*)ctlr->rfdtail->rp; + ctlr->rfdtail = ctlr->rfdtail->next; + ctlr->rfdtail->next = bp; + ((Rfd*)ctlr->rfdtail->rp)->link = PADDR(bp->rp); + ((Rfd*)ctlr->rfdtail->rp)->field |= RfdS; + coherence(); + rfd->field &= ~RfdS; + + /* + * Finally done with the current (possibly replaced) + * head, move on to the next and maintain the sentinel + * between tail and head. + */ + ctlr->rfdhead = bp->next; + bp = ctlr->rfdhead; + } +} + +static void +interrupt(Ureg*, void* arg) +{ + Cb* cb; + Ctlr *ctlr; + Ether *ether; + int status; + + ether = arg; + ctlr = ether->ctlr; + + for(;;){ + ilock(&ctlr->rlock); + status = csr16r(ctlr, Status); + csr8w(ctlr, Ack, (status>>8) & 0xFF); + iunlock(&ctlr->rlock); + + if(!(status & (StatCX|StatFR|StatCNA|StatRNR|StatMDI|StatSWI))) + break; + + /* + * If the watchdog timer for the receiver lockup errata is running, + * let it know the receiver is active. + */ + if(status & (StatFR|StatRNR)){ + ilock(&ctlr->cblock); + ctlr->tick = 0; + iunlock(&ctlr->cblock); + } + + if(status & StatFR){ + receive(ether); + status &= ~StatFR; + } + + if(status & StatRNR){ + command(ctlr, RUresume, 0); + status &= ~StatRNR; + } + + if(status & StatCNA){ + ilock(&ctlr->cblock); + + cb = ctlr->cbtail; + while(ctlr->cbq){ + if(!(cb->status & CbC)) + break; + if(cb->bp){ + freeb(cb->bp); + cb->bp = nil; + } + if((cb->status & CbU) && ctlr->threshold < 0xE0) + ctlr->threshold++; + + ctlr->cbq--; + cb = cb->next; + } + ctlr->cbtail = cb; + + txstart(ether); + iunlock(&ctlr->cblock); + + status &= ~StatCNA; + } + + if(status & (StatCX|StatFR|StatCNA|StatRNR|StatMDI|StatSWI)) + panic("#l%d: status %uX\n", ether->ctlrno, status); + } +} + +static void +ctlrinit(Ctlr* ctlr) +{ + int i; + Block *bp; + Rfd *rfd; + ulong link; + + /* + * Create the Receive Frame Area (RFA) as a ring of allocated + * buffers. + * A sentinel buffer is maintained between the last buffer in + * the ring (marked with RfdS) and the head buffer to defeat the + * hardware prefetch of the next RFD and allow dynamic buffer + * allocation. + */ + link = NullPointer; + for(i = 0; i < Nrfd; i++){ + bp = rfdalloc(link); + if(ctlr->rfdhead == nil) + ctlr->rfdtail = bp; + bp->next = ctlr->rfdhead; + ctlr->rfdhead = bp; + link = PADDR(bp->rp); + } + ctlr->rfdtail->next = ctlr->rfdhead; + rfd = (Rfd*)ctlr->rfdtail->rp; + rfd->link = PADDR(ctlr->rfdhead->rp); + rfd->field |= RfdS; + ctlr->rfdhead = ctlr->rfdhead->next; + + /* + * Create a ring of control blocks for the + * transmit side. + */ + ilock(&ctlr->cblock); + ctlr->cbr = malloc(ctlr->ncb*sizeof(Cb)); + for(i = 0; i < ctlr->ncb; i++){ + ctlr->cbr[i].status = CbC|CbOK; + ctlr->cbr[i].command = CbS|CbNOP; + ctlr->cbr[i].link = PADDR(&ctlr->cbr[NEXT(i, ctlr->ncb)].status); + ctlr->cbr[i].next = &ctlr->cbr[NEXT(i, ctlr->ncb)]; + } + ctlr->cbhead = ctlr->cbr; + ctlr->cbtail = ctlr->cbr; + ctlr->cbq = 0; + + memmove(ctlr->configdata, configdata, sizeof(configdata)); + ctlr->threshold = 80; + ctlr->tick = 0; + + iunlock(&ctlr->cblock); +} + +static int +miir(Ctlr* ctlr, int phyadd, int regadd) +{ + int mcr, timo; + + lock(&ctlr->miilock); + csr32w(ctlr, Mcr, MDIread|(phyadd<<21)|(regadd<<16)); + mcr = 0; + for(timo = 64; timo; timo--){ + mcr = csr32r(ctlr, Mcr); + if(mcr & MDIready) + break; + microdelay(1); + } + unlock(&ctlr->miilock); + + if(mcr & MDIready) + return mcr & 0xFFFF; + + return -1; +} + +static int +miiw(Ctlr* ctlr, int phyadd, int regadd, int data) +{ + int mcr, timo; + + lock(&ctlr->miilock); + csr32w(ctlr, Mcr, MDIwrite|(phyadd<<21)|(regadd<<16)|(data & 0xFFFF)); + mcr = 0; + for(timo = 64; timo; timo--){ + mcr = csr32r(ctlr, Mcr); + if(mcr & MDIready) + break; + microdelay(1); + } + unlock(&ctlr->miilock); + + if(mcr & MDIready) + return 0; + + return -1; +} + +static int +hy93c46r(Ctlr* ctlr, int r) +{ + int data, i, op, size; + + /* + * Hyundai HY93C46 or equivalent serial EEPROM. + * This sequence for reading a 16-bit register 'r' + * in the EEPROM is taken straight from Section + * 3.3.4.2 of the Intel 82557 User's Guide. + */ +reread: + csr16w(ctlr, Ecr, EEcs); + op = EEstart|EEread; + for(i = 2; i >= 0; i--){ + data = (((op>>i) & 0x01)<<2)|EEcs; + csr16w(ctlr, Ecr, data); + csr16w(ctlr, Ecr, data|EEsk); + microdelay(1); + csr16w(ctlr, Ecr, data); + microdelay(1); + } + + /* + * First time through must work out the EEPROM size. + */ + if((size = ctlr->eepromsz) == 0) + size = 8; + + for(size = size-1; size >= 0; size--){ + data = (((r>>size) & 0x01)<<2)|EEcs; + csr16w(ctlr, Ecr, data); + csr16w(ctlr, Ecr, data|EEsk); + delay(1); + csr16w(ctlr, Ecr, data); + microdelay(1); + if(!(csr16r(ctlr, Ecr) & EEdo)) + break; + } + + data = 0; + for(i = 15; i >= 0; i--){ + csr16w(ctlr, Ecr, EEcs|EEsk); + microdelay(1); + if(csr16r(ctlr, Ecr) & EEdo) + data |= (1<<i); + csr16w(ctlr, Ecr, EEcs); + microdelay(1); + } + + csr16w(ctlr, Ecr, 0); + + if(ctlr->eepromsz == 0){ + ctlr->eepromsz = 8-size; + ctlr->eeprom = malloc((1<<ctlr->eepromsz)*sizeof(ushort)); + goto reread; + } + + return data; +} + +static void +i82557pci(void) +{ + Pcidev *p; + Ctlr *ctlr; + int i, nop, port; + + p = nil; + nop = 0; + while(p = pcimatch(p, 0x8086, 0)){ + switch(p->did){ + default: + continue; + case 0x1031: /* Intel 82562EM */ + case 0x1050: /* Intel 82562EZ */ + case 0x2449: /* Intel 82562ET */ + nop = 1; + /*FALLTHROUGH*/ + case 0x1209: /* Intel 82559ER */ + case 0x1229: /* Intel 8255[789] */ + case 0x1030: /* Intel 82559 InBusiness 10/100 */ + case 0x1039: /* Intel 82801BD PRO/100 VE */ + case 0x103A: /* Intel 82562 PRO/100 VE */ + break; + } + + if(pcigetpms(p) > 0){ + pcisetpms(p, 0); + + for(i = 0; i < 6; i++) + pcicfgw32(p, PciBAR0+i*4, p->mem[i].bar); + pcicfgw8(p, PciINTL, p->intl); + pcicfgw8(p, PciLTR, p->ltr); + pcicfgw8(p, PciCLS, p->cls); + pcicfgw16(p, PciPCR, p->pcr); + } + + /* + * bar[0] is the memory-mapped register address (4KB), + * bar[1] is the I/O port register address (32 bytes) and + * bar[2] is for the flash ROM (1MB). + */ + port = p->mem[1].bar & ~0x01; + if(ioalloc(port, p->mem[1].size, 0, "i82557") < 0){ + print("i82557: port 0x%uX in use\n", port); + continue; + } + + ctlr = malloc(sizeof(Ctlr)); + ctlr->port = port; + ctlr->pcidev = p; + ctlr->nop = nop; + + if(ctlrhead != nil) + ctlrtail->next = ctlr; + else + ctlrhead = ctlr; + ctlrtail = ctlr; + + pcisetbme(p); + } +} + +static char* mediatable[9] = { + "10BASE-T", /* TP */ + "10BASE-2", /* BNC */ + "10BASE-5", /* AUI */ + "100BASE-TX", + "10BASE-TFD", + "100BASE-TXFD", + "100BASE-T4", + "100BASE-FX", + "100BASE-FXFD", +}; + +static int +scanphy(Ctlr* ctlr) +{ + int i, oui, x; + + for(i = 0; i < 32; i++){ + if((oui = miir(ctlr, i, 2)) == -1 || oui == 0 || oui == 0xFFFF) + continue; + oui <<= 6; + x = miir(ctlr, i, 3); + oui |= x>>10; + //print("phy%d: oui %uX reg1 %uX\n", i, oui, miir(ctlr, i, 1)); + + ctlr->eeprom[6] = i; + if(oui == 0xAA00) + ctlr->eeprom[6] |= 0x07<<8; + else if(oui == 0x80017){ + if(x & 0x01) + ctlr->eeprom[6] |= 0x0A<<8; + else + ctlr->eeprom[6] |= 0x04<<8; + } + return i; + } + return -1; +} + +static void +shutdown(Ether* ether) +{ + Ctlr *ctlr = ether->ctlr; + +print("ether82557 shutting down\n"); + csr32w(ctlr, Port, 0); + delay(1); + csr8w(ctlr, Interrupt, InterruptM); +} + + +static int +reset(Ether* ether) +{ + int anar, anlpar, bmcr, bmsr, i, k, medium, phyaddr, x; + unsigned short sum; + uchar ea[Eaddrlen]; + Ctlr *ctlr; + + if(ctlrhead == nil) + i82557pci(); + + /* + * Any adapter matches if no ether->port is supplied, + * otherwise the ports must match. + */ + for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){ + if(ctlr->active) + continue; + if(ether->port == 0 || ether->port == ctlr->port){ + ctlr->active = 1; + break; + } + } + if(ctlr == nil) + return -1; + + /* + * Initialise the Ctlr structure. + * Perform a software reset after which should ensure busmastering + * is still enabled. The EtherExpress PRO/100B appears to leave + * the PCI configuration alone (see the 'To do' list above) so punt + * for now. + * Load the RUB and CUB registers for linear addressing (0). + */ + ether->ctlr = ctlr; + ether->port = ctlr->port; + ether->irq = ctlr->pcidev->intl; + ether->tbdf = ctlr->pcidev->tbdf; + + ilock(&ctlr->rlock); + csr32w(ctlr, Port, 0); + delay(1); + csr8w(ctlr, Interrupt, InterruptM); + iunlock(&ctlr->rlock); + + command(ctlr, LoadRUB, 0); + command(ctlr, LoadCUB, 0); + command(ctlr, LoadDCA, PADDR(ctlr->dump)); + + /* + * Initialise the receive frame, transmit ring and configuration areas. + */ + ctlr->ncb = Ncb; + ctlrinit(ctlr); + + /* + * Read the EEPROM. + * Do a dummy read first to get the size + * and allocate ctlr->eeprom. + */ + hy93c46r(ctlr, 0); + sum = 0; + for(i = 0; i < (1<<ctlr->eepromsz); i++){ + x = hy93c46r(ctlr, i); + ctlr->eeprom[i] = x; + sum += x; + } + if(sum != 0xBABA) + print("#l%d: EEPROM checksum - 0x%4.4uX\n", ether->ctlrno, sum); + + /* + * Eeprom[6] indicates whether there is a PHY and whether + * it's not 10Mb-only, in which case use the given PHY address + * to set any PHY specific options and determine the speed. + * Unfortunately, sometimes the EEPROM is blank except for + * the ether address and checksum; in this case look at the + * controller type and if it's am 82558 or 82559 it has an + * embedded PHY so scan for that. + * If no PHY, assume 82503 (serial) operation. + */ + if((ctlr->eeprom[6] & 0x1F00) && !(ctlr->eeprom[6] & 0x8000)) + phyaddr = ctlr->eeprom[6] & 0x00FF; + else + switch(ctlr->pcidev->rid){ + case 0x01: /* 82557 A-step */ + case 0x02: /* 82557 B-step */ + case 0x03: /* 82557 C-step */ + default: + phyaddr = -1; + break; + case 0x04: /* 82558 A-step */ + case 0x05: /* 82558 B-step */ + case 0x06: /* 82559 A-step */ + case 0x07: /* 82559 B-step */ + case 0x08: /* 82559 C-step */ + case 0x09: /* 82559ER A-step */ + phyaddr = scanphy(ctlr); + break; + } + if(phyaddr >= 0){ + /* + * Resolve the highest common ability of the two + * link partners. In descending order: + * 0x0100 100BASE-TX Full Duplex + * 0x0200 100BASE-T4 + * 0x0080 100BASE-TX + * 0x0040 10BASE-T Full Duplex + * 0x0020 10BASE-T + */ + anar = miir(ctlr, phyaddr, 0x04); + anlpar = miir(ctlr, phyaddr, 0x05) & 0x03E0; + anar &= anlpar; + bmcr = 0; + if(anar & 0x380) + bmcr = 0x2000; + if(anar & 0x0140) + bmcr |= 0x0100; + + switch((ctlr->eeprom[6]>>8) & 0x001F){ + + case 0x04: /* DP83840 */ + case 0x0A: /* DP83840A */ + /* + * The DP83840[A] requires some tweaking for + * reliable operation. + * The manual says bit 10 should be unconditionally + * set although it supposedly only affects full-duplex + * operation (an & 0x0140). + */ + x = miir(ctlr, phyaddr, 0x17) & ~0x0520; + x |= 0x0420; + for(i = 0; i < ether->nopt; i++){ + if(cistrcmp(ether->opt[i], "congestioncontrol")) + continue; + x |= 0x0100; + break; + } + miiw(ctlr, phyaddr, 0x17, x); + + /* + * If the link partner can't autonegotiate, determine + * the speed from elsewhere. + */ + if(anlpar == 0){ + miir(ctlr, phyaddr, 0x01); + bmsr = miir(ctlr, phyaddr, 0x01); + x = miir(ctlr, phyaddr, 0x19); + if((bmsr & 0x0004) && !(x & 0x0040)) + bmcr = 0x2000; + } + break; + + case 0x07: /* Intel 82555 */ + /* + * Auto-negotiation may fail if the other end is + * a DP83840A and the cable is short. + */ + miir(ctlr, phyaddr, 0x01); + bmsr = miir(ctlr, phyaddr, 0x01); + if((miir(ctlr, phyaddr, 0) & 0x1000) && !(bmsr & 0x0020)){ + miiw(ctlr, phyaddr, 0x1A, 0x2010); + x = miir(ctlr, phyaddr, 0); + miiw(ctlr, phyaddr, 0, 0x0200|x); + for(i = 0; i < 3000; i++){ + delay(1); + if(miir(ctlr, phyaddr, 0x01) & 0x0020) + break; + } + miiw(ctlr, phyaddr, 0x1A, 0x2000); + + anar = miir(ctlr, phyaddr, 0x04); + anlpar = miir(ctlr, phyaddr, 0x05) & 0x03E0; + anar &= anlpar; + bmcr = 0; + if(anar & 0x380) + bmcr = 0x2000; + if(anar & 0x0140) + bmcr |= 0x0100; + } + break; + } + + /* + * Force speed and duplex if no auto-negotiation. + */ + if(anlpar == 0){ + medium = -1; + for(i = 0; i < ether->nopt; i++){ + for(k = 0; k < nelem(mediatable); k++){ + if(cistrcmp(mediatable[k], ether->opt[i])) + continue; + medium = k; + break; + } + + switch(medium){ + default: + break; + + case 0x00: /* 10BASE-T */ + case 0x01: /* 10BASE-2 */ + case 0x02: /* 10BASE-5 */ + bmcr &= ~(0x2000|0x0100); + ctlr->configdata[19] &= ~0x40; + break; + + case 0x03: /* 100BASE-TX */ + case 0x06: /* 100BASE-T4 */ + case 0x07: /* 100BASE-FX */ + ctlr->configdata[19] &= ~0x40; + bmcr |= 0x2000; + break; + + case 0x04: /* 10BASE-TFD */ + bmcr = (bmcr & ~0x2000)|0x0100; + ctlr->configdata[19] |= 0x40; + break; + + case 0x05: /* 100BASE-TXFD */ + case 0x08: /* 100BASE-FXFD */ + bmcr |= 0x2000|0x0100; + ctlr->configdata[19] |= 0x40; + break; + } + } + if(medium != -1) + miiw(ctlr, phyaddr, 0x00, bmcr); + } + + if(bmcr & 0x2000) + ether->mbps = 100; + + ctlr->configdata[8] = 1; + ctlr->configdata[15] &= ~0x80; + } + else{ + ctlr->configdata[8] = 0; + ctlr->configdata[15] |= 0x80; + } + + /* + * Workaround for some broken HUB chips when connected at 10Mb/s + * half-duplex. + * This is a band-aid, but as there's no dynamic auto-negotiation + * code at the moment, only deactivate the workaround code in txstart + * if the link is 100Mb/s. + */ + if(ether->mbps != 10) + ctlr->nop = 0; + + /* + * Load the chip configuration and start it off. + */ + if(ether->oq == 0) + ether->oq = qopen(256*1024, Qmsg, 0, 0); + configure(ether, 0); + command(ctlr, CUstart, PADDR(&ctlr->cbr->status)); + + /* + * Check if the adapter's station address is to be overridden. + * If not, read it from the EEPROM and set in ether->ea prior to loading + * the station address with the Individual Address Setup command. + */ + memset(ea, 0, Eaddrlen); + if(memcmp(ea, ether->ea, Eaddrlen) == 0){ + for(i = 0; i < Eaddrlen/2; i++){ + x = ctlr->eeprom[i]; + ether->ea[2*i] = x; + ether->ea[2*i+1] = x>>8; + } + } + + ilock(&ctlr->cblock); + ctlr->action = CbIAS; + txstart(ether); + iunlock(&ctlr->cblock); + + /* + * Linkage to the generic ethernet driver. + */ + ether->attach = attach; + ether->transmit = transmit; + ether->interrupt = interrupt; + ether->ifstat = ifstat; + ether->shutdown = shutdown; + + ether->promiscuous = promiscuous; + ether->multicast = multicast; + ether->arg = ether; + + return 0; +} + +void +ether82557link(void) +{ + addethercard("i82557", reset); +} diff --git a/os/pc/ether83815.c b/os/pc/ether83815.c new file mode 100644 index 00000000..2059bad4 --- /dev/null +++ b/os/pc/ether83815.c @@ -0,0 +1,994 @@ +/* + * National Semiconductor DP83815 + * + * Supports only internal PHY and has been tested on: + * Netgear FA311TX (using Netgear DS108 10/100 hub) + * To do: + * check Ethernet address; + * test autonegotiation on 10 Mbit, and 100 Mbit full duplex; + * external PHY via MII (should be common code for MII); + * thresholds; + * ring sizing; + * physical link changes/disconnect; + * push initialisation back to attach. + * + * C H Forsyth, forsyth@vitanuova.com, 18th June 2001. + */ + +#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/netif.h" + +#include "etherif.h" + +#define DEBUG (0) +#define debug if(DEBUG)print + +enum { + Nrde = 64, + Ntde = 64, +}; + +#define Rbsz ROUNDUP(sizeof(Etherpkt)+4, 4) + +typedef struct Des { + ulong next; + int cmdsts; + ulong addr; + Block* bp; +} Des; + +enum { /* cmdsts */ + Own = 1<<31, /* set by data producer to hand to consumer */ + More = 1<<30, /* more of packet in next descriptor */ + Intr = 1<<29, /* interrupt when device is done with it */ + Supcrc = 1<<28, /* suppress crc on transmit */ + Inccrc = 1<<28, /* crc included on receive (always) */ + Ok = 1<<27, /* packet ok */ + Size = 0xFFF, /* packet size in bytes */ + + /* transmit */ + Txa = 1<<26, /* transmission aborted */ + Tfu = 1<<25, /* transmit fifo underrun */ + Crs = 1<<24, /* carrier sense lost */ + Td = 1<<23, /* transmission deferred */ + Ed = 1<<22, /* excessive deferral */ + Owc = 1<<21, /* out of window collision */ + Ec = 1<<20, /* excessive collisions */ + /* 19-16 collision count */ + + /* receive */ + Rxa = 1<<26, /* receive aborted (same as Rxo) */ + Rxo = 1<<25, /* receive overrun */ + Dest = 3<<23, /* destination class */ + Drej= 0<<23, /* packet was rejected */ + Duni= 1<<23, /* unicast */ + Dmulti= 2<<23, /* multicast */ + Dbroad= 3<<23, /* broadcast */ + Long = 1<<22, /* too long packet received */ + Runt = 1<<21, /* packet less than 64 bytes */ + Ise = 1<<20, /* invalid symbol */ + Crce = 1<<19, /* invalid crc */ + Fae = 1<<18, /* frame alignment error */ + Lbp = 1<<17, /* loopback packet */ + Col = 1<<16, /* collision during receive */ +}; + +enum { /* Variants */ + Nat83815 = (0x0020<<16)|0x100B, + Sis900 = (0x0630<<16)|0x1039, /* untested */ +}; + +typedef struct Ctlr Ctlr; +typedef struct Ctlr { + int port; + Pcidev* pcidev; + Ctlr* next; + int active; + int id; /* (pcidev->did<<16)|pcidev->vid */ + + ushort srom[0xB+1]; + uchar sromea[Eaddrlen]; /* MAC address */ + + uchar fd; /* option or auto negotiation */ + + int mbps; + + Lock lock; + + Des* rdr; /* receive descriptor ring */ + int nrdr; /* size of rdr */ + int rdrx; /* index into rdr */ + + Lock tlock; + Des* tdr; /* transmit descriptor ring */ + int ntdr; /* size of tdr */ + int tdrh; /* host index into tdr */ + int tdri; /* interface index into tdr */ + int ntq; /* descriptors active */ + int ntqmax; + + ulong rxa; /* receive statistics */ + ulong rxo; + ulong rlong; + ulong runt; + ulong ise; + ulong crce; + ulong fae; + ulong lbp; + ulong col; + ulong rxsovr; + ulong rxorn; + + ulong txa; /* transmit statistics */ + ulong tfu; + ulong crs; + ulong td; + ulong ed; + ulong owc; + ulong ec; + ulong txurn; + + ulong dperr; /* system errors */ + ulong rmabt; + ulong rtabt; + ulong sserr; + ulong rxsover; +} Ctlr; + +static Ctlr* ctlrhead; +static Ctlr* ctlrtail; + +enum { + /* registers (could memory map) */ + Rcr= 0x00, /* command register */ + Rst= 1<<8, + Rxr= 1<<5, /* receiver reset */ + Txr= 1<<4, /* transmitter reset */ + Rxd= 1<<3, /* receiver disable */ + Rxe= 1<<2, /* receiver enable */ + Txd= 1<<1, /* transmitter disable */ + Txe= 1<<0, /* transmitter enable */ + Rcfg= 0x04, /* configuration */ + Lnksts= 1<<31, /* link good */ + Speed100= 1<<30, /* 100 Mb/s link */ + Fdup= 1<<29, /* full duplex */ + Pol= 1<<28, /* polarity reversal (10baseT) */ + Aneg_dn= 1<<27, /* autonegotiation done */ + Pint_acen= 1<<17, /* PHY interrupt auto clear enable */ + Pause_adv= 1<<16, /* advertise pause during auto neg */ + Paneg_ena= 1<<13, /* auto negotiation enable */ + Paneg_all= 7<<13, /* auto negotiation enable 10/100 half & full */ + Ext_phy= 1<<12, /* enable MII for external PHY */ + Phy_rst= 1<<10, /* reset internal PHY */ + Phy_dis= 1<<9, /* disable internal PHY (eg, low power) */ + Req_alg= 1<<7, /* PCI bus request: set means less aggressive */ + Sb= 1<<6, /* single slot back-off not random */ + Pow= 1<<5, /* out of window timer selection */ + Exd= 1<<4, /* disable excessive deferral timer */ + Pesel= 1<<3, /* parity error algorithm selection */ + Brom_dis= 1<<2, /* disable boot rom interface */ + Bem= 1<<0, /* big-endian mode */ + Rmear= 0x08, /* eeprom access */ + Mdc= 1<<6, /* MII mangement check */ + Mddir= 1<<5, /* MII management direction */ + Mdio= 1<<4, /* MII mangement data */ + Eesel= 1<<3, /* EEPROM chip select */ + Eeclk= 1<<2, /* EEPROM clock */ + Eedo= 1<<1, /* EEPROM data out (from chip) */ + Eedi= 1<<0, /* EEPROM data in (to chip) */ + Rptscr= 0x0C, /* pci test control */ + Risr= 0x10, /* interrupt status */ + Txrcmp= 1<<25, /* transmit reset complete */ + Rxrcmp= 1<<24, /* receiver reset complete */ + Dperr= 1<<23, /* detected parity error */ + Sserr= 1<<22, /* signalled system error */ + Rmabt= 1<<21, /* received master abort */ + Rtabt= 1<<20, /* received target abort */ + Rxsovr= 1<<16, /* RX status FIFO overrun */ + Hiberr= 1<<15, /* high bits error set (OR of 25-16) */ + Phy= 1<<14, /* PHY interrupt */ + Pme= 1<<13, /* power management event (wake online) */ + Swi= 1<<12, /* software interrupt */ + Mib= 1<<11, /* MIB service */ + Txurn= 1<<10, /* TX underrun */ + Txidle= 1<<9, /* TX idle */ + Txerr= 1<<8, /* TX packet error */ + Txdesc= 1<<7, /* TX descriptor (with Intr bit done) */ + Txok= 1<<6, /* TX ok */ + Rxorn= 1<<5, /* RX overrun */ + Rxidle= 1<<4, /* RX idle */ + Rxearly= 1<<3, /* RX early threshold */ + Rxerr= 1<<2, /* RX packet error */ + Rxdesc= 1<<1, /* RX descriptor (with Intr bit done) */ + Rxok= 1<<0, /* RX ok */ + Rimr= 0x14, /* interrupt mask */ + Rier= 0x18, /* interrupt enable */ + Ie= 1<<0, /* interrupt enable */ + Rtxdp= 0x20, /* transmit descriptor pointer */ + Rtxcfg= 0x24, /* transmit configuration */ + Csi= 1<<31, /* carrier sense ignore (needed for full duplex) */ + Hbi= 1<<30, /* heartbeat ignore (needed for full duplex) */ + Atp= 1<<28, /* automatic padding of runt packets */ + Mxdma= 7<<20, /* maximum dma transfer field */ + Mxdma32= 4<<20, /* 4x32-bit words (32 bytes) */ + Mxdma64= 5<<20, /* 8x32-bit words (64 bytes) */ + Flth= 0x3F<<8,/* Tx fill threshold, units of 32 bytes (must be > Mxdma) */ + Drth= 0x3F<<0,/* Tx drain threshold (units of 32 bytes) */ + Flth128= 4<<8, /* fill at 128 bytes */ + Drth512= 16<<0, /* drain at 512 bytes */ + Rrxdp= 0x30, /* receive descriptor pointer */ + Rrxcfg= 0x34, /* receive configuration */ + Atx= 1<<28, /* accept transmit packets (needed for full duplex) */ + Rdrth= 0x1F<<1,/* Rx drain threshold (units of 32 bytes) */ + Rdrth64= 2<<1, /* drain at 64 bytes */ + Rccsr= 0x3C, /* CLKRUN control/status */ + Pmests= 1<<15, /* PME status */ + Rwcsr= 0x40, /* wake on lan control/status */ + Rpcr= 0x44, /* pause control/status */ + Rrfcr= 0x48, /* receive filter/match control */ + Rfen= 1<<31, /* receive filter enable */ + Aab= 1<<30, /* accept all broadcast */ + Aam= 1<<29, /* accept all multicast */ + Aau= 1<<28, /* accept all unicast */ + Apm= 1<<27, /* accept on perfect match */ + Apat= 0xF<<23,/* accept on pattern match */ + Aarp= 1<<22, /* accept ARP */ + Mhen= 1<<21, /* multicast hash enable */ + Uhen= 1<<20, /* unicast hash enable */ + Ulm= 1<<19, /* U/L bit mask */ + /* bits 0-9 are rfaddr */ + Rrfdr= 0x4C, /* receive filter/match data */ + Rbrar= 0x50, /* boot rom address */ + Rbrdr= 0x54, /* boot rom data */ + Rsrr= 0x58, /* silicon revision */ + Rmibc= 0x5C, /* MIB control */ + /* 60-78 MIB data */ + + /* PHY registers */ + Rbmcr= 0x80, /* basic mode configuration */ + Reset= 1<<15, + Sel100= 1<<13, /* select 100Mb/sec if no auto neg */ + Anena= 1<<12, /* auto negotiation enable */ + Anrestart= 1<<9, /* restart auto negotiation */ + Selfdx= 1<<8, /* select full duplex if no auto neg */ + Rbmsr= 0x84, /* basic mode status */ + Ancomp= 1<<5, /* autonegotiation complete */ + Rphyidr1= 0x88, + Rphyidr2= 0x8C, + Ranar= 0x90, /* autonegotiation advertisement */ + Ranlpar= 0x94, /* autonegotiation link partner ability */ + Raner= 0x98, /* autonegotiation expansion */ + Rannptr= 0x9C, /* autonegotiation next page TX */ + Rphysts= 0xC0, /* PHY status */ + Rmicr= 0xC4, /* MII control */ + Inten= 1<<1, /* PHY interrupt enable */ + Rmisr= 0xC8, /* MII status */ + Rfcscr= 0xD0, /* false carrier sense counter */ + Rrecr= 0xD4, /* receive error counter */ + Rpcsr= 0xD8, /* 100Mb config/status */ + Rphycr= 0xE4, /* PHY control */ + Rtbscr= 0xE8, /* 10BaseT status/control */ +}; + +/* + * eeprom addresses + * 7 to 9 (16 bit words): mac address, shifted and reversed + */ + +#define csr32r(c, r) (inl((c)->port+(r))) +#define csr32w(c, r, l) (outl((c)->port+(r), (ulong)(l))) +#define csr16r(c, r) (ins((c)->port+(r))) +#define csr16w(c, r, l) (outs((c)->port+(r), (ulong)(l))) + +static void +dumpcregs(Ctlr *ctlr) +{ + int i; + + for(i=0; i<=0x5C; i+=4) + print("%2.2ux %8.8lux\n", i, csr32r(ctlr, i)); +} + +static void +promiscuous(void* arg, int on) +{ + Ctlr *ctlr; + ulong w; + + ctlr = ((Ether*)arg)->ctlr; + ilock(&ctlr->lock); + w = csr32r(ctlr, Rrfcr); + if(on != ((w&Aau)!=0)){ + csr32w(ctlr, Rrfcr, w & ~Rfen); + csr32w(ctlr, Rrfcr, Rfen | (w ^ Aau)); + } + iunlock(&ctlr->lock); +} + +static void +attach(Ether* ether) +{ + Ctlr *ctlr; + + ctlr = ether->ctlr; + ilock(&ctlr->lock); + if(0) + dumpcregs(ctlr); + csr32w(ctlr, Rcr, Rxe); + iunlock(&ctlr->lock); +} + +static long +ifstat(Ether* ether, void* a, long n, ulong offset) +{ + Ctlr *ctlr; + char *buf, *p; + int i, l, len; + + ctlr = ether->ctlr; + + ether->crcs = ctlr->crce; + ether->frames = ctlr->runt+ctlr->ise+ctlr->rlong+ctlr->fae; + ether->buffs = ctlr->rxorn+ctlr->tfu; + ether->overflows = ctlr->rxsovr; + + if(n == 0) + return 0; + + p = malloc(READSTR); + l = snprint(p, READSTR, "Rxa: %lud\n", ctlr->rxa); + l += snprint(p+l, READSTR-l, "Rxo: %lud\n", ctlr->rxo); + l += snprint(p+l, READSTR-l, "Rlong: %lud\n", ctlr->rlong); + l += snprint(p+l, READSTR-l, "Runt: %lud\n", ctlr->runt); + l += snprint(p+l, READSTR-l, "Ise: %lud\n", ctlr->ise); + l += snprint(p+l, READSTR-l, "Fae: %lud\n", ctlr->fae); + l += snprint(p+l, READSTR-l, "Lbp: %lud\n", ctlr->lbp); + l += snprint(p+l, READSTR-l, "Tfu: %lud\n", ctlr->tfu); + l += snprint(p+l, READSTR-l, "Txa: %lud\n", ctlr->txa); + l += snprint(p+l, READSTR-l, "CRC Error: %lud\n", ctlr->crce); + l += snprint(p+l, READSTR-l, "Collision Seen: %lud\n", ctlr->col); + l += snprint(p+l, READSTR-l, "Frame Too Long: %lud\n", ctlr->rlong); + l += snprint(p+l, READSTR-l, "Runt Frame: %lud\n", ctlr->runt); + l += snprint(p+l, READSTR-l, "Rx Underflow Error: %lud\n", ctlr->rxorn); + l += snprint(p+l, READSTR-l, "Tx Underrun: %lud\n", ctlr->txurn); + l += snprint(p+l, READSTR-l, "Excessive Collisions: %lud\n", ctlr->ec); + l += snprint(p+l, READSTR-l, "Late Collision: %lud\n", ctlr->owc); + l += snprint(p+l, READSTR-l, "Loss of Carrier: %lud\n", ctlr->crs); + l += snprint(p+l, READSTR-l, "Parity: %lud\n", ctlr->dperr); + l += snprint(p+l, READSTR-l, "Aborts: %lud\n", ctlr->rmabt+ctlr->rtabt); + l += snprint(p+l, READSTR-l, "RX Status overrun: %lud\n", ctlr->rxsover); + snprint(p+l, READSTR-l, "ntqmax: %d\n", ctlr->ntqmax); + ctlr->ntqmax = 0; + buf = a; + len = readstr(offset, buf, n, p); + if(offset > l) + offset -= l; + else + offset = 0; + buf += len; + n -= len; + + l = snprint(p, READSTR, "srom:"); + for(i = 0; i < nelem(ctlr->srom); i++){ + if(i && ((i & 0x0F) == 0)) + l += snprint(p+l, READSTR-l, "\n "); + l += snprint(p+l, READSTR-l, " %4.4uX", ctlr->srom[i]); + } + + snprint(p+l, READSTR-l, "\n"); + len += readstr(offset, buf, n, p); + free(p); + + return len; +} + +static void +txstart(Ether* ether) +{ + Ctlr *ctlr; + Block *bp; + Des *des; + int started; + + ctlr = ether->ctlr; + started = 0; + while(ctlr->ntq < ctlr->ntdr-1){ + bp = qget(ether->oq); + if(bp == nil) + break; + des = &ctlr->tdr[ctlr->tdrh]; + des->bp = bp; + des->addr = PADDR(bp->rp); + ctlr->ntq++; + coherence(); + des->cmdsts = Own | BLEN(bp); + ctlr->tdrh = NEXT(ctlr->tdrh, ctlr->ntdr); + started = 1; + } + if(started){ + coherence(); + csr32w(ctlr, Rcr, Txe); /* prompt */ + } + + if(ctlr->ntq > ctlr->ntqmax) + ctlr->ntqmax = ctlr->ntq; +} + +static void +transmit(Ether* ether) +{ + Ctlr *ctlr; + + ctlr = ether->ctlr; + ilock(&ctlr->tlock); + txstart(ether); + iunlock(&ctlr->tlock); +} + +static void +txrxcfg(Ctlr *ctlr, int txdrth) +{ + ulong rx, tx; + + rx = csr32r(ctlr, Rrxcfg); + tx = csr32r(ctlr, Rtxcfg); + if(ctlr->fd){ + rx |= Atx; + tx |= Csi | Hbi; + }else{ + rx &= ~Atx; + tx &= ~(Csi | Hbi); + } + tx &= ~(Mxdma|Drth|Flth); + tx |= Mxdma64 | Flth128 | txdrth; + csr32w(ctlr, Rtxcfg, tx); + rx &= ~(Mxdma|Rdrth); + rx |= Mxdma64 | Rdrth64; + csr32w(ctlr, Rrxcfg, rx); +} + +static void +interrupt(Ureg*, void* arg) +{ + Ctlr *ctlr; + Ether *ether; + int len, status, cmdsts; + Des *des; + Block *bp; + + ether = arg; + ctlr = ether->ctlr; + + while((status = csr32r(ctlr, Risr)) != 0){ + + status &= ~(Pme|Mib); + + if(status & Hiberr){ + if(status & Rxsovr) + ctlr->rxsover++; + if(status & Sserr) + ctlr->sserr++; + if(status & Dperr) + ctlr->dperr++; + if(status & Rmabt) + ctlr->rmabt++; + if(status & Rtabt) + ctlr->rtabt++; + status &= ~(Hiberr|Txrcmp|Rxrcmp|Rxsovr|Dperr|Sserr|Rmabt|Rtabt); + } + + /* + * Received packets. + */ + if(status & (Rxdesc|Rxok|Rxerr|Rxearly|Rxorn)){ + des = &ctlr->rdr[ctlr->rdrx]; + while((cmdsts = des->cmdsts) & Own){ + if((cmdsts&Ok) == 0){ + if(cmdsts & Rxa) + ctlr->rxa++; + if(cmdsts & Rxo) + ctlr->rxo++; + if(cmdsts & Long) + ctlr->rlong++; + if(cmdsts & Runt) + ctlr->runt++; + if(cmdsts & Ise) + ctlr->ise++; + if(cmdsts & Crce) + ctlr->crce++; + if(cmdsts & Fae) + ctlr->fae++; + if(cmdsts & Lbp) + ctlr->lbp++; + if(cmdsts & Col) + ctlr->col++; + } + else if(bp = iallocb(Rbsz)){ + len = (cmdsts&Size)-4; + des->bp->wp = des->bp->rp+len; + etheriq(ether, des->bp, 1); + des->bp = bp; + des->addr = PADDR(bp->rp); + coherence(); + } + + des->cmdsts = Rbsz; + coherence(); + + ctlr->rdrx = NEXT(ctlr->rdrx, ctlr->nrdr); + des = &ctlr->rdr[ctlr->rdrx]; + } + status &= ~(Rxdesc|Rxok|Rxerr|Rxearly|Rxorn); + } + + /* + * Check the transmit side: + * check for Transmit Underflow and Adjust + * the threshold upwards; + * free any transmitted buffers and try to + * top-up the ring. + */ + if(status & Txurn){ + ctlr->txurn++; + ilock(&ctlr->lock); + /* change threshold */ + iunlock(&ctlr->lock); + status &= ~(Txurn); + } + + ilock(&ctlr->tlock); + while(ctlr->ntq){ + des = &ctlr->tdr[ctlr->tdri]; + cmdsts = des->cmdsts; + if(cmdsts & Own) + break; + + if((cmdsts & Ok) == 0){ + if(cmdsts & Txa) + ctlr->txa++; + if(cmdsts & Tfu) + ctlr->tfu++; + if(cmdsts & Td) + ctlr->td++; + if(cmdsts & Ed) + ctlr->ed++; + if(cmdsts & Owc) + ctlr->owc++; + if(cmdsts & Ec) + ctlr->ec++; + ether->oerrs++; + } + + freeb(des->bp); + des->bp = nil; + des->cmdsts = 0; + + ctlr->ntq--; + ctlr->tdri = NEXT(ctlr->tdri, ctlr->ntdr); + } + txstart(ether); + iunlock(&ctlr->tlock); + + status &= ~(Txurn|Txidle|Txerr|Txdesc|Txok); + + /* + * Anything left not catered for? + */ + if(status) + print("#l%d: status %8.8uX\n", ether->ctlrno, status); + } +} + +static void +ctlrinit(Ether* ether) +{ + Ctlr *ctlr; + Des *des, *last; + + ctlr = ether->ctlr; + + /* + * Allocate suitable aligned descriptors + * for the transmit and receive rings; + * initialise the receive ring; + * initialise the transmit ring; + * unmask interrupts and start the transmit side. + */ + des = xspanalloc((ctlr->nrdr+ctlr->ntdr)*sizeof(Des), 32, 0); + ctlr->tdr = des; + ctlr->rdr = des+ctlr->ntdr; + + last = nil; + for(des = ctlr->rdr; des < &ctlr->rdr[ctlr->nrdr]; des++){ + des->bp = iallocb(Rbsz); + if (des->bp == nil) + error(Enomem); + des->cmdsts = Rbsz; + des->addr = PADDR(des->bp->rp); + if(last != nil) + last->next = PADDR(des); + last = des; + } + ctlr->rdr[ctlr->nrdr-1].next = PADDR(ctlr->rdr); + ctlr->rdrx = 0; + csr32w(ctlr, Rrxdp, PADDR(ctlr->rdr)); + + last = nil; + for(des = ctlr->tdr; des < &ctlr->tdr[ctlr->ntdr]; des++){ + des->cmdsts = 0; + des->bp = nil; + des->addr = ~0; + if(last != nil) + last->next = PADDR(des); + last = des; + } + ctlr->tdr[ctlr->ntdr-1].next = PADDR(ctlr->tdr); + ctlr->tdrh = 0; + ctlr->tdri = 0; + csr32w(ctlr, Rtxdp, PADDR(ctlr->tdr)); + + txrxcfg(ctlr, Drth512); + + csr32w(ctlr, Rimr, Dperr|Sserr|Rmabt|Rtabt|Rxsovr|Hiberr|Txurn|Txerr|Txdesc|Txok|Rxorn|Rxerr|Rxdesc|Rxok); /* Phy|Pme|Mib */ + csr32r(ctlr, Risr); /* clear status */ + csr32w(ctlr, Rier, Ie); +} + +static void +eeclk(Ctlr *ctlr, int clk) +{ + csr32w(ctlr, Rmear, Eesel | clk); + microdelay(2); +} + +static void +eeidle(Ctlr *ctlr) +{ + int i; + + eeclk(ctlr, 0); + eeclk(ctlr, Eeclk); + for(i=0; i<25; i++){ + eeclk(ctlr, 0); + eeclk(ctlr, Eeclk); + } + eeclk(ctlr, 0); + csr32w(ctlr, Rmear, 0); + microdelay(2); +} + +static int +eegetw(Ctlr *ctlr, int a) +{ + int d, i, w, v; + + eeidle(ctlr); + eeclk(ctlr, 0); + eeclk(ctlr, Eeclk); + d = 0x180 | a; + for(i=0x400; i; i>>=1){ + v = (d & i) ? Eedi : 0; + eeclk(ctlr, v); + eeclk(ctlr, Eeclk|v); + } + eeclk(ctlr, 0); + + w = 0; + for(i=0x8000; i; i >>= 1){ + eeclk(ctlr, Eeclk); + if(csr32r(ctlr, Rmear) & Eedo) + w |= i; + microdelay(2); + eeclk(ctlr, 0); + } + eeidle(ctlr); + return w; +} + +static void +resetctlr(Ctlr *ctlr) +{ + int i; + + csr32w(ctlr, Rcr, Rst); + for(i=0;; i++){ + if(i > 100) + panic("ns83815: soft reset did not complete"); + microdelay(250); + if((csr32r(ctlr, Rcr) & Rst) == 0) + break; + delay(1); + } +} + +static void +shutdown(Ether* ether) +{ + Ctlr *ctlr = ether->ctlr; + +print("ether83815 shutting down\n"); + csr32w(ctlr, Rcr, Rxd|Txd); /* disable transceiver */ + resetctlr(ctlr); +} + +static void +softreset(Ctlr* ctlr, int resetphys) +{ + int i, w; + + /* + * Soft-reset the controller + */ + resetctlr(ctlr); + csr32w(ctlr, Rccsr, Pmests); + csr32w(ctlr, Rccsr, 0); + csr32w(ctlr, Rcfg, csr32r(ctlr, Rcfg) | Pint_acen); + + if(resetphys){ + /* + * Soft-reset the PHY + */ + csr32w(ctlr, Rbmcr, Reset); + for(i=0;; i++){ + if(i > 100) + panic("ns83815: PHY soft reset time out"); + if((csr32r(ctlr, Rbmcr) & Reset) == 0) + break; + delay(1); + } + } + + /* + * Initialisation values, in sequence (see 4.4 Recommended Registers Configuration) + */ + csr16w(ctlr, 0xCC, 0x0001); /* PGSEL */ + csr16w(ctlr, 0xE4, 0x189C); /* PMCCSR */ + csr16w(ctlr, 0xFC, 0x0000); /* TSTDAT */ + csr16w(ctlr, 0xF4, 0x5040); /* DSPCFG */ + csr16w(ctlr, 0xF8, 0x008C); /* SDCFG */ + + /* + * Auto negotiate + */ + w = csr16r(ctlr, Rbmsr); /* clear latched bits */ + debug("anar: %4.4ux\n", csr16r(ctlr, Ranar)); + csr16w(ctlr, Rbmcr, Anena); + if(csr16r(ctlr, Ranar) == 0 || (csr32r(ctlr, Rcfg) & Aneg_dn) == 0){ + csr16w(ctlr, Rbmcr, Anena|Anrestart); + for(i=0;; i++){ + if(i > 6000){ + print("ns83815: auto neg timed out\n"); + break; + } + if((w = csr16r(ctlr, Rbmsr)) & Ancomp) + break; + delay(1); + } + debug("%d ms\n", i); + w &= 0xFFFF; + debug("bmsr: %4.4ux\n", w); + } + USED(w); + debug("anar: %4.4ux\n", csr16r(ctlr, Ranar)); + debug("anlpar: %4.4ux\n", csr16r(ctlr, Ranlpar)); + debug("aner: %4.4ux\n", csr16r(ctlr, Raner)); + debug("physts: %4.4ux\n", csr16r(ctlr, Rphysts)); + debug("tbscr: %4.4ux\n", csr16r(ctlr, Rtbscr)); +} + +static int +media(Ether* ether) +{ + Ctlr* ctlr; + ulong cfg; + + ctlr = ether->ctlr; + cfg = csr32r(ctlr, Rcfg); + ctlr->fd = (cfg & Fdup) != 0; + if(cfg & Speed100) + return 100; + if((cfg & Lnksts) == 0) + return 100; /* no link: use 100 to ensure larger queues */ + return 10; +} + +static char* mediatable[9] = { + "10BASE-T", /* TP */ + "10BASE-2", /* BNC */ + "10BASE-5", /* AUI */ + "100BASE-TX", + "10BASE-TFD", + "100BASE-TXFD", + "100BASE-T4", + "100BASE-FX", + "100BASE-FXFD", +}; + +static void +srom(Ctlr* ctlr) +{ + int i, j; + + for(i = 0; i < nelem(ctlr->srom); i++) + ctlr->srom[i] = eegetw(ctlr, i); + + /* + * the MAC address is reversed, straddling word boundaries + */ + memset(ctlr->sromea, 0, sizeof(ctlr->sromea)); + j = 6*16 + 15; + for(i=0; i<48; i++){ + ctlr->sromea[i>>3] |= ((ctlr->srom[j>>4] >> (15-(j&0xF))) & 1) << (i&7); + j++; + } +} + +static void +scanpci83815(void) +{ + Ctlr *ctlr; + Pcidev *p; + + p = nil; + while(p = pcimatch(p, 0, 0)){ + if(p->ccrb != 0x02 || p->ccru != 0) + continue; + switch((p->did<<16)|p->vid){ + default: + continue; + + case Nat83815: + case Sis900: + break; + } + + /* + * bar[0] is the I/O port register address and + * bar[1] is the memory-mapped register address. + */ + ctlr = malloc(sizeof(Ctlr)); + ctlr->port = p->mem[0].bar & ~0x01; + ctlr->pcidev = p; + ctlr->id = (p->did<<16)|p->vid; + + if(ioalloc(ctlr->port, p->mem[0].size, 0, "ns83815") < 0){ + print("ns83815: port 0x%uX in use\n", ctlr->port); + free(ctlr); + continue; + } + + softreset(ctlr, 0); + srom(ctlr); + + if(ctlrhead != nil) + ctlrtail->next = ctlr; + else + ctlrhead = ctlr; + ctlrtail = ctlr; + } +} + +/* multicast already on, don't need to do anything */ +static void +multicast(void*, uchar*, int) +{ +} + +static int +reset(Ether* ether) +{ + Ctlr *ctlr; + int i, x; + uchar ea[Eaddrlen]; + static int scandone; + + if(scandone == 0){ + scanpci83815(); + scandone = 1; + } + + /* + * Any adapter matches if no ether->port is supplied, + * otherwise the ports must match. + */ + for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){ + if(ctlr->active) + continue; + if(ether->port == 0 || ether->port == ctlr->port){ + ctlr->active = 1; + break; + } + } + if(ctlr == nil) + return -1; + + ether->ctlr = ctlr; + ether->port = ctlr->port; + ether->irq = ctlr->pcidev->intl; + ether->tbdf = ctlr->pcidev->tbdf; + + /* + * Check if the adapter's station address is to be overridden. + * If not, read it from the EEPROM and set in ether->ea prior to + * loading the station address in the hardware. + */ + memset(ea, 0, Eaddrlen); + if(memcmp(ea, ether->ea, Eaddrlen) == 0) + memmove(ether->ea, ctlr->sromea, Eaddrlen); + for(i=0; i<Eaddrlen; i+=2){ + x = ether->ea[i] | (ether->ea[i+1]<<8); + csr32w(ctlr, Rrfcr, i); + csr32w(ctlr, Rrfdr, x); + } + csr32w(ctlr, Rrfcr, Rfen|Apm|Aab|Aam); + + ether->mbps = media(ether); + + /* + * Look for a medium override in case there's no autonegotiation + * the autonegotiation fails. + */ + + for(i = 0; i < ether->nopt; i++){ + if(cistrcmp(ether->opt[i], "FD") == 0){ + ctlr->fd = 1; + continue; + } + for(x = 0; x < nelem(mediatable); x++){ + debug("compare <%s> <%s>\n", mediatable[x], + ether->opt[i]); + if(cistrcmp(mediatable[x], ether->opt[i]) == 0){ + if(x != 4 && x >= 3) + ether->mbps = 100; + else + ether->mbps = 10; + switch(x){ + default: + ctlr->fd = 0; + break; + + case 0x04: /* 10BASE-TFD */ + case 0x05: /* 100BASE-TXFD */ + case 0x08: /* 100BASE-FXFD */ + ctlr->fd = 1; + break; + } + break; + } + } + } + + /* + * Initialise descriptor rings, ethernet address. + */ + ctlr->nrdr = Nrde; + ctlr->ntdr = Ntde; + pcisetbme(ctlr->pcidev); + ctlrinit(ether); + + /* + * Linkage to the generic ethernet driver. + */ + ether->attach = attach; + ether->transmit = transmit; + ether->interrupt = interrupt; + ether->ifstat = ifstat; + + ether->arg = ether; + ether->promiscuous = promiscuous; + ether->multicast = multicast; + ether->shutdown = shutdown; + return 0; +} + +void +ether83815link(void) +{ + addethercard("83815", reset); +} diff --git a/os/pc/ether8390.c b/os/pc/ether8390.c new file mode 100644 index 00000000..42bde3cf --- /dev/null +++ b/os/pc/ether8390.c @@ -0,0 +1,812 @@ +/* + * National Semiconductor DP8390 and clone + * Network Interface Controller. + */ +#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/netif.h" + +#include "etherif.h" +#include "ether8390.h" + +enum { /* NIC core registers */ + Cr = 0x00, /* command register, all pages */ + + /* Page 0, read */ + Clda0 = 0x01, /* current local DMA address 0 */ + Clda1 = 0x02, /* current local DMA address 1 */ + Bnry = 0x03, /* boundary pointer (R/W) */ + Tsr = 0x04, /* transmit status register */ + Ncr = 0x05, /* number of collisions register */ + Fifo = 0x06, /* FIFO */ + Isr = 0x07, /* interrupt status register (R/W) */ + Crda0 = 0x08, /* current remote DMA address 0 */ + Crda1 = 0x09, /* current remote DMA address 1 */ + Rsr = 0x0C, /* receive status register */ + Ref0 = 0x0D, /* frame alignment errors */ + Ref1 = 0x0E, /* CRC errors */ + Ref2 = 0x0F, /* missed packet errors */ + + /* Page 0, write */ + Pstart = 0x01, /* page start register */ + Pstop = 0x02, /* page stop register */ + Tpsr = 0x04, /* transmit page start address */ + Tbcr0 = 0x05, /* transmit byte count register 0 */ + Tbcr1 = 0x06, /* transmit byte count register 1 */ + Rsar0 = 0x08, /* remote start address register 0 */ + Rsar1 = 0x09, /* remote start address register 1 */ + Rbcr0 = 0x0A, /* remote byte count register 0 */ + Rbcr1 = 0x0B, /* remote byte count register 1 */ + Rcr = 0x0C, /* receive configuration register */ + Tcr = 0x0D, /* transmit configuration register */ + Dcr = 0x0E, /* data configuration register */ + Imr = 0x0F, /* interrupt mask */ + + /* Page 1, read/write */ + Par0 = 0x01, /* physical address register 0 */ + Curr = 0x07, /* current page register */ + Mar0 = 0x08, /* multicast address register 0 */ +}; + +enum { /* Cr */ + Stp = 0x01, /* stop */ + Sta = 0x02, /* start */ + Txp = 0x04, /* transmit packet */ + Rd0 = 0x08, /* remote DMA command */ + Rd1 = 0x10, + Rd2 = 0x20, + RdREAD = Rd0, /* remote read */ + RdWRITE = Rd1, /* remote write */ + RdSEND = Rd1|Rd0, /* send packet */ + RdABORT = Rd2, /* abort/complete remote DMA */ + Ps0 = 0x40, /* page select */ + Ps1 = 0x80, + Page0 = 0x00, + Page1 = Ps0, + Page2 = Ps1, +}; + +enum { /* Isr/Imr */ + Prx = 0x01, /* packet received */ + Ptx = 0x02, /* packet transmitted */ + Rxe = 0x04, /* receive error */ + Txe = 0x08, /* transmit error */ + Ovw = 0x10, /* overwrite warning */ + Cnt = 0x20, /* counter overflow */ + Rdc = 0x40, /* remote DMA complete */ + Rst = 0x80, /* reset status */ +}; + +enum { /* Dcr */ + Wts = 0x01, /* word transfer select */ + Bos = 0x02, /* byte order select */ + Las = 0x04, /* long address select */ + Ls = 0x08, /* loopback select */ + Arm = 0x10, /* auto-initialise remote */ + Ft0 = 0x20, /* FIFO threshold select */ + Ft1 = 0x40, + Ft1WORD = 0x00, + Ft2WORD = Ft0, + Ft4WORD = Ft1, + Ft6WORD = Ft1|Ft0, +}; + +enum { /* Tcr */ + Crc = 0x01, /* inhibit CRC */ + Lb0 = 0x02, /* encoded loopback control */ + Lb1 = 0x04, + LpbkNORMAL = 0x00, /* normal operation */ + LpbkNIC = Lb0, /* internal NIC module loopback */ + LpbkENDEC = Lb1, /* internal ENDEC module loopback */ + LpbkEXTERNAL = Lb1|Lb0, /* external loopback */ + Atd = 0x08, /* auto transmit disable */ + Ofst = 0x10, /* collision offset enable */ +}; + +enum { /* Tsr */ + Ptxok = 0x01, /* packet transmitted */ + Col = 0x04, /* transmit collided */ + Abt = 0x08, /* tranmit aborted */ + Crs = 0x10, /* carrier sense lost */ + Fu = 0x20, /* FIFO underrun */ + Cdh = 0x40, /* CD heartbeat */ + Owc = 0x80, /* out of window collision */ +}; + +enum { /* Rcr */ + Sep = 0x01, /* save errored packets */ + Ar = 0x02, /* accept runt packets */ + Ab = 0x04, /* accept broadcast */ + Am = 0x08, /* accept multicast */ + Pro = 0x10, /* promiscuous physical */ + Mon = 0x20, /* monitor mode */ +}; + +enum { /* Rsr */ + Prxok = 0x01, /* packet received intact */ + Crce = 0x02, /* CRC error */ + Fae = 0x04, /* frame alignment error */ + Fo = 0x08, /* FIFO overrun */ + Mpa = 0x10, /* missed packet */ + Phy = 0x20, /* physical/multicast address */ + Dis = 0x40, /* receiver disabled */ + Dfr = 0x80, /* deferring */ +}; + +typedef struct Hdr Hdr; +struct Hdr { + uchar status; + uchar next; + uchar len0; + uchar len1; +}; + +void +dp8390getea(Ether* ether, uchar* ea) +{ + Dp8390 *ctlr; + uchar cr; + int i; + + ctlr = ether->ctlr; + + /* + * Get the ethernet address from the chip. + * Take care to restore the command register + * afterwards. + */ + ilock(ctlr); + cr = regr(ctlr, Cr) & ~Txp; + regw(ctlr, Cr, Page1|(~(Ps1|Ps0) & cr)); + for(i = 0; i < Eaddrlen; i++) + ea[i] = regr(ctlr, Par0+i); + regw(ctlr, Cr, cr); + iunlock(ctlr); +} + +void +dp8390setea(Ether* ether) +{ + int i; + uchar cr; + Dp8390 *ctlr; + + ctlr = ether->ctlr; + + /* + * Set the ethernet address into the chip. + * Take care to restore the command register + * afterwards. Don't care about multicast + * addresses as multicast is never enabled + * (currently). + */ + ilock(ctlr); + cr = regr(ctlr, Cr) & ~Txp; + regw(ctlr, Cr, Page1|(~(Ps1|Ps0) & cr)); + for(i = 0; i < Eaddrlen; i++) + regw(ctlr, Par0+i, ether->ea[i]); + regw(ctlr, Cr, cr); + iunlock(ctlr); +} + +static void* +_dp8390read(Dp8390* ctlr, void* to, ulong from, ulong len) +{ + uchar cr; + int timo; + + /* + * Read some data at offset 'from' in the card's memory + * using the DP8390 remote DMA facility, and place it at + * 'to' in main memory, via the I/O data port. + */ + cr = regr(ctlr, Cr) & ~Txp; + regw(ctlr, Cr, Page0|RdABORT|Sta); + regw(ctlr, Isr, Rdc); + + /* + * Set up the remote DMA address and count. + */ + len = ROUNDUP(len, ctlr->width); + regw(ctlr, Rbcr0, len & 0xFF); + regw(ctlr, Rbcr1, (len>>8) & 0xFF); + regw(ctlr, Rsar0, from & 0xFF); + regw(ctlr, Rsar1, (from>>8) & 0xFF); + + /* + * Start the remote DMA read and suck the data + * out of the I/O port. + */ + regw(ctlr, Cr, Page0|RdREAD|Sta); + rdread(ctlr, to, len); + + /* + * Wait for the remote DMA to complete. The timeout + * is necessary because this routine may be called on + * a non-existent chip during initialisation and, due + * to the miracles of the bus, it's possible to get this + * far and still be talking to a slot full of nothing. + */ + for(timo = 10000; (regr(ctlr, Isr) & Rdc) == 0 && timo; timo--) + ; + + regw(ctlr, Isr, Rdc); + regw(ctlr, Cr, cr); + + return to; +} + +void* +dp8390read(Dp8390* ctlr, void* to, ulong from, ulong len) +{ + void *v; + + ilock(ctlr); + v = _dp8390read(ctlr, to, from, len); + iunlock(ctlr); + + return v; +} + +static void* +dp8390write(Dp8390* ctlr, ulong to, void* from, ulong len) +{ + ulong crda; + uchar cr; + int timo, width; + +top: + /* + * Write some data to offset 'to' in the card's memory + * using the DP8390 remote DMA facility, reading it at + * 'from' in main memory, via the I/O data port. + */ + cr = regr(ctlr, Cr) & ~Txp; + regw(ctlr, Cr, Page0|RdABORT|Sta); + regw(ctlr, Isr, Rdc); + + len = ROUNDUP(len, ctlr->width); + + /* + * Set up the remote DMA address and count. + * This is straight from the DP8390[12D] datasheet, + * hence the initial set up for read. + * Assumption here that the A7000 EtherV card will + * never need a dummyrr. + */ + if(ctlr->dummyrr && (ctlr->width == 1 || ctlr->width == 2)){ + if(ctlr->width == 2) + width = 1; + else + width = 0; + crda = to-1-width; + regw(ctlr, Rbcr0, (len+1+width) & 0xFF); + regw(ctlr, Rbcr1, ((len+1+width)>>8) & 0xFF); + regw(ctlr, Rsar0, crda & 0xFF); + regw(ctlr, Rsar1, (crda>>8) & 0xFF); + regw(ctlr, Cr, Page0|RdREAD|Sta); + + for(timo=0;; timo++){ + if(timo > 10000){ + print("ether8390: dummyrr timeout; assuming nodummyrr\n"); + ctlr->dummyrr = 0; + goto top; + } + crda = regr(ctlr, Crda0); + crda |= regr(ctlr, Crda1)<<8; + if(crda == to){ + /* + * Start the remote DMA write and make sure + * the registers are correct. + */ + regw(ctlr, Cr, Page0|RdWRITE|Sta); + + crda = regr(ctlr, Crda0); + crda |= regr(ctlr, Crda1)<<8; + if(crda != to) + panic("crda write %lud to %lud\n", crda, to); + + break; + } + } + } + else{ + regw(ctlr, Rsar0, to & 0xFF); + regw(ctlr, Rsar1, (to>>8) & 0xFF); + regw(ctlr, Rbcr0, len & 0xFF); + regw(ctlr, Rbcr1, (len>>8) & 0xFF); + regw(ctlr, Cr, Page0|RdWRITE|Sta); + } + + /* + * Pump the data into the I/O port + * then wait for the remote DMA to finish. + */ + rdwrite(ctlr, from, len); + for(timo = 10000; (regr(ctlr, Isr) & Rdc) == 0 && timo; timo--) + ; + + regw(ctlr, Isr, Rdc); + regw(ctlr, Cr, cr); + + return (void*)to; +} + +static void +ringinit(Dp8390* ctlr) +{ + regw(ctlr, Pstart, ctlr->pstart); + regw(ctlr, Pstop, ctlr->pstop); + regw(ctlr, Bnry, ctlr->pstop-1); + + regw(ctlr, Cr, Page1|RdABORT|Stp); + regw(ctlr, Curr, ctlr->pstart); + regw(ctlr, Cr, Page0|RdABORT|Stp); + + ctlr->nxtpkt = ctlr->pstart; +} + +static uchar +getcurr(Dp8390* ctlr) +{ + uchar cr, curr; + + cr = regr(ctlr, Cr) & ~Txp; + regw(ctlr, Cr, Page1|(~(Ps1|Ps0) & cr)); + curr = regr(ctlr, Curr); + regw(ctlr, Cr, cr); + + return curr; +} + +static void +receive(Ether* ether) +{ + Dp8390 *ctlr; + uchar curr, *p; + Hdr hdr; + ulong count, data, len; + Block *bp; + + ctlr = ether->ctlr; + for(curr = getcurr(ctlr); ctlr->nxtpkt != curr; curr = getcurr(ctlr)){ + data = ctlr->nxtpkt*Dp8390BufSz; + if(ctlr->ram) + memmove(&hdr, (void*)(ether->mem+data), sizeof(Hdr)); + else + _dp8390read(ctlr, &hdr, data, sizeof(Hdr)); + + /* + * Don't believe the upper byte count, work it + * out from the software next-page pointer and + * the current next-page pointer. + */ + if(hdr.next > ctlr->nxtpkt) + len = hdr.next - ctlr->nxtpkt - 1; + else + len = (ctlr->pstop-ctlr->nxtpkt) + (hdr.next-ctlr->pstart) - 1; + if(hdr.len0 > (Dp8390BufSz-sizeof(Hdr))) + len--; + + len = ((len<<8)|hdr.len0)-4; + + /* + * Chip is badly scrogged, reinitialise the ring. + */ + if(hdr.next < ctlr->pstart || hdr.next >= ctlr->pstop + || len < 60 || len > sizeof(Etherpkt)){ + print("dp8390: H#%2.2ux#%2.2ux#%2.2ux#%2.2ux,%lud\n", + hdr.status, hdr.next, hdr.len0, hdr.len1, len); + regw(ctlr, Cr, Page0|RdABORT|Stp); + ringinit(ctlr); + regw(ctlr, Cr, Page0|RdABORT|Sta); + + return; + } + + /* + * If it's a good packet read it in to the software buffer. + * If the packet wraps round the hardware ring, read it in + * two pieces. + */ + if((hdr.status & (Fo|Fae|Crce|Prxok)) == Prxok && (bp = iallocb(len))){ + p = bp->rp; + bp->wp = p+len; + data += sizeof(Hdr); + + if((data+len) >= ctlr->pstop*Dp8390BufSz){ + count = ctlr->pstop*Dp8390BufSz - data; + if(ctlr->ram) + memmove(p, (void*)(ether->mem+data), count); + else + _dp8390read(ctlr, p, data, count); + p += count; + data = ctlr->pstart*Dp8390BufSz; + len -= count; + } + if(len){ + if(ctlr->ram) + memmove(p, (void*)(ether->mem+data), len); + else + _dp8390read(ctlr, p, data, len); + } + + /* + * Copy the packet to whoever wants it. + */ + etheriq(ether, bp, 1); + } + + /* + * Finished with this packet, update the + * hardware and software ring pointers. + */ + ctlr->nxtpkt = hdr.next; + + hdr.next--; + if(hdr.next < ctlr->pstart) + hdr.next = ctlr->pstop-1; + regw(ctlr, Bnry, hdr.next); + } +} + +static void +txstart(Ether* ether) +{ + int len; + Dp8390 *ctlr; + Block *bp; + uchar minpkt[ETHERMINTU], *rp; + + ctlr = ether->ctlr; + + /* + * This routine is called both from the top level and from interrupt + * level and expects to be called with ctlr already locked. + */ + if(ctlr->txbusy) + return; + bp = qget(ether->oq); + if(bp == nil) + return; + + /* + * Make sure the packet is of minimum length; + * copy it to the card's memory by the appropriate means; + * start the transmission. + */ + len = BLEN(bp); + rp = bp->rp; + if(len < ETHERMINTU){ + rp = minpkt; + memmove(rp, bp->rp, len); + memset(rp+len, 0, ETHERMINTU-len); + len = ETHERMINTU; + } + + if(ctlr->ram) + memmove((void*)(ether->mem+ctlr->tstart*Dp8390BufSz), rp, len); + else + dp8390write(ctlr, ctlr->tstart*Dp8390BufSz, rp, len); + freeb(bp); + + regw(ctlr, Tbcr0, len & 0xFF); + regw(ctlr, Tbcr1, (len>>8) & 0xFF); + regw(ctlr, Cr, Page0|RdABORT|Txp|Sta); + + ether->outpackets++; + ctlr->txbusy = 1; +} + +static void +transmit(Ether* ether) +{ + Dp8390 *ctlr; + + ctlr = ether->ctlr; + + ilock(ctlr); + txstart(ether); + iunlock(ctlr); +} + +static void +overflow(Ether *ether) +{ + Dp8390 *ctlr; + uchar txp; + int resend; + + ctlr = ether->ctlr; + + /* + * The following procedure is taken from the DP8390[12D] datasheet, + * it seems pretty adamant that this is what has to be done. + */ + txp = regr(ctlr, Cr) & Txp; + regw(ctlr, Cr, Page0|RdABORT|Stp); + delay(2); + regw(ctlr, Rbcr0, 0); + regw(ctlr, Rbcr1, 0); + + resend = 0; + if(txp && (regr(ctlr, Isr) & (Txe|Ptx)) == 0) + resend = 1; + + regw(ctlr, Tcr, LpbkNIC); + regw(ctlr, Cr, Page0|RdABORT|Sta); + receive(ether); + regw(ctlr, Isr, Ovw); + regw(ctlr, Tcr, LpbkNORMAL); + + if(resend) + regw(ctlr, Cr, Page0|RdABORT|Txp|Sta); +} + +static void +interrupt(Ureg*, void* arg) +{ + Ether *ether; + Dp8390 *ctlr; + uchar isr, r; + + ether = arg; + ctlr = ether->ctlr; + + /* + * While there is something of interest, + * clear all the interrupts and process. + */ + ilock(ctlr); + regw(ctlr, Imr, 0x00); + while(isr = (regr(ctlr, Isr) & (Cnt|Ovw|Txe|Rxe|Ptx|Prx))){ + if(isr & Ovw){ + overflow(ether); + regw(ctlr, Isr, Ovw); + ether->overflows++; + } + + /* + * Packets have been received. + * Take a spin round the ring. + */ + if(isr & (Rxe|Prx)){ + receive(ether); + regw(ctlr, Isr, Rxe|Prx); + } + + /* + * A packet completed transmission, successfully or + * not. Start transmission on the next buffered packet, + * and wake the output routine. + */ + if(isr & (Txe|Ptx)){ + r = regr(ctlr, Tsr); + if((isr & Txe) && (r & (Cdh|Fu|Crs|Abt))){ + print("dp8390: Tsr#%2.2ux|", r); + ether->oerrs++; + } + + regw(ctlr, Isr, Txe|Ptx); + + if(isr & Ptx) + ether->outpackets++; + ctlr->txbusy = 0; + txstart(ether); + } + + if(isr & Cnt){ + ether->frames += regr(ctlr, Ref0); + ether->crcs += regr(ctlr, Ref1); + ether->buffs += regr(ctlr, Ref2); + regw(ctlr, Isr, Cnt); + } + } + regw(ctlr, Imr, Cnt|Ovw|Txe|Rxe|Ptx|Prx); + iunlock(ctlr); +} + +static uchar allmar[8] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + +static void +setfilter(Ether *ether, Dp8390 *ctlr) +{ + uchar r, cr; + int i; + uchar *mar; + + r = Ab; + mar = 0; + if(ether->prom){ + r |= Pro|Am; + mar = allmar; + } else if(ether->nmaddr){ + r |= Am; + mar = ctlr->mar; + } + if(mar){ + cr = regr(ctlr, Cr) & ~Txp; + regw(ctlr, Cr, Page1|(~(Ps1|Ps0) & cr)); + for(i = 0; i < 8; i++) + regw(ctlr, Mar0+i, *(mar++)); + regw(ctlr, Cr, cr); + } + regw(ctlr, Rcr, r); +} + +static void +promiscuous(void *arg, int ) +{ + Ether *ether; + Dp8390 *ctlr; + + ether = arg; + ctlr = ether->ctlr; + + ilock(ctlr); + setfilter(ether, ctlr); + iunlock(ctlr); +} + +static void +setbit(Dp8390 *ctlr, int bit, int on) +{ + int i, h; + + i = bit/8; + h = bit%8; + if(on){ + if(++(ctlr->mref[bit]) == 1) + ctlr->mar[i] |= 1<<h; + } else { + if(--(ctlr->mref[bit]) <= 0){ + ctlr->mref[bit] = 0; + ctlr->mar[i] &= ~(1<<h); + } + } +} + +static uchar reverse[64]; + +static void +multicast(void* arg, uchar *addr, int on) +{ + Ether *ether; + Dp8390 *ctlr; + int i; + ulong h; + + ether = arg; + ctlr = ether->ctlr; + if(reverse[1] == 0){ + for(i = 0; i < 64; i++) + reverse[i] = ((i&1)<<5) | ((i&2)<<3) | ((i&4)<<1) + | ((i&8)>>1) | ((i&16)>>3) | ((i&32)>>5); + } + + /* + * change filter bits + */ + h = ethercrc(addr, 6); + ilock(ctlr); + setbit(ctlr, reverse[h&0x3f], on); + setfilter(ether, ctlr); + iunlock(ctlr); +} + +static void +attach(Ether* ether) +{ + Dp8390 *ctlr; + uchar r; + + ctlr = ether->ctlr; + + /* + * Enable the chip for transmit/receive. + * The init routine leaves the chip in monitor + * mode. Clear the missed-packet counter, it + * increments while in monitor mode. + * Sometimes there's an interrupt pending at this + * point but there's nothing in the Isr, so + * any pending interrupts are cleared and the + * mask of acceptable interrupts is enabled here. + */ + r = Ab; + if(ether->prom) + r |= Pro; + if(ether->nmaddr) + r |= Am; + ilock(ctlr); + regw(ctlr, Isr, 0xFF); + regw(ctlr, Imr, Cnt|Ovw|Txe|Rxe|Ptx|Prx); + regw(ctlr, Rcr, r); + r = regr(ctlr, Ref2); + regw(ctlr, Tcr, LpbkNORMAL); + iunlock(ctlr); + USED(r); +} + +static void +disable(Dp8390* ctlr) +{ + int timo; + + /* + * Stop the chip. Set the Stp bit and wait for the chip + * to finish whatever was on its tiny mind before it sets + * the Rst bit. + * The timeout is needed because there may not be a real + * chip there if this is called when probing for a device + * at boot. + */ + regw(ctlr, Cr, Page0|RdABORT|Stp); + regw(ctlr, Rbcr0, 0); + regw(ctlr, Rbcr1, 0); + for(timo = 10000; (regr(ctlr, Isr) & Rst) == 0 && timo; timo--) + ; +} + +int +dp8390reset(Ether* ether) +{ + Dp8390 *ctlr; + + ctlr = ether->ctlr; + + /* + * This is the initialisation procedure described + * as 'mandatory' in the datasheet, with references + * to the 3C503 technical reference manual. + */ + disable(ctlr); + if(ctlr->width != 1) + regw(ctlr, Dcr, Ft4WORD|Ls|Wts); + else + regw(ctlr, Dcr, Ft4WORD|Ls); + + regw(ctlr, Rbcr0, 0); + regw(ctlr, Rbcr1, 0); + + regw(ctlr, Tcr, LpbkNIC); + regw(ctlr, Rcr, Mon); + + /* + * Init the ring hardware and software ring pointers. + * Can't initialise ethernet address as it may not be + * known yet. + */ + ringinit(ctlr); + regw(ctlr, Tpsr, ctlr->tstart); + + /* + * Clear any pending interrupts and mask then all off. + */ + regw(ctlr, Isr, 0xFF); + regw(ctlr, Imr, 0); + + /* + * Leave the chip initialised, + * but in monitor mode. + */ + regw(ctlr, Cr, Page0|RdABORT|Sta); + + /* + * Set up the software configuration. + */ + ether->attach = attach; + ether->transmit = transmit; + ether->interrupt = interrupt; + ether->ifstat = 0; + + ether->promiscuous = promiscuous; + ether->multicast = multicast; + ether->arg = ether; + + return 0; +} diff --git a/os/pc/ether8390.h b/os/pc/ether8390.h new file mode 100644 index 00000000..b5bf6e55 --- /dev/null +++ b/os/pc/ether8390.h @@ -0,0 +1,74 @@ +/* + * Ctlr for the boards using the National Semiconductor DP8390 + * and SMC 83C90 Network Interface Controller. + * Common code is in ether8390.c. + */ +typedef struct { + Lock; + + ulong port; /* I/O address of 8390 */ + ulong data; /* I/O data port if no shared memory */ + + uchar width; /* data transfer width in bytes */ + uchar ram; /* true if card has shared memory */ + uchar dummyrr; /* do dummy remote read */ + + uchar nxtpkt; /* receive: software bndry */ + uchar pstart; + uchar pstop; + + int txbusy; /* transmit */ + uchar tstart; /* 8390 ring addresses */ + + uchar mar[8]; /* shadow multicast address registers */ + int mref[64]; /* reference counts for multicast groups */ +} Dp8390; + +#define Dp8390BufSz 256 + +extern int dp8390reset(Ether*); +extern void *dp8390read(Dp8390*, void*, ulong, ulong); +extern void dp8390getea(Ether*, uchar*); +extern void dp8390setea(Ether*); + +/* + * x86-specific code. + */ +#define regr(c, r) inb((c)->port+(r)) +#define regw(c, r, v) outb((c)->port+(r), (v)) + +static void +rdread(Dp8390* ctlr, void* to, int len) +{ + switch(ctlr->width){ + default: + panic("dp8390 rdread: width %d\n", ctlr->width); + break; + + case 2: + inss(ctlr->data, to, len/2); + break; + + case 1: + insb(ctlr->data, to, len); + break; + } +} + +static void +rdwrite(Dp8390* ctlr, void* from, int len) +{ + switch(ctlr->width){ + default: + panic("dp8390 rdwrite: width %d\n", ctlr->width); + break; + + case 2: + outss(ctlr->data, from, len/2); + break; + + case 1: + outsb(ctlr->data, from, len); + break; + } +} diff --git a/os/pc/etherec2t.c b/os/pc/etherec2t.c new file mode 100644 index 00000000..ffbec852 --- /dev/null +++ b/os/pc/etherec2t.c @@ -0,0 +1,173 @@ +/* + * Supposed NE2000 PCMCIA clones, see the comments in ether2000.c + */ +#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/netif.h" + +#include "etherif.h" +#include "ether8390.h" + +enum { + Data = 0x10, /* offset from I/O base of data port */ + Reset = 0x1F, /* offset from I/O base of reset port */ +}; + +typedef struct Ec2t { + char* name; + int iochecksum; +} Ec2t; + +static Ec2t ec2tpcmcia[] = { + { "EC2T", 0, }, /* Linksys Combo PCMCIA EthernetCard */ + { "PCMPC100", 1, }, /* EtherFast 10/100 PC Card */ + { "PCM100", 1, }, /* EtherFast PCM100 Card */ + { "EN2216", 0, }, /* Accton EtherPair-PCMCIA */ + { "FA410TX", 1, }, /* Netgear FA410TX */ + { "Network Everywhere", 0, }, /* Linksys NP10T 10BaseT Card */ + { "10/100 Port Attached", 1, }, /* SMC 8040TX */ + { "FA411", 0 }, /* Netgear FA411 PCMCIA */ + { nil, 0, }, +}; + +static int +reset(Ether* ether) +{ + ushort buf[16]; + ulong port; + Dp8390 *ctlr; + int i, slot; + uchar ea[Eaddrlen], sum, x; + Ec2t *ec2t, tmpec2t; + + /* + * Set up the software configuration. + * Use defaults for port, irq, mem and size + * if not specified. + * The manual says 16KB memory, the box + * says 32KB. The manual seems to be correct. + */ + if(ether->port == 0) + ether->port = 0x300; + if(ether->irq == 0) + ether->irq = 9; + if(ether->mem == 0) + ether->mem = 0x4000; + if(ether->size == 0) + ether->size = 16*1024; + port = ether->port; + + if(ioalloc(ether->port, 0x20, 0, "ec2t") < 0) + return -1; + slot = -1; + for(ec2t = ec2tpcmcia; ec2t->name != nil; ec2t++){ + if((slot = pcmspecial(ec2t->name, ether)) >= 0) + break; + } + if(ec2t->name == nil){ + ec2t = &tmpec2t; + ec2t->name = nil; + ec2t->iochecksum = 0; + for(i = 0; i < ether->nopt; i++){ + if(cistrncmp(ether->opt[i], "id=", 3) == 0){ + ec2t->name = ðer->opt[i][3]; + slot = pcmspecial(ec2t->name, ether); + } + else if(cistrncmp(ether->opt[i], "iochecksum", 10) == 0) + ec2t->iochecksum = 1; + } + } + if(slot < 0){ + iofree(port); + return -1; + } + + ether->ctlr = malloc(sizeof(Dp8390)); + ctlr = ether->ctlr; + ctlr->width = 2; + ctlr->ram = 0; + + ctlr->port = port; + ctlr->data = port+Data; + + ctlr->tstart = HOWMANY(ether->mem, Dp8390BufSz); + ctlr->pstart = ctlr->tstart + HOWMANY(sizeof(Etherpkt), Dp8390BufSz); + ctlr->pstop = ctlr->tstart + HOWMANY(ether->size, Dp8390BufSz); + + ctlr->dummyrr = 0; + for(i = 0; i < ether->nopt; i++){ + if(cistrcmp(ether->opt[i], "nodummyrr") == 0) + ctlr->dummyrr = 0; + else if(cistrncmp(ether->opt[i], "dummyrr=", 8) == 0) + ctlr->dummyrr = strtol(ðer->opt[i][8], nil, 0); + } + + /* + * Reset the board. This is done by doing a read + * followed by a write to the Reset address. + */ + buf[0] = inb(port+Reset); + delay(2); + outb(port+Reset, buf[0]); + delay(2); + + /* + * Init the (possible) chip, then use the (possible) + * chip to read the (possible) PROM for ethernet address + * and a marker byte. + * Could just look at the DP8390 command register after + * initialisation has been tried, but that wouldn't be + * enough, there are other ethernet boards which could + * match. + */ + dp8390reset(ether); + sum = 0; + if(ec2t->iochecksum){ + /* + * These cards have the ethernet address in I/O space. + * There's a checksum over 8 bytes which sums to 0xFF. + */ + for(i = 0; i < 8; i++){ + x = inb(port+0x14+i); + sum += x; + buf[i] = (x<<8)|x; + } + } + else{ + memset(buf, 0, sizeof(buf)); + dp8390read(ctlr, buf, 0, sizeof(buf)); + if((buf[0x0E] & 0xFF) == 0x57 && (buf[0x0F] & 0xFF) == 0x57) + sum = 0xFF; + } + if(sum != 0xFF){ + pcmspecialclose(slot); + iofree(ether->port); + free(ether->ctlr); + return -1; + } + + /* + * Stupid machine. Shorts were asked for, + * shorts were delivered, although the PROM is a byte array. + * Set the ethernet address. + */ + memset(ea, 0, Eaddrlen); + if(memcmp(ea, ether->ea, Eaddrlen) == 0){ + for(i = 0; i < sizeof(ether->ea); i++) + ether->ea[i] = buf[i]; + } + dp8390setea(ether); + + return 0; +} + +void +etherec2tlink(void) +{ + addethercard("EC2T", reset); +} diff --git a/os/pc/etherelnk3.c b/os/pc/etherelnk3.c new file mode 100644 index 00000000..e0be41f1 --- /dev/null +++ b/os/pc/etherelnk3.c @@ -0,0 +1,2132 @@ +/* + * Etherlink III, Fast EtherLink and Fast EtherLink XL adapters. + * To do: + * check robustness in the face of errors (e.g. busmaster & rxUnderrun); + * RxEarly and busmaster; + * autoSelect; + * PCI latency timer and master enable; + * errata list; + * rewrite all initialisation. + */ +#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/netif.h" + +#include "etherif.h" + +#define XCVRDEBUG if(0)print + +enum { + IDport = 0x0110, /* anywhere between 0x0100 and 0x01F0 */ +}; + +enum { /* all windows */ + CommandR = 0x000E, + IntStatusR = 0x000E, +}; + +enum { /* Commands */ + GlobalReset = 0x0000, + SelectRegisterWindow = 0x0001, + EnableDcConverter = 0x0002, + RxDisable = 0x0003, + RxEnable = 0x0004, + RxReset = 0x0005, + Stall = 0x0006, /* 3C90x */ + TxDone = 0x0007, + RxDiscard = 0x0008, + TxEnable = 0x0009, + TxDisable = 0x000A, + TxReset = 0x000B, + RequestInterrupt = 0x000C, + AcknowledgeInterrupt = 0x000D, + SetInterruptEnable = 0x000E, + SetIndicationEnable = 0x000F, /* SetReadZeroMask */ + SetRxFilter = 0x0010, + SetRxEarlyThresh = 0x0011, + SetTxAvailableThresh = 0x0012, + SetTxStartThresh = 0x0013, + StartDma = 0x0014, /* initiate busmaster operation */ + StatisticsEnable = 0x0015, + StatisticsDisable = 0x0016, + DisableDcConverter = 0x0017, + SetTxReclaimThresh = 0x0018, /* PIO-only adapters */ + PowerUp = 0x001B, /* not all adapters */ + PowerDownFull = 0x001C, /* not all adapters */ + PowerAuto = 0x001D, /* not all adapters */ +}; + +enum { /* (Global|Rx|Tx)Reset command bits */ + tpAuiReset = 0x0001, /* 10BaseT and AUI transceivers */ + endecReset = 0x0002, /* internal Ethernet encoder/decoder */ + networkReset = 0x0004, /* network interface logic */ + fifoReset = 0x0008, /* FIFO control logic */ + aismReset = 0x0010, /* autoinitialise state-machine logic */ + hostReset = 0x0020, /* bus interface logic */ + dmaReset = 0x0040, /* bus master logic */ + vcoReset = 0x0080, /* on-board 10Mbps VCO */ + updnReset = 0x0100, /* upload/download (Rx/TX) logic */ + + resetMask = 0x01FF, +}; + +enum { /* Stall command bits */ + upStall = 0x0000, + upUnStall = 0x0001, + dnStall = 0x0002, + dnUnStall = 0x0003, +}; + +enum { /* SetRxFilter command bits */ + receiveIndividual = 0x0001, /* match station address */ + receiveMulticast = 0x0002, + receiveBroadcast = 0x0004, + receiveAllFrames = 0x0008, /* promiscuous */ +}; + +enum { /* StartDma command bits */ + Upload = 0x0000, /* transfer data from adapter to memory */ + Download = 0x0001, /* transfer data from memory to adapter */ +}; + +enum { /* IntStatus bits */ + interruptLatch = 0x0001, + hostError = 0x0002, /* Adapter Failure */ + txComplete = 0x0004, + txAvailable = 0x0008, + rxComplete = 0x0010, + rxEarly = 0x0020, + intRequested = 0x0040, + updateStats = 0x0080, + transferInt = 0x0100, /* Bus Master Transfer Complete */ + dnComplete = 0x0200, + upComplete = 0x0400, + busMasterInProgress = 0x0800, + commandInProgress = 0x1000, + + interruptMask = 0x07FE, +}; + +#define COMMAND(port, cmd, a) outs((port)+CommandR, ((cmd)<<11)|(a)) +#define STATUS(port) ins((port)+IntStatusR) + +enum { /* Window 0 - setup */ + Wsetup = 0x0000, + /* registers */ + ManufacturerID = 0x0000, /* 3C5[08]*, 3C59[27] */ + ProductID = 0x0002, /* 3C5[08]*, 3C59[27] */ + ConfigControl = 0x0004, /* 3C5[08]*, 3C59[27] */ + AddressConfig = 0x0006, /* 3C5[08]*, 3C59[27] */ + ResourceConfig = 0x0008, /* 3C5[08]*, 3C59[27] */ + EepromCommand = 0x000A, + EepromData = 0x000C, + /* AddressConfig Bits */ + autoSelect9 = 0x0080, + xcvrMask9 = 0xC000, + /* ConfigControl bits */ + Ena = 0x0001, + base10TAvailable9 = 0x0200, + coaxAvailable9 = 0x1000, + auiAvailable9 = 0x2000, + /* EepromCommand bits */ + EepromReadRegister = 0x0080, + EepromReadOffRegister = 0x00B0, + EepromRead8bRegister = 0x0230, + EepromBusy = 0x8000, +}; + +#define EEPROMCMD(port, cmd, a) outs((port)+EepromCommand, (cmd)|(a)) +#define EEPROMBUSY(port) (ins((port)+EepromCommand) & EepromBusy) +#define EEPROMDATA(port) ins((port)+EepromData) + +enum { /* Window 1 - operating set */ + Wop = 0x0001, + /* registers */ + Fifo = 0x0000, + RxError = 0x0004, /* 3C59[0257] only */ + RxStatus = 0x0008, + TIMER = 0x000A, + TxStatus = 0x000B, + TxFree = 0x000C, + /* RxError bits */ + rxOverrun = 0x0001, + runtFrame = 0x0002, + alignmentError = 0x0004, /* Framing */ + crcError = 0x0008, + oversizedFrame = 0x0010, + dribbleBits = 0x0080, + /* RxStatus bits */ + rxBytes = 0x1FFF, /* 3C59[0257] mask */ + rxBytes9 = 0x07FF, /* 3C5[078]9 mask */ + rxError9 = 0x3800, /* 3C5[078]9 error mask */ + rxOverrun9 = 0x0000, + oversizedFrame9 = 0x0800, + dribbleBits9 = 0x1000, + runtFrame9 = 0x1800, + alignmentError9 = 0x2000, /* Framing */ + crcError9 = 0x2800, + rxError = 0x4000, + rxIncomplete = 0x8000, + /* TxStatus Bits */ + txStatusOverflow = 0x0004, + maxCollisions = 0x0008, + txUnderrun = 0x0010, + txJabber = 0x0020, + interruptRequested = 0x0040, + txStatusComplete = 0x0080, +}; + +enum { /* Window 2 - station address */ + Wstation = 0x0002, + + ResetOp905B = 0x000C, +}; + +enum { /* Window 3 - FIFO management */ + Wfifo = 0x0003, + /* registers */ + InternalConfig = 0x0000, /* 3C509B, 3C589, 3C59[0257] */ + OtherInt = 0x0004, /* 3C59[0257] */ + RomControl = 0x0006, /* 3C509B, 3C59[27] */ + MacControl = 0x0006, /* 3C59[0257] */ + ResetOptions = 0x0008, /* 3C59[0257] */ + MediaOptions = 0x0008, /* 3C905B */ + RxFree = 0x000A, + /* InternalConfig bits */ + disableBadSsdDetect = 0x00000100, + ramLocation = 0x00000200, /* 0 external, 1 internal */ + ramPartition5to3 = 0x00000000, + ramPartition3to1 = 0x00010000, + ramPartition1to1 = 0x00020000, + ramPartition3to5 = 0x00030000, + ramPartitionMask = 0x00030000, + xcvr10BaseT = 0x00000000, + xcvrAui = 0x00100000, /* 10BASE5 */ + xcvr10Base2 = 0x00300000, + xcvr100BaseTX = 0x00400000, + xcvr100BaseFX = 0x00500000, + xcvrMii = 0x00600000, + xcvrMask = 0x00700000, + autoSelect = 0x01000000, + /* MacControl bits */ + deferExtendEnable = 0x0001, + deferTIMERSelect = 0x001E, /* mask */ + fullDuplexEnable = 0x0020, + allowLargePackets = 0x0040, + extendAfterCollision = 0x0080, /* 3C90xB */ + flowControlEnable = 0x0100, /* 3C90xB */ + vltEnable = 0x0200, /* 3C90xB */ + /* ResetOptions bits */ + baseT4Available = 0x0001, + baseTXAvailable = 0x0002, + baseFXAvailable = 0x0004, + base10TAvailable = 0x0008, + coaxAvailable = 0x0010, + auiAvailable = 0x0020, + miiConnector = 0x0040, +}; + +enum { /* Window 4 - diagnostic */ + Wdiagnostic = 0x0004, + /* registers */ + VcoDiagnostic = 0x0002, + FifoDiagnostic = 0x0004, + NetworkDiagnostic = 0x0006, + PhysicalMgmt = 0x0008, + MediaStatus = 0x000A, + BadSSD = 0x000C, + UpperBytesOk = 0x000D, + /* FifoDiagnostic bits */ + txOverrun = 0x0400, + rxUnderrun = 0x2000, + receiving = 0x8000, + /* PhysicalMgmt bits */ + mgmtClk = 0x0001, + mgmtData = 0x0002, + mgmtDir = 0x0004, + cat5LinkTestDefeat = 0x8000, + /* MediaStatus bits */ + dataRate100 = 0x0002, + crcStripDisable = 0x0004, + enableSqeStats = 0x0008, + collisionDetect = 0x0010, + carrierSense = 0x0020, + jabberGuardEnable = 0x0040, + linkBeatEnable = 0x0080, + jabberDetect = 0x0200, + polarityReversed = 0x0400, + linkBeatDetect = 0x0800, + txInProg = 0x1000, + dcConverterEnabled = 0x4000, + auiDisable = 0x8000, /* 10BaseT transceiver selected */ +}; + +enum { /* Window 5 - internal state */ + Wstate = 0x0005, + /* registers */ + TxStartThresh = 0x0000, + TxAvailableThresh = 0x0002, + RxEarlyThresh = 0x0006, + RxFilter = 0x0008, + InterruptEnable = 0x000A, + IndicationEnable = 0x000C, +}; + +enum { /* Window 6 - statistics */ + Wstatistics = 0x0006, + /* registers */ + CarrierLost = 0x0000, + SqeErrors = 0x0001, + MultipleColls = 0x0002, + SingleCollFrames = 0x0003, + LateCollisions = 0x0004, + RxOverruns = 0x0005, + FramesXmittedOk = 0x0006, + FramesRcvdOk = 0x0007, + FramesDeferred = 0x0008, + UpperFramesOk = 0x0009, + BytesRcvdOk = 0x000A, + BytesXmittedOk = 0x000C, +}; + +enum { /* Window 7 - bus master operations */ + Wmaster = 0x0007, + /* registers */ + MasterAddress = 0x0000, + MasterLen = 0x0006, + MasterStatus = 0x000C, + /* MasterStatus bits */ + masterAbort = 0x0001, + targetAbort = 0x0002, + targetRetry = 0x0004, + targetDisc = 0x0008, + masterDownload = 0x1000, + masterUpload = 0x4000, + masterInProgress = 0x8000, + + masterMask = 0xD00F, +}; + +enum { /* 3C90x extended register set */ + TIMER905 = 0x001A, /* 8-bits */ + TxStatus905 = 0x001B, /* 8-bits */ + PktStatus = 0x0020, /* 32-bits */ + DnListPtr = 0x0024, /* 32-bits, 8-byte aligned */ + FragAddr = 0x0028, /* 32-bits */ + FragLen = 0x002C, /* 16-bits */ + ListOffset = 0x002E, /* 8-bits */ + TxFreeThresh = 0x002F, /* 8-bits */ + UpPktStatus = 0x0030, /* 32-bits */ + FreeTIMER = 0x0034, /* 16-bits */ + UpListPtr = 0x0038, /* 32-bits, 8-byte aligned */ + + /* PktStatus bits */ + fragLast = 0x00000001, + dnCmplReq = 0x00000002, + dnStalled = 0x00000004, + upCompleteX = 0x00000008, + dnCompleteX = 0x00000010, + upRxEarlyEnable = 0x00000020, + armCountdown = 0x00000040, + dnInProg = 0x00000080, + counterSpeed = 0x00000010, /* 0 3.2uS, 1 320nS */ + countdownMode = 0x00000020, + /* UpPktStatus bits (dpd->control) */ + upPktLenMask = 0x00001FFF, + upStalled = 0x00002000, + upError = 0x00004000, + upPktComplete = 0x00008000, + upOverrun = 0x00010000, /* RxError<<16 */ + upRuntFrame = 0x00020000, + upAlignmentError = 0x00040000, + upCRCError = 0x00080000, + upOversizedFrame = 0x00100000, + upDribbleBits = 0x00800000, + upOverflow = 0x01000000, + + dnIndicate = 0x80000000, /* FrameStartHeader (dpd->control) */ + + updnLastFrag = 0x80000000, /* (dpd->len) */ + + Nup = 32, + Ndn = 64, +}; + +/* + * Up/Dn Packet Descriptors. + * The hardware info (np, control, addr, len) must be 8-byte aligned + * and this structure size must be a multiple of 8. + */ +typedef struct Pd Pd; +typedef struct Pd { + ulong np; /* next pointer */ + ulong control; /* FSH or UpPktStatus */ + ulong addr; + ulong len; + + Pd* next; + Block* bp; +} Pd; + +typedef struct Ctlr Ctlr; +typedef struct Ctlr { + int port; + Pcidev* pcidev; + int irq; + Ctlr* next; + int active; + int did; + + Lock wlock; /* window access */ + + int attached; + int busmaster; + Block* rbp; /* receive buffer */ + + Block* txbp; /* FIFO -based transmission */ + int txthreshold; + int txbusy; + + int nup; /* full-busmaster -based reception */ + void* upbase; + Pd* upr; + Pd* uphead; + + int ndn; /* full-busmaster -based transmission */ + void* dnbase; + Pd* dnr; + Pd* dnhead; + Pd* dntail; + int dnq; + + long interrupts; /* statistics */ + long bogusinterrupts; + long timer[2]; + long stats[BytesRcvdOk+3]; + + int upqmax; + int upqmaxhw; + ulong upinterrupts; + ulong upqueued; + ulong upstalls; + int dnqmax; + int dnqmaxhw; + ulong dninterrupts; + ulong dnqueued; + + int xcvr; /* transceiver type */ + int eepromcmd; /* EEPROM read command */ + int rxstatus9; /* old-style RxStatus register */ + int rxearly; /* RxEarlyThreshold */ + int ts; /* threshold shift */ + int upenabled; + int dnenabled; + ulong cbfnpa; /* CardBus functions */ + ulong* cbfn; +} Ctlr; + +static Ctlr* ctlrhead; +static Ctlr* ctlrtail; + +static void +init905(Ctlr* ctlr) +{ + Block *bp; + Pd *pd, *prev; + + /* + * Create rings for the receive and transmit sides. + * Take care with alignment: + * make sure ring base is 8-byte aligned; + * make sure each entry is 8-byte aligned. + */ + ctlr->upbase = malloc((ctlr->nup+1)*sizeof(Pd)); + ctlr->upr = (Pd*)ROUNDUP((ulong)ctlr->upbase, 8); + + prev = ctlr->upr; + for(pd = &ctlr->upr[ctlr->nup-1]; pd >= ctlr->upr; pd--){ + pd->np = PADDR(&prev->np); + pd->control = 0; + bp = iallocb(sizeof(Etherpkt)); + if(bp == nil) + panic("can't allocate ethernet receive ring"); + pd->addr = PADDR(bp->rp); + pd->len = updnLastFrag|sizeof(Etherpkt); + + pd->next = prev; + prev = pd; + pd->bp = bp; + } + ctlr->uphead = ctlr->upr; + + ctlr->dnbase = malloc((ctlr->ndn+1)*sizeof(Pd)); + ctlr->dnr = (Pd*)ROUNDUP((ulong)ctlr->dnbase, 8); + + prev = ctlr->dnr; + for(pd = &ctlr->dnr[ctlr->ndn-1]; pd >= ctlr->dnr; pd--){ + pd->next = prev; + prev = pd; + } + ctlr->dnhead = ctlr->dnr; + ctlr->dntail = ctlr->dnr; + ctlr->dnq = 0; +} + +static Block* +rbpalloc(Block* (*f)(int)) +{ + Block *bp; + ulong addr; + + /* + * The receive buffers must be on a 32-byte + * boundary for EISA busmastering. + */ + if(bp = f(ROUNDUP(sizeof(Etherpkt), 4) + 31)){ + addr = (ulong)bp->base; + addr = ROUNDUP(addr, 32); + bp->rp = (uchar*)addr; + } + + return bp; +} + +static uchar* +startdma(Ether* ether, ulong address) +{ + int port, status, w; + uchar *wp; + + port = ether->port; + + w = (STATUS(port)>>13) & 0x07; + COMMAND(port, SelectRegisterWindow, Wmaster); + + wp = KADDR(inl(port+MasterAddress)); + status = ins(port+MasterStatus); + if(status & (masterInProgress|targetAbort|masterAbort)) + print("#l%d: BM status 0x%uX\n", ether->ctlrno, status); + outs(port+MasterStatus, masterMask); + outl(port+MasterAddress, address); + outs(port+MasterLen, sizeof(Etherpkt)); + COMMAND(port, StartDma, Upload); + + COMMAND(port, SelectRegisterWindow, w); + return wp; +} + +static void +promiscuous(void* arg, int on) +{ + int filter, port; + Ether *ether; + + ether = (Ether*)arg; + port = ether->port; + + filter = receiveBroadcast|receiveIndividual; + if(ether->nmaddr) + filter |= receiveMulticast; + if(on) + filter |= receiveAllFrames; + COMMAND(port, SetRxFilter, filter); +} + +static void +multicast(void* arg, uchar *addr, int on) +{ + int filter, port; + Ether *ether; + + USED(addr, on); + + ether = (Ether*)arg; + port = ether->port; + + filter = receiveBroadcast|receiveIndividual; + if(ether->nmaddr) + filter |= receiveMulticast; + if(ether->prom) + filter |= receiveAllFrames; + COMMAND(port, SetRxFilter, filter); +} + +/* On the 575B and C, interrupts need to be acknowledged in CardBus memory space */ +static void +intrackcb(ulong *cbfn) +{ + cbfn[1] = 0x8000; +} + +static void +attach(Ether* ether) +{ + int port, x; + Ctlr *ctlr; + + ctlr = ether->ctlr; + ilock(&ctlr->wlock); + if(ctlr->attached){ + iunlock(&ctlr->wlock); + return; + } + + port = ether->port; + + /* + * Set the receiver packet filter for this and broadcast addresses, + * set the interrupt masks for all interrupts, enable the receiver + * and transmitter. + */ + promiscuous(ether, ether->prom); + + x = interruptMask; + if(ctlr->busmaster == 1) + x &= ~(rxEarly|rxComplete); + else{ + if(ctlr->dnenabled) + x &= ~transferInt; + if(ctlr->upenabled) + x &= ~(rxEarly|rxComplete); + } + COMMAND(port, SetIndicationEnable, x); + COMMAND(port, SetInterruptEnable, x); + COMMAND(port, RxEnable, 0); + COMMAND(port, TxEnable, 0); + + /* + * If this is a CardBus card, acknowledge any interrupts. + */ + if(ctlr->cbfn != nil) + intrackcb(ctlr->cbfn); + + /* + * Prime the busmaster channel for receiving directly into a + * receive packet buffer if necessary. + */ + if(ctlr->busmaster == 1) + startdma(ether, PADDR(ctlr->rbp->rp)); + else{ + if(ctlr->upenabled) + outl(port+UpListPtr, PADDR(&ctlr->uphead->np)); + } + + ctlr->attached = 1; + iunlock(&ctlr->wlock); +} + +static void +statistics(Ether* ether) +{ + int port, i, u, w; + Ctlr *ctlr; + + port = ether->port; + ctlr = ether->ctlr; + + /* + * 3C59[27] require a read between a PIO write and + * reading a statistics register. + */ + w = (STATUS(port)>>13) & 0x07; + COMMAND(port, SelectRegisterWindow, Wstatistics); + STATUS(port); + + for(i = 0; i < UpperFramesOk; i++) + ctlr->stats[i] += inb(port+i) & 0xFF; + u = inb(port+UpperFramesOk) & 0xFF; + ctlr->stats[FramesXmittedOk] += (u & 0x30)<<4; + ctlr->stats[FramesRcvdOk] += (u & 0x03)<<8; + ctlr->stats[BytesRcvdOk] += ins(port+BytesRcvdOk) & 0xFFFF; + ctlr->stats[BytesRcvdOk+1] += ins(port+BytesXmittedOk) & 0xFFFF; + + switch(ctlr->xcvr){ + + case xcvrMii: + case xcvr100BaseTX: + case xcvr100BaseFX: + COMMAND(port, SelectRegisterWindow, Wdiagnostic); + STATUS(port); + ctlr->stats[BytesRcvdOk+2] += inb(port+BadSSD); + break; + } + + COMMAND(port, SelectRegisterWindow, w); +} + +static void +txstart(Ether* ether) +{ + int port, len; + Ctlr *ctlr; + Block *bp; + + port = ether->port; + ctlr = ether->ctlr; + + /* + * Attempt to top-up the transmit FIFO. If there's room simply + * stuff in the packet length (unpadded to a dword boundary), the + * packet data (padded) and remove the packet from the queue. + * If there's no room post an interrupt for when there is. + * This routine is called both from the top level and from interrupt + * level and expects to be called with ctlr->wlock already locked + * and the correct register window (Wop) in place. + */ + for(;;){ + if(ctlr->txbp){ + bp = ctlr->txbp; + ctlr->txbp = 0; + } + else{ + bp = qget(ether->oq); + if(bp == nil) + break; + } + + len = ROUNDUP(BLEN(bp), 4); + if(len+4 <= ins(port+TxFree)){ + outl(port+Fifo, BLEN(bp)); + outsl(port+Fifo, bp->rp, len/4); + + freeb(bp); + + ether->outpackets++; + } + else{ + ctlr->txbp = bp; + if(ctlr->txbusy == 0){ + ctlr->txbusy = 1; + COMMAND(port, SetTxAvailableThresh, len>>ctlr->ts); + } + break; + } + } +} + +static void +txstart905(Ether* ether) +{ + Ctlr *ctlr; + int port, stalled, timeo; + Block *bp; + Pd *pd; + + ctlr = ether->ctlr; + port = ether->port; + + /* + * Free any completed packets. + */ + pd = ctlr->dntail; + while(ctlr->dnq){ + if(PADDR(&pd->np) == inl(port+DnListPtr)) + break; + if(pd->bp){ + freeb(pd->bp); + pd->bp = nil; + } + ctlr->dnq--; + pd = pd->next; + } + ctlr->dntail = pd; + + stalled = 0; + while(ctlr->dnq < (ctlr->ndn-1)){ + bp = qget(ether->oq); + if(bp == nil) + break; + + pd = ctlr->dnhead->next; + pd->np = 0; + pd->control = dnIndicate|BLEN(bp); + pd->addr = PADDR(bp->rp); + pd->len = updnLastFrag|BLEN(bp); + pd->bp = bp; + + if(stalled == 0 && ctlr->dnq && inl(port+DnListPtr)){ + COMMAND(port, Stall, dnStall); + for(timeo = 100; (STATUS(port) & commandInProgress) && timeo; timeo--) + ; + if(timeo == 0) + print("#l%d: dnstall %d\n", ether->ctlrno, timeo); + stalled = 1; + } + + coherence(); + ctlr->dnhead->np = PADDR(&pd->np); + ctlr->dnhead->control &= ~dnIndicate; + ctlr->dnhead = pd; + if(ctlr->dnq == 0) + ctlr->dntail = pd; + ctlr->dnq++; + + ctlr->dnqueued++; + } + + if(ctlr->dnq > ctlr->dnqmax) + ctlr->dnqmax = ctlr->dnq; + + /* + * If the adapter is not currently processing anything + * and there is something on the queue, start it processing. + */ + if(inl(port+DnListPtr) == 0 && ctlr->dnq) + outl(port+DnListPtr, PADDR(&ctlr->dnhead->np)); + if(stalled) + COMMAND(port, Stall, dnUnStall); +} + +static void +transmit(Ether* ether) +{ + Ctlr *ctlr; + int port, w; + + port = ether->port; + ctlr = ether->ctlr; + + ilock(&ctlr->wlock); + if(ctlr->dnenabled) + txstart905(ether); + else{ + w = (STATUS(port)>>13) & 0x07; + COMMAND(port, SelectRegisterWindow, Wop); + txstart(ether); + COMMAND(port, SelectRegisterWindow, w); + } + iunlock(&ctlr->wlock); +} + +static void +receive905(Ether* ether) +{ + Ctlr *ctlr; + int len, port, q; + Pd *pd; + Block *bp; + + ctlr = ether->ctlr; + port = ether->port; + + if(inl(port+UpPktStatus) & upStalled) + ctlr->upstalls++; + q = 0; + for(pd = ctlr->uphead; pd->control & upPktComplete; pd = pd->next){ + if(pd->control & upError){ + if(pd->control & upOverrun) + ether->overflows++; + if(pd->control & (upOversizedFrame|upRuntFrame)) + ether->buffs++; + if(pd->control & upAlignmentError) + ether->frames++; + if(pd->control & upCRCError) + ether->crcs++; + } + else if(bp = iallocb(sizeof(Etherpkt)+4)){ + len = pd->control & rxBytes; + pd->bp->wp = pd->bp->rp+len; + etheriq(ether, pd->bp, 1); + pd->bp = bp; + pd->addr = PADDR(bp->rp); + coherence(); + } + + pd->control = 0; + COMMAND(port, Stall, upUnStall); + + q++; + } + ctlr->uphead = pd; + + ctlr->upqueued += q; + if(q > ctlr->upqmax) + ctlr->upqmax = q; +} + +static void +receive(Ether* ether) +{ + int len, port, rxerror, rxstatus; + Ctlr *ctlr; + Block *bp; + + port = ether->port; + ctlr = ether->ctlr; + + while(((rxstatus = ins(port+RxStatus)) & rxIncomplete) == 0){ + if(ctlr->busmaster == 1 && (STATUS(port) & busMasterInProgress)) + break; + + /* + * If there was an error, log it and continue. + * Unfortunately the 3C5[078]9 has the error info in the status register + * and the 3C59[0257] implement a separate RxError register. + */ + if(rxstatus & rxError){ + if(ctlr->rxstatus9){ + switch(rxstatus & rxError9){ + + case rxOverrun9: + ether->overflows++; + break; + + case oversizedFrame9: + case runtFrame9: + ether->buffs++; + break; + + case alignmentError9: + ether->frames++; + break; + + case crcError9: + ether->crcs++; + break; + + } + } + else{ + rxerror = inb(port+RxError); + if(rxerror & rxOverrun) + ether->overflows++; + if(rxerror & (oversizedFrame|runtFrame)) + ether->buffs++; + if(rxerror & alignmentError) + ether->frames++; + if(rxerror & crcError) + ether->crcs++; + } + } + + /* + * If there was an error or a new receive buffer can't be + * allocated, discard the packet and go on to the next. + */ + if((rxstatus & rxError) || (bp = rbpalloc(iallocb)) == 0){ + COMMAND(port, RxDiscard, 0); + while(STATUS(port) & commandInProgress) + ; + + if(ctlr->busmaster == 1) + startdma(ether, PADDR(ctlr->rbp->rp)); + + continue; + } + + /* + * A valid receive packet awaits: + * if using PIO, read it into the buffer; + * discard the packet from the FIFO; + * if using busmastering, start a new transfer for + * the next packet and as a side-effect get the + * end-pointer of the one just received; + * pass the packet on to whoever wants it. + */ + if(ctlr->busmaster == 0 || ctlr->busmaster == 2){ + len = (rxstatus & rxBytes9); + ctlr->rbp->wp = ctlr->rbp->rp + len; + insl(port+Fifo, ctlr->rbp->rp, HOWMANY(len, 4)); + } + + COMMAND(port, RxDiscard, 0); + while(STATUS(port) & commandInProgress) + ; + + if(ctlr->busmaster == 1) + ctlr->rbp->wp = startdma(ether, PADDR(bp->rp)); + + etheriq(ether, ctlr->rbp, 1); + ctlr->rbp = bp; + } +} + +static int +ejectable(int did) +{ + switch (did) { + case 0x5157: + return 1; + + default: + return 0; + } +} + +static void +interrupt(Ureg*, void* arg) +{ + Ether *ether; + int port, status, s, txstatus, w, x; + Ctlr *ctlr; + + ether = arg; + port = ether->port; + ctlr = ether->ctlr; + + ilock(&ctlr->wlock); + status = STATUS(port); + if(!(status & (interruptMask|interruptLatch))){ + ctlr->bogusinterrupts++; + iunlock(&ctlr->wlock); + return; + } + w = (status>>13) & 0x07; + COMMAND(port, SelectRegisterWindow, Wop); + + ctlr->interrupts++; + if(ctlr->busmaster == 2) + ctlr->timer[0] += inb(port+TIMER905) & 0xFF; + else + ctlr->timer[0] += inb(port+TIMER) & 0xFF; + + do{ + if(status & hostError){ + /* + * Adapter failure, try to find out why, reset if + * necessary. What happens if Tx is active and a reset + * occurs, need to retransmit? This probably isn't right. + */ + COMMAND(port, SelectRegisterWindow, Wdiagnostic); + x = ins(port+FifoDiagnostic); + COMMAND(port, SelectRegisterWindow, Wop); + + if (status == 0xFFFF && x == 0xFFFF && ejectable(ctlr->did)) { + print("#l%d: Card ejected?\n", ether->ctlrno); + iunlock(&ctlr->wlock); + return; + } + + print("#l%d: status 0x%uX, diag 0x%uX\n", + ether->ctlrno, status, x); + + if(x & txOverrun){ + if(ctlr->busmaster == 0) + COMMAND(port, TxReset, 0); + else + COMMAND(port, TxReset, (updnReset|dmaReset)); + COMMAND(port, TxEnable, 0); + } + + if(x & rxUnderrun){ + /* + * This shouldn't happen... + * Reset the receiver and restore the filter and RxEarly + * threshold before re-enabling. + * Need to restart any busmastering? + */ + COMMAND(port, SelectRegisterWindow, Wstate); + s = (port+RxFilter) & 0x000F; + COMMAND(port, SelectRegisterWindow, Wop); + COMMAND(port, RxReset, 0); + while(STATUS(port) & commandInProgress) + ; + COMMAND(port, SetRxFilter, s); + COMMAND(port, SetRxEarlyThresh, ctlr->rxearly>>ctlr->ts); + COMMAND(port, RxEnable, 0); + } + + status &= ~hostError; + } + + if(status & (transferInt|rxComplete)){ + receive(ether); + status &= ~(transferInt|rxComplete); + } + + if(status & (upComplete)){ + COMMAND(port, AcknowledgeInterrupt, upComplete); + receive905(ether); + status &= ~upComplete; + ctlr->upinterrupts++; + } + + if(status & txComplete){ + /* + * Pop the TxStatus stack, accumulating errors. + * Adjust the TX start threshold if there was an underrun. + * If there was a Jabber or Underrun error, reset + * the transmitter, taking care not to reset the dma logic + * as a busmaster receive may be in progress. + * For all conditions enable the transmitter. + */ + if(ctlr->busmaster == 2) + txstatus = port+TxStatus905; + else + txstatus = port+TxStatus; + s = 0; + do{ + if(x = inb(txstatus)) + outb(txstatus, 0); + s |= x; + }while(STATUS(port) & txComplete); + + if(s & txUnderrun){ + if(ctlr->dnenabled){ + while(inl(port+PktStatus) & dnInProg) + ; + } + COMMAND(port, SelectRegisterWindow, Wdiagnostic); + while(ins(port+MediaStatus) & txInProg) + ; + COMMAND(port, SelectRegisterWindow, Wop); + if(ctlr->txthreshold < ETHERMAXTU) + ctlr->txthreshold += ETHERMINTU; + } + + /* + * According to the manual, maxCollisions does not require + * a TxReset, merely a TxEnable. However, evidence points to + * it being necessary on the 3C905. The jury is still out. + * On busy or badly configured networks maxCollisions can + * happen frequently enough for messages to be annoying so + * keep quiet about them by popular request. + */ + if(s & (txJabber|txUnderrun|maxCollisions)){ + if(ctlr->busmaster == 0) + COMMAND(port, TxReset, 0); + else + COMMAND(port, TxReset, (updnReset|dmaReset)); + while(STATUS(port) & commandInProgress) + ; + COMMAND(port, SetTxStartThresh, ctlr->txthreshold>>ctlr->ts); + if(ctlr->busmaster == 2) + outl(port+TxFreeThresh, HOWMANY(ETHERMAXTU, 256)); + if(ctlr->dnenabled) + status |= dnComplete; + } + + if(s & ~(txStatusComplete|maxCollisions)) + print("#l%d: txstatus 0x%uX, threshold %d\n", + ether->ctlrno, s, ctlr->txthreshold); + COMMAND(port, TxEnable, 0); + ether->oerrs++; + status &= ~txComplete; + status |= txAvailable; + } + + if(status & txAvailable){ + COMMAND(port, AcknowledgeInterrupt, txAvailable); + ctlr->txbusy = 0; + txstart(ether); + status &= ~txAvailable; + } + + if(status & dnComplete){ + COMMAND(port, AcknowledgeInterrupt, dnComplete); + txstart905(ether); + status &= ~dnComplete; + ctlr->dninterrupts++; + } + + if(status & updateStats){ + statistics(ether); + status &= ~updateStats; + } + + /* + * Currently, this shouldn't happen. + */ + if(status & rxEarly){ + COMMAND(port, AcknowledgeInterrupt, rxEarly); + status &= ~rxEarly; + } + + /* + * Panic if there are any interrupts not dealt with. + */ + if(status & interruptMask) + panic("#l%d: interrupt mask 0x%uX\n", ether->ctlrno, status); + + COMMAND(port, AcknowledgeInterrupt, interruptLatch); + if(ctlr->cbfn != nil) + intrackcb(ctlr->cbfn); + + }while((status = STATUS(port)) & (interruptMask|interruptLatch)); + + if(ctlr->busmaster == 2) + ctlr->timer[1] += inb(port+TIMER905) & 0xFF; + else + ctlr->timer[1] += inb(port+TIMER) & 0xFF; + + COMMAND(port, SelectRegisterWindow, w); + iunlock(&ctlr->wlock); +} + +static long +ifstat(Ether* ether, void* a, long n, ulong offset) +{ + char *p; + int len; + Ctlr *ctlr; + + if(n == 0) + return 0; + + ctlr = ether->ctlr; + + ilock(&ctlr->wlock); + statistics(ether); + iunlock(&ctlr->wlock); + + p = malloc(READSTR); + len = snprint(p, READSTR, "interrupts: %lud\n", ctlr->interrupts); + len += snprint(p+len, READSTR-len, "bogusinterrupts: %lud\n", ctlr->bogusinterrupts); + len += snprint(p+len, READSTR-len, "timer: %lud %lud\n", + ctlr->timer[0], ctlr->timer[1]); + len += snprint(p+len, READSTR-len, "carrierlost: %lud\n", + ctlr->stats[CarrierLost]); + len += snprint(p+len, READSTR-len, "sqeerrors: %lud\n", + ctlr->stats[SqeErrors]); + len += snprint(p+len, READSTR-len, "multiplecolls: %lud\n", + ctlr->stats[MultipleColls]); + len += snprint(p+len, READSTR-len, "singlecollframes: %lud\n", + ctlr->stats[SingleCollFrames]); + len += snprint(p+len, READSTR-len, "latecollisions: %lud\n", + ctlr->stats[LateCollisions]); + len += snprint(p+len, READSTR-len, "rxoverruns: %lud\n", + ctlr->stats[RxOverruns]); + len += snprint(p+len, READSTR-len, "framesxmittedok: %lud\n", + ctlr->stats[FramesXmittedOk]); + len += snprint(p+len, READSTR-len, "framesrcvdok: %lud\n", + ctlr->stats[FramesRcvdOk]); + len += snprint(p+len, READSTR-len, "framesdeferred: %lud\n", + ctlr->stats[FramesDeferred]); + len += snprint(p+len, READSTR-len, "bytesrcvdok: %lud\n", + ctlr->stats[BytesRcvdOk]); + len += snprint(p+len, READSTR-len, "bytesxmittedok: %lud\n", + ctlr->stats[BytesRcvdOk+1]); + + if(ctlr->upenabled){ + if(ctlr->upqmax > ctlr->upqmaxhw) + ctlr->upqmaxhw = ctlr->upqmax; + len += snprint(p+len, READSTR-len, "up: q %lud i %lud m %d h %d s %lud\n", + ctlr->upqueued, ctlr->upinterrupts, + ctlr->upqmax, ctlr->upqmaxhw, ctlr->upstalls); + ctlr->upqmax = 0; + } + if(ctlr->dnenabled){ + if(ctlr->dnqmax > ctlr->dnqmaxhw) + ctlr->dnqmaxhw = ctlr->dnqmax; + len += snprint(p+len, READSTR-len, "dn: q %lud i %lud m %d h %d\n", + ctlr->dnqueued, ctlr->dninterrupts, ctlr->dnqmax, ctlr->dnqmaxhw); + ctlr->dnqmax = 0; + } + + snprint(p+len, READSTR-len, "badssd: %lud\n", ctlr->stats[BytesRcvdOk+2]); + + n = readstr(offset, a, n, p); + free(p); + + return n; +} + +static void +txrxreset(int port) +{ + COMMAND(port, TxReset, 0); + while(STATUS(port) & commandInProgress) + ; + COMMAND(port, RxReset, 0); + while(STATUS(port) & commandInProgress) + ; +} + +static Ctlr* +tcmadapter(int port, int irq, Pcidev* pcidev) +{ + Ctlr *ctlr; + + ctlr = malloc(sizeof(Ctlr)); + ctlr->port = port; + ctlr->irq = irq; + ctlr->pcidev = pcidev; + ctlr->eepromcmd = EepromReadRegister; + + if(ctlrhead != nil) + ctlrtail->next = ctlr; + else + ctlrhead = ctlr; + ctlrtail = ctlr; + + return ctlr; +} + +/* + * Write two 0 bytes to identify the IDport and then reset the + * ID sequence. Then send the ID sequence to the card to get + * the card into command state. + */ +static void +idseq(void) +{ + int i; + uchar al; + static int reset, untag; + + /* + * One time only: + * reset any adapters listening + */ + if(reset == 0){ + outb(IDport, 0); + outb(IDport, 0); + outb(IDport, 0xC0); + delay(20); + reset = 1; + } + + outb(IDport, 0); + outb(IDport, 0); + for(al = 0xFF, i = 0; i < 255; i++){ + outb(IDport, al); + if(al & 0x80){ + al <<= 1; + al ^= 0xCF; + } + else + al <<= 1; + } + + /* + * One time only: + * write ID sequence to get the attention of all adapters; + * untag all adapters. + * If a global reset is done here on all adapters it will confuse + * any ISA cards configured for EISA mode. + */ + if(untag == 0){ + outb(IDport, 0xD0); + untag = 1; + } +} + +static ulong +activate(void) +{ + int i; + ushort x, acr; + + /* + * Do the little configuration dance: + * + * 2. write the ID sequence to get to command state. + */ + idseq(); + + /* + * 3. Read the Manufacturer ID from the EEPROM. + * This is done by writing the IDPort with 0x87 (0x80 + * is the 'read EEPROM' command, 0x07 is the offset of + * the Manufacturer ID field in the EEPROM). + * The data comes back 1 bit at a time. + * A delay seems necessary between reading the bits. + * + * If the ID doesn't match, there are no more adapters. + */ + outb(IDport, 0x87); + delay(20); + for(x = 0, i = 0; i < 16; i++){ + delay(20); + x <<= 1; + x |= inb(IDport) & 0x01; + } + if(x != 0x6D50) + return 0; + + /* + * 3. Read the Address Configuration from the EEPROM. + * The Address Configuration field is at offset 0x08 in the EEPROM). + */ + outb(IDport, 0x88); + for(acr = 0, i = 0; i < 16; i++){ + delay(20); + acr <<= 1; + acr |= inb(IDport) & 0x01; + } + + return (acr & 0x1F)*0x10 + 0x200; +} + +static void +tcm509isa(void) +{ + int irq, port; + + /* + * Attempt to activate all adapters. If adapter is set for + * EISA mode (0x3F0), tag it and ignore. Otherwise, activate + * it fully. + */ + while(port = activate()){ + if(ioalloc(port, 0x10, 0, "tcm509isa") < 0){ + print("tcm509isa: port 0x%uX in use\n", port); + continue; + } + + /* + * 6. Tag the adapter so it won't respond in future. + */ + outb(IDport, 0xD1); + if(port == 0x3F0){ + iofree(port); + continue; + } + + /* + * 6. Activate the adapter by writing the Activate command + * (0xFF). + */ + outb(IDport, 0xFF); + delay(20); + + /* + * 8. Can now talk to the adapter's I/O base addresses. + * Use the I/O base address from the acr just read. + * + * Enable the adapter and clear out any lingering status + * and interrupts. + */ + while(STATUS(port) & commandInProgress) + ; + COMMAND(port, SelectRegisterWindow, Wsetup); + outs(port+ConfigControl, Ena); + + txrxreset(port); + COMMAND(port, AcknowledgeInterrupt, 0xFF); + + irq = (ins(port+ResourceConfig)>>12) & 0x0F; + tcmadapter(port, irq, nil); + } +} + +static void +tcm5XXeisa(void) +{ + ushort x; + int irq, port, slot; + + /* + * Check if this is an EISA machine. + * If not, nothing to do. + */ + if(strncmp((char*)KADDR(0xFFFD9), "EISA", 4)) + return; + + /* + * Continue through the EISA slots looking for a match on both + * 3COM as the manufacturer and 3C579-* or 3C59[27]-* as the product. + * If an adapter is found, select window 0, enable it and clear + * out any lingering status and interrupts. + */ + for(slot = 1; slot < MaxEISA; slot++){ + port = slot*0x1000; + if(ioalloc(port, 0x1000, 0, "tcm5XXeisa") < 0){ + print("tcm5XXeisa: port 0x%uX in use\n", port); + continue; + } + if(ins(port+0xC80+ManufacturerID) != 0x6D50){ + iofree(port); + continue; + } + x = ins(port+0xC80+ProductID); + if((x & 0xF0FF) != 0x9050 && (x & 0xFF00) != 0x5900){ + iofree(port); + continue; + } + + COMMAND(port, SelectRegisterWindow, Wsetup); + outs(port+ConfigControl, Ena); + + txrxreset(port); + COMMAND(port, AcknowledgeInterrupt, 0xFF); + + irq = (ins(port+ResourceConfig)>>12) & 0x0F; + tcmadapter(port, irq, nil); + } +} + +static void +tcm59Xpci(void) +{ + Pcidev *p; + Ctlr *ctlr; + int irq, port; + + p = nil; + while(p = pcimatch(p, 0x10B7, 0)){ + if(p->ccrb != 0x02 || p->ccru != 0) + continue; + /* + * Not prepared to deal with memory-mapped + * devices yet. + */ + if(!(p->mem[0].bar & 0x01)) + continue; + port = p->mem[0].bar & ~0x01; + if((port = ioalloc((port == 0)? -1: port, p->mem[0].size, + 0, "tcm59Xpci")) < 0){ + print("tcm59Xpci: port 0x%uX in use\n", port); + continue; + } + irq = p->intl; + + txrxreset(port); + COMMAND(port, AcknowledgeInterrupt, 0xFF); + + ctlr = tcmadapter(port, irq, p); + switch(p->did){ + default: + break; + case 0x5157: + ctlr->eepromcmd = EepromRead8bRegister; + ctlr->cbfnpa = upamalloc(p->mem[2].bar, p->mem[2].size, 0); + break; + case 0x6056: + ctlr->eepromcmd = EepromReadOffRegister; + ctlr->cbfnpa = upamalloc(p->mem[2].bar, p->mem[2].size, 0); + break; + } + if(ctlr->cbfnpa != 0) + ctlr->cbfn = KADDR(ctlr->cbfnpa); + pcisetbme(p); + } +} + +static char* tcmpcmcia[] = { + "3C589", /* 3COM 589[ABCD] */ + "3C562", /* 3COM 562 */ + "589E", /* 3COM Megahertz 589E */ + nil, +}; + +static Ctlr* +tcm5XXpcmcia(Ether* ether) +{ + int i; + Ctlr *ctlr; + + if(ether->type == nil) + return nil; + + for(i = 0; tcmpcmcia[i] != nil; i++){ + if(cistrcmp(ether->type, tcmpcmcia[i])) + continue; + ctlr = tcmadapter(ether->port, ether->irq, nil); + ctlr->active = 1; + return ctlr; + } + + return nil; +} + +static void +setxcvr(Ctlr* ctlr, int xcvr) +{ + int port, x; + + port = ctlr->port; + if(ctlr->rxstatus9){ + COMMAND(port, SelectRegisterWindow, Wsetup); + x = ins(port+AddressConfig) & ~xcvrMask9; + x |= (xcvr>>20)<<14; + outs(port+AddressConfig, x); + } + else{ + COMMAND(port, SelectRegisterWindow, Wfifo); + x = inl(port+InternalConfig) & ~xcvrMask; + x |= xcvr; + outl(port+InternalConfig, x); + } + + txrxreset(port); +} + +static void +setfullduplex(int port) +{ + int x; + + COMMAND(port, SelectRegisterWindow, Wfifo); + x = ins(port+MacControl); + outs(port+MacControl, fullDuplexEnable|x); + + txrxreset(port); +} + +static int +miimdi(int port, int n) +{ + int data, i; + + /* + * Read n bits from the MII Management Register. + */ + data = 0; + for(i = n-1; i >= 0; i--){ + if(ins(port) & mgmtData) + data |= (1<<i); + microdelay(1); + outs(port, mgmtClk); + microdelay(1); + outs(port, 0); + microdelay(1); + } + + return data; +} + +static void +miimdo(int port, int bits, int n) +{ + int i, mdo; + + /* + * Write n bits to the MII Management Register. + */ + for(i = n-1; i >= 0; i--){ + if(bits & (1<<i)) + mdo = mgmtDir|mgmtData; + else + mdo = mgmtDir; + outs(port, mdo); + microdelay(1); + outs(port, mdo|mgmtClk); + microdelay(1); + outs(port, mdo); + microdelay(1); + } +} + +static int +miir(int port, int phyad, int regad) +{ + int data, w; + + w = (STATUS(port)>>13) & 0x07; + COMMAND(port, SelectRegisterWindow, Wdiagnostic); + port += PhysicalMgmt; + + /* + * Preamble; + * ST+OP+PHYAD+REGAD; + * TA + 16 data bits. + */ + miimdo(port, 0xFFFFFFFF, 32); + miimdo(port, 0x1800|(phyad<<5)|regad, 14); + data = miimdi(port, 18); + + port -= PhysicalMgmt; + COMMAND(port, SelectRegisterWindow, w); + + if(data & 0x10000) + return -1; + + return data & 0xFFFF; +} + +static int +scanphy(int port) +{ + int i, x; + + for(i = 0; i < 32; i++){ + if((x = miir(port, i, 2)) == -1 || x == 0) + continue; + x <<= 6; + x |= miir(port, i, 3)>>10; + XCVRDEBUG("phy%d: oui %uX reg1 %uX\n", i, x, miir(port, i, 1)); + USED(x); + + return i; + } + return 24; +} + +static struct { + char *name; + int avail; + int xcvr; +} media[] = { + "10BaseT", base10TAvailable, xcvr10BaseT, + "10Base2", coaxAvailable, xcvr10Base2, + "100BaseTX", baseTXAvailable, xcvr100BaseTX, + "100BaseFX", baseFXAvailable, xcvr100BaseFX, + "aui", auiAvailable, xcvrAui, + "mii", miiConnector, xcvrMii +}; + +static int +autoselect(Ctlr* ctlr) +{ + int media, port, x; + + /* + * Pathetic attempt at automatic media selection. + * Really just to get the Fast Etherlink 10BASE-T/100BASE-TX + * cards operational. + * It's a bonus if it works for anything else. + */ + port = ctlr->port; + if(ctlr->rxstatus9){ + COMMAND(port, SelectRegisterWindow, Wsetup); + x = ins(port+ConfigControl); + media = 0; + if(x & base10TAvailable9) + media |= base10TAvailable; + if(x & coaxAvailable9) + media |= coaxAvailable; + if(x & auiAvailable9) + media |= auiAvailable; + } + else{ + COMMAND(port, SelectRegisterWindow, Wfifo); + media = ins(port+ResetOptions); + } + XCVRDEBUG("autoselect: media %uX\n", media); + + if(media & miiConnector) + return xcvrMii; + + COMMAND(port, SelectRegisterWindow, Wdiagnostic); + XCVRDEBUG("autoselect: media status %uX\n", ins(port+MediaStatus)); + + if(media & baseTXAvailable){ + /* + * Must have InternalConfig register. + */ + setxcvr(ctlr, xcvr100BaseTX); + + COMMAND(port, SelectRegisterWindow, Wdiagnostic); + x = ins(port+MediaStatus) & ~(dcConverterEnabled|jabberGuardEnable); + outs(port+MediaStatus, linkBeatEnable|x); + delay(10); + + if(ins(port+MediaStatus) & linkBeatDetect) + return xcvr100BaseTX; + outs(port+MediaStatus, x); + } + + if(media & base10TAvailable){ + setxcvr(ctlr, xcvr10BaseT); + + COMMAND(port, SelectRegisterWindow, Wdiagnostic); + x = ins(port+MediaStatus) & ~dcConverterEnabled; + outs(port+MediaStatus, linkBeatEnable|jabberGuardEnable|x); + delay(100); + + XCVRDEBUG("autoselect: 10BaseT media status %uX\n", ins(port+MediaStatus)); + if(ins(port+MediaStatus) & linkBeatDetect) + return xcvr10BaseT; + outs(port+MediaStatus, x); + } + + /* + * Botch. + */ + return autoSelect; +} + +static int +eepromdata(Ctlr* ctlr, int offset) +{ + int port; + + port = ctlr->port; + + COMMAND(port, SelectRegisterWindow, Wsetup); + while(EEPROMBUSY(port)) + ; + EEPROMCMD(port, ctlr->eepromcmd, offset); + while(EEPROMBUSY(port)) + ; + return EEPROMDATA(port); +} + +static void +resetctlr(Ctlr *ctlr) +{ + int x, port = ctlr->port; + + txrxreset(port); + x = ins(port+ResetOp905B); + XCVRDEBUG("905[BC] reset ops 0x%uX\n", x); + x &= ~0x4010; + if(ctlr->did == 0x5157){ + x |= 0x0010; /* Invert LED */ + outs(port+ResetOp905B, x); + } + if(ctlr->did == 0x6056){ + x |= 0x4000; + outs(port+ResetOp905B, x); + + COMMAND(port, SelectRegisterWindow, Wsetup); + outs(port, 0x0800); + } +} + +static void +shutdown(Ether *ether) +{ +print("etherelnk3 shutting down\n"); + resetctlr(ether->ctlr); +} + +int +etherelnk3reset(Ether* ether) +{ + char *p; + Ctlr *ctlr; + uchar ea[Eaddrlen]; + static int scandone; + int anar, anlpar, i, j, phyaddr, phystat, port, timeo, x; + + /* + * Scan for adapter on PCI, EISA and finally + * using the little ISA configuration dance. + */ + if(scandone == 0){ + tcm59Xpci(); + tcm5XXeisa(); + tcm509isa(); + scandone = 1; + } + + /* + * Any adapter matches if no ether->port is supplied, + * otherwise the ports must match. + */ + for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){ + if(ctlr->active) + continue; + if(ether->port == 0 || ether->port == ctlr->port){ + ctlr->active = 1; + break; + } + } + if(ctlr == nil && (ctlr = tcm5XXpcmcia(ether)) == 0) + return -1; + + ether->ctlr = ctlr; + port = ctlr->port; + ether->port = port; + ether->irq = ctlr->irq; + if(ctlr->pcidev != nil) + ether->tbdf = ctlr->pcidev->tbdf; + else + ether->tbdf = BUSUNKNOWN; + + /* + * Read the DeviceID from the EEPROM, it's at offset 0x03, + * and do something depending on capabilities. + */ + switch(ctlr->did = eepromdata(ctlr, 0x03)){ + case 0x5157: /* 3C575 Cyclone */ + case 0x6056: + /*FALLTHROUGH*/ + case 0x4500: /* 3C450 HomePNA Tornado */ + case 0x7646: /* 3CSOHO100-TX */ + case 0x9055: /* 3C905B-TX */ + case 0x9200: /* 3C905C-TX */ + case 0x9201: /* 3C920 */ + /*FALLTHROUGH*/ + case 0x9000: /* 3C900-TPO */ + case 0x9001: /* 3C900-COMBO */ + case 0x9005: /* 3C900B-COMBO */ + case 0x9050: /* 3C905-TX */ + case 0x9051: /* 3C905-T4 */ + if(BUSTYPE(ether->tbdf) != BusPCI) + goto buggery; + ctlr->busmaster = 2; + goto vortex; + case 0x5900: /* 3C590-[TP|COMBO|TPO] */ + case 0x5920: /* 3C592-[TP|COMBO|TPO] */ + case 0x5950: /* 3C595-TX */ + case 0x5951: /* 3C595-T4 */ + case 0x5952: /* 3C595-MII */ + case 0x5970: /* 3C597-TX */ + case 0x5971: /* 3C597-T4 */ + case 0x5972: /* 3C597-MII */ + ctlr->busmaster = 1; + vortex: + COMMAND(port, SelectRegisterWindow, Wfifo); + ctlr->xcvr = inl(port+InternalConfig) & (autoSelect|xcvrMask); + ctlr->rxearly = 8188; + ctlr->rxstatus9 = 0; + break; + buggery: + default: + ctlr->busmaster = 0; + COMMAND(port, SelectRegisterWindow, Wsetup); + x = ins(port+AddressConfig); + ctlr->xcvr = ((x & xcvrMask9)>>14)<<20; + if(x & autoSelect9) + ctlr->xcvr |= autoSelect; + ctlr->rxearly = 2044; + ctlr->rxstatus9 = 1; + break; + } + if(ctlr->rxearly >= 2048) + ctlr->ts = 2; + + /* + * Check if the adapter's station address is to be overridden. + * If not, read it from the EEPROM and set in ether->ea prior to + * loading the station address in Wstation. + * The EEPROM returns 16-bits at a time. + */ + memset(ea, 0, Eaddrlen); + if(memcmp(ea, ether->ea, Eaddrlen) == 0){ + for(i = 0; i < Eaddrlen/2; i++){ + x = eepromdata(ctlr, i); + ether->ea[2*i] = x>>8; + ether->ea[2*i+1] = x; + } + } + + COMMAND(port, SelectRegisterWindow, Wstation); + for(i = 0; i < Eaddrlen; i++) + outb(port+i, ether->ea[i]); + + /* + * Enable the transceiver if necessary and determine whether + * busmastering can be used. Due to bugs in the first revision + * of the 3C59[05], don't use busmastering at 10Mbps. + */ + XCVRDEBUG("reset: xcvr %uX\n", ctlr->xcvr); + + /* + * Allow user to specify desired media in plan9.ini + */ + for(i = 0; i < ether->nopt; i++){ + if(cistrncmp(ether->opt[i], "media=", 6) != 0) + continue; + p = ether->opt[i]+6; + for(j = 0; j < nelem(media); j++) + if(cistrcmp(p, media[j].name) == 0) + ctlr->xcvr = media[j].xcvr; + } + + /* + * forgive me, but i am weak + */ + switch(ctlr->did){ + default: + if(ctlr->xcvr & autoSelect) + ctlr->xcvr = autoselect(ctlr); + break; + case 0x5157: + case 0x6056: + case 0x4500: + case 0x7646: + case 0x9055: + case 0x9200: + case 0x9201: + ctlr->xcvr = xcvrMii; + resetctlr(ctlr); + break; + } + XCVRDEBUG("xcvr selected: %uX, did 0x%uX\n", ctlr->xcvr, ctlr->did); + + switch(ctlr->xcvr){ + case xcvrMii: + /* + * Quick hack. + */ + if(ctlr->did == 0x5157) + phyaddr = 0; + else if(ctlr->did == 0x6056) + phyaddr = scanphy(port); + else + phyaddr = 24; + for(i = 0; i < 7; i++) + XCVRDEBUG(" %2.2uX", miir(port, phyaddr, i)); + XCVRDEBUG("\n"); + + for(timeo = 0; timeo < 30; timeo++){ + phystat = miir(port, phyaddr, 0x01); + if(phystat & 0x20) + break; + XCVRDEBUG(" %2.2uX", phystat); + delay(100); + } + XCVRDEBUG(" %2.2uX", miir(port, phyaddr, 0x01)); + XCVRDEBUG("\n"); + + anar = miir(port, phyaddr, 0x04); + anlpar = miir(port, phyaddr, 0x05) & 0x03E0; + anar &= anlpar; + miir(port, phyaddr, 0x00); + XCVRDEBUG("mii an: %uX anlp: %uX r0:%uX r1:%uX\n", + anar, anlpar, miir(port, phyaddr, 0x00), + miir(port, phyaddr, 0x01)); + for(i = 0; i < ether->nopt; i++){ + if(cistrcmp(ether->opt[i], "fullduplex") == 0) + anar |= 0x0100; + else if(cistrcmp(ether->opt[i], "100BASE-TXFD") == 0) + anar |= 0x0100; + else if(cistrcmp(ether->opt[i], "force100") == 0) + anar |= 0x0080; + } + XCVRDEBUG("mii anar: %uX\n", anar); + if(anar & 0x0100){ /* 100BASE-TXFD */ + ether->mbps = 100; + setfullduplex(port); + } + else if(anar & 0x0200){ /* 100BASE-T4 */ + /* nothing to do */ + } + else if(anar & 0x0080) /* 100BASE-TX */ + ether->mbps = 100; + else if(anar & 0x0040) /* 10BASE-TFD */ + setfullduplex(port); + else{ /* 10BASE-T */ + /* nothing to do */ + } + break; + case xcvr100BaseTX: + case xcvr100BaseFX: + COMMAND(port, SelectRegisterWindow, Wfifo); + x = inl(port+InternalConfig) & ~ramPartitionMask; + outl(port+InternalConfig, x|ramPartition1to1); + + COMMAND(port, SelectRegisterWindow, Wdiagnostic); + x = ins(port+MediaStatus) & ~(dcConverterEnabled|jabberGuardEnable); + x |= linkBeatEnable; + outs(port+MediaStatus, x); + + if(x & dataRate100) + ether->mbps = 100; + break; + case xcvr10BaseT: + /* + * Enable Link Beat and Jabber to start the + * transceiver. + */ + COMMAND(port, SelectRegisterWindow, Wdiagnostic); + x = ins(port+MediaStatus) & ~dcConverterEnabled; + x |= linkBeatEnable|jabberGuardEnable; + outs(port+MediaStatus, x); + + if((ctlr->did & 0xFF00) == 0x5900) + ctlr->busmaster = 0; + break; + case xcvr10Base2: + COMMAND(port, SelectRegisterWindow, Wdiagnostic); + x = ins(port+MediaStatus) & ~(linkBeatEnable|jabberGuardEnable); + outs(port+MediaStatus, x); + + /* + * Start the DC-DC converter. + * Wait > 800 microseconds. + */ + COMMAND(port, EnableDcConverter, 0); + delay(1); + break; + } + + /* + * Wop is the normal operating register set. + * The 3C59[0257] adapters allow access to more than one register window + * at a time, but there are situations where switching still needs to be + * done, so just do it. + * Clear out any lingering Tx status. + */ + COMMAND(port, SelectRegisterWindow, Wop); + if(ctlr->busmaster == 2) + x = port+TxStatus905; + else + x = port+TxStatus; + while(inb(x)) + outb(x, 0); + + /* + * Clear out the + * adapter statistics, clear the statistics logged into ctlr + * and enable statistics collection. + */ + ilock(&ctlr->wlock); + statistics(ether); + memset(ctlr->stats, 0, sizeof(ctlr->stats)); + + COMMAND(port, StatisticsEnable, 0); + + /* + * Allocate any receive buffers. + */ + switch(ctlr->busmaster){ + case 2: + ctlr->dnenabled = 1; + + /* + * 10MUpldBug. + * Disabling is too severe, can use receive busmastering at + * 100Mbps OK, but how to tell which rate is actually being used - + * the 3c905 always seems to have dataRate100 set? + * Believe the bug doesn't apply if upRxEarlyEnable is set + * and the threshold is set such that uploads won't start + * until the whole packet has been received. + */ + ctlr->upenabled = 1; + x = eepromdata(ctlr, 0x0F); + if(!(x & 0x01)) + outl(port+PktStatus, upRxEarlyEnable); + + if(ctlr->upenabled || ctlr->dnenabled){ + ctlr->nup = Nup; + ctlr->ndn = Ndn; + init905(ctlr); + } + else { + ctlr->rbp = rbpalloc(iallocb); + if(ctlr->rbp == nil) + panic("can't reset ethernet: out of memory"); + } + outl(port+TxFreeThresh, HOWMANY(ETHERMAXTU, 256)); + break; + default: + ctlr->rbp = rbpalloc(iallocb); + if(ctlr->rbp == nil) + panic("can't reset ethernet: out of memory"); + break; + } + + /* + * Set a base TxStartThresh which will be incremented + * if any txUnderrun errors occur and ensure no RxEarly + * interrupts happen. + */ + ctlr->txthreshold = ETHERMAXTU/2; + COMMAND(port, SetTxStartThresh, ctlr->txthreshold>>ctlr->ts); + COMMAND(port, SetRxEarlyThresh, ctlr->rxearly>>ctlr->ts); + + iunlock(&ctlr->wlock); + + /* + * Linkage to the generic ethernet driver. + */ + ether->attach = attach; + ether->transmit = transmit; + ether->interrupt = interrupt; + ether->ifstat = ifstat; + + ether->promiscuous = promiscuous; + ether->multicast = multicast; + ether->shutdown = shutdown; + ether->arg = ether; + + return 0; +} + +void +etherelnk3link(void) +{ + addethercard("elnk3", etherelnk3reset); + addethercard("3C509", etherelnk3reset); + addethercard("3C575", etherelnk3reset); +} diff --git a/os/pc/etherga620.c b/os/pc/etherga620.c new file mode 100644 index 00000000..45e34faf --- /dev/null +++ b/os/pc/etherga620.c @@ -0,0 +1,1224 @@ +/* + * Netgear GA620 Gigabit Ethernet Card. + * Specific for the Alteon Tigon 2 and Intel Pentium or later. + * To Do: + * cache alignment for PCI Write-and-Invalidate + * mini ring (what size)? + * tune coalescing values + * statistics formatting + * don't update Spi if nothing to send + * receive ring alignment + * watchdog for link management? + */ +#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/netif.h" + +#define malign(n) xspanalloc((n), 32, 0) + +#include "etherif.h" +#include "etherga620fw.h" + +enum { + Mhc = 0x0040, /* Miscellaneous Host Control */ + Mlc = 0x0044, /* Miscellaneous Local Control */ + Mc = 0x0050, /* Miscellaneous Configuration */ + Ps = 0x005C, /* PCI State */ + Wba = 0x0068, /* Window Base Address */ + Wd = 0x006C, /* Window Data */ + + DMAas = 0x011C, /* DMA Assist State */ + + CPUAstate = 0x0140, /* CPU A State */ + CPUApc = 0x0144, /* CPU A Programme Counter */ + + CPUBstate = 0x0240, /* CPU B State */ + + Hi = 0x0504, /* Host In Interrupt Handler */ + Cpi = 0x050C, /* Command Producer Index */ + Spi = 0x0514, /* Send Producer Index */ + Rspi = 0x051C, /* Receive Standard Producer Index */ + Rjpi = 0x0524, /* Receive Jumbo Producer Index */ + Rmpi = 0x052C, /* Receive Mini Producer Index */ + + Mac = 0x0600, /* MAC Address */ + Gip = 0x0608, /* General Information Pointer */ + Om = 0x0618, /* Operating Mode */ + DMArc = 0x061C, /* DMA Read Configuration */ + DMAwc = 0x0620, /* DMA Write Configuration */ + Tbr = 0x0624, /* Transmit Buffer Ratio */ + Eci = 0x0628, /* Event Consumer Index */ + Cci = 0x062C, /* Command Consumer Index */ + + Rct = 0x0630, /* Receive Coalesced Ticks */ + Sct = 0x0634, /* Send Coalesced Ticks */ + St = 0x0638, /* Stat Ticks */ + SmcBD = 0x063C, /* Send Max. Coalesced BDs */ + RmcBD = 0x0640, /* Receive Max. Coalesced BDs */ + Nt = 0x0644, /* NIC Tracing */ + Gln = 0x0648, /* Gigabit Link Negotiation */ + Fln = 0x064C, /* 10/100 Link Negotiation */ + Ifx = 0x065C, /* Interface Index */ + IfMTU = 0x0660, /* Interface MTU */ + Mi = 0x0664, /* Mask Interrupts */ + Gls = 0x0668, /* Gigabit Link State */ + Fls = 0x066C, /* 10/100 Link State */ + + Cr = 0x0700, /* Command Ring */ + + Lmw = 0x0800, /* Local Memory Window */ +}; + +enum { /* Mhc */ + Is = 0x00000001, /* Interrupt State */ + Ci = 0x00000002, /* Clear Interrupt */ + Hr = 0x00000008, /* Hard Reset */ + Eebs = 0x00000010, /* Enable Endian Byte Swap */ + Eews = 0x00000020, /* Enable Endian Word (64-bit) swap */ + Mpio = 0x00000040, /* Mask PCI Interrupt Output */ +}; + +enum { /* Mlc */ + SRAM512 = 0x00000200, /* SRAM Bank Size of 512KB */ + SRAMmask = 0x00000300, + EEclk = 0x00100000, /* Serial EEPROM Clock Output */ + EEdoe = 0x00200000, /* Serial EEPROM Data Out Enable */ + EEdo = 0x00400000, /* Serial EEPROM Data Out Value */ + EEdi = 0x00800000, /* Serial EEPROM Data Input */ +}; + +enum { /* Mc */ + SyncSRAM = 0x00100000, /* Set Synchronous SRAM Timing */ +}; + +enum { /* Ps */ + PCIwm32 = 0x000000C0, /* Write Max DMA 32 */ + PCImrm = 0x00020000, /* Use Memory Read Multiple Command */ + PCI66 = 0x00080000, + PCI32 = 0x00100000, + PCIrcmd = 0x06000000, /* PCI Read Command */ + PCIwcmd = 0x70000000, /* PCI Write Command */ +}; + +enum { /* CPUAstate */ + CPUrf = 0x00000010, /* ROM Fail */ + CPUhalt = 0x00010000, /* Halt the internal CPU */ + CPUhie = 0x00040000, /* HALT instruction executed */ +}; + +enum { /* Om */ + BswapBD = 0x00000002, /* Byte Swap Buffer Descriptors */ + WswapBD = 0x00000004, /* Word Swap Buffer Descriptors */ + Warn = 0x00000008, + BswapDMA = 0x00000010, /* Byte Swap DMA Data */ + Only1DMA = 0x00000040, /* Only One DMA Active at a time */ + NoJFrag = 0x00000200, /* Don't Fragment Jumbo Frames */ + Fatal = 0x40000000, +}; + +enum { /* Lmw */ + Lmwsz = 2*1024, /* Local Memory Window Size */ + + Sr = 0x3800, /* Send Ring (accessed via Lmw) */ +}; + +enum { /* Link */ + Lpref = 0x00008000, /* Preferred Link */ + L10MB = 0x00010000, + L100MB = 0x00020000, + L1000MB = 0x00040000, + Lfd = 0x00080000, /* Full Duplex */ + Lhd = 0x00100000, /* Half Duplex */ + Lefc = 0x00200000, /* Emit Flow Control Packets */ + Lofc = 0x00800000, /* Obey Flow Control Packets */ + Lean = 0x20000000, /* Enable Autonegotiation/Sensing */ + Le = 0x40000000, /* Link Enable */ +}; + +typedef struct Host64 { + uint hi; + uint lo; +} Host64; + +typedef struct Ere { /* Event Ring Element */ + int event; /* (event<<24)|(code<<12)|index */ + int unused; +} Ere; + +typedef int Cmd; /* (cmd<<24)|(flags<<12)|index */ + +typedef struct Rbd { /* Receive Buffer Descriptor */ + Host64 addr; + int indexlen; /* (ring-index<<16)|buffer-length */ + int flags; /* only lower 16-bits */ + int checksum; /* (ip<<16)|tcp/udp */ + int error; /* only upper 16-bits */ + int reserved; + void* opaque; /* passed to receive return ring */ +} Rbd; + +typedef struct Sbd { /* Send Buffer Descriptor */ + Host64 addr; + int lenflags; /* (len<<16)|flags */ + int reserved; +} Sbd; + +enum { /* Buffer Descriptor Flags */ + Fend = 0x00000004, /* Frame Ends in this Buffer */ + Frjr = 0x00000010, /* Receive Jumbo Ring Buffer */ + Funicast = 0x00000020, /* Unicast packet (2-bit field) */ + Fmulticast = 0x00000040, /* Multicast packet */ + Fbroadcast = 0x00000060, /* Broadcast packet */ + Ferror = 0x00000400, /* Frame Has Error */ + Frmr = 0x00001000, /* Receive Mini Ring Buffer */ +}; + +enum { /* Buffer Error Flags */ + Ecrc = 0x00010000, /* bad CRC */ + Ecollision = 0x00020000, /* collision */ + Elink = 0x00040000, /* link lost */ + Ephy = 0x00080000, /* unspecified PHY frame decode error */ + Eodd = 0x00100000, /* odd number of nibbles */ + Emac = 0x00200000, /* unspecified MAC abort */ + Elen64 = 0x00400000, /* short packet */ + Eresources = 0x00800000, /* MAC out of internal resources */ + Egiant = 0x01000000, /* packet too big */ +}; + +typedef struct Rcb { /* Ring Control Block */ + Host64 addr; /* points to the Rbd ring */ + int control; /* (max_len<<16)|flags */ + int unused; +} Rcb; + +enum { + TcpUdpCksum = 0x0001, /* Perform TCP or UDP checksum */ + IpCksum = 0x0002, /* Perform IP checksum */ + NoPseudoHdrCksum= 0x0008, /* Don't include the pseudo header */ + VlanAssist = 0x0010, /* Enable VLAN tagging */ + CoalUpdateOnly = 0x0020, /* Coalesce transmit interrupts */ + HostRing = 0x0040, /* Sr in host memory */ + SnapCksum = 0x0080, /* Parse + offload 802.3 SNAP frames */ + UseExtRxBd = 0x0100, /* Extended Rbd for Jumbo frames */ + RingDisabled = 0x0200, /* Jumbo or Mini RCB only */ +}; + +typedef struct Gib { /* General Information Block */ + int statistics[256]; /* Statistics */ + Rcb ercb; /* Event Ring */ + Rcb crcb; /* Command Ring */ + Rcb srcb; /* Send Ring */ + Rcb rsrcb; /* Receive Standard Ring */ + Rcb rjrcb; /* Receive Jumbo Ring */ + Rcb rmrcb; /* Receive Mini Ring */ + Rcb rrrcb; /* Receive Return Ring */ + Host64 epp; /* Event Producer */ + Host64 rrrpp; /* Receive Return Ring Producer */ + Host64 scp; /* Send Consumer */ + Host64 rsp; /* Refresh Stats */ +} Gib; + +enum { /* Host/NIC Interface ring sizes */ + Ner = 256, /* event ring */ + Ncr = 64, /* command ring */ + Nsr = 512, /* send ring */ + Nrsr = 512, /* receive standard ring */ + Nrjr = 256, /* receive jumbo ring */ + Nrmr = 1024, /* receive mini ring */ + Nrrr = 2048, /* receive return ring */ +}; + +enum { + NrsrHI = 72, /* Fill-level of Rsr (m.b. < Nrsr) */ + NrsrLO = 54, /* Level at which to top-up ring */ + NrjrHI = 0, /* Fill-level of Rjr (m.b. < Nrjr) */ + NrjrLO = 0, /* Level at which to top-up ring */ + NrmrHI = 0, /* Fill-level of Rmr (m.b. < Nrmr) */ + NrmrLO = 0, /* Level at which to top-up ring */ +}; + +typedef struct Ctlr Ctlr; +typedef struct Ctlr { + int port; + Pcidev* pcidev; + Ctlr* next; + int active; + int id; + + uchar ea[Eaddrlen]; + + int* nic; + Gib* gib; + + Ere* er; + + Lock srlock; + Sbd* sr; + Block** srb; + int nsr; /* currently in send ring */ + + Rbd* rsr; + int nrsr; /* currently in Receive Standard Ring */ + Rbd* rjr; + int nrjr; /* currently in Receive Jumbo Ring */ + Rbd* rmr; + int nrmr; /* currently in Receive Mini Ring */ + Rbd* rrr; + int rrrci; /* Receive Return Ring Consumer Index */ + + int epi[2]; /* Event Producer Index */ + int rrrpi[2]; /* Receive Return Ring Producer Index */ + int sci[3]; /* Send Consumer Index ([2] is host) */ + + int interrupts; /* statistics */ + int mi; + uvlong ticks; + + int coalupdateonly; /* tuning */ + int hardwarecksum; + int rct; /* Receive Coalesce Ticks */ + int sct; /* Send Coalesce Ticks */ + int st; /* Stat Ticks */ + int smcbd; /* Send Max. Coalesced BDs */ + int rmcbd; /* Receive Max. Coalesced BDs */ +} Ctlr; + +static Ctlr* ctlrhead; +static Ctlr* ctlrtail; + +#define csr32r(c, r) (*((c)->nic+((r)/4))) +#define csr32w(c, r, v) (*((c)->nic+((r)/4)) = (v)) + +static void +sethost64(Host64* host64, void* addr) +{ + uvlong uvl; + + uvl = PCIWADDR(addr); + host64->hi = uvl>>32; + host64->lo = uvl & 0xFFFFFFFFL; +} + +static void +ga620command(Ctlr* ctlr, int cmd, int flags, int index) +{ + int cpi; + + cpi = csr32r(ctlr, Cpi); + csr32w(ctlr, Cr+(cpi*4), (cmd<<24)|(flags<<12)|index); + cpi = NEXT(cpi, Ncr); + csr32w(ctlr, Cpi, cpi); +} + +static void +ga620attach(Ether* edev) +{ + Ctlr *ctlr; + + ctlr = edev->ctlr; + USED(ctlr); +} + +static long +ga620ifstat(Ether* edev, void* a, long n, ulong offset) +{ + char *p; + Ctlr *ctlr; + int i, l, r; + + ctlr = edev->ctlr; + + if(n == 0) + return 0; + p = malloc(READSTR); + l = 0; + for(i = 0; i < 256; i++){ + if((r = ctlr->gib->statistics[i]) == 0) + continue; + l += snprint(p+l, READSTR-l, "%d: %ud\n", i, r); + } + + l += snprint(p+l, READSTR-l, "interrupts: %ud\n", ctlr->interrupts); + l += snprint(p+l, READSTR-l, "mi: %ud\n", ctlr->mi); + l += snprint(p+l, READSTR-l, "ticks: %llud\n", ctlr->ticks); + l += snprint(p+l, READSTR-l, "coalupdateonly: %d\n", ctlr->coalupdateonly); + l += snprint(p+l, READSTR-l, "hardwarecksum: %d\n", ctlr->hardwarecksum); + l += snprint(p+l, READSTR-l, "rct: %d\n", ctlr->rct); + l += snprint(p+l, READSTR-l, "sct: %d\n", ctlr->sct); + l += snprint(p+l, READSTR-l, "smcbd: %d\n", ctlr->smcbd); + snprint(p+l, READSTR-l, "rmcbd: %d\n", ctlr->rmcbd); + + n = readstr(offset, a, n, p); + free(p); + + return n; +} + +static long +ga620ctl(Ether* edev, void* buf, long n) +{ + char *p; + Cmdbuf *cb; + Ctlr *ctlr; + int control, i, r; + + ctlr = edev->ctlr; + if(ctlr == nil) + error(Enonexist); + r = 0; + cb = parsecmd(buf, n); + if(cb->nf < 2) + r = -1; + else if(cistrcmp(cb->f[0], "coalupdateonly") == 0){ + if(cistrcmp(cb->f[1], "off") == 0){ + control = ctlr->gib->srcb.control; + control &= ~CoalUpdateOnly; + ctlr->gib->srcb.control = control; + ctlr->coalupdateonly = 0; + } + else if(cistrcmp(cb->f[1], "on") == 0){ + control = ctlr->gib->srcb.control; + control |= CoalUpdateOnly; + ctlr->gib->srcb.control = control; + ctlr->coalupdateonly = 1; + } + else + r = -1; + } + else if(cistrcmp(cb->f[0], "hardwarecksum") == 0){ + if(cistrcmp(cb->f[1], "off") == 0){ + control = ctlr->gib->srcb.control; + control &= ~(TcpUdpCksum|NoPseudoHdrCksum); + ctlr->gib->srcb.control = control; + + control = ctlr->gib->rsrcb.control; + control &= ~(TcpUdpCksum|NoPseudoHdrCksum); + ctlr->gib->rsrcb.control = control; + + ctlr->hardwarecksum = 0; + } + else if(cistrcmp(cb->f[1], "on") == 0){ + control = ctlr->gib->srcb.control; + control |= (TcpUdpCksum|NoPseudoHdrCksum); + ctlr->gib->srcb.control = control; + + control = ctlr->gib->rsrcb.control; + control |= (TcpUdpCksum|NoPseudoHdrCksum); + ctlr->gib->rsrcb.control = control; + + ctlr->hardwarecksum = 1; + } + else + r = -1; + } + else if(cistrcmp(cb->f[0], "rct") == 0){ + i = strtol(cb->f[1], &p, 0); + if(i < 0 || p == cb->f[1]) + r = -1; + else{ + ctlr->rct = i; + csr32w(ctlr, Rct, ctlr->rct); + } + } + else if(cistrcmp(cb->f[0], "sct") == 0){ + i = strtol(cb->f[1], &p, 0); + if(i < 0 || p == cb->f[1]) + r = -1; + else{ + ctlr->sct = i; + csr32w(ctlr, Sct, ctlr->sct); + } + } + else if(cistrcmp(cb->f[0], "st") == 0){ + i = strtol(cb->f[1], &p, 0); + if(i < 0 || p == cb->f[1]) + r = -1; + else{ + ctlr->st = i; + csr32w(ctlr, St, ctlr->st); + } + } + else if(cistrcmp(cb->f[0], "smcbd") == 0){ + i = strtol(cb->f[1], &p, 0); + if(i < 0 || p == cb->f[1]) + r = -1; + else{ + ctlr->smcbd = i; + csr32w(ctlr, SmcBD, ctlr->smcbd); + } + } + else if(cistrcmp(cb->f[0], "rmcbd") == 0){ + i = strtol(cb->f[1], &p, 0); + if(i < 0 || p == cb->f[1]) + r = -1; + else{ + ctlr->rmcbd = i; + csr32w(ctlr, RmcBD, ctlr->rmcbd); + } + } + else + r = -1; + + free(cb); + if(r == 0) + return n; + return r; +} + +static int +_ga620transmit(Ether* edev) +{ + Sbd *sbd; + Block *bp; + Ctlr *ctlr; + int sci, spi, work; + + /* + * For now there are no smarts here, just empty the + * ring and try to fill it back up. Tuning comes later. + */ + ctlr = edev->ctlr; + ilock(&ctlr->srlock); + + /* + * Free any completed packets. + * Ctlr->sci[0] is where the NIC has got to consuming the ring. + * Ctlr->sci[2] is where the host has got to tidying up after the + * NIC has done with the packets. + */ + work = 0; + for(sci = ctlr->sci[2]; sci != ctlr->sci[0]; sci = NEXT(sci, Nsr)){ + if(ctlr->srb[sci] == nil) + continue; + freeb(ctlr->srb[sci]); + ctlr->srb[sci] = nil; + work++; + } + ctlr->sci[2] = sci; + + sci = PREV(sci, Nsr); + for(spi = csr32r(ctlr, Spi); spi != sci; spi = NEXT(spi, Nsr)){ + if((bp = qget(edev->oq)) == nil) + break; + + sbd = &ctlr->sr[spi]; + sethost64(&sbd->addr, bp->rp); + sbd->lenflags = (BLEN(bp)<<16)|Fend; + + ctlr->srb[spi] = bp; + work++; + } + csr32w(ctlr, Spi, spi); + + iunlock(&ctlr->srlock); + + return work; +} + +static void +ga620transmit(Ether* edev) +{ + _ga620transmit(edev); +} + +static void +ga620replenish(Ctlr* ctlr) +{ + Rbd *rbd; + int rspi; + Block *bp; + + rspi = csr32r(ctlr, Rspi); + while(ctlr->nrsr < NrsrHI){ + if((bp = iallocb(ETHERMAXTU+4)) == nil) + break; + rbd = &ctlr->rsr[rspi]; + sethost64(&rbd->addr, bp->rp); + rbd->indexlen = (rspi<<16)|(ETHERMAXTU+4); + rbd->flags = 0; + rbd->opaque = bp; + + rspi = NEXT(rspi, Nrsr); + ctlr->nrsr++; + } + csr32w(ctlr, Rspi, rspi); +} + +static void +ga620event(Ctlr* ctlr, int eci, int epi) +{ + int event; + + while(eci != epi){ + event = ctlr->er[eci].event; + switch(event>>24){ + case 0x01: /* firmware operational */ + ga620command(ctlr, 0x01, 0x01, 0x00); + ga620command(ctlr, 0x0B, 0x00, 0x00); +print("%8.8uX: %8.8uX\n", ctlr->port, event); + break; + case 0x04: /* statistics updated */ + break; + case 0x06: /* link state changed */ +print("%8.8uX: %8.8uX %8.8uX %8.8uX\n", + ctlr->port, event, csr32r(ctlr, Gls), csr32r(ctlr, Fls)); + break; + case 0x07: /* event error */ + default: + print("er[%d] = %8.8uX\n", eci, event); + break; + } + eci = NEXT(eci, Ner); + } + csr32w(ctlr, Eci, eci); +} + +static void +ga620receive(Ether* edev) +{ + int len; + Rbd *rbd; + Block *bp; + Ctlr* ctlr; + + ctlr = edev->ctlr; + while(ctlr->rrrci != ctlr->rrrpi[0]){ + rbd = &ctlr->rrr[ctlr->rrrci]; + /* + * Errors are collected in the statistics block so + * no need to tally them here, let ifstat do the work. + */ + len = rbd->indexlen & 0xFFFF; + if(!(rbd->flags & Ferror) && len != 0){ + bp = rbd->opaque; + bp->wp = bp->rp+len; + etheriq(edev, bp, 1); + } + else + freeb(rbd->opaque); + rbd->opaque = nil; + + if(rbd->flags & Frjr) + ctlr->nrjr--; + else if(rbd->flags & Frmr) + ctlr->nrmr--; + else + ctlr->nrsr--; + + ctlr->rrrci = NEXT(ctlr->rrrci, Nrrr); + } +} + +static void +ga620interrupt(Ureg*, void* arg) +{ + int csr, ie, work; + Ctlr *ctlr; + Ether *edev; + uvlong tsc0, tsc1; + + edev = arg; + ctlr = edev->ctlr; + + if(!(csr32r(ctlr, Mhc) & Is)) + return; + cycles(&tsc0); + + ctlr->interrupts++; + csr32w(ctlr, Hi, 1); + + ie = 0; + work = 0; + while(ie < 2){ + if(ctlr->rrrci != ctlr->rrrpi[0]){ + ga620receive(edev); + work = 1; + } + + if(_ga620transmit(edev) != 0) + work = 1; + + csr = csr32r(ctlr, Eci); + if(csr != ctlr->epi[0]){ + ga620event(ctlr, csr, ctlr->epi[0]); + work = 1; + } + + if(ctlr->nrsr <= NrsrLO) + ga620replenish(ctlr); + if(work == 0){ + if(ie == 0) + csr32w(ctlr, Hi, 0); + ie++; + } + work = 0; + } + + cycles(&tsc1); + ctlr->ticks += tsc1-tsc0; +} + +static void +ga620lmw(Ctlr* ctlr, int addr, int* data, int len) +{ + int i, l, lmw, v; + + /* + * Write to or clear ('data' == nil) 'len' bytes of the NIC + * local memory at address 'addr'. + * The destination address and count should be 32-bit aligned. + */ + v = 0; + while(len > 0){ + /* + * 1) Set the window. The (Lmwsz-1) bits are ignored + * in Wba when accessing through the local memory window; + * 2) Find the minimum of how many bytes still to + * transfer and how many left in this window; + * 3) Create the offset into the local memory window in the + * shared memory space then copy (or zero) the data; + * 4) Bump the counts. + */ + csr32w(ctlr, Wba, addr); + + l = ROUNDUP(addr+1, Lmwsz) - addr; + if(l > len) + l = len; + + lmw = Lmw + (addr & (Lmwsz-1)); + for(i = 0; i < l; i += 4){ + if(data != nil) + v = *data++; + csr32w(ctlr, lmw+i, v); + } + + len -= l; + addr += l; + } +} + +static int +ga620init(Ether* edev) +{ + Ctlr *ctlr; + Host64 host64; + int csr, ea, i, flags; + + ctlr = edev->ctlr; + + /* + * Load the MAC address. + */ + ea = (edev->ea[0]<<8)|edev->ea[1]; + csr32w(ctlr, Mac, ea); + ea = (edev->ea[2]<<24)|(edev->ea[3]<<16)|(edev->ea[4]<<8)|edev->ea[5]; + csr32w(ctlr, Mac+4, ea); + + /* + * General Information Block. + */ + ctlr->gib = malloc(sizeof(Gib)); + sethost64(&host64, ctlr->gib); + csr32w(ctlr, Gip, host64.hi); + csr32w(ctlr, Gip+4, host64.lo); + + /* + * Event Ring. + * This is located in host memory. Allocate the ring, + * tell the NIC where it is and initialise the indices. + */ + ctlr->er = malign(sizeof(Ere)*Ner); + sethost64(&ctlr->gib->ercb.addr, ctlr->er); + sethost64(&ctlr->gib->epp, ctlr->epi); + csr32w(ctlr, Eci, 0); + + /* + * Command Ring. + * This is located in the General Communications Region + * and so the value placed in the Rcb is unused, the NIC + * knows where it is. Stick in the value according to + * the datasheet anyway. + * Initialise the ring and indices. + */ + ctlr->gib->crcb.addr.lo = Cr-0x400; + for(i = 0; i < Ncr*4; i += 4) + csr32w(ctlr, Cr+i, 0); + csr32w(ctlr, Cpi, 0); + csr32w(ctlr, Cci, 0); + + /* + * Send Ring. + * This ring is either in NIC memory at a fixed location depending + * on how big the ring is or it is in host memory. If in NIC + * memory it is accessed via the Local Memory Window; with a send + * ring size of 128 the window covers the whole ring and then need + * only be set once: + * ctlr->sr = KADDR(ctlr->port+Lmw); + * ga620lmw(ctlr, Sr, nil, sizeof(Sbd)*Nsr); + * ctlr->gib->srcb.addr.lo = Sr; + * There is nowhere in the Sbd to hold the Block* associated + * with this entry so an external array must be kept. + */ + ctlr->sr = malign(sizeof(Sbd)*Nsr); + sethost64(&ctlr->gib->srcb.addr, ctlr->sr); + if(ctlr->hardwarecksum) + flags = TcpUdpCksum|NoPseudoHdrCksum|HostRing; + else + flags = HostRing; + if(ctlr->coalupdateonly) + flags |= CoalUpdateOnly; + ctlr->gib->srcb.control = (Nsr<<16)|flags; + sethost64(&ctlr->gib->scp, ctlr->sci); + csr32w(ctlr, Spi, 0); + ctlr->srb = malloc(sizeof(Block*)*Nsr); + + /* + * Receive Standard Ring. + */ + ctlr->rsr = malign(sizeof(Rbd)*Nrsr); + sethost64(&ctlr->gib->rsrcb.addr, ctlr->rsr); + if(ctlr->hardwarecksum) + flags = TcpUdpCksum|NoPseudoHdrCksum; + else + flags = 0; + ctlr->gib->rsrcb.control = ((ETHERMAXTU+4)<<16)|flags; + csr32w(ctlr, Rspi, 0); + + /* + * Jumbo and Mini Rings. Unused for now. + */ + ctlr->gib->rjrcb.control = RingDisabled; + ctlr->gib->rmrcb.control = RingDisabled; + + /* + * Receive Return Ring. + * This is located in host memory. Allocate the ring, + * tell the NIC where it is and initialise the indices. + */ + ctlr->rrr = malign(sizeof(Rbd)*Nrrr); + sethost64(&ctlr->gib->rrrcb.addr, ctlr->rrr); + ctlr->gib->rrrcb.control = (Nrrr<<16)|0; + sethost64(&ctlr->gib->rrrpp, ctlr->rrrpi); + ctlr->rrrci = 0; + + /* + * Refresh Stats Pointer. + * For now just point it at the existing statistics block. + */ + sethost64(&ctlr->gib->rsp, ctlr->gib->statistics); + + /* + * DMA configuration. + * Use the recommended values. + */ + csr32w(ctlr, DMArc, 0x80); + csr32w(ctlr, DMAwc, 0x80); + + /* + * Transmit Buffer Ratio. + * Set to 1/3 of available buffer space (units are 1/64ths) + * if using Jumbo packets, ~64KB otherwise (assume 1MB on NIC). + */ + if(NrjrHI > 0 || Nsr > 128) + csr32w(ctlr, Tbr, 64/3); + else + csr32w(ctlr, Tbr, 4); + + /* + * Tuneable parameters. + * These defaults are based on the tuning hints in the Alteon + * Host/NIC Software Interface Definition and example software. + */ + ctlr->rct = 1/*100*/; + csr32w(ctlr, Rct, ctlr->rct); + ctlr->sct = 0; + csr32w(ctlr, Sct, ctlr->sct); + ctlr->st = 1000000; + csr32w(ctlr, St, ctlr->st); + ctlr->smcbd = Nsr/4; + csr32w(ctlr, SmcBD, ctlr->smcbd); + ctlr->rmcbd = 4/*6*/; + csr32w(ctlr, RmcBD, ctlr->rmcbd); + + /* + * Enable DMA Assist Logic. + */ + csr = csr32r(ctlr, DMAas) & ~0x03; + csr32w(ctlr, DMAas, csr|0x01); + + /* + * Link negotiation. + * The bits are set here but the NIC must be given a command + * once it is running to set negotiation in motion. + */ + csr32w(ctlr, Gln, Le|Lean|Lofc|Lfd|L1000MB|Lpref); + csr32w(ctlr, Fln, Le|Lean|Lhd|Lfd|L100MB|L10MB); + + /* + * A unique index for this controller and the maximum packet + * length expected. + * For now only standard packets are expected. + */ + csr32w(ctlr, Ifx, 1); + csr32w(ctlr, IfMTU, ETHERMAXTU+4); + + /* + * Enable Interrupts. + * There are 3 ways to mask interrupts - a bit in the Mhc (which + * is already cleared), the Mi register and the Hi mailbox. + * Writing to the Hi mailbox has the side-effect of clearing the + * PCI interrupt. + */ + csr32w(ctlr, Mi, 0); + csr32w(ctlr, Hi, 0); + + /* + * Start the firmware. + */ + csr32w(ctlr, CPUApc, tigon2FwStartAddr); + csr = csr32r(ctlr, CPUAstate) & ~CPUhalt; + csr32w(ctlr, CPUAstate, csr); + + return 0; +} + +static int +at24c32io(Ctlr* ctlr, char* op, int data) +{ + char *lp, *p; + int i, loop, mlc, r; + + mlc = csr32r(ctlr, Mlc); + + r = 0; + loop = -1; + lp = nil; + for(p = op; *p != '\0'; p++){ + switch(*p){ + default: + return -1; + case ' ': + continue; + case ':': /* start of 8-bit loop */ + if(lp != nil) + return -1; + lp = p; + loop = 7; + continue; + case ';': /* end of 8-bit loop */ + if(lp == nil) + return -1; + loop--; + if(loop >= 0) + p = lp; + else + lp = nil; + continue; + case 'C': /* assert clock */ + mlc |= EEclk; + break; + case 'c': /* deassert clock */ + mlc &= ~EEclk; + break; + case 'D': /* next bit in 'data' byte */ + if(loop < 0) + return -1; + if(data & (1<<loop)) + mlc |= EEdo; + else + mlc &= ~EEdo; + break; + case 'E': /* enable data output */ + mlc |= EEdoe; + break; + case 'e': /* disable data output */ + mlc &= ~EEdoe; + break; + case 'I': /* input bit */ + i = (csr32r(ctlr, Mlc) & EEdi) != 0; + if(loop >= 0) + r |= (i<<loop); + else + r = i; + continue; + case 'O': /* assert data output */ + mlc |= EEdo; + break; + case 'o': /* deassert data output */ + mlc &= ~EEdo; + break; + } + csr32w(ctlr, Mlc, mlc); + microdelay(1); + } + if(loop >= 0) + return -1; + return r; +} + +static int +at24c32r(Ctlr* ctlr, int addr) +{ + int data; + + /* + * Read a byte at address 'addr' from the Atmel AT24C32 + * Serial EEPROM. The 2-wire EEPROM access is controlled + * by 4 bits in Mlc. See the AT24C32 datasheet for + * protocol details. + */ + /* + * Start condition - a high to low transition of data + * with the clock high must precede any other command. + */ + at24c32io(ctlr, "OECoc", 0); + + /* + * Perform a random read at 'addr'. A dummy byte + * write sequence is performed to clock in the device + * and data word addresses (0 and 'addr' respectively). + */ + data = -1; + if(at24c32io(ctlr, "oE :DCc; oeCIc", 0xA0) != 0) + goto stop; + if(at24c32io(ctlr, "oE :DCc; oeCIc", addr>>8) != 0) + goto stop; + if(at24c32io(ctlr, "oE :DCc; oeCIc", addr) != 0) + goto stop; + + /* + * Now send another start condition followed by a + * request to read the device. The EEPROM responds + * by clocking out the data. + */ + at24c32io(ctlr, "OECoc", 0); + if(at24c32io(ctlr, "oE :DCc; oeCIc", 0xA1) != 0) + goto stop; + data = at24c32io(ctlr, ":CIc;", 0xA1); + +stop: + /* + * Stop condition - a low to high transition of data + * with the clock high is a stop condition. After a read + * sequence, the stop command will place the EEPROM in + * a standby power mode. + */ + at24c32io(ctlr, "oECOc", 0); + + return data; +} + +static int +ga620detach(Ctlr* ctlr) +{ + int timeo; + + /* + * Hard reset (don't know which endian so catch both); + * enable for little-endian mode; + * wait for code to be loaded from serial EEPROM or flash; + * make sure CPU A is halted. + */ + csr32w(ctlr, Mhc, (Hr<<24)|Hr); + csr32w(ctlr, Mhc, ((Eews|Ci)<<24)|(Eews|Ci)); + + microdelay(1); + for(timeo = 0; timeo < 500000; timeo++){ + if((csr32r(ctlr, CPUAstate) & (CPUhie|CPUrf)) == CPUhie) + break; + microdelay(1); + } + if((csr32r(ctlr, CPUAstate) & (CPUhie|CPUrf)) != CPUhie) + return -1; + csr32w(ctlr, CPUAstate, CPUhalt); + + /* + * After reset, CPU B seems to be stuck in 'CPUrf'. + * Worry about it later. + */ + csr32w(ctlr, CPUBstate, CPUhalt); + + return 0; +} + +static void +ga620shutdown(Ether* ether) +{ +print("ga620shutdown\n"); + ga620detach(ether->ctlr); +} + +static int +ga620reset(Ctlr* ctlr) +{ + int cls, csr, i; + + if(ga620detach(ctlr) < 0) + return -1; + + /* + * Tigon 2 PCI NICs have 512KB SRAM per bank. + * Clear out any lingering serial EEPROM state + * bits. + */ + csr = csr32r(ctlr, Mlc) & ~(EEdi|EEdo|EEdoe|EEclk|SRAMmask); + csr32w(ctlr, Mlc, SRAM512|csr); + csr = csr32r(ctlr, Mc); + csr32w(ctlr, Mc, SyncSRAM|csr); + + /* + * Initialise PCI State register. + * If PCI Write-and-Invalidate is enabled set the max write DMA + * value to the host cache-line size (32 on Pentium or later). + */ + csr = csr32r(ctlr, Ps) & (PCI32|PCI66); + csr |= PCIwcmd|PCIrcmd|PCImrm; + if(ctlr->pcidev->pcr & 0x0010){ + cls = pcicfgr8(ctlr->pcidev, PciCLS) * 4; + if(cls != 32) + pcicfgw8(ctlr->pcidev, PciCLS, 32/4); + csr |= PCIwm32; + } + csr32w(ctlr, Ps, csr); + + /* + * Operating Mode. + */ + csr32w(ctlr, Om, Fatal|NoJFrag|BswapDMA|WswapBD); + + /* + * Snarf the MAC address from the serial EEPROM. + */ + for(i = 0; i < Eaddrlen; i++){ + if((ctlr->ea[i] = at24c32r(ctlr, 0x8E+i)) == -1) + return -1; + } + + /* + * Load the firmware. + */ + ga620lmw(ctlr, tigon2FwTextAddr, tigon2FwText, tigon2FwTextLen); + ga620lmw(ctlr, tigon2FwRodataAddr, tigon2FwRodata, tigon2FwRodataLen); + ga620lmw(ctlr, tigon2FwDataAddr, tigon2FwData, tigon2FwDataLen); + ga620lmw(ctlr, tigon2FwSbssAddr, nil, tigon2FwSbssLen); + ga620lmw(ctlr, tigon2FwBssAddr, nil, tigon2FwBssLen); + + return 0; +} + +static void +ga620pci(void) +{ + int port; + Pcidev *p; + Ctlr *ctlr; + + p = nil; + while(p = pcimatch(p, 0, 0)){ + if(p->ccrb != 0x02 || p->ccru != 0) + continue; + + switch((p->did<<16)|p->vid){ + default: + continue; + case (0x620A<<16)|0x1385: /* Netgear GA620 */ + case (0x630A<<16)|0x1385: /* Netgear GA620T */ + case (0x0001<<16)|0x12AE: /* Alteon Acenic fiber + * and DEC DEGPA-SA */ + case (0x0002<<16)|0x12AE: /* Alteon Acenic copper */ + case (0x0009<<16)|0x10A9: /* SGI Acenic */ + break; + } + + port = upamalloc(p->mem[0].bar & ~0x0F, p->mem[0].size, 0); + if(port == 0){ + print("ga620: can't map %8.8luX\n", p->mem[0].bar); + continue; + } + + ctlr = malloc(sizeof(Ctlr)); + ctlr->port = port; + ctlr->pcidev = p; + ctlr->id = (p->did<<16)|p->vid; + + ctlr->nic = KADDR(ctlr->port); + if(ga620reset(ctlr)){ + free(ctlr); + continue; + } + + if(ctlrhead != nil) + ctlrtail->next = ctlr; + else + ctlrhead = ctlr; + ctlrtail = ctlr; + } +} + +static int +ga620pnp(Ether* edev) +{ + Ctlr *ctlr; + uchar ea[Eaddrlen]; + + if(ctlrhead == nil) + ga620pci(); + + /* + * Any adapter matches if no edev->port is supplied, + * otherwise the ports must match. + */ + for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){ + if(ctlr->active) + continue; + if(edev->port == 0 || edev->port == ctlr->port){ + ctlr->active = 1; + break; + } + } + if(ctlr == nil) + return -1; + + edev->ctlr = ctlr; + edev->port = ctlr->port; + edev->irq = ctlr->pcidev->intl; + edev->tbdf = ctlr->pcidev->tbdf; + edev->mbps = 1000; + + /* + * Check if the adapter's station address is to be overridden. + * If not, read it from the EEPROM and set in ether->ea prior to + * loading the station address in the hardware. + */ + memset(ea, 0, Eaddrlen); + if(memcmp(ea, edev->ea, Eaddrlen) == 0) + memmove(edev->ea, ctlr->ea, Eaddrlen); + + ga620init(edev); + + /* + * Linkage to the generic ethernet driver. + */ + edev->attach = ga620attach; + edev->transmit = ga620transmit; + edev->interrupt = ga620interrupt; + edev->ifstat = ga620ifstat; + edev->ctl = ga620ctl; + edev->shutdown = ga620shutdown; + + edev->arg = edev; + edev->promiscuous = nil; + + return 0; +} + +void +etherga620link(void) +{ + addethercard("GA620", ga620pnp); +} diff --git a/os/pc/etherga620fw.h b/os/pc/etherga620fw.h new file mode 100644 index 00000000..c30859e7 --- /dev/null +++ b/os/pc/etherga620fw.h @@ -0,0 +1,4858 @@ +/* Generated by genfw.c */ +#define tigon2FwReleaseMajor 0xc +#define tigon2FwReleaseMinor 0x4 +#define tigon2FwReleaseFix 0xb +#define tigon2FwStartAddr 0x00004000 +#define tigon2FwTextAddr 0x00004000 +#define tigon2FwTextLen 0x11bc0 +#define tigon2FwRodataAddr 0x00015bc0 +#define tigon2FwRodataLen 0x10d0 +#define tigon2FwDataAddr 0x00016cc0 +#define tigon2FwDataLen 0x1c0 +#define tigon2FwSbssAddr 0x00016e80 +#define tigon2FwSbssLen 0xcc +#define tigon2FwBssAddr 0x00016f50 +#define tigon2FwBssLen 0x20c0 +static int tigon2FwText[/*(MAX_TEXT_LEN/4) + 1*/] = { +0x0, +0x10000003, 0x0, 0xd, 0xd, +0x3c1d0001, 0x8fbd6d20, 0x3a0f021, 0x3c100000, +0x26104000, 0xc0010c0, 0x0, 0xd, +0x3c1d0001, 0x8fbd6d24, 0x3a0f021, 0x3c100000, +0x26104000, 0xc0017e0, 0x0, 0xd, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x2000008, +0x0, 0x800172f, 0x3c0a0001, 0x800172f, +0x3c0a0002, 0x800172f, 0x0, 0x8002cac, +0x0, 0x8002c4f, 0x0, 0x800172f, +0x3c0a0004, 0x800328a, 0x0, 0x8001a52, +0x0, 0x800394d, 0x0, 0x80038f4, +0x0, 0x800172f, 0x3c0a0006, 0x80039bb, +0x3c0a0007, 0x800172f, 0x3c0a0008, 0x800172f, +0x3c0a0009, 0x8003a13, 0x0, 0x8002ea6, +0x0, 0x800172f, 0x3c0a000b, 0x800172f, +0x3c0a000c, 0x800172f, 0x3c0a000d, 0x80028fb, +0x0, 0x8002890, 0x0, 0x800172f, +0x3c0a000e, 0x800208c, 0x0, 0x8001964, +0x0, 0x8001a04, 0x0, 0x8003ca6, +0x0, 0x8003c94, 0x0, 0x800172f, +0x0, 0x800191a, 0x0, 0x800172f, +0x0, 0x800172f, 0x3c0a0013, 0x800172f, +0x3c0a0014, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x27bdffe0, +0x3c1cc000, 0xafbf001c, 0xafb00018, 0x8f820140, +0x24030003, 0xaf8300ec, 0x34420004, 0xc002b20, +0xaf820140, 0x3c0100c0, 0xc001763, 0xac203ffc, +0x401821, 0x3c020010, 0x3c010001, 0xac236e9c, +0x10620011, 0x43102b, 0x14400002, 0x3c020020, +0x3c020008, 0x1062000c, 0x24050100, 0x3c060001, +0x8cc66e9c, 0x3c040001, 0x24845c74, 0x3821, +0xafa00010, 0xc002b3b, 0xafa00014, 0x3c020020, +0x3c010001, 0xac226e9c, 0x24020008, 0x3c010001, +0xac226eb4, 0x2402001f, 0x3c010001, 0xac226ec4, +0x24020016, 0x3c010001, 0xac226e98, 0x3c05fffe, +0x34a56f08, 0x3c020001, 0x8c426e9c, 0x3c030002, +0x24639010, 0x3c040001, 0x8c846cc4, 0x431023, +0x14800002, 0x458021, 0x2610fa38, 0x2402f000, +0x2028024, 0xc001785, 0x2002021, 0x2022823, +0x3c040020, 0x821823, 0x651823, 0x247bb000, +0x3c03fffe, 0x3463bf08, 0x363b821, 0x3c0600bf, +0x34c6f000, 0x3c070001, 0x8ce76cc0, 0x3c0300bf, +0x3463e000, 0x852023, 0x3c010001, 0xac246ea8, +0x822023, 0x3c010001, 0xac256e90, 0x52842, +0x3c010001, 0xac226e84, 0x27620ffc, 0x3c010001, +0xac226d20, 0x27621ffc, 0xdb3023, 0x7b1823, +0x3c010001, 0xac246e88, 0x3c010001, 0xac256eac, +0x3c010001, 0xac226d24, 0xaf860150, 0x10e00011, +0xaf830250, 0x3c1d0001, 0x8fbd6ccc, 0x3a0f021, +0xc001749, 0x0, 0x3c020001, 0x8c426cd0, +0x3c030001, 0x8c636cd4, 0x2442fe00, 0x24630200, +0x3c010001, 0xac226cd0, 0x3c010001, 0x10000004, +0xac236cd4, 0x3c1d0001, 0x8fbd6d20, 0x3a0f021, +0x3c020001, 0x8c426cc4, 0x1040000d, 0x26fafa38, +0x3c020001, 0x8c426cd0, 0x3c030001, 0x8c636cd4, +0x3c1a0001, 0x8f5a6cd4, 0x2442fa38, 0x246305c8, +0x3c010001, 0xac226cd0, 0x3c010001, 0xac236cd4, +0x3c020001, 0x8c426cc8, 0x14400003, 0x0, +0x3c010001, 0xac206cd0, 0xc001151, 0x0, +0x8fbf001c, 0x8fb00018, 0x3e00008, 0x27bd0020, +0x3c020001, 0x8c426cd0, 0x3c030001, 0x8c636cd4, +0x27bdff98, 0xafb00048, 0x3c100001, 0x8e1066b8, +0xafb20050, 0x3c120000, 0x26524100, 0xafbf0060, +0xafbe005c, 0xafb50058, 0xafb30054, 0xafb1004c, +0xafa20034, 0xafa30030, 0xafa00010, 0xafa00014, +0x8f860040, 0x3c040001, 0x24845c80, 0x24050200, +0x3c010001, 0xac326e80, 0xc002b3b, 0x2003821, +0x8f830040, 0x3c02f000, 0x621824, 0x3c026000, +0x1062000b, 0xa3a0003f, 0x240e0001, 0x3c040001, +0x24845c88, 0xa3ae003f, 0xafa00010, 0xafa00014, +0x8f860040, 0x24050300, 0xc002b3b, 0x2003821, +0x8f820240, 0x3c030001, 0x431025, 0xaf820240, +0xaf800048, 0x8f820048, 0x14400005, 0x0, +0xaf800048, 0x8f820048, 0x10400004, 0x0, +0xaf800048, 0x10000003, 0x2e02021, 0xaf80004c, +0x2e02021, 0x3c050001, 0xc002ba8, 0x34a540f8, +0x3402021, 0xc002ba8, 0x240505c8, 0x3c020001, +0x8c426ea8, 0x3c0d0001, 0x8dad6e88, 0x3c030001, +0x8c636e84, 0x3c080001, 0x8d086e90, 0x3c090001, +0x8d296eac, 0x3c0a0001, 0x8d4a6eb4, 0x3c0b0001, +0x8d6b6ec4, 0x3c0c0001, 0x8d8c6e98, 0x3c040001, +0x24845c94, 0x24050400, 0xaf42013c, 0x8f42013c, +0x24060001, 0x24070001, 0xaf400000, 0xaf4d0138, +0xaf430144, 0xaf480148, 0xaf49014c, 0xaf4a0150, +0xaf4b0154, 0xaf4c0158, 0x2442ff80, 0xaf420140, +0x24020001, 0xafa20010, 0xc002b3b, 0xafa00014, +0x8f420138, 0xafa20010, 0x8f42013c, 0xafa20014, +0x8f460144, 0x8f470148, 0x3c040001, 0x24845ca0, +0xc002b3b, 0x24050500, 0xafb70010, 0xafba0014, +0x8f46014c, 0x8f470150, 0x3c040001, 0x24845cac, +0xc002b3b, 0x24050600, 0x3c020001, 0x8c426e9c, +0x3603821, 0x3c060002, 0x24c69010, 0x2448ffff, +0x1061824, 0xe81024, 0x43102b, 0x10400006, +0x24050900, 0x3c040001, 0x24845cb8, 0xafa80010, +0xc002b3b, 0xafa00014, 0x8f82000c, 0xafa20010, +0x8f82003c, 0xafa20014, 0x8f860000, 0x8f870004, +0x3c040001, 0x24845cc4, 0xc002b3b, 0x24051000, +0x8c020220, 0x8c030224, 0x8c060218, 0x8c07021c, +0x3c040001, 0x24845ccc, 0x24051100, 0xafa20010, +0xc002b3b, 0xafa30014, 0xaf800054, 0xaf80011c, +0x8c020218, 0x30420002, 0x10400009, 0x0, +0x8c020220, 0x3c030002, 0x34630004, 0x431025, +0xaf42000c, 0x8c02021c, 0x10000008, 0x34420004, +0x8c020220, 0x3c030002, 0x34630006, 0x431025, +0xaf42000c, 0x8c02021c, 0x34420006, 0xaf420014, +0x8c020218, 0x30420010, 0x1040000a, 0x0, +0x8c02021c, 0x34420004, 0xaf420010, 0x8c020220, +0x3c03000a, 0x34630004, 0x431025, 0x10000009, +0xaf420008, 0x8c020220, 0x3c03000a, 0x34630006, +0x431025, 0xaf420008, 0x8c02021c, 0x34420006, +0xaf420010, 0x24020001, 0xaf8200a0, 0xaf8200b0, +0x8f830054, 0x8f820054, 0xaf8000d0, 0xaf8000c0, +0x10000002, 0x24630064, 0x8f820054, 0x621023, +0x2c420065, 0x1440fffc, 0x0, 0x8c040208, +0x8c05020c, 0x26e20028, 0xaee20020, 0x24020490, +0xaee20010, 0xaee40008, 0xaee5000c, 0x26e40008, +0x8c820000, 0x8c830004, 0xaf820090, 0xaf830094, +0x8c820018, 0xaf8200b4, 0x9482000a, 0xaf82009c, +0x8f420014, 0xaf8200b0, 0x8f8200b0, 0x30420004, +0x1440fffd, 0x0, 0x8f8200b0, 0x3c03ef00, +0x431024, 0x10400021, 0x0, 0x8f8200b4, +0xafa20010, 0x8f820090, 0x8f830094, 0x3c040001, +0x24845cd4, 0xafa30014, 0x8f8600b0, 0x8f87009c, +0x3c050001, 0xc002b3b, 0x34a5200d, 0x3c040001, +0x24845ce0, 0x240203c0, 0xafa20010, 0xafa00014, +0x8f860144, 0x3c070001, 0x24e75ce8, 0xc002b3b, +0x3405dead, 0x8f82011c, 0x34420002, 0xaf82011c, +0x8f820220, 0x34420004, 0xaf820220, 0x8f820140, +0x3c030001, 0x431025, 0xaf820140, 0x96e20472, +0x96e60452, 0x96e70462, 0xafa20010, 0x96e20482, +0x3c040001, 0x24845d14, 0x24051200, 0xc002b3b, +0xafa20014, 0x96f00452, 0x32020001, 0x10400002, +0xb021, 0x24160001, 0x32020002, 0x54400001, +0x36d60002, 0x32020008, 0x54400001, 0x36d60004, +0x32020010, 0x54400001, 0x36d60008, 0x32020020, +0x54400001, 0x36d60010, 0x32020040, 0x54400001, +0x36d60020, 0x32020080, 0x54400001, 0x36d60040, +0x96e60482, 0x30c20200, 0x54400001, 0x36d64000, +0x96e30472, 0x30620200, 0x10400003, 0x30620100, +0x10000003, 0x36d62000, 0x54400001, 0x36d61000, +0x96f00462, 0x32c24000, 0x14400004, 0x3207009b, +0x30c2009b, 0x14e20007, 0x240e0001, 0x32c22000, +0x1440000d, 0x32020001, 0x3062009b, 0x10e20009, +0x240e0001, 0x3c040001, 0x24845d20, 0x24051300, +0x2003821, 0xa3ae003f, 0xafa30010, 0xc002b3b, +0xafa00014, 0x32020001, 0x54400001, 0x36d60080, +0x32020002, 0x54400001, 0x36d60100, 0x32020008, +0x54400001, 0x36d60200, 0x32020010, 0x54400001, +0x36d60400, 0x32020080, 0x54400001, 0x36d60800, +0x8c020218, 0x30420200, 0x10400002, 0x3c020008, +0x2c2b025, 0x8c020218, 0x30420800, 0x10400002, +0x3c020080, 0x2c2b025, 0x8c020218, 0x30420400, +0x10400002, 0x3c020100, 0x2c2b025, 0x8c020218, +0x30420100, 0x10400002, 0x3c020200, 0x2c2b025, +0x8c020218, 0x30420080, 0x10400002, 0x3c020400, +0x2c2b025, 0x8c020218, 0x30422000, 0x10400002, +0x3c020010, 0x2c2b025, 0x8c020218, 0x30424000, +0x10400002, 0x3c020020, 0x2c2b025, 0x8c020218, +0x30421000, 0x10400002, 0x3c020040, 0x2c2b025, +0x8ee20498, 0x8ee3049c, 0xaf420160, 0xaf430164, +0x8ee204a0, 0x8ee304a4, 0xaf420168, 0xaf43016c, +0x8ee204a8, 0x8ee304ac, 0xaf420170, 0xaf430174, +0x8ee20428, 0x8ee3042c, 0xaf420178, 0xaf43017c, +0x8ee20448, 0x8ee3044c, 0xaf420180, 0xaf430184, +0x8ee20458, 0x8ee3045c, 0xaf420188, 0xaf43018c, +0x8ee20468, 0x8ee3046c, 0xaf420190, 0xaf430194, +0x8ee20478, 0x8ee3047c, 0xaf420198, 0xaf43019c, +0x8ee20488, 0x8ee3048c, 0xaf4201a0, 0xaf4301a4, +0x8ee204b0, 0x8ee304b4, 0x24040080, 0xaf4201a8, +0xaf4301ac, 0xc002ba8, 0x24050080, 0x8c02025c, +0x27440224, 0xaf4201f0, 0x8c020260, 0x24050200, +0x24060008, 0xc002bbf, 0xaf4201f8, 0x3c043b9a, +0x3484ca00, 0x3821, 0x24020006, 0x24030002, +0xaf4201f4, 0x240203e8, 0xaf430204, 0xaf430200, +0xaf4401fc, 0xaf420294, 0x24020001, 0xaf430290, +0xaf42029c, 0x3c030001, 0x671821, 0x90636cd8, +0x3471021, 0x24e70001, 0xa043022c, 0x2ce2000f, +0x1440fff8, 0x3471821, 0x24e70001, 0x3c080001, +0x350840f8, 0x8f820040, 0x3c040001, 0x24845d2c, +0x24051400, 0x21702, 0x24420030, 0xa062022c, +0x3471021, 0xa040022c, 0x8c070218, 0x2c03021, +0x240205c8, 0xafa20010, 0xc002b3b, 0xafa80014, +0x3c040001, 0x24845d38, 0x3c050000, 0x24a55c80, +0x24060010, 0x27b10030, 0x2203821, 0x27b30034, +0xc0017a3, 0xafb30010, 0x3c030001, 0x8c636cc8, +0x1060000a, 0x408021, 0x8fa30030, 0x2405ff00, +0x8fa20034, 0x246400ff, 0x852024, 0x831823, +0x431023, 0xafa20034, 0xafa40030, 0x3c040001, +0x24845d44, 0x3c050000, 0x24a54100, 0x24060108, +0x2203821, 0xc0017a3, 0xafb30010, 0x409021, +0x32c20003, 0x3c010001, 0xac326e80, 0x10400045, +0x2203821, 0x8f820050, 0x3c030010, 0x431024, +0x10400016, 0x0, 0x8c020218, 0x30420040, +0x1040000f, 0x24020001, 0x8f820050, 0x8c030218, +0x240e0001, 0x3c040001, 0x24845d50, 0xa3ae003f, +0xafa20010, 0xafa30014, 0x8f870040, 0x24051500, +0xc002b3b, 0x2c03021, 0x10000004, 0x0, +0x3c010001, 0x370821, 0xa02240f4, 0x3c040001, +0x24845d5c, 0x3c050001, 0x24a55b40, 0x3c060001, +0x24c65bac, 0xc53023, 0x8f420010, 0x27b30030, +0x2603821, 0x27b10034, 0x34420a00, 0xaf420010, +0xc0017a3, 0xafb10010, 0x3c040001, 0x24845d70, +0x3c050001, 0x24a5b714, 0x3c060001, 0x24c6ba90, +0xc53023, 0x2603821, 0xaf420108, 0xc0017a3, +0xafb10010, 0x3c040001, 0x24845d8c, 0x3c050001, +0x24a5be58, 0x3c060001, 0x24c6c900, 0xc53023, +0x2603821, 0x3c010001, 0xac226ef4, 0xc0017a3, +0xafb10010, 0x3c040001, 0x24845da4, 0x10000024, +0x24051600, 0x3c040001, 0x24845dac, 0x3c050001, +0x24a5a10c, 0x3c060001, 0x24c6a238, 0xc53023, +0xc0017a3, 0xafb30010, 0x3c040001, 0x24845dbc, +0x3c050001, 0x24a5b2b0, 0x3c060001, 0x24c6b70c, +0xc53023, 0x2203821, 0xaf420108, 0xc0017a3, +0xafb30010, 0x3c040001, 0x24845dd0, 0x3c050001, +0x24a5ba98, 0x3c060001, 0x24c6be50, 0xc53023, +0x2203821, 0x3c010001, 0xac226ef4, 0xc0017a3, +0xafb30010, 0x3c040001, 0x24845de4, 0x24051650, +0x2c03021, 0x3821, 0x3c010001, 0xac226ef8, +0xafa00010, 0xc002b3b, 0xafa00014, 0x32c20020, +0x10400021, 0x27a70030, 0x3c040001, 0x24845df0, +0x3c050001, 0x24a5b13c, 0x3c060001, 0x24c6b2a8, +0xc53023, 0x24022000, 0xaf42001c, 0x27a20034, +0xc0017a3, 0xafa20010, 0x21900, 0x31982, +0x3c040800, 0x641825, 0xae430028, 0x24030010, +0xaf43003c, 0x96e30450, 0xaf430040, 0x8f430040, +0x3c040001, 0x24845e04, 0xafa00014, 0xafa30010, +0x8f47001c, 0x24051660, 0x3c010001, 0xac226ef0, +0x10000025, 0x32c60020, 0x8ee20448, 0x8ee3044c, +0xaf43001c, 0x8f42001c, 0x2442e000, 0x2c422001, +0x1440000a, 0x240e0001, 0x3c040001, 0x24845e10, +0xa3ae003f, 0xafa00010, 0xafa00014, 0x8f46001c, +0x24051700, 0xc002b3b, 0x3821, 0x3c020000, +0x24425cbc, 0x21100, 0x21182, 0x3c030800, +0x431025, 0xae420028, 0x24020008, 0xaf42003c, +0x96e20450, 0xaf420040, 0x8f420040, 0x3c040001, +0x24845e1c, 0xafa00014, 0xafa20010, 0x8f47001c, +0x24051800, 0x32c60020, 0xc002b3b, 0x0, +0x3c050fff, 0x3c030001, 0x8c636ef4, 0x34a5ffff, +0x2403021, 0x3c020001, 0x8c426ef8, 0x3c040800, +0x651824, 0x31882, 0x641825, 0x451024, +0x21082, 0x441025, 0xacc20080, 0x32c20180, +0x10400056, 0xacc30020, 0x8f82005c, 0x3c030080, +0x431024, 0x1040000d, 0x0, 0x8f820050, +0xafa20010, 0x8f82005c, 0x240e0001, 0x3c040001, +0x24845e28, 0xa3ae003f, 0xafa20014, 0x8f870040, +0x24051900, 0xc002b3b, 0x2c03021, 0x8f820050, +0x3c030010, 0x431024, 0x10400016, 0x0, +0x8c020218, 0x30420040, 0x1040000f, 0x24020001, +0x8f820050, 0x8c030218, 0x240e0001, 0x3c040001, +0x24845d50, 0xa3ae003f, 0xafa20010, 0xafa30014, +0x8f870040, 0x24052000, 0xc002b3b, 0x2c03021, +0x10000004, 0x0, 0x3c010001, 0x370821, +0xa02240f4, 0x3c040001, 0x24845e34, 0x3c050001, +0x24a55ac0, 0x3c060001, 0x24c65b38, 0xc53023, +0x8f420008, 0x27b30030, 0x2603821, 0x27b10034, +0x34420e00, 0xaf420008, 0xc0017a3, 0xafb10010, +0x3c040001, 0x24845e4c, 0x3c050001, 0x24a5d8b4, +0x3c060001, 0x24c6e3c8, 0xc53023, 0x2603821, +0xaf42010c, 0xc0017a3, 0xafb10010, 0x3c040001, +0x24845e64, 0x3c050001, 0x24a5e9ac, 0x3c060001, +0x24c6f0f0, 0xc53023, 0x2603821, 0x3c010001, +0xac226f04, 0xc0017a3, 0xafb10010, 0x3c040001, +0x24845e7c, 0x10000027, 0x24052100, 0x3c040001, +0x24845e84, 0x3c050001, 0x24a59fc8, 0x3c060001, +0x24c6a104, 0xc53023, 0x27b10030, 0x2203821, +0x27b30034, 0xc0017a3, 0xafb30010, 0x3c040001, +0x24845e94, 0x3c050001, 0x24a5cad4, 0x3c060001, +0x24c6d8ac, 0xc53023, 0x2203821, 0xaf42010c, +0xc0017a3, 0xafb30010, 0x3c040001, 0x24845ea4, +0x3c050001, 0x24a5e84c, 0x3c060001, 0x24c6e9a4, +0xc53023, 0x2203821, 0x3c010001, 0xac226f04, +0xc0017a3, 0xafb30010, 0x3c040001, 0x24845eb8, +0x24052150, 0x2c03021, 0x3821, 0x3c010001, +0xac226f10, 0xafa00010, 0xc002b3b, 0xafa00014, +0x3c110fff, 0x3c030001, 0x8c636f04, 0x3631ffff, +0x2409821, 0x3c020001, 0x8c426f10, 0x3c0e0800, +0x711824, 0x31882, 0x6e1825, 0x511024, +0x21082, 0x4e1025, 0xae630038, 0xae620078, +0x8c020218, 0x30420040, 0x14400004, 0x24020001, +0x3c010001, 0x370821, 0xa02240f4, 0x3c040001, +0x24845ec4, 0x3c050001, 0x24a5e3d0, 0x3c060001, +0x24c6e52c, 0xc53023, 0x27be0030, 0x3c03821, +0x27b50034, 0xc0017a3, 0xafb50010, 0x3c010001, +0xac226efc, 0x511024, 0x21082, 0x3c0e0800, +0x4e1025, 0xae620050, 0x32c22000, 0x10400006, +0x3c03821, 0x3c020000, 0x24425cbc, 0x2221024, +0x1000000f, 0x21082, 0x3c040001, 0x24845ed8, +0x3c050001, 0x24a5e534, 0x3c060001, 0x24c6e6e4, +0xc53023, 0xc0017a3, 0xafb50010, 0x3c010001, +0xac226f14, 0x511024, 0x21082, 0x3c0e0800, +0x4e1025, 0xae620048, 0x32c24000, 0x10400005, +0x27a70030, 0x3c020000, 0x24425cbc, 0x1000000e, +0x21100, 0x3c040001, 0x24845ef0, 0x3c050001, +0x24a5e6ec, 0x3c060001, 0x24c6e844, 0xc53023, +0x27a20034, 0xc0017a3, 0xafa20010, 0x3c010001, +0xac226f08, 0x21100, 0x21182, 0x3c030800, +0x431025, 0xae420060, 0x3c040001, 0x24845f08, +0x3c050001, 0x24a58230, 0x3c060001, 0x24c68650, +0xc53023, 0x27b10030, 0x2203821, 0x27b30034, +0xc0017a3, 0xafb30010, 0x3c0e0fff, 0x35ceffff, +0x3c040001, 0x24845f14, 0x3c050000, 0x24a56468, +0x3c060000, 0x24c66588, 0xc53023, 0x2203821, +0x240f021, 0x3c010001, 0xac226edc, 0x4e1024, +0x21082, 0x3c150800, 0x551025, 0xafae0044, +0xafc200b8, 0xc0017a3, 0xafb30010, 0x3c040001, +0x24845f20, 0x3c050000, 0x24a56590, 0x3c060000, +0x24c66808, 0x8fae0044, 0xc53023, 0x2203821, +0x3c010001, 0xac226ed0, 0x4e1024, 0x21082, +0x551025, 0xafc200e8, 0xc0017a3, 0xafb30010, +0x3c040001, 0x24845f38, 0x3c050000, 0x24a56810, +0x3c060000, 0x24c66940, 0x8fae0044, 0xc53023, +0x2203821, 0x3c010001, 0xac226ec8, 0x4e1024, +0x21082, 0x551025, 0xafc200c0, 0xc0017a3, +0xafb30010, 0x3c040001, 0x24845f50, 0x3c050001, +0x24a5fad0, 0x3c060001, 0x24c6fba8, 0x8fae0044, +0xc53023, 0x2203821, 0x3c010001, 0xac226ed4, +0x4e1024, 0x21082, 0x551025, 0xafc200c8, +0xc0017a3, 0xafb30010, 0x3c040001, 0x24845f5c, +0x3c050001, 0x24a5c93c, 0x3c060001, 0x24c6ca20, +0xc53023, 0x2203821, 0xaf420110, 0xc0017a3, +0xafb30010, 0x3c040001, 0x24845f6c, 0x3c050001, +0x24a5c910, 0x3c060001, 0x24c6c934, 0xc53023, +0x2203821, 0xaf420124, 0xc0017a3, 0xafb30010, +0x3c040001, 0x24845f7c, 0x3c050001, 0x24a55a80, +0x3c060001, 0x24c65aac, 0xc53023, 0x2203821, +0xaf420120, 0xaf420114, 0xc0017a3, 0xafb30010, +0x3c040001, 0x24845f88, 0x3c050001, 0x24a5f298, +0x3c060001, 0x24c6f6b4, 0xc53023, 0x2203821, +0xaf420118, 0xc0017a3, 0xafb30010, 0x8fae0044, +0x3c010001, 0xac226f18, 0x4e1024, 0x21082, +0x551025, 0xc003fc3, 0xafc200d0, 0xc003c40, +0x0, 0xc0027a8, 0x0, 0xac000228, +0xac00022c, 0x96e20450, 0x2442ffff, 0xaf420038, +0x96e20460, 0xaf420080, 0x32c24000, 0x14400003, +0x0, 0x96e20480, 0xaf420084, 0x96e70490, +0x50e00001, 0x24070800, 0x24e2ffff, 0xaf420088, +0xaf42007c, 0x24020800, 0x10e2000f, 0x32c24000, +0x10400003, 0x24020400, 0x10e2000b, 0x0, +0x240e0001, 0x3c040001, 0x24845f98, 0xa3ae003f, +0x96e60490, 0x24052170, 0x2c03821, 0xafa00010, +0xc002b3b, 0xafa00014, 0x8f430138, 0x8f440138, +0x24020001, 0xa34205c2, 0xaf430094, 0xaf440098, +0xafa00010, 0xafa00014, 0x8f460080, 0x8f470084, +0x3c040001, 0x24845fa4, 0xc002b3b, 0x24052200, +0xc0024a4, 0x3c110800, 0x3c1433d8, 0x3694cb58, +0x3c020800, 0x34420080, 0x3c040001, 0x24845fb0, +0x3c050000, 0x24a55d00, 0x3c060000, 0x24c65d1c, +0xc53023, 0x27a70030, 0xaf820060, 0x2402ffff, +0xaf820064, 0x27a20034, 0xc0017a3, 0xafa20010, +0x3c010001, 0xac226eb8, 0x21100, 0x21182, +0x511025, 0xc0018fc, 0xae420000, 0x8f820240, +0x3c030001, 0x431025, 0xaf820240, 0x3c020000, +0x24424034, 0xaf820244, 0xaf800240, 0x8f820060, +0x511024, 0x14400005, 0x3c030800, 0x8f820060, +0x431024, 0x1040fffd, 0x0, 0xc003c4d, +0x8821, 0x3c020100, 0xafa20020, 0x8f530018, +0x240200ff, 0x56620001, 0x26710001, 0x8c020228, +0x1622000e, 0x1330c0, 0x8f42033c, 0x24420001, +0xaf42033c, 0x8f42033c, 0x8c020228, 0x3c040001, +0x24845c24, 0x3c050009, 0xafa00014, 0xafa20010, +0x8fa60020, 0x1000003f, 0x34a50100, 0xd71021, +0x8fa30020, 0x8fa40024, 0xac4304c0, 0xac4404c4, +0xc01821, 0x8f440178, 0x8f45017c, 0x1021, +0x24070004, 0xafa70010, 0xafb10014, 0x8f48000c, +0x24c604c0, 0x2e63021, 0xafa80018, 0x8f48010c, +0x24070008, 0xa32821, 0xa3482b, 0x822021, +0x100f809, 0x892021, 0x1440000b, 0x24070008, +0x8f820120, 0xafa20010, 0x8f820124, 0x3c040001, +0x24845c2c, 0x3c050009, 0xafa20014, 0x8fa60020, +0x1000001c, 0x34a50200, 0x8f440160, 0x8f450164, +0x8f43000c, 0xaf510018, 0x8f860120, 0x24020010, +0xafa20010, 0xafb10014, 0xafa30018, 0x8f42010c, +0x40f809, 0x24c6001c, 0x14400010, 0x0, +0x8f420340, 0x24420001, 0xaf420340, 0x8f420340, +0x8f820120, 0xafa20010, 0x8f820124, 0x3c040001, +0x24845c34, 0x3c050009, 0xafa20014, 0x8fa60020, +0x34a50300, 0xc002b3b, 0x2603821, 0x8f4202e4, +0x24420001, 0xaf4202e4, 0x8f4202e4, 0x93a2003f, +0x10400069, 0x3c020700, 0x34423000, 0xafa20028, +0x8f530018, 0x240200ff, 0x12620002, 0x8821, +0x26710001, 0x8c020228, 0x1622000e, 0x1330c0, +0x8f42033c, 0x24420001, 0xaf42033c, 0x8f42033c, +0x8c020228, 0x3c040001, 0x24845c24, 0x3c050009, +0xafa00014, 0xafa20010, 0x8fa60028, 0x1000003f, +0x34a50100, 0xd71021, 0x8fa30028, 0x8fa4002c, +0xac4304c0, 0xac4404c4, 0xc01821, 0x8f440178, +0x8f45017c, 0x1021, 0x24070004, 0xafa70010, +0xafb10014, 0x8f48000c, 0x24c604c0, 0x2e63021, +0xafa80018, 0x8f48010c, 0x24070008, 0xa32821, +0xa3482b, 0x822021, 0x100f809, 0x892021, +0x1440000b, 0x24070008, 0x8f820120, 0xafa20010, +0x8f820124, 0x3c040001, 0x24845c2c, 0x3c050009, +0xafa20014, 0x8fa60028, 0x1000001c, 0x34a50200, +0x8f440160, 0x8f450164, 0x8f43000c, 0xaf510018, +0x8f860120, 0x24020010, 0xafa20010, 0xafb10014, +0xafa30018, 0x8f42010c, 0x40f809, 0x24c6001c, +0x14400010, 0x0, 0x8f420340, 0x24420001, +0xaf420340, 0x8f420340, 0x8f820120, 0xafa20010, +0x8f820124, 0x3c040001, 0x24845c34, 0x3c050009, +0xafa20014, 0x8fa60028, 0x34a50300, 0xc002b3b, +0x2603821, 0x8f4202f0, 0x24420001, 0xaf4202f0, +0x8f4202f0, 0x3c040001, 0x24845fc0, 0xafa00010, +0xafa00014, 0x8fa60028, 0x24052300, 0xc002b3b, +0x3821, 0x10000004, 0x0, 0x8c020264, +0x10400005, 0x0, 0x8f8200a0, 0x30420004, +0x1440fffa, 0x0, 0x8f820044, 0x34420004, +0xaf820044, 0x8f420308, 0x24420001, 0xaf420308, +0x8f420308, 0x8f8200d8, 0x8f8300d4, 0x431023, +0x2442ff80, 0xaf420090, 0x8f420090, 0x2842ff81, +0x10400006, 0x24020001, 0x8f420090, 0x8f430144, +0x431021, 0xaf420090, 0x24020001, 0xaf42008c, +0x32c20008, 0x10400006, 0x0, 0x8f820214, +0x3c038100, 0x3042ffff, 0x431025, 0xaf820214, +0x3c030001, 0x8c636d94, 0x30620002, 0x10400009, +0x30620001, 0x3c040001, 0x24845fcc, 0x3c050000, +0x24a56d50, 0x3c060000, 0x24c671c8, 0x10000012, +0xc53023, 0x10400009, 0x0, 0x3c040001, +0x24845fdc, 0x3c050000, 0x24a571d0, 0x3c060000, +0x24c67678, 0x10000008, 0xc53023, 0x3c040001, +0x24845fec, 0x3c050000, 0x24a56948, 0x3c060000, +0x24c66d48, 0xc53023, 0x27a70030, 0x27a20034, +0xc0017a3, 0xafa20010, 0x3c010001, 0xac226ecc, +0x3c020001, 0x8c426ecc, 0x3c030800, 0x21100, +0x21182, 0x431025, 0xae420040, 0x8f8200a0, +0xafa20010, 0x8f8200b0, 0xafa20014, 0x8f86005c, +0x8f87011c, 0x3c040001, 0x24845ffc, 0x3c010001, +0xac366ea4, 0x3c010001, 0xac206e94, 0x3c010001, +0xac3c6e8c, 0x3c010001, 0xac3b6ebc, 0x3c010001, +0xac376ec0, 0x3c010001, 0xac3a6ea0, 0xc002b3b, +0x24052400, 0x8f820200, 0xafa20010, 0x8f820220, +0xafa20014, 0x8f860044, 0x8f870050, 0x3c040001, +0x24846008, 0xc002b3b, 0x24052500, 0x8f830060, +0x74100b, 0x242000a, 0x200f821, 0x0, +0xd, 0x8fbf0060, 0x8fbe005c, 0x8fb50058, +0x8fb30054, 0x8fb20050, 0x8fb1004c, 0x8fb00048, +0x3e00008, 0x27bd0068, 0x27bdffe0, 0x3c040001, +0x24846014, 0x24052600, 0x3021, 0x3821, +0xafbf0018, 0xafa00010, 0xc002b3b, 0xafa00014, +0x8fbf0018, 0x3e00008, 0x27bd0020, 0x3e00008, +0x0, 0x3e00008, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x3e00008, 0x0, 0x3e00008, 0x0, +0x27bdfde0, 0x27a50018, 0x3c04dead, 0x3484beef, +0xafbf0218, 0x8f820150, 0x3c03001f, 0x3463ffff, +0xafa40018, 0xa22823, 0xa32824, 0x8ca20000, +0x1044000a, 0x0, 0xafa50010, 0x8ca20000, +0xafa20014, 0x8f860150, 0x8f870250, 0x3c040001, +0x2484601c, 0xc002b3b, 0x24052700, 0x8fbf0218, +0x3e00008, 0x27bd0220, 0x27bdffe0, 0x3c06abba, +0x34c6babe, 0xafb00018, 0x3c100004, 0x3c07007f, +0x34e7ffff, 0xafbf001c, 0x102840, 0x8e040000, +0x8ca30000, 0xaca00000, 0xae060000, 0x8ca20000, +0xaca30000, 0x10460005, 0xae040000, 0xa08021, +0xf0102b, 0x1040fff5, 0x102840, 0x3c040001, +0x24846028, 0x24052800, 0x2003021, 0x3821, +0xafa00010, 0xc002b3b, 0xafa00014, 0x2001021, +0x8fbf001c, 0x8fb00018, 0x3e00008, 0x27bd0020, +0x8c020224, 0x3047003f, 0x10e00010, 0x803021, +0x2821, 0x24030020, 0xe31024, 0x10400002, +0x63042, 0xa62821, 0x31842, 0x1460fffb, +0xe31024, 0x2402f000, 0xa22824, 0x3402ffff, +0x45102b, 0x14400003, 0x3c020001, 0x10000008, +0x3c020001, 0x3442ffff, 0x851823, 0x43102b, +0x14400003, 0xa01021, 0x3c02fffe, 0x821021, +0x3e00008, 0x0, 0x27bdffd0, 0xafb50028, +0x8fb50040, 0xafb20020, 0xa09021, 0xafb1001c, +0x24c60003, 0xafbf002c, 0xafb30024, 0xafb00018, +0x8ea20000, 0x2403fffc, 0xc38024, 0x50102b, +0x1440001b, 0xe08821, 0x8e330000, 0xafb00010, +0x8ea20000, 0xafa20014, 0x8e270000, 0x24053000, +0xc002b3b, 0x2403021, 0x8e230000, 0x702021, +0x64102b, 0x10400007, 0x2402821, 0x8ca20000, +0xac620000, 0x24630004, 0x64102b, 0x1440fffb, +0x24a50004, 0x8ea20000, 0x501023, 0xaea20000, +0x8e220000, 0x501021, 0x1000000b, 0xae220000, +0x2402002d, 0xa0820000, 0xafb00010, 0x8ea20000, +0x2409821, 0xafa20014, 0x8e270000, 0x24053100, +0xc002b3b, 0x2603021, 0x2601021, 0x8fbf002c, +0x8fb50028, 0x8fb30024, 0x8fb20020, 0x8fb1001c, +0x8fb00018, 0x3e00008, 0x27bd0030, 0x27bdffe8, +0x3c1cc000, 0x3c05fffe, 0x3c030001, 0x8c636e84, +0x3c040001, 0x8c846e90, 0x34a5bf08, 0x24021ffc, +0x3c010001, 0xac226cd0, 0x3c0200c0, 0x3c010001, +0xac226cd4, 0x3c020020, 0xafbf0010, 0x3c0100c0, +0xac201ffc, 0x431023, 0x441023, 0x245bb000, +0x365b821, 0x3c1d0001, 0x8fbd6ccc, 0x3a0f021, +0x3c0400c0, 0x34840200, 0x3c1a00c0, 0x3c0300c0, +0x346307c8, 0x24021dfc, 0x3c010001, 0xac226cd0, +0x24021834, 0x3c010001, 0xac246cd4, 0x3c010001, +0xac226cd0, 0x3c010001, 0xac236cd4, 0xc00180d, +0x375a0200, 0x8fbf0010, 0x3e00008, 0x27bd0018, +0x27bdffc8, 0x3c040001, 0x24846034, 0x24053200, +0x3c020001, 0x8c426cd0, 0x3c030001, 0x8c636cd4, +0x3021, 0x3603821, 0xafbf0030, 0xafb3002c, +0xafb20028, 0xafb10024, 0xafb00020, 0xafa2001c, +0xafa30018, 0xafb70010, 0xc002b3b, 0xafba0014, +0xc001916, 0x0, 0x8f820240, 0x34420004, +0xaf820240, 0x24020001, 0xaf420000, 0x3c020001, +0x571021, 0x904240f4, 0x10400092, 0x2403fffc, +0x3c100001, 0x2610ac73, 0x3c120001, 0x2652a84c, +0x2121023, 0x438024, 0x8fa3001c, 0x3c040001, +0x24846040, 0x70102b, 0x1440001a, 0x27b30018, +0x8fb10018, 0x24053000, 0x2403021, 0xafb00010, +0xafa30014, 0xc002b3b, 0x2203821, 0x8fa30018, +0x702021, 0x64102b, 0x10400007, 0x2403021, +0x8cc20000, 0xac620000, 0x24630004, 0x64102b, +0x1440fffb, 0x24c60004, 0x8fa2001c, 0x501023, +0xafa2001c, 0x8e620000, 0x501021, 0x1000000a, +0xae620000, 0x2408821, 0x24053100, 0xafb00010, +0xafa30014, 0x8fa70018, 0x2203021, 0x2402002d, +0xc002b3b, 0xa0820000, 0x24070020, 0x8fa3001c, +0x3c040001, 0x2484605c, 0x24120020, 0x3c010001, +0xac316eb0, 0x2c620020, 0x1440001d, 0x27b10018, +0x8fb00018, 0x24053000, 0x3c060001, 0x24c66f50, +0xafa70010, 0xafa30014, 0xc002b3b, 0x2003821, +0x8fa30018, 0x3c040001, 0x24846f50, 0x24650020, +0x65102b, 0x10400007, 0x0, 0x8c820000, +0xac620000, 0x24630004, 0x65102b, 0x1440fffb, +0x24840004, 0x8fa2001c, 0x521023, 0xafa2001c, +0x8e220000, 0x521021, 0x1000000b, 0xae220000, +0x3c100001, 0x26106f50, 0x24053100, 0xafa70010, +0xafa30014, 0x8fa70018, 0x2003021, 0x2402002d, +0xc002b3b, 0xa0820000, 0x24070020, 0x3c040001, +0x24846070, 0x8fa3001c, 0x24120020, 0x3c010001, +0xac306ee4, 0x2c620020, 0x1440001d, 0x27b10018, +0x8fb00018, 0x24053000, 0x3c060001, 0x24c66f70, +0xafa70010, 0xafa30014, 0xc002b3b, 0x2003821, +0x8fa30018, 0x3c040001, 0x24846f70, 0x24650020, +0x65102b, 0x10400007, 0x0, 0x8c820000, +0xac620000, 0x24630004, 0x65102b, 0x1440fffb, +0x24840004, 0x8fa2001c, 0x521023, 0xafa2001c, +0x8e220000, 0x521021, 0x1000000b, 0xae220000, +0x3c100001, 0x26106f70, 0x24053100, 0xafa70010, +0xafa30014, 0x8fa70018, 0x2003021, 0x2402002d, +0xc002b3b, 0xa0820000, 0x3c010001, 0x10000031, +0xac306ee0, 0x3c100001, 0x2610821f, 0x3c120001, +0x2652809c, 0x2121023, 0x438024, 0x8fa3001c, +0x3c040001, 0x24846084, 0x70102b, 0x1440001a, +0x27b30018, 0x8fb10018, 0x24053000, 0x2403021, +0xafb00010, 0xafa30014, 0xc002b3b, 0x2203821, +0x8fa30018, 0x702021, 0x64102b, 0x10400007, +0x2403021, 0x8cc20000, 0xac620000, 0x24630004, +0x64102b, 0x1440fffb, 0x24c60004, 0x8fa2001c, +0x501023, 0xafa2001c, 0x8e620000, 0x501021, +0x1000000a, 0xae620000, 0x2408821, 0x24053100, +0xafb00010, 0xafa30014, 0x8fa70018, 0x2203021, +0x2402002d, 0xc002b3b, 0xa0820000, 0x3c010001, +0xac316eb0, 0x3c030001, 0x8c636eb0, 0x24020400, +0x60f809, 0xaf820070, 0x8fbf0030, 0x8fb3002c, +0x8fb20028, 0x8fb10024, 0x8fb00020, 0x3e00008, +0x27bd0038, 0x0, 0x0, 0x8f820040, +0x3c03f000, 0x431024, 0x3c036000, 0x14430006, +0x0, 0x8f820050, 0x2403ff80, 0x431024, +0x34420055, 0xaf820050, 0x8f820054, 0x244203e8, +0xaf820058, 0x240201f4, 0xaf4200e0, 0x24020004, +0xaf4200e8, 0x24020002, 0xaf4001b0, 0xaf4000e4, +0xaf4200dc, 0xaf4000d8, 0xaf4000d4, 0x3e00008, +0xaf4000d0, 0x8f820054, 0x24420005, 0x3e00008, +0xaf820078, 0x27bdffe8, 0xafbf0010, 0x8f820054, +0x244203e8, 0xaf820058, 0x3c020800, 0x2c21024, +0x10400004, 0x3c02f7ff, 0x3442ffff, 0x2c2b024, +0x36940040, 0x3c020001, 0x8c426da8, 0x10400017, +0x3c020200, 0x3c030001, 0x8c636f1c, 0x10600016, +0x282a025, 0x3c020001, 0x8c426e44, 0x14400012, +0x3c020200, 0x3c020001, 0x8c426d94, 0x30420003, +0x1440000d, 0x3c020200, 0x8f830224, 0x3c020002, +0x8c428fec, 0x10620008, 0x3c020200, 0xc003daf, +0x0, 0x10000004, 0x3c020200, 0xc004196, +0x0, 0x3c020200, 0x2c21024, 0x10400003, +0x0, 0xc001f4b, 0x0, 0x8f4200d8, +0x8f4300dc, 0x24420001, 0xaf4200d8, 0x43102b, +0x14400003, 0x0, 0xaf4000d8, 0x36940080, +0x8c030238, 0x1060000c, 0x0, 0x8f4201b0, +0x244203e8, 0xaf4201b0, 0x43102b, 0x14400006, +0x0, 0x934205c5, 0x14400003, 0x0, +0xc001da0, 0x0, 0x8fbf0010, 0x3e00008, +0x27bd0018, 0x3e00008, 0x0, 0x27bdffd8, +0xafbf0020, 0x8f43002c, 0x8f420038, 0x10620059, +0x0, 0x3c020001, 0x571021, 0x904240f0, +0x10400026, 0x24070008, 0x8f440170, 0x8f450174, +0x8f48000c, 0x8f860120, 0x24020020, 0xafa20010, +0xafa30014, 0xafa80018, 0x8f42010c, 0x40f809, +0x24c6001c, 0x14400011, 0x24020001, 0x3c010001, +0x370821, 0xa02240f0, 0x8f820124, 0xafa20010, +0x8f820128, 0x3c040001, 0x24846128, 0xafa20014, +0x8f46002c, 0x8f870120, 0x3c050009, 0xc002b3b, +0x34a50900, 0x1000005c, 0x0, 0x8f420300, +0x24420001, 0xaf420300, 0x8f420300, 0x8f42002c, +0xa34005c1, 0x10000027, 0xaf420038, 0x8f440170, +0x8f450174, 0x8f43002c, 0x8f48000c, 0x8f860120, +0x24020080, 0xafa20010, 0xafa30014, 0xafa80018, +0x8f42010c, 0x40f809, 0x24c6001c, 0x14400011, +0x24020001, 0x3c010001, 0x370821, 0xa02240f1, +0x8f820124, 0xafa20010, 0x8f820128, 0x3c040001, +0x24846134, 0xafa20014, 0x8f46002c, 0x8f870120, +0x3c050009, 0xc002b3b, 0x34a51100, 0x10000036, +0x0, 0x8f420300, 0x8f43002c, 0x24420001, +0xaf420300, 0x8f420300, 0x24020001, 0xa34205c1, +0xaf430038, 0x3c010001, 0x370821, 0xa02040f1, +0x3c010001, 0x370821, 0xa02040f0, 0x10000026, +0xaf400034, 0x934205c1, 0x1040001d, 0x0, +0xa34005c1, 0x8f820040, 0x30420001, 0x14400008, +0x2021, 0x8c030104, 0x24020001, 0x50620005, +0x24040001, 0x8c020264, 0x10400003, 0x801021, +0x24040001, 0x801021, 0x10400006, 0x0, +0x8f42030c, 0x24420001, 0xaf42030c, 0x10000008, +0x8f42030c, 0x8f820044, 0x34420004, 0xaf820044, +0x8f420308, 0x24420001, 0xaf420308, 0x8f420308, +0x3c010001, 0x370821, 0xa02040f0, 0x3c010001, +0x370821, 0xa02040f1, 0x8f420000, 0x10400007, +0x0, 0xaf80004c, 0x8f82004c, 0x1040fffd, +0x0, 0x10000005, 0x0, 0xaf800048, +0x8f820048, 0x1040fffd, 0x0, 0x8f820060, +0x3c03ff7f, 0x3463ffff, 0x431024, 0xaf820060, +0x8f420000, 0x10400003, 0x0, 0x10000002, +0xaf80004c, 0xaf800048, 0x8fbf0020, 0x3e00008, +0x27bd0028, 0x3e00008, 0x0, 0x27bdffd8, +0xafbf0020, 0x8f430044, 0x8f42007c, 0x10620029, +0x24070008, 0x8f440168, 0x8f45016c, 0x8f48000c, +0x8f860120, 0x24020040, 0xafa20010, 0xafa30014, +0xafa80018, 0x8f42010c, 0x40f809, 0x24c6001c, +0x14400011, 0x24020001, 0x3c010001, 0x370821, +0xa02240f2, 0x8f820124, 0xafa20010, 0x8f820128, +0x3c040001, 0x2484613c, 0xafa20014, 0x8f460044, +0x8f870120, 0x3c050009, 0xc002b3b, 0x34a51300, +0x1000000f, 0x0, 0x8f420304, 0x24420001, +0xaf420304, 0x8f420304, 0x8f420044, 0xaf42007c, +0x3c010001, 0x370821, 0xa02040f2, 0x10000004, +0xaf400078, 0x3c010001, 0x370821, 0xa02040f2, +0x8f420000, 0x10400007, 0x0, 0xaf80004c, +0x8f82004c, 0x1040fffd, 0x0, 0x10000005, +0x0, 0xaf800048, 0x8f820048, 0x1040fffd, +0x0, 0x8f820060, 0x3c03feff, 0x3463ffff, +0x431024, 0xaf820060, 0x8f420000, 0x10400003, +0x0, 0x10000002, 0xaf80004c, 0xaf800048, +0x8fbf0020, 0x3e00008, 0x27bd0028, 0x3e00008, +0x0, 0x3c020001, 0x8c426da8, 0x27bdffa8, +0xafbf0050, 0xafbe004c, 0xafb50048, 0xafb30044, +0xafb20040, 0xafb1003c, 0xafb00038, 0x104000d5, +0x8f900044, 0x8f4200d0, 0x24430001, 0x2842000b, +0x144000e4, 0xaf4300d0, 0x8f420004, 0x30420002, +0x1440009c, 0xaf4000d0, 0x8f420004, 0x3c030001, +0x8c636d98, 0x34420002, 0xaf420004, 0x24020001, +0x14620003, 0x3c020600, 0x10000002, 0x34423000, +0x34421000, 0xafa20020, 0x8f4a0018, 0xafaa0034, +0x27aa0020, 0xafaa002c, 0x8faa0034, 0x240200ff, +0x11420002, 0x1821, 0x25430001, 0x8c020228, +0x609821, 0x1662000e, 0x3c050009, 0x8f42033c, +0x24420001, 0xaf42033c, 0x8f42033c, 0x8c020228, +0x8fa70034, 0x3c040001, 0x2484610c, 0xafa00014, +0xafa20010, 0x8fa60020, 0x10000070, 0x34a50500, +0x8faa0034, 0xa38c0, 0xf71021, 0x8fa30020, +0x8fa40024, 0xac4304c0, 0xac4404c4, 0x8f830054, +0x8f820054, 0x247103e8, 0x2221023, 0x2c4203e9, +0x1040001b, 0xa821, 0xe09021, 0x265e04c0, +0x8f440178, 0x8f45017c, 0x2401821, 0x240a0004, +0xafaa0010, 0xafb30014, 0x8f48000c, 0x1021, +0x2fe3021, 0xafa80018, 0x8f48010c, 0x24070008, +0xa32821, 0xa3482b, 0x822021, 0x100f809, +0x892021, 0x54400006, 0x24150001, 0x8f820054, +0x2221023, 0x2c4203e9, 0x1440ffe9, 0x0, +0x32a200ff, 0x54400018, 0xaf530018, 0x8f420378, +0x24420001, 0xaf420378, 0x8f420378, 0x8f820120, +0x8faa002c, 0x8fa70034, 0xafa20010, 0x8f820124, +0x3c040001, 0x24846118, 0xafa20014, 0x8d460000, +0x3c050009, 0x10000035, 0x34a50600, 0x8f420308, +0x24150001, 0x24420001, 0xaf420308, 0x8f420308, +0x1000001e, 0x32a200ff, 0x8f830054, 0x8f820054, +0x247103e8, 0x2221023, 0x2c4203e9, 0x10400016, +0xa821, 0x3c1e0020, 0x24120010, 0x8f42000c, +0x8f440160, 0x8f450164, 0x8f860120, 0xafb20010, +0xafb30014, 0x5e1025, 0xafa20018, 0x8f42010c, +0x24070008, 0x40f809, 0x24c6001c, 0x1440ffe3, +0x0, 0x8f820054, 0x2221023, 0x2c4203e9, +0x1440ffee, 0x0, 0x32a200ff, 0x14400011, +0x3c050009, 0x8f420378, 0x24420001, 0xaf420378, +0x8f420378, 0x8f820120, 0x8faa002c, 0x8fa70034, +0xafa20010, 0x8f820124, 0x3c040001, 0x24846120, +0xafa20014, 0x8d460000, 0x34a50700, 0xc002b3b, +0x0, 0x8f4202ec, 0x24420001, 0xaf4202ec, +0x8f4202ec, 0x8f420004, 0x30420001, 0x50400029, +0x36100040, 0x3c020400, 0x2c21024, 0x10400013, +0x2404ffdf, 0x8f420250, 0x8f430254, 0x8f4401b4, +0x14640006, 0x36100040, 0x8f420270, 0x8f430274, +0x8f4401b8, 0x10640007, 0x2402ffdf, 0x8f420250, +0x8f430254, 0x8f440270, 0x8f450274, 0x10000012, +0x3a100020, 0x1000002b, 0x2028024, 0x8f420250, +0x8f430254, 0x8f4501b4, 0x14650006, 0x2048024, +0x8f420270, 0x8f430274, 0x8f4401b8, 0x50640021, +0x36100040, 0x8f420250, 0x8f430254, 0x8f440270, +0x8f450274, 0x3a100040, 0xaf4301b4, 0x10000019, +0xaf4501b8, 0x8f4200d4, 0x24430001, 0x10000011, +0x28420033, 0x8f420004, 0x30420001, 0x10400009, +0x3c020400, 0x2c21024, 0x10400004, 0x2402ffdf, +0x2028024, 0x1000000b, 0x36100040, 0x10000009, +0x36100060, 0x8f4200d4, 0x36100040, 0x24430001, +0x284201f5, 0x14400003, 0xaf4300d4, 0xaf4000d4, +0x3a100020, 0xaf900044, 0x2402ff7f, 0x282a024, +0x8fbf0050, 0x8fbe004c, 0x8fb50048, 0x8fb30044, +0x8fb20040, 0x8fb1003c, 0x8fb00038, 0x3e00008, +0x27bd0058, 0x3e00008, 0x0, 0x3c020001, +0x8c426da8, 0x27bdffb0, 0xafbf0048, 0xafbe0044, +0xafb50040, 0xafb3003c, 0xafb20038, 0xafb10034, +0x104000c7, 0xafb00030, 0x8f4200d0, 0x24430001, +0x2842000b, 0x144000da, 0xaf4300d0, 0x8f420004, +0x30420002, 0x14400097, 0xaf4000d0, 0x8f420004, +0x3c030001, 0x8c636d98, 0x34420002, 0xaf420004, +0x24020001, 0x14620003, 0x3c020600, 0x10000002, +0x34423000, 0x34421000, 0xafa20020, 0x1821, +0x8f5e0018, 0x27aa0020, 0x240200ff, 0x13c20002, +0xafaa002c, 0x27c30001, 0x8c020228, 0x609021, +0x1642000e, 0x1e38c0, 0x8f42033c, 0x24420001, +0xaf42033c, 0x8f42033c, 0x8c020228, 0x3c040001, +0x2484610c, 0x3c050009, 0xafa00014, 0xafa20010, +0x8fa60020, 0x1000006d, 0x34a50500, 0xf71021, +0x8fa30020, 0x8fa40024, 0xac4304c0, 0xac4404c4, +0x8f830054, 0x8f820054, 0x247003e8, 0x2021023, +0x2c4203e9, 0x1040001b, 0x9821, 0xe08821, +0x263504c0, 0x8f440178, 0x8f45017c, 0x2201821, +0x240a0004, 0xafaa0010, 0xafb20014, 0x8f48000c, +0x1021, 0x2f53021, 0xafa80018, 0x8f48010c, +0x24070008, 0xa32821, 0xa3482b, 0x822021, +0x100f809, 0x892021, 0x54400006, 0x24130001, +0x8f820054, 0x2021023, 0x2c4203e9, 0x1440ffe9, +0x0, 0x326200ff, 0x54400017, 0xaf520018, +0x8f420378, 0x24420001, 0xaf420378, 0x8f420378, +0x8f820120, 0x8faa002c, 0xafa20010, 0x8f820124, +0x3c040001, 0x24846118, 0x3c050009, 0xafa20014, +0x8d460000, 0x10000035, 0x34a50600, 0x8f420308, +0x24130001, 0x24420001, 0xaf420308, 0x8f420308, +0x1000001e, 0x326200ff, 0x8f830054, 0x8f820054, +0x247003e8, 0x2021023, 0x2c4203e9, 0x10400016, +0x9821, 0x3c150020, 0x24110010, 0x8f42000c, +0x8f440160, 0x8f450164, 0x8f860120, 0xafb10010, +0xafb20014, 0x551025, 0xafa20018, 0x8f42010c, +0x24070008, 0x40f809, 0x24c6001c, 0x1440ffe3, +0x0, 0x8f820054, 0x2021023, 0x2c4203e9, +0x1440ffee, 0x0, 0x326200ff, 0x14400011, +0x0, 0x8f420378, 0x24420001, 0xaf420378, +0x8f420378, 0x8f820120, 0x8faa002c, 0xafa20010, +0x8f820124, 0x3c040001, 0x24846120, 0x3c050009, +0xafa20014, 0x8d460000, 0x34a50700, 0xc002b3b, +0x3c03821, 0x8f4202ec, 0x24420001, 0xaf4202ec, +0x8f4202ec, 0x8f420004, 0x30420001, 0x10400018, +0x24040001, 0x8f420250, 0x8f430254, 0x8f4501b4, +0x3c010001, 0x14650006, 0xa0246cf1, 0x8f420270, +0x8f430274, 0x8f4401b8, 0x10640021, 0x0, +0x8f420250, 0x8f430254, 0x3c040001, 0x90846cf0, +0x8f460270, 0x8f470274, 0x38840001, 0xaf4301b4, +0xaf4701b8, 0x3c010001, 0x10000025, 0xa0246cf0, +0x8f4200d4, 0x3c010001, 0xa0206cf0, 0x24430001, +0x28420033, 0x1440001e, 0xaf4300d4, 0x3c020001, +0x90426cf1, 0xaf4000d4, 0x10000017, 0x38420001, +0x8f420004, 0x30420001, 0x10400008, 0x0, +0xc00565a, 0x2021, 0x3c010001, 0xa0206cf1, +0x3c010001, 0x1000000e, 0xa0206cf0, 0x8f4200d4, +0x3c010001, 0xa0206cf0, 0x24430001, 0x284201f5, +0x14400007, 0xaf4300d4, 0x3c020001, 0x90426cf1, +0xaf4000d4, 0x421026, 0x3c010001, 0xa0226cf1, +0x3c030001, 0x8c636d98, 0x24020002, 0x1462000c, +0x3c030002, 0x3c030001, 0x90636cf1, 0x24020001, +0x5462001f, 0x2021, 0x3c020001, 0x90426cf0, +0x1443001b, 0x24040005, 0x10000019, 0x24040006, +0x3c020002, 0x8c428ff4, 0x431024, 0x1040000b, +0x24020001, 0x3c030001, 0x90636cf1, 0x54620010, +0x2021, 0x3c020001, 0x90426cf0, 0x1443000c, +0x24040003, 0x1000000a, 0x24040004, 0x3c030001, +0x90636cf1, 0x14620006, 0x2021, 0x3c020001, +0x90426cf0, 0x24040001, 0x50440001, 0x24040002, +0xc00565a, 0x0, 0x2402ff7f, 0x282a024, +0x8fbf0048, 0x8fbe0044, 0x8fb50040, 0x8fb3003c, +0x8fb20038, 0x8fb10034, 0x8fb00030, 0x3e00008, +0x27bd0050, 0x3e00008, 0x0, 0x3c020001, +0x8c426da8, 0x27bdffb0, 0xafbf0048, 0xafbe0044, +0xafb50040, 0xafb3003c, 0xafb20038, 0xafb10034, +0x104000de, 0xafb00030, 0x8f4200d0, 0x3c040001, +0x8c846d98, 0x24430001, 0x2842000b, 0xaf4400e8, +0x144000fe, 0xaf4300d0, 0x8f420004, 0x30420002, +0x14400095, 0xaf4000d0, 0x8f420004, 0x34420002, +0xaf420004, 0x24020001, 0x14820003, 0x3c020600, +0x10000002, 0x34423000, 0x34421000, 0xafa20020, +0x1821, 0x8f5e0018, 0x27aa0020, 0x240200ff, +0x13c20002, 0xafaa002c, 0x27c30001, 0x8c020228, +0x609021, 0x1642000e, 0x1e38c0, 0x8f42033c, +0x24420001, 0xaf42033c, 0x8f42033c, 0x8c020228, +0x3c040001, 0x2484610c, 0x3c050009, 0xafa00014, +0xafa20010, 0x8fa60020, 0x1000006d, 0x34a50500, +0xf71021, 0x8fa30020, 0x8fa40024, 0xac4304c0, +0xac4404c4, 0x8f830054, 0x8f820054, 0x247003e8, +0x2021023, 0x2c4203e9, 0x1040001b, 0x9821, +0xe08821, 0x263504c0, 0x8f440178, 0x8f45017c, +0x2201821, 0x240a0004, 0xafaa0010, 0xafb20014, +0x8f48000c, 0x1021, 0x2f53021, 0xafa80018, +0x8f48010c, 0x24070008, 0xa32821, 0xa3482b, +0x822021, 0x100f809, 0x892021, 0x54400006, +0x24130001, 0x8f820054, 0x2021023, 0x2c4203e9, +0x1440ffe9, 0x0, 0x326200ff, 0x54400017, +0xaf520018, 0x8f420378, 0x24420001, 0xaf420378, +0x8f420378, 0x8f820120, 0x8faa002c, 0xafa20010, +0x8f820124, 0x3c040001, 0x24846118, 0x3c050009, +0xafa20014, 0x8d460000, 0x10000035, 0x34a50600, +0x8f420308, 0x24130001, 0x24420001, 0xaf420308, +0x8f420308, 0x1000001e, 0x326200ff, 0x8f830054, +0x8f820054, 0x247003e8, 0x2021023, 0x2c4203e9, +0x10400016, 0x9821, 0x3c150020, 0x24110010, +0x8f42000c, 0x8f440160, 0x8f450164, 0x8f860120, +0xafb10010, 0xafb20014, 0x551025, 0xafa20018, +0x8f42010c, 0x24070008, 0x40f809, 0x24c6001c, +0x1440ffe3, 0x0, 0x8f820054, 0x2021023, +0x2c4203e9, 0x1440ffee, 0x0, 0x326200ff, +0x14400011, 0x0, 0x8f420378, 0x24420001, +0xaf420378, 0x8f420378, 0x8f820120, 0x8faa002c, +0xafa20010, 0x8f820124, 0x3c040001, 0x24846120, +0x3c050009, 0xafa20014, 0x8d460000, 0x34a50700, +0xc002b3b, 0x3c03821, 0x8f4202ec, 0x24420001, +0xaf4202ec, 0x8f4202ec, 0x8f420004, 0x30420001, +0x10400033, 0x3c020400, 0x2c21024, 0x10400017, +0x0, 0x934205c0, 0x8f440250, 0x8f450254, +0x8f4301b4, 0x34420020, 0x14a30006, 0xa34205c0, +0x8f420270, 0x8f430274, 0x8f4401b8, 0x10640008, +0x0, 0x8f420250, 0x8f430254, 0x934405c0, +0x8f460270, 0x8f470274, 0x10000016, 0x38840040, +0x934205c0, 0x10000048, 0x304200bf, 0x934205c0, +0x8f440250, 0x8f450254, 0x8f4301b4, 0x304200bf, +0x14a30006, 0xa34205c0, 0x8f420270, 0x8f430274, +0x8f4401b8, 0x1064000b, 0x0, 0x8f420250, +0x8f430254, 0x934405c0, 0x8f460270, 0x8f470274, +0x38840020, 0xaf4301b4, 0xaf4701b8, 0x10000033, +0xa34405c0, 0x934205c0, 0x1000002f, 0x34420020, +0x934205c0, 0x8f4300d4, 0x34420020, 0xa34205c0, +0x24620001, 0x10000023, 0x28630033, 0x8f4200e4, +0x8f4300e0, 0x24420001, 0xaf4200e4, 0x43102a, +0x14400006, 0x24030001, 0x8f4200e8, 0x14430002, +0xaf4000e4, 0x24030004, 0xaf4300e8, 0x8f420004, +0x30420001, 0x1040000d, 0x3c020400, 0x2c21024, +0x10400007, 0x0, 0x934205c0, 0x34420040, +0xa34205c0, 0x934205c0, 0x1000000f, 0x304200df, +0x934205c0, 0x1000000c, 0x34420060, 0x934205c0, +0x8f4300d4, 0x34420020, 0xa34205c0, 0x24620001, +0x286300fb, 0x14600005, 0xaf4200d4, 0x934205c0, +0xaf4000d4, 0x38420040, 0xa34205c0, 0x934205c0, +0x8f4300e8, 0x3042007f, 0xa34205c0, 0x24020001, +0x14620005, 0x0, 0x934405c0, 0x42102, +0x10000003, 0x348400f0, 0x934405c0, 0x3484000f, +0xc005640, 0x0, 0x2402ff7f, 0x282a024, +0x8fbf0048, 0x8fbe0044, 0x8fb50040, 0x8fb3003c, +0x8fb20038, 0x8fb10034, 0x8fb00030, 0x3e00008, +0x27bd0050, 0x3e00008, 0x0, 0x27bdffb0, +0x274401c0, 0x26e30028, 0x24650400, 0x65102b, +0xafbf0048, 0xafbe0044, 0xafb50040, 0xafb3003c, +0xafb20038, 0xafb10034, 0x10400007, 0xafb00030, +0x8c820000, 0xac620000, 0x24630004, 0x65102b, +0x1440fffb, 0x24840004, 0x8c020080, 0xaee20044, +0x8c0200c0, 0xaee20040, 0x8c020084, 0xaee20030, +0x8c020084, 0xaee2023c, 0x8c020088, 0xaee20240, +0x8c02008c, 0xaee20244, 0x8c020090, 0xaee20248, +0x8c020094, 0xaee2024c, 0x8c020098, 0xaee20250, +0x8c02009c, 0xaee20254, 0x8c0200a0, 0xaee20258, +0x8c0200a4, 0xaee2025c, 0x8c0200a8, 0xaee20260, +0x8c0200ac, 0xaee20264, 0x8c0200b0, 0xaee20268, +0x8c0200b4, 0xaee2026c, 0x8c0200b8, 0xaee20270, +0x8c0200bc, 0x24040001, 0xaee20274, 0xaee00034, +0x41080, 0x571021, 0x8ee30034, 0x8c42023c, +0x24840001, 0x621821, 0x2c82000f, 0xaee30034, +0x1440fff8, 0x41080, 0x8c0200cc, 0xaee20048, +0x8c0200d0, 0xaee2004c, 0x8c0200e0, 0xaee201f8, +0x8c0200e4, 0xaee201fc, 0x8c0200e8, 0xaee20200, +0x8c0200ec, 0xaee20204, 0x8c0200f0, 0xaee20208, +0x8ee400c0, 0x8ee500c4, 0x8c0200fc, 0x45102b, +0x1040000b, 0x0, 0x8ee200c0, 0x8ee300c4, +0x24040001, 0x24050000, 0x651821, 0x65302b, +0x441021, 0x461021, 0xaee200c0, 0xaee300c4, +0x8c0200fc, 0x8ee400c0, 0x8ee500c4, 0x2408ffff, +0x24090000, 0x401821, 0x1021, 0x882024, +0xa92824, 0x822025, 0xa32825, 0xaee400c0, +0xaee500c4, 0x8ee400d0, 0x8ee500d4, 0x8c0200f4, +0x45102b, 0x1040000b, 0x0, 0x8ee200d0, +0x8ee300d4, 0x24040001, 0x24050000, 0x651821, +0x65302b, 0x441021, 0x461021, 0xaee200d0, +0xaee300d4, 0x8c0200f4, 0x8ee400d0, 0x8ee500d4, +0x401821, 0x1021, 0x882024, 0xa92824, +0x822025, 0xa32825, 0xaee400d0, 0xaee500d4, +0x8ee400c8, 0x8ee500cc, 0x8c0200f8, 0x45102b, +0x1040000b, 0x0, 0x8ee200c8, 0x8ee300cc, +0x24040001, 0x24050000, 0x651821, 0x65302b, +0x441021, 0x461021, 0xaee200c8, 0xaee300cc, +0x8c0200f8, 0x8ee400c8, 0x8ee500cc, 0x401821, +0x1021, 0x882024, 0xa92824, 0x822025, +0xa32825, 0x24020008, 0xaee400c8, 0xaee500cc, +0xafa20010, 0xafa00014, 0x8f42000c, 0x8c040208, +0x8c05020c, 0xafa20018, 0x8f42010c, 0x26e60028, +0x40f809, 0x24070400, 0x104000f0, 0x3c020400, +0xafa20020, 0x934205c6, 0x10400089, 0x1821, +0x8f5e0018, 0x27aa0020, 0x240200ff, 0x13c20002, +0xafaa002c, 0x27c30001, 0x8c020228, 0x609021, +0x1642000e, 0x1e38c0, 0x8f42033c, 0x24420001, +0xaf42033c, 0x8f42033c, 0x8c020228, 0x3c040001, +0x2484610c, 0x3c050009, 0xafa00014, 0xafa20010, +0x8fa60020, 0x1000006b, 0x34a50500, 0xf71021, +0x8fa30020, 0x8fa40024, 0xac4304c0, 0xac4404c4, +0x8f830054, 0x8f820054, 0x247003e8, 0x2021023, +0x2c4203e9, 0x1040001b, 0x9821, 0xe08821, +0x263504c0, 0x8f440178, 0x8f45017c, 0x2201821, +0x240a0004, 0xafaa0010, 0xafb20014, 0x8f48000c, +0x1021, 0x2f53021, 0xafa80018, 0x8f48010c, +0x24070008, 0xa32821, 0xa3482b, 0x822021, +0x100f809, 0x892021, 0x54400006, 0x24130001, +0x8f820054, 0x2021023, 0x2c4203e9, 0x1440ffe9, +0x0, 0x326200ff, 0x54400017, 0xaf520018, +0x8f420378, 0x24420001, 0xaf420378, 0x8f420378, +0x8f820120, 0x8faa002c, 0xafa20010, 0x8f820124, +0x3c040001, 0x24846118, 0x3c050009, 0xafa20014, +0x8d460000, 0x10000033, 0x34a50600, 0x8f420308, +0x24130001, 0x24420001, 0xaf420308, 0x8f420308, +0x1000001c, 0x326200ff, 0x8f830054, 0x8f820054, +0x247003e8, 0x2021023, 0x2c4203e9, 0x10400014, +0x9821, 0x24110010, 0x8f42000c, 0x8f440160, +0x8f450164, 0x8f860120, 0xafb10010, 0xafb20014, +0xafa20018, 0x8f42010c, 0x24070008, 0x40f809, +0x24c6001c, 0x1440ffe5, 0x0, 0x8f820054, +0x2021023, 0x2c4203e9, 0x1440ffef, 0x0, +0x326200ff, 0x54400012, 0x24020001, 0x8f420378, +0x24420001, 0xaf420378, 0x8f420378, 0x8f820120, +0x8faa002c, 0xafa20010, 0x8f820124, 0x3c040001, +0x24846120, 0x3c050009, 0xafa20014, 0x8d460000, +0x34a50700, 0xc002b3b, 0x3c03821, 0x1021, +0x1440005b, 0x24020001, 0x10000065, 0x0, +0x8f510018, 0x240200ff, 0x12220002, 0x8021, +0x26300001, 0x8c020228, 0x1602000e, 0x1130c0, +0x8f42033c, 0x24420001, 0xaf42033c, 0x8f42033c, +0x8c020228, 0x3c040001, 0x248460f4, 0x3c050009, +0xafa00014, 0xafa20010, 0x8fa60020, 0x1000003f, +0x34a50100, 0xd71021, 0x8fa30020, 0x8fa40024, +0xac4304c0, 0xac4404c4, 0xc01821, 0x8f440178, +0x8f45017c, 0x1021, 0x24070004, 0xafa70010, +0xafb00014, 0x8f48000c, 0x24c604c0, 0x2e63021, +0xafa80018, 0x8f48010c, 0x24070008, 0xa32821, +0xa3482b, 0x822021, 0x100f809, 0x892021, +0x1440000b, 0x24070008, 0x8f820120, 0xafa20010, +0x8f820124, 0x3c040001, 0x248460fc, 0x3c050009, +0xafa20014, 0x8fa60020, 0x1000001c, 0x34a50200, +0x8f440160, 0x8f450164, 0x8f43000c, 0xaf500018, +0x8f860120, 0x24020010, 0xafa20010, 0xafb00014, +0xafa30018, 0x8f42010c, 0x40f809, 0x24c6001c, +0x54400011, 0x24020001, 0x8f420340, 0x24420001, +0xaf420340, 0x8f420340, 0x8f820120, 0xafa20010, +0x8f820124, 0x3c040001, 0x24846104, 0x3c050009, +0xafa20014, 0x8fa60020, 0x34a50300, 0xc002b3b, +0x2203821, 0x1021, 0x1040000d, 0x24020001, +0x8f4202e8, 0xa34005c6, 0xaf4001b0, 0x24420001, +0xaf4202e8, 0x8f4202e8, 0x8ee20150, 0x24420001, +0xaee20150, 0x10000003, 0x8ee20150, 0x24020001, +0xa34205c6, 0x8fbf0048, 0x8fbe0044, 0x8fb50040, +0x8fb3003c, 0x8fb20038, 0x8fb10034, 0x8fb00030, +0x3e00008, 0x27bd0050, 0x27bdffd8, 0xafbf0020, +0x8f8200b0, 0x30420004, 0x10400068, 0x0, +0x8f430128, 0x8f820104, 0x14620005, 0x0, +0x8f430130, 0x8f8200b4, 0x10620006, 0x0, +0x8f820104, 0xaf420128, 0x8f8200b4, 0x1000005b, +0xaf420130, 0x8f8200b0, 0x3c030080, 0x431024, +0x1040000d, 0x0, 0x8f82011c, 0x34420002, +0xaf82011c, 0x8f8200b0, 0x2403fffb, 0x431024, +0xaf8200b0, 0x8f82011c, 0x2403fffd, 0x431024, +0x1000004a, 0xaf82011c, 0x8f430128, 0x8f820104, +0x14620005, 0x0, 0x8f430130, 0x8f8200b4, +0x10620010, 0x0, 0x8f820104, 0xaf420128, +0x8f8200b4, 0x8f430128, 0xaf420130, 0xafa30010, +0x8f420130, 0x3c040001, 0x24846144, 0xafa20014, +0x8f86011c, 0x8f8700b0, 0x3c050005, 0x10000031, +0x34a50900, 0x8f420128, 0xafa20010, 0x8f420130, +0x3c040001, 0x24846150, 0xafa20014, 0x8f86011c, +0x8f8700b0, 0x3c050005, 0xc002b3b, 0x34a51000, +0x8f82011c, 0x34420002, 0xaf82011c, 0x8f830104, +0x8f8200b0, 0x34420001, 0xaf8200b0, 0x24020008, +0xaf830104, 0xafa20010, 0xafa00014, 0x8f42000c, +0x8c040208, 0x8c05020c, 0xafa20018, 0x8f42010c, +0x26e60028, 0x40f809, 0x24070400, 0x8f82011c, +0x2403fffd, 0x431024, 0xaf82011c, 0x8ee201dc, +0x24420001, 0xaee201dc, 0x8ee201dc, 0x8f420128, +0xafa20010, 0x8f420130, 0x3c040001, 0x2484615c, +0xafa20014, 0x8f86011c, 0x8f8700b0, 0x3c050005, +0x34a51100, 0xc002b3b, 0x0, 0x8f8200a0, +0x30420004, 0x10400069, 0x0, 0x8f43012c, +0x8f820124, 0x14620005, 0x0, 0x8f430134, +0x8f8200a4, 0x10620006, 0x0, 0x8f820124, +0xaf42012c, 0x8f8200a4, 0x1000005c, 0xaf420134, +0x8f8200a0, 0x3c030080, 0x431024, 0x1040000d, +0x0, 0x8f82011c, 0x34420002, 0xaf82011c, +0x8f8200a0, 0x2403fffb, 0x431024, 0xaf8200a0, +0x8f82011c, 0x2403fffd, 0x431024, 0x1000004b, +0xaf82011c, 0x8f43012c, 0x8f820124, 0x14620005, +0x0, 0x8f430134, 0x8f8200a4, 0x10620010, +0x0, 0x8f820124, 0xaf42012c, 0x8f8200a4, +0x8f43012c, 0xaf420134, 0xafa30010, 0x8f420134, +0x3c040001, 0x24846168, 0xafa20014, 0x8f86011c, +0x8f8700a0, 0x3c050005, 0x10000032, 0x34a51200, +0x8f42012c, 0xafa20010, 0x8f420134, 0x3c040001, +0x24846174, 0xafa20014, 0x8f86011c, 0x8f8700a0, +0x3c050005, 0xc002b3b, 0x34a51300, 0x8f82011c, +0x34420002, 0xaf82011c, 0x8f830124, 0x8f8200a0, +0x34420001, 0xaf8200a0, 0x24020080, 0xaf830124, +0xafa20010, 0xafa00014, 0x8f420014, 0x8c040208, +0x8c05020c, 0xafa20018, 0x8f420108, 0x3c060001, +0x24c66ed8, 0x40f809, 0x24070004, 0x8f82011c, +0x2403fffd, 0x431024, 0xaf82011c, 0x8ee201dc, +0x24420001, 0xaee201dc, 0x8ee201dc, 0x8f42012c, +0xafa20010, 0x8f420134, 0x3c040001, 0x24846180, +0xafa20014, 0x8f86011c, 0x8f8700a0, 0x3c050005, +0x34a51400, 0xc002b3b, 0x0, 0x8fbf0020, +0x3e00008, 0x27bd0028, 0x3c081000, 0x24070001, +0x3c060080, 0x3c050100, 0x8f820070, 0x481024, +0x1040fffd, 0x0, 0x8f820054, 0x24420005, +0xaf820078, 0x8c040234, 0x10800016, 0x1821, +0x3c020001, 0x571021, 0x8c4240e8, 0x24420005, +0x3c010001, 0x370821, 0xac2240e8, 0x3c020001, +0x571021, 0x8c4240e8, 0x44102b, 0x14400009, +0x0, 0x3c030080, 0x3c010001, 0x370821, +0xac2040e8, 0x3c010001, 0x370821, 0x1000000b, +0xa02740f0, 0x3c020001, 0x571021, 0x904240f0, +0x54400006, 0x661825, 0x3c020001, 0x571021, +0x904240f1, 0x54400001, 0x661825, 0x8c040230, +0x10800013, 0x0, 0x3c020001, 0x571021, +0x8c4240ec, 0x24420005, 0x3c010001, 0x370821, +0xac2240ec, 0x3c020001, 0x571021, 0x8c4240ec, +0x44102b, 0x14400006, 0x0, 0x3c010001, +0x370821, 0xac2040ec, 0x10000006, 0x651825, +0x3c020001, 0x571021, 0x904240f2, 0x54400001, +0x651825, 0x1060ffbc, 0x0, 0x8f420000, +0x10400007, 0x0, 0xaf80004c, 0x8f82004c, +0x1040fffd, 0x0, 0x10000005, 0x0, +0xaf800048, 0x8f820048, 0x1040fffd, 0x0, +0x8f820060, 0x431025, 0xaf820060, 0x8f420000, +0x10400003, 0x0, 0x1000ffa7, 0xaf80004c, +0x1000ffa5, 0xaf800048, 0x3e00008, 0x0, +0x0, 0x0, 0x0, 0x27bdffe0, +0xafbf0018, 0x8f860064, 0x30c20004, 0x10400025, +0x24040004, 0x8c020114, 0xaf420020, 0xaf840064, +0x8f4202fc, 0x24420001, 0xaf4202fc, 0x8f4202fc, +0x8f820064, 0x30420004, 0x14400005, 0x0, +0x8c030114, 0x8f420020, 0x1462fff2, 0x0, +0x8f420000, 0x10400007, 0x8f43003c, 0xaf80004c, +0x8f82004c, 0x1040fffd, 0x0, 0x10000005, +0x0, 0xaf800048, 0x8f820048, 0x1040fffd, +0x0, 0x8f820060, 0x431025, 0xaf820060, +0x8f420000, 0x10400073, 0x0, 0x1000006f, +0x0, 0x30c20008, 0x10400020, 0x24040008, +0x8c02011c, 0xaf420048, 0xaf840064, 0x8f4202a8, +0x24420001, 0xaf4202a8, 0x8f4202a8, 0x8f820064, +0x30420008, 0x14400005, 0x0, 0x8c03011c, +0x8f420048, 0x1462fff2, 0x0, 0x8f420000, +0x10400007, 0x0, 0xaf80004c, 0x8f82004c, +0x1040fffd, 0x0, 0x10000005, 0x0, +0xaf800048, 0x8f820048, 0x1040fffd, 0x0, +0x8f820060, 0x1000ffd9, 0x34420200, 0x30c20020, +0x10400023, 0x24040020, 0x8c02012c, 0xaf420068, +0xaf840064, 0x8f4202d8, 0x24420001, 0xaf4202d8, +0x8f4202d8, 0x8f820064, 0x30420020, 0x14400005, +0x32c24000, 0x8c03012c, 0x8f420068, 0x1462fff2, +0x32c24000, 0x14400002, 0x3c020001, 0x2c2b025, +0x8f420000, 0x10400007, 0x0, 0xaf80004c, +0x8f82004c, 0x1040fffd, 0x0, 0x10000005, +0x0, 0xaf800048, 0x8f820048, 0x1040fffd, +0x0, 0x8f820060, 0x1000ffb4, 0x34420800, +0x30c20010, 0x10400029, 0x24040010, 0x8c020124, +0xaf420058, 0xaf840064, 0x8f4202d4, 0x24420001, +0xaf4202d4, 0x8f4202d4, 0x8f820064, 0x30420010, +0x14400005, 0x32c22000, 0x8c030124, 0x8f420058, +0x1462fff2, 0x32c22000, 0x50400001, 0x36d68000, +0x8f420000, 0x10400007, 0x0, 0xaf80004c, +0x8f82004c, 0x1040fffd, 0x0, 0x10000005, +0x0, 0xaf800048, 0x8f820048, 0x1040fffd, +0x0, 0x8f820060, 0x34420100, 0xaf820060, +0x8f420000, 0x10400003, 0x0, 0x1000006c, +0xaf80004c, 0x1000006a, 0xaf800048, 0x30c20001, +0x10400004, 0x24020001, 0xaf820064, 0x10000064, +0x0, 0x30c20002, 0x1440000b, 0x3c050003, +0x3c040001, 0x24846244, 0x34a50500, 0x3821, +0xafa00010, 0xc002b3b, 0xafa00014, 0x2402ffc0, +0x10000057, 0xaf820064, 0x8c05022c, 0x8c02010c, +0x10a20048, 0x51080, 0x8c460300, 0x24a20001, +0x3045003f, 0x24020003, 0xac05022c, 0x61e02, +0x10620005, 0x24020010, 0x1062001d, 0x30c20fff, +0x10000039, 0x0, 0x8f4302a8, 0x8f440000, +0x30c20fff, 0xaf420048, 0x24630001, 0xaf4302a8, +0x10800007, 0x8f4202a8, 0xaf80004c, 0x8f82004c, +0x1040fffd, 0x0, 0x10000005, 0x0, +0xaf800048, 0x8f820048, 0x1040fffd, 0x0, +0x8f820060, 0x34420200, 0xaf820060, 0x8f420000, +0x1040001f, 0x0, 0x1000001b, 0x0, +0xaf420058, 0x32c22000, 0x50400001, 0x36d68000, +0x8f4202d4, 0x8f430000, 0x24420001, 0xaf4202d4, +0x10600007, 0x8f4202d4, 0xaf80004c, 0x8f82004c, +0x1040fffd, 0x0, 0x10000005, 0x0, +0xaf800048, 0x8f820048, 0x1040fffd, 0x0, +0x8f820060, 0x34420100, 0xaf820060, 0x8f420000, +0x10400003, 0x0, 0x10000006, 0xaf80004c, +0x10000004, 0xaf800048, 0xc002196, 0xc02021, +0x402821, 0x8c02010c, 0x14a20002, 0x24020002, +0xaf820064, 0x8f820064, 0x30420002, 0x14400004, +0x0, 0x8c02010c, 0x14a2ffac, 0x0, +0x8fbf0018, 0x3e00008, 0x27bd0020, 0x3e00008, +0x0, 0x27bdffa0, 0xafb00040, 0x808021, +0x101602, 0x2442ffff, 0x304300ff, 0x2c620013, +0xafbf0058, 0xafbe0054, 0xafb50050, 0xafb3004c, +0xafb20048, 0xafb10044, 0x104001f3, 0xafa50034, +0x31080, 0x3c010001, 0x220821, 0x8c226288, +0x400008, 0x0, 0x101302, 0x30440fff, +0x24020001, 0x10820005, 0x24020002, 0x1082000c, +0x2402fffe, 0x10000024, 0x3c050003, 0x8f430004, +0x3c020001, 0x8c426f04, 0xaf440200, 0xaf440204, +0x3c040001, 0x8c846e80, 0x10000009, 0x34630001, +0x8f430004, 0xaf440200, 0xaf440204, 0x3c040001, +0x8c846e80, 0x621824, 0x3c020001, 0x2442ca28, +0x21100, 0x21182, 0xaf430004, 0x3c030800, +0x431025, 0xac820038, 0x8f840054, 0x41442, +0x41c82, 0x431021, 0x41cc2, 0x431023, +0x41d02, 0x431021, 0x41d42, 0x431023, +0x10000009, 0xaf420208, 0x3c040001, 0x24846250, +0x34a51000, 0x2003021, 0x3821, 0xafa00010, +0xc002b3b, 0xafa00014, 0x8f4202a0, 0x24420001, +0xaf4202a0, 0x1000021f, 0x8f4202a0, 0x27b00028, +0x2002021, 0x24050210, 0xc002bbf, 0x24060008, +0xc002518, 0x2002021, 0x10000216, 0x0, +0x8faa0034, 0x27a40028, 0xa1880, 0x25420001, +0x3042003f, 0xafa20034, 0x8c650300, 0x8faa0034, +0x21080, 0x8c430300, 0x25420001, 0x3042003f, +0xafa20034, 0xac02022c, 0xafa50028, 0xc002518, +0xafa3002c, 0x10000203, 0x0, 0x27b00028, +0x2002021, 0x24050210, 0xc002bbf, 0x24060008, +0xc002657, 0x2002021, 0x100001fa, 0x0, +0x8faa0034, 0x27a40028, 0xa1880, 0x25420001, +0x3042003f, 0xafa20034, 0x8c650300, 0x8faa0034, +0x21080, 0x8c430300, 0x25420001, 0x3042003f, +0xafa20034, 0xac02022c, 0xafa50028, 0xc002657, +0xafa3002c, 0x100001e7, 0x0, 0x101302, +0x30430fff, 0x24020001, 0x10620005, 0x24020002, +0x1062001e, 0x3c020002, 0x10000033, 0x3c050003, +0x3c030002, 0x2c31024, 0x54400037, 0x2c3b025, +0x8f820228, 0x3c010001, 0x370821, 0xac2238d8, +0x8f82022c, 0x3c010001, 0x370821, 0xac2238dc, +0x8f820230, 0x3c010001, 0x370821, 0xac2238e0, +0x8f820234, 0x3c010001, 0x370821, 0xac2238e4, +0x2402ffff, 0xaf820228, 0xaf82022c, 0xaf820230, +0xaf820234, 0x10000020, 0x2c3b025, 0x2c21024, +0x10400012, 0x3c02fffd, 0x3c020001, 0x571021, +0x8c4238d8, 0xaf820228, 0x3c020001, 0x571021, +0x8c4238dc, 0xaf82022c, 0x3c020001, 0x571021, +0x8c4238e0, 0xaf820230, 0x3c020001, 0x571021, +0x8c4238e4, 0xaf820234, 0x3c02fffd, 0x3442ffff, +0x10000009, 0x2c2b024, 0x3c040001, 0x2484625c, +0x34a51100, 0x2003021, 0x3821, 0xafa00010, +0xc002b3b, 0xafa00014, 0x8f4202cc, 0x24420001, +0xaf4202cc, 0x1000019f, 0x8f4202cc, 0x101302, +0x30450fff, 0x24020001, 0x10a20005, 0x24020002, +0x10a2000d, 0x3c0408ff, 0x10000014, 0x3c050003, +0x3c0208ff, 0x3442ffff, 0x8f830220, 0x3c040004, +0x2c4b025, 0x621824, 0x34630008, 0xaf830220, +0x10000012, 0xaf450298, 0x3484fff7, 0x3c03fffb, +0x8f820220, 0x3463ffff, 0x2c3b024, 0x441024, +0xaf820220, 0x10000009, 0xaf450298, 0x3c040001, +0x24846268, 0x34a51200, 0x2003021, 0x3821, +0xafa00010, 0xc002b3b, 0xafa00014, 0x8f4202bc, +0x24420001, 0xaf4202bc, 0x10000176, 0x8f4202bc, +0x27840208, 0x24050200, 0xc002bbf, 0x24060008, +0x27440224, 0x24050200, 0xc002bbf, 0x24060008, +0x8f4202c4, 0x24420001, 0xaf4202c4, 0x10000169, +0x8f4202c4, 0x101302, 0x30430fff, 0x24020001, +0x10620011, 0x28620002, 0x50400005, 0x24020002, +0x10600007, 0x0, 0x10000017, 0x0, +0x1062000f, 0x0, 0x10000013, 0x0, +0x8c060248, 0x2021, 0xc005104, 0x24050004, +0x10000007, 0x0, 0x8c060248, 0x2021, +0xc005104, 0x24050004, 0x10000010, 0x0, +0x8c06024c, 0x2021, 0xc005104, 0x24050001, +0x1000000a, 0x0, 0x3c040001, 0x24846274, +0x3c050003, 0x34a51300, 0x2003021, 0x3821, +0xafa00010, 0xc002b3b, 0xafa00014, 0x8f4202c0, +0x24420001, 0xaf4202c0, 0x1000013a, 0x8f4202c0, +0xc002426, 0x0, 0x10000136, 0x0, +0x24020001, 0xa34205c5, 0x24100100, 0x8f4401a8, +0x8f4501ac, 0xafb00010, 0xafa00014, 0x8f420014, +0xafa20018, 0x8f420108, 0x26e60028, 0x40f809, +0x24070400, 0x1040fff5, 0x0, 0x10000125, +0x0, 0x3c03ffff, 0x34637fff, 0x8f420368, +0x8f440360, 0x2c3b024, 0x1821, 0xaf400058, +0xaf40005c, 0xaf400060, 0xaf400064, 0x441023, +0xaf420368, 0x3c020900, 0xaf400360, 0xafa20020, +0x8f5e0018, 0x27aa0020, 0x240200ff, 0x13c20002, +0xafaa003c, 0x27c30001, 0x8c020228, 0x609021, +0x1642000e, 0x1e38c0, 0x8f42033c, 0x24420001, +0xaf42033c, 0x8f42033c, 0x8c020228, 0x3c040001, +0x2484620c, 0x3c050009, 0xafa00014, 0xafa20010, +0x8fa60020, 0x1000006b, 0x34a50500, 0xf71021, +0x8fa30020, 0x8fa40024, 0xac4304c0, 0xac4404c4, +0x8f830054, 0x8f820054, 0x247003e8, 0x2021023, +0x2c4203e9, 0x1040001b, 0x9821, 0xe08821, +0x263504c0, 0x8f440178, 0x8f45017c, 0x2201821, +0x240a0004, 0xafaa0010, 0xafb20014, 0x8f48000c, +0x1021, 0x2f53021, 0xafa80018, 0x8f48010c, +0x24070008, 0xa32821, 0xa3482b, 0x822021, +0x100f809, 0x892021, 0x54400006, 0x24130001, +0x8f820054, 0x2021023, 0x2c4203e9, 0x1440ffe9, +0x0, 0x326200ff, 0x54400017, 0xaf520018, +0x8f420378, 0x24420001, 0xaf420378, 0x8f420378, +0x8f820120, 0x8faa003c, 0xafa20010, 0x8f820124, +0x3c040001, 0x24846218, 0x3c050009, 0xafa20014, +0x8d460000, 0x10000033, 0x34a50600, 0x8f420308, +0x24130001, 0x24420001, 0xaf420308, 0x8f420308, +0x1000001c, 0x326200ff, 0x8f830054, 0x8f820054, +0x247003e8, 0x2021023, 0x2c4203e9, 0x10400014, +0x9821, 0x24110010, 0x8f42000c, 0x8f440160, +0x8f450164, 0x8f860120, 0xafb10010, 0xafb20014, +0xafa20018, 0x8f42010c, 0x24070008, 0x40f809, +0x24c6001c, 0x1440ffe5, 0x0, 0x8f820054, +0x2021023, 0x2c4203e9, 0x1440ffef, 0x0, +0x326200ff, 0x14400011, 0x0, 0x8f420378, +0x24420001, 0xaf420378, 0x8f420378, 0x8f820120, +0x8faa003c, 0xafa20010, 0x8f820124, 0x3c040001, +0x24846220, 0x3c050009, 0xafa20014, 0x8d460000, +0x34a50700, 0xc002b3b, 0x3c03821, 0x8f4202b0, +0x24420001, 0xaf4202b0, 0x8f4202b0, 0x8f4202f8, +0x24420001, 0xaf4202f8, 0x1000008a, 0x8f4202f8, +0x8c02025c, 0x27440224, 0xaf4201f0, 0x8c020260, +0x24050200, 0x24060008, 0xc002bbf, 0xaf4201f8, +0x8f820220, 0x30420008, 0x14400002, 0x24020001, +0x24020002, 0xaf420298, 0x8f4202ac, 0x24420001, +0xaf4202ac, 0x10000077, 0x8f4202ac, 0x3c0200ff, +0x3442ffff, 0x2021824, 0x32c20180, 0x14400006, +0x3402fffb, 0x43102b, 0x14400003, 0x0, +0x1000006c, 0xaf4300bc, 0x3c040001, 0x24846280, +0x3c050003, 0x34a51500, 0x2003021, 0x3821, +0xafa00010, 0xc002b3b, 0xafa00014, 0x3c020700, +0x34421000, 0x101e02, 0x621825, 0xafa30020, +0x8f510018, 0x240200ff, 0x12220002, 0x8021, +0x26300001, 0x8c020228, 0x1602000e, 0x1130c0, +0x8f42033c, 0x24420001, 0xaf42033c, 0x8f42033c, +0x8c020228, 0x3c040001, 0x248461f4, 0x3c050009, +0xafa00014, 0xafa20010, 0x8fa60020, 0x1000003f, +0x34a50100, 0xd71021, 0x8fa30020, 0x8fa40024, +0xac4304c0, 0xac4404c4, 0xc01821, 0x8f440178, +0x8f45017c, 0x1021, 0x24070004, 0xafa70010, +0xafb00014, 0x8f48000c, 0x24c604c0, 0x2e63021, +0xafa80018, 0x8f48010c, 0x24070008, 0xa32821, +0xa3482b, 0x822021, 0x100f809, 0x892021, +0x1440000b, 0x24070008, 0x8f820120, 0xafa20010, +0x8f820124, 0x3c040001, 0x248461fc, 0x3c050009, +0xafa20014, 0x8fa60020, 0x1000001c, 0x34a50200, +0x8f440160, 0x8f450164, 0x8f43000c, 0xaf500018, +0x8f860120, 0x24020010, 0xafa20010, 0xafb00014, +0xafa30018, 0x8f42010c, 0x40f809, 0x24c6001c, +0x14400010, 0x0, 0x8f420340, 0x24420001, +0xaf420340, 0x8f420340, 0x8f820120, 0xafa20010, +0x8f820124, 0x3c040001, 0x24846204, 0x3c050009, +0xafa20014, 0x8fa60020, 0x34a50300, 0xc002b3b, +0x2203821, 0x8f4202e0, 0x24420001, 0xaf4202e0, +0x8f4202e0, 0x8f4202f0, 0x24420001, 0xaf4202f0, +0x8f4202f0, 0x8fa20034, 0x8fbf0058, 0x8fbe0054, +0x8fb50050, 0x8fb3004c, 0x8fb20048, 0x8fb10044, +0x8fb00040, 0x3e00008, 0x27bd0060, 0x27bdfff8, +0x2408ffff, 0x10a00014, 0x4821, 0x3c0aedb8, +0x354a8320, 0x90870000, 0x24840001, 0x3021, +0x1071026, 0x30420001, 0x10400002, 0x81842, +0x6a1826, 0x604021, 0x24c60001, 0x2cc20008, +0x1440fff7, 0x73842, 0x25290001, 0x125102b, +0x1440fff0, 0x0, 0x1001021, 0x3e00008, +0x27bd0008, 0x27bdffb0, 0xafbf0048, 0xafbe0044, +0xafb50040, 0xafb3003c, 0xafb20038, 0xafb10034, +0xafb00030, 0x8f870220, 0xafa70024, 0x8f870200, +0xafa7002c, 0x8f820220, 0x3c0308ff, 0x3463ffff, +0x431024, 0x34420004, 0xaf820220, 0x8f820200, +0x3c03c0ff, 0x3463ffff, 0x431024, 0x34420004, +0xaf820200, 0x8f530358, 0x8f55035c, 0x8f5e0360, +0x8f470364, 0xafa70014, 0x8f470368, 0xafa7001c, +0x8f4202d0, 0x274401c0, 0x24420001, 0xaf4202d0, +0x8f5002d0, 0x8f510204, 0x8f520200, 0xc002ba8, +0x24050400, 0xaf530358, 0xaf55035c, 0xaf5e0360, +0x8fa70014, 0xaf470364, 0x8fa7001c, 0xaf470368, +0xaf5002d0, 0xaf510204, 0xaf520200, 0x8c02025c, +0x27440224, 0xaf4201f0, 0x8c020260, 0x24050200, +0x24060008, 0xaf4201f8, 0x24020006, 0xc002bbf, +0xaf4201f4, 0x3c023b9a, 0x3442ca00, 0xaf4201fc, +0x240203e8, 0x24040002, 0x24030001, 0xaf420294, +0xaf440290, 0xaf43029c, 0x8f820220, 0x30420008, +0x10400004, 0x0, 0xaf430298, 0x10000003, +0x3021, 0xaf440298, 0x3021, 0x3c030001, +0x661821, 0x90636d00, 0x3461021, 0x24c60001, +0xa043022c, 0x2cc2000f, 0x1440fff8, 0x3461821, +0x24c60001, 0x8f820040, 0x24040080, 0x24050080, +0x21702, 0x24420030, 0xa062022c, 0x3461021, +0xc002ba8, 0xa040022c, 0x8fa70024, 0x30e20004, +0x14400006, 0x0, 0x8f820220, 0x3c0308ff, +0x3463fffb, 0x431024, 0xaf820220, 0x8fa7002c, +0x30e20004, 0x14400006, 0x0, 0x8f820200, +0x3c03c0ff, 0x3463fffb, 0x431024, 0xaf820200, +0x8fbf0048, 0x8fbe0044, 0x8fb50040, 0x8fb3003c, +0x8fb20038, 0x8fb10034, 0x8fb00030, 0x3e00008, +0x27bd0050, 0x0, 0x0, 0xaf400104, +0x24040001, 0x410c0, 0x2e21821, 0x24820001, +0x3c010001, 0x230821, 0xa42234d0, 0x402021, +0x2c820080, 0x1440fff8, 0x410c0, 0x24020001, +0x3c010001, 0x370821, 0xa42038d0, 0xaf420100, +0xaf800228, 0xaf80022c, 0xaf800230, 0xaf800234, +0x3e00008, 0x0, 0x27bdffe8, 0xafbf0014, +0xafb00010, 0x8f420104, 0x28420005, 0x10400026, +0x808021, 0x3c020001, 0x8f430104, 0x344230d0, +0x2e22021, 0x318c0, 0x621821, 0x2e31821, +0x83102b, 0x10400015, 0x1021, 0x96070000, +0x24840006, 0x24660006, 0x9482fffc, 0x14470009, +0x2821, 0x9483fffe, 0x96020002, 0x14620006, +0xa01021, 0x94820000, 0x96030004, 0x431026, +0x2c450001, 0xa01021, 0x14400009, 0x24840008, +0x86102b, 0x1440fff0, 0x1021, 0x304200ff, +0x14400030, 0x24020001, 0x1000002e, 0x1021, +0x1000fffa, 0x24020001, 0x2002021, 0xc00240c, +0x24050006, 0x3042007f, 0x218c0, 0x2e31021, +0x3c010001, 0x220821, 0x942230d0, 0x1040fff2, +0x2e31021, 0x3c060001, 0xc23021, 0x94c630d0, +0x10c0ffed, 0x3c080001, 0x350834d2, 0x96070000, +0x610c0, 0x572021, 0x882021, 0x94820000, +0x14470009, 0x2821, 0x94830002, 0x96020002, +0x14620006, 0xa01021, 0x94820004, 0x96030004, +0x431026, 0x2c450001, 0xa01021, 0x14400007, +0x610c0, 0x2e21021, 0x3c060001, 0xc23021, +0x94c634d0, 0x14c0ffeb, 0x610c0, 0x10c0ffd2, +0x24020001, 0x8fbf0014, 0x8fb00010, 0x3e00008, +0x27bd0018, 0x3e00008, 0x0, 0x27bdffb0, +0x801021, 0xafb00030, 0x24500002, 0x2002021, +0x24050006, 0xafb10034, 0x408821, 0xafbf0048, +0xafbe0044, 0xafb50040, 0xafb3003c, 0xc00240c, +0xafb20038, 0x3047007f, 0x710c0, 0x2e21021, +0x3c050001, 0xa22821, 0x94a530d0, 0x50a0001c, +0xa03021, 0x3c090001, 0x352934d2, 0x96280002, +0x510c0, 0x572021, 0x892021, 0x94820000, +0x14480009, 0x3021, 0x94830002, 0x96020002, +0x14620006, 0xc01021, 0x94820004, 0x96030004, +0x431026, 0x2c460001, 0xc01021, 0x14400007, +0x510c0, 0x2e21021, 0x3c050001, 0xa22821, +0x94a534d0, 0x14a0ffeb, 0x510c0, 0xa03021, +0x10c00014, 0x610c0, 0x571821, 0x3c010001, +0x230821, 0x8c2334d0, 0x571021, 0xafa30010, +0x3c010001, 0x220821, 0x8c2234d4, 0x3c040001, +0x24846394, 0xafa20014, 0x8e260000, 0x8e270004, +0x3c050004, 0xc002b3b, 0x34a50400, 0x10000063, +0x3c020800, 0x8f450100, 0x10a00006, 0x510c0, +0x2e21021, 0x3c010001, 0x220821, 0x942234d0, +0xaf420100, 0xa03021, 0x14c00011, 0x628c0, +0x710c0, 0x2e21021, 0xafa70010, 0x3c010001, +0x220821, 0x942230d0, 0x3c040001, 0x248463a0, +0xafa20014, 0x8e260000, 0x8e270004, 0x3c050004, +0xc002b3b, 0x34a50500, 0x10000048, 0x3c020800, +0xb71821, 0x3c020001, 0x96040000, 0x344234d2, +0x621821, 0xa4640000, 0x8e020002, 0x720c0, +0xac620002, 0x2e41021, 0x3c030001, 0x621821, +0x946330d0, 0x2e51021, 0x3c010001, 0x220821, +0xa42334d0, 0x2e41021, 0x3c010001, 0x220821, +0xa42630d0, 0x8f420104, 0x24420001, 0x28420080, +0x1040000f, 0x3c020002, 0x8f420104, 0x3c040001, +0x348430d2, 0x96030000, 0x210c0, 0x571021, +0x441021, 0xa4430000, 0x8e030002, 0xac430002, +0x8f420104, 0x24420001, 0xaf420104, 0x3c020002, +0x2c21024, 0x10400011, 0x72142, 0x3c030001, +0x346338d8, 0x24020003, 0x441023, 0x21080, +0x572021, 0x832021, 0x571021, 0x431021, +0x30e5001f, 0x8c430000, 0x24020001, 0xa21004, +0x621825, 0x1000000c, 0xac830000, 0x24020003, +0x441023, 0x21080, 0x5c2821, 0x5c1021, +0x30e4001f, 0x8c430228, 0x24020001, 0x821004, +0x621825, 0xaca30228, 0x3c020800, 0x34421000, +0x1821, 0xafa20020, 0x8f5e0018, 0x27aa0020, +0x240200ff, 0x13c20002, 0xafaa002c, 0x27c30001, +0x8c020228, 0x609021, 0x1642000e, 0x1e38c0, +0x8f42033c, 0x24420001, 0xaf42033c, 0x8f42033c, +0x8c020228, 0x3c040001, 0x2484635c, 0x3c050009, +0xafa00014, 0xafa20010, 0x8fa60020, 0x1000006b, +0x34a50500, 0xf71021, 0x8fa30020, 0x8fa40024, +0xac4304c0, 0xac4404c4, 0x8f830054, 0x8f820054, +0x247003e8, 0x2021023, 0x2c4203e9, 0x1040001b, +0x9821, 0xe08821, 0x263504c0, 0x8f440178, +0x8f45017c, 0x2201821, 0x240a0004, 0xafaa0010, +0xafb20014, 0x8f48000c, 0x1021, 0x2f53021, +0xafa80018, 0x8f48010c, 0x24070008, 0xa32821, +0xa3482b, 0x822021, 0x100f809, 0x892021, +0x54400006, 0x24130001, 0x8f820054, 0x2021023, +0x2c4203e9, 0x1440ffe9, 0x0, 0x326200ff, +0x54400017, 0xaf520018, 0x8f420378, 0x24420001, +0xaf420378, 0x8f420378, 0x8f820120, 0x8faa002c, +0xafa20010, 0x8f820124, 0x3c040001, 0x24846368, +0x3c050009, 0xafa20014, 0x8d460000, 0x10000033, +0x34a50600, 0x8f420308, 0x24130001, 0x24420001, +0xaf420308, 0x8f420308, 0x1000001c, 0x326200ff, +0x8f830054, 0x8f820054, 0x247003e8, 0x2021023, +0x2c4203e9, 0x10400014, 0x9821, 0x24110010, +0x8f42000c, 0x8f440160, 0x8f450164, 0x8f860120, +0xafb10010, 0xafb20014, 0xafa20018, 0x8f42010c, +0x24070008, 0x40f809, 0x24c6001c, 0x1440ffe5, +0x0, 0x8f820054, 0x2021023, 0x2c4203e9, +0x1440ffef, 0x0, 0x326200ff, 0x14400011, +0x0, 0x8f420378, 0x24420001, 0xaf420378, +0x8f420378, 0x8f820120, 0x8faa002c, 0xafa20010, +0x8f820124, 0x3c040001, 0x24846370, 0x3c050009, +0xafa20014, 0x8d460000, 0x34a50700, 0xc002b3b, +0x3c03821, 0x8f4202b4, 0x24420001, 0xaf4202b4, +0x8f4202b4, 0x8f4202f4, 0x24420001, 0xaf4202f4, +0x8f4202f4, 0x8fbf0048, 0x8fbe0044, 0x8fb50040, +0x8fb3003c, 0x8fb20038, 0x8fb10034, 0x8fb00030, +0x3e00008, 0x27bd0050, 0x27bdffa0, 0x801021, +0xafb00040, 0x24500002, 0x2002021, 0x24050006, +0xafb10044, 0x408821, 0xafbf0058, 0xafbe0054, +0xafb50050, 0xafb3004c, 0xc00240c, 0xafb20048, +0x3048007f, 0x810c0, 0x2e21021, 0x3c060001, +0xc23021, 0x94c630d0, 0x10c0001c, 0x3821, +0x3c0a0001, 0x354a34d2, 0x96290002, 0x610c0, +0x572021, 0x8a2021, 0x94820000, 0x14490009, +0x2821, 0x94830002, 0x96020002, 0x14620006, +0xa01021, 0x94820004, 0x96030004, 0x431026, +0x2c450001, 0xa01021, 0x14400008, 0x610c0, +0xc03821, 0x2e21021, 0x3c060001, 0xc23021, +0x94c634d0, 0x14c0ffea, 0x610c0, 0x14c00011, +0xafa70028, 0x810c0, 0x2e21021, 0xafa80010, +0x3c010001, 0x220821, 0x942230d0, 0x3c040001, +0x248463ac, 0xafa20014, 0x8e260000, 0x8e270004, +0x3c050004, 0xc002b3b, 0x34a50900, 0x10000075, +0x3c020800, 0x10e0000c, 0x610c0, 0x2e21021, +0x3c030001, 0x621821, 0x946334d0, 0x710c0, +0x2e21021, 0x3c010001, 0x220821, 0xa42334d0, +0x1000000b, 0x3c040001, 0x2e21021, 0x3c030001, +0x621821, 0x946334d0, 0x810c0, 0x2e21021, +0x3c010001, 0x220821, 0xa42330d0, 0x3c040001, +0x348430d0, 0x8f430100, 0x610c0, 0x2e21021, +0x3c010001, 0x220821, 0xa42334d0, 0x8f420104, +0x2e43821, 0x2821, 0x18400029, 0xaf460100, +0x24e60006, 0x94c3fffc, 0x96020000, 0x14620009, +0x2021, 0x94c3fffe, 0x96020002, 0x14620006, +0x801021, 0x94c20000, 0x96030004, 0x431026, +0x2c440001, 0x801021, 0x50400014, 0x24a50001, +0x8f420104, 0x2442ffff, 0xa2102a, 0x1040000b, +0x24e40004, 0x94820006, 0x8c830008, 0xa482fffe, +0xac830000, 0x8f420104, 0x24a50001, 0x2442ffff, +0xa2102a, 0x1440fff7, 0x24840008, 0x8f420104, +0x2442ffff, 0x10000006, 0xaf420104, 0x8f420104, +0x24c60008, 0xa2102a, 0x1440ffda, 0x24e70008, +0x810c0, 0x2e21021, 0x3c010001, 0x220821, +0x942230d0, 0x14400023, 0x3c020800, 0x3c020002, +0x2c21024, 0x10400012, 0x82142, 0x3c030001, +0x346338d8, 0x24020003, 0x441023, 0x21080, +0x572021, 0x832021, 0x571021, 0x431021, +0x3105001f, 0x24030001, 0x8c420000, 0xa31804, +0x31827, 0x431024, 0x1000000d, 0xac820000, +0x24020003, 0x441023, 0x21080, 0x5c2821, +0x5c1021, 0x3104001f, 0x24030001, 0x8c420228, +0x831804, 0x31827, 0x431024, 0xaca20228, +0x3c020800, 0x34422000, 0x1821, 0xafa20020, +0x8f5e0018, 0x27ab0020, 0x240200ff, 0x13c20002, +0xafab0034, 0x27c30001, 0x8c020228, 0x609021, +0x1642000e, 0x1e38c0, 0x8f42033c, 0x24420001, +0xaf42033c, 0x8f42033c, 0x8c020228, 0x3c040001, +0x2484635c, 0x3c050009, 0xafa00014, 0xafa20010, +0x8fa60020, 0x1000006b, 0x34a50500, 0xf71021, +0x8fa30020, 0x8fa40024, 0xac4304c0, 0xac4404c4, +0x8f830054, 0x8f820054, 0x247003e8, 0x2021023, +0x2c4203e9, 0x1040001b, 0x9821, 0xe08821, +0x263504c0, 0x8f440178, 0x8f45017c, 0x2201821, +0x240b0004, 0xafab0010, 0xafb20014, 0x8f48000c, +0x1021, 0x2f53021, 0xafa80018, 0x8f48010c, +0x24070008, 0xa32821, 0xa3482b, 0x822021, +0x100f809, 0x892021, 0x54400006, 0x24130001, +0x8f820054, 0x2021023, 0x2c4203e9, 0x1440ffe9, +0x0, 0x326200ff, 0x54400017, 0xaf520018, +0x8f420378, 0x24420001, 0xaf420378, 0x8f420378, +0x8f820120, 0x8fab0034, 0xafa20010, 0x8f820124, +0x3c040001, 0x24846368, 0x3c050009, 0xafa20014, +0x8d660000, 0x10000033, 0x34a50600, 0x8f420308, +0x24130001, 0x24420001, 0xaf420308, 0x8f420308, +0x1000001c, 0x326200ff, 0x8f830054, 0x8f820054, +0x247003e8, 0x2021023, 0x2c4203e9, 0x10400014, +0x9821, 0x24110010, 0x8f42000c, 0x8f440160, +0x8f450164, 0x8f860120, 0xafb10010, 0xafb20014, +0xafa20018, 0x8f42010c, 0x24070008, 0x40f809, +0x24c6001c, 0x1440ffe5, 0x0, 0x8f820054, +0x2021023, 0x2c4203e9, 0x1440ffef, 0x0, +0x326200ff, 0x14400011, 0x0, 0x8f420378, +0x24420001, 0xaf420378, 0x8f420378, 0x8f820120, +0x8fab0034, 0xafa20010, 0x8f820124, 0x3c040001, +0x24846370, 0x3c050009, 0xafa20014, 0x8d660000, +0x34a50700, 0xc002b3b, 0x3c03821, 0x8f4202b8, +0x24420001, 0xaf4202b8, 0x8f4202b8, 0x8f4202f4, +0x24420001, 0xaf4202f4, 0x8f4202f4, 0x8fbf0058, +0x8fbe0054, 0x8fb50050, 0x8fb3004c, 0x8fb20048, +0x8fb10044, 0x8fb00040, 0x3e00008, 0x27bd0060, +0x0, 0x0, 0x0, 0x27bdffe0, +0x27644000, 0xafbf0018, 0xc002ba8, 0x24051000, +0x3c030001, 0x34632cc0, 0x3c040001, 0x34842ec8, +0x24020020, 0xaf82011c, 0x2e31021, 0xaf800100, +0xaf800104, 0xaf800108, 0xaf800110, 0xaf800114, +0xaf800118, 0xaf800120, 0xaf800124, 0xaf800128, +0xaf800130, 0xaf800134, 0xaf800138, 0xaf4200ec, +0x2e31021, 0xaf4200f0, 0x2e41021, 0xaf4200f4, +0x2e41021, 0xaf4200f8, 0x3c020001, 0x571021, +0x904240f4, 0x1440001c, 0x3c050001, 0x8f82011c, +0x3c040001, 0x24846470, 0x3c050001, 0x34420001, +0xaf82011c, 0xafa00010, 0xafa00014, 0x8f86011c, +0x34a50100, 0xc002b3b, 0x3821, 0x8c020218, +0x30420040, 0x10400014, 0x0, 0x8f82011c, +0x3c040001, 0x2484647c, 0x3c050001, 0x34420004, +0xaf82011c, 0xafa00010, 0xafa00014, 0x8f86011c, +0x10000007, 0x34a50200, 0x3c040001, 0x24846484, +0xafa00010, 0xafa00014, 0x8f86011c, 0x34a50300, +0xc002b3b, 0x3821, 0x8fbf0018, 0x3e00008, +0x27bd0020, 0x8fa90010, 0x8f83012c, 0x8faa0014, +0x8fab0018, 0x1060000a, 0x27624fe0, 0x14620002, +0x24680020, 0x27684800, 0x8f820128, 0x11020004, +0x0, 0x8f820124, 0x15020007, 0x0, +0x8f430334, 0x1021, 0x24630001, 0xaf430334, +0x10000039, 0x8f430334, 0xac640000, 0xac650004, +0xac660008, 0xa467000e, 0xac690018, 0xac6a001c, +0xac6b0010, 0xac620014, 0xaf880120, 0x8f4200fc, +0x8f4400f4, 0x2442ffff, 0xaf4200fc, 0x8c820000, +0x10490005, 0x3042ff8f, 0x10400019, 0x3122ff8f, +0x10400018, 0x3c020001, 0x8c830004, 0x2c620010, +0x10400013, 0x3c020001, 0x24630001, 0xac830004, +0x8f4300f8, 0x344230c8, 0x2e21021, 0x54620004, +0x24620008, 0x3c020001, 0x34422ec8, 0x2e21021, +0x14440015, 0x24020001, 0x8f820128, 0x24420020, +0xaf820128, 0x8f820128, 0x1000000f, 0x24020001, +0x3c020001, 0x344230c8, 0x2e21021, 0x54820004, +0x24820008, 0x3c020001, 0x34422ec8, 0x2e21021, +0x402021, 0x24020001, 0xaf4400f4, 0xac890000, +0xac820004, 0x24020001, 0x3e00008, 0x0, +0x3e00008, 0x0, 0x8fa90010, 0x8f83010c, +0x8faa0014, 0x8fab0018, 0x1060000a, 0x276247e0, +0x14620002, 0x24680020, 0x27684000, 0x8f820108, +0x11020004, 0x0, 0x8f820104, 0x15020007, +0x0, 0x8f430338, 0x1021, 0x24630001, +0xaf430338, 0x10000035, 0x8f430338, 0xac640000, +0xac650004, 0xac660008, 0xa467000e, 0xac690018, +0xac6a001c, 0xac6b0010, 0xac620014, 0xaf880100, +0x8f4400ec, 0x8c820000, 0x30420006, 0x10400019, +0x31220006, 0x10400018, 0x3c020001, 0x8c830004, +0x2c620010, 0x10400013, 0x3c020001, 0x24630001, +0xac830004, 0x8f4300f0, 0x34422ec0, 0x2e21021, +0x54620004, 0x24620008, 0x3c020001, 0x34422cc0, +0x2e21021, 0x14440015, 0x24020001, 0x8f820108, +0x24420020, 0xaf820108, 0x8f820108, 0x1000000f, +0x24020001, 0x3c020001, 0x34422ec0, 0x2e21021, +0x54820004, 0x24820008, 0x3c020001, 0x34422cc0, +0x2e21021, 0x402021, 0x24020001, 0xaf4400ec, +0xac890000, 0xac820004, 0x24020001, 0x3e00008, +0x0, 0x3e00008, 0x0, 0x27bdffd8, +0x3c040001, 0x2484648c, 0x3c050001, 0xafbf0024, +0xafb20020, 0xafb1001c, 0xafb00018, 0x8f900104, +0x8f9100b0, 0x8f92011c, 0x34a52500, 0x8f820100, +0x2403021, 0x2203821, 0xafa20010, 0xc002b3b, +0xafb00014, 0x8e020008, 0xafa20010, 0x8e02000c, +0x3c040001, 0x24846498, 0xafa20014, 0x8e060000, +0x8e070004, 0x3c050001, 0xc002b3b, 0x34a52510, +0x8e020018, 0xafa20010, 0x8e02001c, 0x3c040001, +0x248464a4, 0xafa20014, 0x8e060010, 0x8e070014, +0x3c050001, 0xc002b3b, 0x34a52520, 0x3c027f00, +0x2221024, 0x3c030800, 0x54430016, 0x3c030200, +0x8f82009c, 0x3042ffff, 0x14400012, 0x3c030200, +0x3c040001, 0x248464b0, 0x3c050002, 0x34a5f030, +0x3021, 0x3821, 0x36420002, 0xaf82011c, +0x36220001, 0xaf8200b0, 0xaf900104, 0xaf92011c, +0xafa00010, 0xc002b3b, 0xafa00014, 0x10000024, +0x0, 0x2c31024, 0x1040000d, 0x2231024, +0x1040000b, 0x36420002, 0xaf82011c, 0x36220001, +0xaf8200b0, 0xaf900104, 0xaf92011c, 0x8f420330, +0x24420001, 0xaf420330, 0x10000015, 0x8f420330, +0x3c040001, 0x248464b8, 0x240202a9, 0xafa20010, +0xafa00014, 0x8f860144, 0x3c070001, 0x24e764c0, +0xc002b3b, 0x3405dead, 0x8f82011c, 0x34420002, +0xaf82011c, 0x8f820220, 0x34420004, 0xaf820220, +0x8f820140, 0x3c030001, 0x431025, 0xaf820140, +0x8fbf0024, 0x8fb20020, 0x8fb1001c, 0x8fb00018, +0x3e00008, 0x27bd0028, 0x27bdffd8, 0x3c040001, +0x248464e8, 0x3c050001, 0xafbf0024, 0xafb20020, +0xafb1001c, 0xafb00018, 0x8f900124, 0x8f9100a0, +0x8f92011c, 0x34a52600, 0x8f820120, 0x2403021, +0x2203821, 0xafa20010, 0xc002b3b, 0xafb00014, +0x8e020008, 0xafa20010, 0x8e02000c, 0x3c040001, +0x248464f4, 0xafa20014, 0x8e060000, 0x8e070004, +0x3c050001, 0xc002b3b, 0x34a52610, 0x8e020018, +0xafa20010, 0x8e02001c, 0x3c040001, 0x24846500, +0xafa20014, 0x8e060010, 0x8e070014, 0x3c050001, +0xc002b3b, 0x34a52620, 0x3c027f00, 0x2221024, +0x3c030800, 0x54430016, 0x3c030200, 0x8f8200ac, +0x3042ffff, 0x14400012, 0x3c030200, 0x3c040001, +0x2484650c, 0x3c050001, 0x34a5f030, 0x3021, +0x3821, 0x36420002, 0xaf82011c, 0x36220001, +0xaf8200a0, 0xaf900124, 0xaf92011c, 0xafa00010, +0xc002b3b, 0xafa00014, 0x10000024, 0x0, +0x2c31024, 0x1040000d, 0x2231024, 0x1040000b, +0x36420002, 0xaf82011c, 0x36220001, 0xaf8200a0, +0xaf900124, 0xaf92011c, 0x8f42032c, 0x24420001, +0xaf42032c, 0x10000015, 0x8f42032c, 0x3c040001, +0x248464b8, 0x240202e2, 0xafa20010, 0xafa00014, +0x8f860144, 0x3c070001, 0x24e764c0, 0xc002b3b, +0x3405dead, 0x8f82011c, 0x34420002, 0xaf82011c, +0x8f820220, 0x34420004, 0xaf820220, 0x8f820140, +0x3c030001, 0x431025, 0xaf820140, 0x8fbf0024, +0x8fb20020, 0x8fb1001c, 0x8fb00018, 0x3e00008, +0x27bd0028, 0x6021, 0x5021, 0x3021, +0x2821, 0x6821, 0x4821, 0x7821, +0x7021, 0x8f880124, 0x8f870104, 0x1580002e, +0x8f8b011c, 0x11a00014, 0x31620800, 0x8f820120, +0x10460029, 0x0, 0x3c040001, 0x8c846ee4, +0x8cc20000, 0x8cc30004, 0xac820000, 0xac830004, +0x8cc20008, 0xac820008, 0x94c2000e, 0xa482000e, +0x8cc20010, 0x240c0001, 0xac820010, 0x8cc20014, +0x10000012, 0x24c60020, 0x10400017, 0x0, +0x3c040001, 0x8c846ee4, 0x8d020000, 0x8d030004, +0xac820000, 0xac830004, 0x8d020008, 0xac820008, +0x9502000e, 0xa482000e, 0x8d020010, 0x25060020, +0xac820010, 0x8d020014, 0x240c0001, 0xc01821, +0xac820014, 0x27624fe0, 0x43102b, 0x54400001, +0x27634800, 0x603021, 0x1540002f, 0x31620100, +0x11200014, 0x31628000, 0x8f820100, 0x1045002a, +0x31620100, 0x3c040001, 0x8c846ee0, 0x8ca20000, +0x8ca30004, 0xac820000, 0xac830004, 0x8ca20008, +0xac820008, 0x94a2000e, 0xa482000e, 0x8ca20010, +0x240a0001, 0xac820010, 0x8ca20014, 0x10000012, +0x24a50020, 0x10400018, 0x31620100, 0x3c040001, +0x8c846ee0, 0x8ce20000, 0x8ce30004, 0xac820000, +0xac830004, 0x8ce20008, 0xac820008, 0x94e2000e, +0xa482000e, 0x8ce20010, 0x24e50020, 0xac820010, +0x8ce20014, 0x240a0001, 0xa01821, 0xac820014, +0x276247e0, 0x43102b, 0x54400001, 0x27634000, +0x602821, 0x31620100, 0x5440001d, 0x31621000, +0x11a00009, 0x31a20800, 0x10400004, 0x25020020, +0x8f8200a8, 0xa5e20000, 0x25020020, 0xaf820124, +0x8f880124, 0x6821, 0x11800011, 0x31621000, +0x3c040001, 0x8c846ee4, 0x8c820000, 0x8c830004, +0xaf820080, 0xaf830084, 0x8c820008, 0xaf8200a4, +0x9482000e, 0xaf8200ac, 0x8c820010, 0x6021, +0xaf8200a0, 0x8c8d0010, 0x8c8f0014, 0x31621000, +0x1440ff82, 0x0, 0x1120000f, 0x31220800, +0x10400004, 0x3c020002, 0x8f8200b8, 0xa5c20000, +0x3c020002, 0x1221024, 0x10400004, 0x24e20020, +0x8f8200b4, 0xaf8200d4, 0x24e20020, 0xaf820104, +0x8f870104, 0x4821, 0x1140ff70, 0x0, +0x3c040001, 0x8c846ee0, 0x8c820000, 0x8c830004, +0xaf820090, 0xaf830094, 0x8c820008, 0xaf8200b4, +0x9482000e, 0xaf82009c, 0x8c820010, 0x5021, +0xaf8200b0, 0x8c890010, 0x1000ff60, 0x8c8e0014, +0x3e00008, 0x0, 0x6021, 0x5821, +0x3021, 0x2821, 0x6821, 0x5021, +0x7821, 0x7021, 0x8f880124, 0x8f870104, +0x3c180100, 0x1580002e, 0x8f89011c, 0x11a00014, +0x31220800, 0x8f820120, 0x10460029, 0x0, +0x3c040001, 0x8c846ee4, 0x8cc20000, 0x8cc30004, +0xac820000, 0xac830004, 0x8cc20008, 0xac820008, +0x94c2000e, 0xa482000e, 0x8cc20010, 0x240c0001, +0xac820010, 0x8cc20014, 0x10000012, 0x24c60020, +0x10400017, 0x0, 0x3c040001, 0x8c846ee4, +0x8d020000, 0x8d030004, 0xac820000, 0xac830004, +0x8d020008, 0xac820008, 0x9502000e, 0xa482000e, +0x8d020010, 0x25060020, 0xac820010, 0x8d020014, +0x240c0001, 0xc01821, 0xac820014, 0x27624fe0, +0x43102b, 0x54400001, 0x27634800, 0x603021, +0x1560002f, 0x31220100, 0x11400014, 0x31228000, +0x8f820100, 0x1045002a, 0x31220100, 0x3c040001, +0x8c846ee0, 0x8ca20000, 0x8ca30004, 0xac820000, +0xac830004, 0x8ca20008, 0xac820008, 0x94a2000e, +0xa482000e, 0x8ca20010, 0x240b0001, 0xac820010, +0x8ca20014, 0x10000012, 0x24a50020, 0x10400018, +0x31220100, 0x3c040001, 0x8c846ee0, 0x8ce20000, +0x8ce30004, 0xac820000, 0xac830004, 0x8ce20008, +0xac820008, 0x94e2000e, 0xa482000e, 0x8ce20010, +0x24e50020, 0xac820010, 0x8ce20014, 0x240b0001, +0xa01821, 0xac820014, 0x276247e0, 0x43102b, +0x54400001, 0x27634000, 0x602821, 0x31220100, +0x5440001d, 0x31221000, 0x11a00009, 0x31a20800, +0x10400004, 0x25020020, 0x8f8200a8, 0xa5e20000, +0x25020020, 0xaf820124, 0x8f880124, 0x6821, +0x11800011, 0x31221000, 0x3c040001, 0x8c846ee4, +0x8c820000, 0x8c830004, 0xaf820080, 0xaf830084, +0x8c820008, 0xaf8200a4, 0x9482000e, 0xaf8200ac, +0x8c820010, 0x6021, 0xaf8200a0, 0x8c8d0010, +0x8c8f0014, 0x31221000, 0x14400022, 0x0, +0x1140000f, 0x31420800, 0x10400004, 0x3c020002, +0x8f8200b8, 0xa5c20000, 0x3c020002, 0x1421024, +0x10400004, 0x24e20020, 0x8f8200b4, 0xaf8200d4, +0x24e20020, 0xaf820104, 0x8f870104, 0x5021, +0x11600010, 0x0, 0x3c040001, 0x8c846ee0, +0x8c820000, 0x8c830004, 0xaf820090, 0xaf830094, +0x8c820008, 0xaf8200b4, 0x9482000e, 0xaf82009c, +0x8c820010, 0x5821, 0xaf8200b0, 0x8c8a0010, +0x8c8e0014, 0x8f820070, 0x3c031000, 0x431024, +0x1040ff5c, 0x0, 0x8f820054, 0x24420005, +0xaf820078, 0x8c040234, 0x10800016, 0x1821, +0x3c020001, 0x571021, 0x8c4240e8, 0x24420005, +0x3c010001, 0x370821, 0xac2240e8, 0x3c020001, +0x571021, 0x8c4240e8, 0x44102b, 0x14400009, +0x24020001, 0x3c030080, 0x3c010001, 0x370821, +0xac2040e8, 0x3c010001, 0x370821, 0x1000000c, +0xa02240f0, 0x3c020001, 0x571021, 0x904240f0, +0x14400006, 0x3c020080, 0x3c020001, 0x571021, +0x904240f1, 0x10400002, 0x3c020080, 0x621825, +0x8c040230, 0x10800013, 0x0, 0x3c020001, +0x571021, 0x8c4240ec, 0x24420005, 0x3c010001, +0x370821, 0xac2240ec, 0x3c020001, 0x571021, +0x8c4240ec, 0x44102b, 0x14400006, 0x0, +0x3c010001, 0x370821, 0xac2040ec, 0x10000006, +0x781825, 0x3c020001, 0x571021, 0x904240f2, +0x54400001, 0x781825, 0x1060ff1a, 0x0, +0x8f420000, 0x10400007, 0x0, 0xaf80004c, +0x8f82004c, 0x1040fffd, 0x0, 0x10000005, +0x0, 0xaf800048, 0x8f820048, 0x1040fffd, +0x0, 0x8f820060, 0x431025, 0xaf820060, +0x8f420000, 0x10400003, 0x0, 0x1000ff05, +0xaf80004c, 0x1000ff03, 0xaf800048, 0x3e00008, +0x0, 0x0, 0x0, 0x3c020001, +0x8c426d28, 0x27bdffe8, 0xafbf0014, 0x14400012, +0xafb00010, 0x3c100001, 0x26106f90, 0x2002021, +0xc002ba8, 0x24052000, 0x26021fe0, 0x3c010001, +0xac226eec, 0x3c010001, 0xac226ee8, 0xac020250, +0x24022000, 0xac100254, 0xac020258, 0x24020001, +0x3c010001, 0xac226d28, 0x8fbf0014, 0x8fb00010, +0x3e00008, 0x27bd0018, 0x3c090001, 0x8d296eec, +0x8c820000, 0x8fa30010, 0x8fa80014, 0xad220000, +0x8c820004, 0xad250008, 0xad220004, 0x8f820054, +0xad260010, 0xad270014, 0xad230018, 0xad28001c, +0xad22000c, 0x2529ffe0, 0x3c020001, 0x24426f90, +0x122102b, 0x10400003, 0x0, 0x3c090001, +0x8d296ee8, 0x3c020001, 0x8c426d10, 0xad220000, +0x3c020001, 0x8c426d10, 0x3c010001, 0xac296eec, +0xad220004, 0xac090250, 0x3e00008, 0x0, +0x27bdffd0, 0xafb00010, 0x3c100001, 0x8e106eec, +0x3c020001, 0x8c426d10, 0xafb10014, 0x808821, +0xafbe0024, 0x8fbe0040, 0x8fa40048, 0xafb20018, +0xa09021, 0xafbf0028, 0xafb50020, 0xafb3001c, +0xae020000, 0x3c020001, 0x8c426d10, 0xc09821, +0xe0a821, 0x10800006, 0xae020004, 0x26050008, +0xc002bb3, 0x24060018, 0x10000005, 0x2610ffe0, +0x26040008, 0xc002ba8, 0x24050018, 0x2610ffe0, +0x3c030001, 0x24636f90, 0x203102b, 0x10400003, +0x0, 0x3c100001, 0x8e106ee8, 0x8e220000, +0xae020000, 0x8e220004, 0xae120008, 0xae020004, +0x8f820054, 0xae130010, 0xae150014, 0xae1e0018, +0x8fa80044, 0xae08001c, 0xae02000c, 0x2610ffe0, +0x203102b, 0x10400003, 0x0, 0x3c100001, +0x8e106ee8, 0x3c020001, 0x8c426d10, 0xae020000, +0x3c020001, 0x8c426d10, 0x3c010001, 0xac306eec, +0xae020004, 0xac100250, 0x8fbf0028, 0x8fbe0024, +0x8fb50020, 0x8fb3001c, 0x8fb20018, 0x8fb10014, +0x8fb00010, 0x3e00008, 0x27bd0030, 0x851821, +0x83102b, 0x10400006, 0x0, 0xac800000, +0x24840004, 0x83102b, 0x5440fffd, 0xac800000, +0x3e00008, 0x0, 0xa61821, 0xa3102b, +0x10400007, 0x0, 0x8c820000, 0xaca20000, +0x24a50004, 0xa3102b, 0x1440fffb, 0x24840004, +0x3e00008, 0x0, 0x861821, 0x83102b, +0x10400007, 0x0, 0x8ca20000, 0xac820000, +0x24840004, 0x83102b, 0x1440fffb, 0x24a50004, +0x3e00008, 0x0, 0x63080, 0x861821, +0x83102b, 0x10400006, 0x0, 0xac850000, +0x24840004, 0x83102b, 0x5440fffd, 0xac850000, +0x3e00008, 0x0, 0x0, 0x26e50028, +0xa03021, 0x274301c0, 0x8f4d0358, 0x8f47035c, +0x8f480360, 0x8f490364, 0x8f4a0368, 0x8f4b0204, +0x8f4c0200, 0x24640400, 0x64102b, 0x10400008, +0x3c0208ff, 0x8cc20000, 0xac620000, 0x24630004, +0x64102b, 0x1440fffb, 0x24c60004, 0x3c0208ff, +0x3442ffff, 0x3c03c0ff, 0xaf4d0358, 0xaf47035c, +0xaf480360, 0xaf490364, 0xaf4a0368, 0xaf4b0204, +0xaf4c0200, 0x8f840220, 0x3463ffff, 0x8f860200, +0x821024, 0x34420004, 0xc31824, 0x34630004, +0xaf820220, 0xaf830200, 0x8ca20214, 0xac020084, +0x8ca20218, 0xac020088, 0x8ca2021c, 0xac02008c, +0x8ca20220, 0xac020090, 0x8ca20224, 0xac020094, +0x8ca20228, 0xac020098, 0x8ca2022c, 0xac02009c, +0x8ca20230, 0xac0200a0, 0x8ca20234, 0xac0200a4, +0x8ca20238, 0xac0200a8, 0x8ca2023c, 0xac0200ac, +0x8ca20240, 0xac0200b0, 0x8ca20244, 0xac0200b4, +0x8ca20248, 0xac0200b8, 0x8ca2024c, 0xac0200bc, +0x8ca2001c, 0xac020080, 0x8ca20018, 0xac0200c0, +0x8ca20020, 0xac0200cc, 0x8ca20024, 0xac0200d0, +0x8ca201d0, 0xac0200e0, 0x8ca201d4, 0xac0200e4, +0x8ca201d8, 0xac0200e8, 0x8ca201dc, 0xac0200ec, +0x8ca201e0, 0xac0200f0, 0x8ca20098, 0x8ca3009c, +0xac0300fc, 0x8ca200a8, 0x8ca300ac, 0xac0300f4, +0x8ca200a0, 0x8ca300a4, 0x30840004, 0xac0300f8, +0x14800007, 0x30c20004, 0x8f820220, 0x3c0308ff, +0x3463fffb, 0x431024, 0xaf820220, 0x30c20004, +0x14400006, 0x0, 0x8f820200, 0x3c03c0ff, +0x3463fffb, 0x431024, 0xaf820200, 0x8f4202dc, +0xa34005c5, 0x24420001, 0xaf4202dc, 0x8f4202dc, +0x3e00008, 0x0, 0x27bdffd8, 0xafbf0024, +0xafb00020, 0x8f430024, 0x8f420020, 0x10620038, +0x0, 0x8f430020, 0x8f420024, 0x622023, +0x4810003, 0x0, 0x8f420040, 0x822021, +0x8f430030, 0x8f420024, 0x43102b, 0x14400005, +0x0, 0x8f430040, 0x8f420024, 0x10000005, +0x621023, 0x8f420030, 0x8f430024, 0x431023, +0x2442ffff, 0x406021, 0x8c102a, 0x54400001, +0x806021, 0x8f4a0024, 0x8f490040, 0x8f480024, +0x8f440180, 0x8f450184, 0x8f460024, 0x8f4b001c, +0x24070001, 0xafa70010, 0x84100, 0x1001821, +0x14c5021, 0x2529ffff, 0x1498024, 0xafb00014, +0x8f470014, 0x1021, 0x63100, 0xafa70018, +0xa32821, 0xa3382b, 0x822021, 0x872021, +0x8f420108, 0x1663021, 0x40f809, 0xc3900, +0x54400001, 0xaf500024, 0x8f430024, 0x8f420020, +0x14620018, 0x0, 0x8f420000, 0x10400007, +0x0, 0xaf80004c, 0x8f82004c, 0x1040fffd, +0x0, 0x10000005, 0x0, 0xaf800048, +0x8f820048, 0x1040fffd, 0x0, 0x8f820060, +0x2403ffef, 0x431024, 0xaf820060, 0x8f420000, +0x10400003, 0x0, 0x10000002, 0xaf80004c, +0xaf800048, 0x8fbf0024, 0x8fb00020, 0x3e00008, +0x27bd0028, 0x3e00008, 0x0, 0x27bdffc0, +0x32c20020, 0xafbf0038, 0xafb30034, 0xafb20030, +0xafb1002c, 0x10400004, 0xafb00028, 0x8f530028, +0x10000002, 0x0, 0x8f530020, 0x8f420030, +0x105300eb, 0x21100, 0x8f43001c, 0x628021, +0x8e040000, 0x8e050004, 0x96120008, 0x8f420090, +0x9611000a, 0x3246ffff, 0x46102a, 0x10400017, +0x0, 0x8f8200d8, 0x8f430098, 0x431023, +0x2442dcbe, 0xaf420090, 0x8f420090, 0x2842dcbf, +0x10400005, 0x0, 0x8f420090, 0x8f430144, +0x431021, 0xaf420090, 0x8f420090, 0x46102a, +0x10400006, 0x0, 0x8f420348, 0x24420001, +0xaf420348, 0x100000e1, 0x8f420348, 0x8f8200fc, +0x14400006, 0x0, 0x8f420344, 0x24420001, +0xaf420344, 0x100000d9, 0x8f420344, 0x934205c2, +0x1040000b, 0x32c20008, 0x10400008, 0x32220200, +0x10400006, 0x3c034000, 0x9602000e, 0xaf4300ac, +0x21400, 0x10000002, 0xaf4200b0, 0xaf4000ac, +0x32220004, 0x1040007f, 0x32220800, 0x10400003, +0x3247ffff, 0x10000002, 0x24020020, 0x24020004, +0xafa20010, 0x8f420030, 0xafa20014, 0x8f420010, +0x3c030002, 0x431025, 0xafa20018, 0x8f460098, +0x8f420108, 0x40f809, 0x0, 0x104000b7, +0x0, 0x8f42009c, 0x8f430094, 0x2421021, +0xaf42009c, 0xae03000c, 0x8f4200ac, 0x10400008, +0x3c034000, 0x8f420094, 0x431025, 0xafa20020, +0x8f42009c, 0x8f4300b0, 0x10000004, 0x431025, +0x8f420094, 0xafa20020, 0x8f42009c, 0xafa20024, +0x8f8200fc, 0x8fa30020, 0x8fa40024, 0xac430000, +0xac440004, 0x24420008, 0xaf8200f0, 0x8f42009c, +0x8f440270, 0x8f450274, 0x401821, 0x1021, +0xa32821, 0xa3302b, 0x822021, 0x862021, +0x32230060, 0x24020040, 0xaf440270, 0xaf450274, +0x10620017, 0x2c620041, 0x10400005, 0x24020020, +0x10620008, 0x24020001, 0x10000026, 0x0, +0x24020060, 0x10620019, 0x24020001, 0x10000021, +0x0, 0x8f420278, 0x8f43027c, 0x24630001, +0x2c640001, 0x441021, 0xaf420278, 0xaf43027c, +0x8f420278, 0x8f43027c, 0x10000016, 0x24020001, +0x8f420280, 0x8f430284, 0x24630001, 0x2c640001, +0x441021, 0xaf420280, 0xaf430284, 0x8f420280, +0x8f430284, 0x1000000b, 0x24020001, 0x8f420288, +0x8f43028c, 0x24630001, 0x2c640001, 0x441021, +0xaf420288, 0xaf43028c, 0x8f420288, 0x8f43028c, +0x24020001, 0xa34205c2, 0x8f420098, 0x3244ffff, +0x2406fff8, 0x8f45013c, 0x441021, 0x24420007, +0x461024, 0x24840007, 0xaf420094, 0x8f420090, +0x8f430094, 0x862024, 0x441023, 0x65182b, +0x14600005, 0xaf420090, 0x8f420094, 0x8f430144, +0x431023, 0xaf420094, 0x8f420094, 0x10000023, +0xaf40009c, 0x3247ffff, 0x50e00022, 0x32c20020, +0x14400002, 0x24020010, 0x24020002, 0xafa20010, +0x8f420030, 0xafa20014, 0x8f420010, 0xafa20018, +0x8f460098, 0x8f420108, 0x40f809, 0x0, +0x1040003a, 0x3245ffff, 0x8f420098, 0x8f430090, +0x8f46013c, 0x451021, 0xaf420098, 0x8f42009c, +0x8f440098, 0xa34005c2, 0x651823, 0xaf430090, +0x451021, 0x86202b, 0x14800005, 0xaf42009c, +0x8f420098, 0x8f430144, 0x431023, 0xaf420098, +0x32c20020, 0x10400005, 0x0, 0x8f420358, +0x2442ffff, 0xaf420358, 0x8f420358, 0x8f420030, +0x8f430040, 0x24420001, 0x2463ffff, 0x431024, +0xaf420030, 0x8f420030, 0x14530018, 0x0, +0x8f420000, 0x10400007, 0x0, 0xaf80004c, +0x8f82004c, 0x1040fffd, 0x0, 0x10000005, +0x0, 0xaf800048, 0x8f820048, 0x1040fffd, +0x0, 0x8f820060, 0x2403fff7, 0x431024, +0xaf820060, 0x8f420000, 0x10400003, 0x0, +0x10000002, 0xaf80004c, 0xaf800048, 0x8fbf0038, +0x8fb30034, 0x8fb20030, 0x8fb1002c, 0x8fb00028, +0x3e00008, 0x27bd0040, 0x3e00008, 0x0, +0x27bdffd0, 0x32c20020, 0xafbf002c, 0xafb20028, +0xafb10024, 0x10400004, 0xafb00020, 0x8f520028, +0x10000002, 0x0, 0x8f520020, 0x8f420030, +0x105200b5, 0x21100, 0x8f43001c, 0x628021, +0x8e040000, 0x8e050004, 0x96110008, 0x8f420090, +0x9607000a, 0x3226ffff, 0x46102a, 0x10400017, +0x0, 0x8f8200d8, 0x8f430098, 0x431023, +0x2442dc46, 0xaf420090, 0x8f420090, 0x2842dc47, +0x10400005, 0x0, 0x8f420090, 0x8f430144, +0x431021, 0xaf420090, 0x8f420090, 0x46102a, +0x10400006, 0x0, 0x8f420348, 0x24420001, +0xaf420348, 0x100000ab, 0x8f420348, 0x8f8600fc, +0x10c0000c, 0x0, 0x8f8200f4, 0x2403fff8, +0x431024, 0x461023, 0x218c3, 0x58600001, +0x24630100, 0x8f42008c, 0x43102b, 0x14400006, +0x712c2, 0x8f420344, 0x24420001, 0xaf420344, +0x10000098, 0x8f420344, 0x934305c2, 0x1060000f, +0x30460001, 0x8f420010, 0x34480400, 0x32c20008, +0x10400008, 0x30e20200, 0x10400006, 0x3c034000, +0x9602000e, 0xaf4300ac, 0x21400, 0x10000004, +0xaf4200b0, 0x10000002, 0xaf4000ac, 0x8f480010, +0x30e20004, 0x10400045, 0x3227ffff, 0x8f4900ac, +0x11200005, 0x30c200ff, 0x14400006, 0x24020040, +0x10000004, 0x24020008, 0x14400002, 0x24020020, +0x24020004, 0xafa20010, 0x8f430030, 0x11200004, +0xafa30014, 0x8f4200b0, 0x621025, 0xafa20014, +0x3c020002, 0x1021025, 0xafa20018, 0x8f460098, +0x8f420108, 0x40f809, 0x0, 0x10400069, +0x3224ffff, 0x8f42008c, 0x8f430094, 0x24420001, +0xaf42008c, 0x24020001, 0xae03000c, 0xa34205c2, +0x8f420098, 0x2406fff8, 0x8f45013c, 0x441021, +0x24420007, 0x461024, 0x24840007, 0xaf420094, +0x8f420090, 0x8f430094, 0x862024, 0x441023, +0x65182b, 0x14600005, 0xaf420090, 0x8f420094, +0x8f430144, 0x431023, 0xaf420094, 0x8f430094, +0x8f420140, 0x43102b, 0x10400009, 0x0, +0x8f43013c, 0x8f440094, 0x8f420090, 0x8f450138, +0x641823, 0x431023, 0xaf420090, 0xaf450094, +0x8f420094, 0x1000001f, 0xaf420098, 0x10e0001d, +0x30c200ff, 0x14400002, 0x24020010, 0x24020002, +0xafa20010, 0x8f420030, 0xafa80018, 0xafa20014, +0x8f460098, 0x8f420108, 0x40f809, 0x0, +0x10400030, 0x3225ffff, 0x8f420098, 0x8f44013c, +0x451021, 0xaf420098, 0x8f420090, 0x8f430098, +0xa34005c2, 0x451023, 0x64182b, 0x14600005, +0xaf420090, 0x8f420098, 0x8f430144, 0x431023, +0xaf420098, 0x8f420030, 0x8f430040, 0x24420001, +0x2463ffff, 0x431024, 0xaf420030, 0x8f420030, +0x14520018, 0x0, 0x8f420000, 0x10400007, +0x0, 0xaf80004c, 0x8f82004c, 0x1040fffd, +0x0, 0x10000005, 0x0, 0xaf800048, +0x8f820048, 0x1040fffd, 0x0, 0x8f820060, +0x2403fff7, 0x431024, 0xaf820060, 0x8f420000, +0x10400003, 0x0, 0x10000002, 0xaf80004c, +0xaf800048, 0x8fbf002c, 0x8fb20028, 0x8fb10024, +0x8fb00020, 0x3e00008, 0x27bd0030, 0x3e00008, +0x0, 0x27bdffd8, 0x3c020001, 0x34422ec0, +0xafbf0020, 0x8f4300f0, 0x8f840108, 0x2e21021, +0x54620004, 0x24620008, 0x3c020001, 0x34422cc0, +0x2e21021, 0x401821, 0xaf4300f0, 0xac600000, +0x8f4200ec, 0x8c660004, 0x14620004, 0x3c020001, +0x24820020, 0x1000000f, 0xaf820108, 0x8f4300f0, +0x34422ec0, 0x2e21021, 0x54620004, 0x24620008, +0x3c020001, 0x34422cc0, 0x2e21021, 0x401821, +0x8c620004, 0x21140, 0x821021, 0xaf820108, +0xac600000, 0x8c850018, 0x30a20036, 0x1040006c, +0x30a20001, 0x8c82001c, 0x8f430040, 0x8f440034, +0x24420001, 0x2463ffff, 0x431024, 0x862021, +0xaf42002c, 0x30a20030, 0x14400006, 0xaf440034, +0x8f420034, 0x8c03023c, 0x43102b, 0x144000b4, +0x0, 0x32c20010, 0x10400028, 0x24070008, +0x8f440170, 0x8f450174, 0x8f43002c, 0x8f48000c, +0x8f860120, 0x24020080, 0xafa20010, 0xafa30014, +0xafa80018, 0x8f42010c, 0x40f809, 0x24c6001c, +0x14400011, 0x24020001, 0x3c010001, 0x370821, +0xa02240f1, 0x8f820124, 0xafa20010, 0x8f820128, +0x3c040001, 0x248467c4, 0xafa20014, 0x8f46002c, +0x8f870120, 0x3c050009, 0xc002b3b, 0x34a51100, +0x10000036, 0x0, 0x8f420300, 0x8f43002c, +0x24420001, 0xaf420300, 0x8f420300, 0x24020001, +0xa34205c1, 0x10000026, 0xaf430038, 0x8f440170, +0x8f450174, 0x8f43002c, 0x8f48000c, 0x8f860120, +0x24020020, 0xafa20010, 0xafa30014, 0xafa80018, +0x8f42010c, 0x40f809, 0x24c6001c, 0x14400011, +0x24020001, 0x3c010001, 0x370821, 0xa02240f0, +0x8f820124, 0xafa20010, 0x8f820128, 0x3c040001, +0x248467b8, 0xafa20014, 0x8f46002c, 0x8f870120, +0x3c050009, 0xc002b3b, 0x34a50900, 0x1000000f, +0x0, 0x8f420300, 0x24420001, 0xaf420300, +0x8f420300, 0x8f42002c, 0xa34005c1, 0xaf420038, +0x3c010001, 0x370821, 0xa02040f1, 0x3c010001, +0x370821, 0xa02040f0, 0xaf400034, 0x8f420314, +0x24420001, 0xaf420314, 0x10000059, 0x8f420314, +0x10400022, 0x30a27000, 0x8c85001c, 0x8f420028, +0xa22023, 0x4810003, 0x0, 0x8f420040, +0x822021, 0x8f420358, 0x8f430000, 0xaf450028, +0x441021, 0x10600007, 0xaf420358, 0xaf80004c, +0x8f82004c, 0x1040fffd, 0x0, 0x10000005, +0x0, 0xaf800048, 0x8f820048, 0x1040fffd, +0x0, 0x8f820060, 0x34420008, 0xaf820060, +0x8f420000, 0x10400003, 0x0, 0x10000038, +0xaf80004c, 0x10000036, 0xaf800048, 0x1040002f, +0x30a21000, 0x1040000c, 0x30a24000, 0x8c83001c, +0x8f420050, 0x622023, 0x4820001, 0x24840200, +0x8f42035c, 0x441021, 0xaf42035c, 0x8f420368, +0x1000001a, 0xaf430050, 0x1040000c, 0x32c28000, +0x8c83001c, 0x8f420070, 0x622023, 0x4820001, +0x24840400, 0x8f420364, 0x441021, 0xaf420364, +0x8f420368, 0x1000000d, 0xaf430070, 0x1040000e, +0x3c020800, 0x8c83001c, 0x8f420060, 0x622023, +0x4820001, 0x24840100, 0x8f420360, 0x441021, +0xaf420360, 0x8f420368, 0xaf430060, 0x441021, +0xaf420368, 0x3c020800, 0x2c21024, 0x50400008, +0x36940040, 0x10000006, 0x0, 0x30a20100, +0x10400003, 0x0, 0xc002bd8, 0x0, +0x8fbf0020, 0x3e00008, 0x27bd0028, 0x3e00008, +0x0, 0x27bdffa8, 0xafbf0050, 0xafbe004c, +0xafb50048, 0xafb30044, 0xafb20040, 0xafb1003c, +0xafb00038, 0x8f910108, 0x26220020, 0xaf820108, +0x8e320018, 0xa821, 0x32420024, 0x104001ba, +0xf021, 0x8e26001c, 0x8f43001c, 0x61100, +0x621821, 0x8c70000c, 0x9604000c, 0x962d0016, +0x9473000a, 0x2c8305dd, 0x38828870, 0x2c420001, +0x621825, 0x10600015, 0x2821, 0x32c20040, +0x10400015, 0x24020800, 0x96030014, 0x14620012, +0x3402aaaa, 0x9603000e, 0x14620007, 0x2021, +0x96030010, 0x24020300, 0x14620004, 0x801021, +0x96020012, 0x2c440001, 0x801021, 0x54400006, +0x24050016, 0x10000004, 0x0, 0x24020800, +0x50820001, 0x2405000e, 0x934205c3, 0x14400008, +0x5821, 0x240b0001, 0x32620180, 0xaf4500a8, +0xaf5000a0, 0x10400002, 0xaf4600a4, 0xa34b05c3, +0x10a00085, 0x2054021, 0x91020000, 0x3821, +0x3042000f, 0x25080, 0x32c20002, 0x10400012, +0x10a1821, 0x32620002, 0x10400010, 0x32c20001, +0x1002021, 0x94820000, 0x24840002, 0xe23821, +0x83102b, 0x1440fffb, 0x30e2ffff, 0x71c02, +0x623821, 0x71c02, 0x30e2ffff, 0x623821, +0x71027, 0xa502000a, 0x32c20001, 0x1040006a, +0x32620001, 0x10400068, 0x0, 0x8f4200a8, +0x10400065, 0x0, 0x8f4200a0, 0x8f4300a8, +0x431021, 0x904c0009, 0x318900ff, 0x39230006, +0x3182b, 0x39220011, 0x2102b, 0x621824, +0x1060000c, 0x3c050006, 0x8f4200a4, 0x3c040001, +0x248467d4, 0xafa20010, 0x8f4200a0, 0x34a54600, +0x1203821, 0xc002b3b, 0xafa20014, 0x1000004e, +0x0, 0x32c20004, 0x14400013, 0x2821, +0x316200ff, 0x14400004, 0x0, 0x95020002, +0x1000000d, 0x4a2823, 0x9505000c, 0x9502000e, +0x95030010, 0xa22821, 0xa32821, 0x95030012, +0x91040009, 0x95020002, 0xa32821, 0xa42821, +0x4a1023, 0xa22821, 0x2002021, 0x94820000, +0x24840002, 0xe23821, 0x88102b, 0x1440fffb, +0x71c02, 0x30e2ffff, 0x623821, 0x71c02, +0x30e2ffff, 0x623821, 0x1a52821, 0x51c02, +0x30a2ffff, 0x622821, 0x51c02, 0x30a2ffff, +0x622821, 0xa72823, 0x51402, 0xa22821, +0x30a5ffff, 0x50a00001, 0x3405ffff, 0x316200ff, +0x14400008, 0x318300ff, 0x8f4300a0, 0x8f4200a8, +0x624021, 0x91020000, 0x3042000f, 0x25080, +0x318300ff, 0x24020006, 0x14620003, 0x10a1021, +0x10000002, 0x24440010, 0x24440006, 0x316200ff, +0x14400006, 0x0, 0x94820000, 0xa22821, +0x51c02, 0x30a2ffff, 0x622821, 0x934205c3, +0x10400003, 0x32620100, 0x50400003, 0xa4850000, +0x52827, 0xa4850000, 0x9622000e, 0x8f43009c, +0x621821, 0x32a200ff, 0x10400007, 0xaf43009c, +0x3c024000, 0x2021025, 0xafa20020, 0x8f42009c, +0x10000003, 0x5e1025, 0xafb00020, 0x8f42009c, +0xafa20024, 0x32620080, 0x10400010, 0x32620100, +0x8f4200b4, 0x24430001, 0x210c0, 0x571021, +0xaf4300b4, 0x8fa30020, 0x8fa40024, 0x3c010001, +0x220821, 0xac2338e8, 0x3c010001, 0x220821, +0xac2438ec, 0x100000a5, 0x32c20020, 0x10400064, +0x0, 0x8f4200b4, 0x24430001, 0x210c0, +0x571021, 0xaf4300b4, 0x8fa30020, 0x8fa40024, +0x3c010001, 0x220821, 0xac2338e8, 0x3c010001, +0x220821, 0xac2438ec, 0x8f4200b4, 0x10400051, +0x3821, 0x3c090001, 0x352938e8, 0x3c08001f, +0x3508ffff, 0x240bffff, 0x340affff, 0x710c0, +0x571021, 0x491021, 0x8c430000, 0x8c440004, +0xafa30028, 0xafa4002c, 0x8f8200fc, 0x8fa30028, +0x8fa4002c, 0xac430000, 0xac440004, 0x24420008, +0xaf8200f0, 0x8f42008c, 0x2442ffff, 0xaf42008c, +0x97a2002e, 0x8f440270, 0x8f450274, 0x401821, +0x1021, 0xa32821, 0xa3302b, 0x822021, +0x862021, 0xaf440270, 0xaf450274, 0x8fa20028, +0x481024, 0x90430000, 0x30630001, 0x1460000b, +0x402021, 0x8f420278, 0x8f43027c, 0x24630001, +0x2c640001, 0x441021, 0xaf420278, 0xaf43027c, +0x8f420278, 0x1000001a, 0x8f43027c, 0x8c820000, +0x144b000e, 0x0, 0x94820004, 0x144a000b, +0x0, 0x8f420288, 0x8f43028c, 0x24630001, +0x2c640001, 0x441021, 0xaf420288, 0xaf43028c, +0x8f420288, 0x1000000a, 0x8f43028c, 0x8f420280, +0x8f430284, 0x24630001, 0x2c640001, 0x441021, +0xaf420280, 0xaf430284, 0x8f420280, 0x8f430284, +0x8f4200b4, 0x24e70001, 0xe2102b, 0x1440ffb8, +0x710c0, 0xa34005c3, 0x1000003f, 0xaf4000b4, +0x8f8200fc, 0x8fa30020, 0x8fa40024, 0xac430000, +0xac440004, 0x24420008, 0xaf8200f0, 0x8f42009c, +0x8f46008c, 0x8f440270, 0x8f450274, 0x401821, +0x1021, 0x24c6ffff, 0xaf46008c, 0xa32821, +0xa3302b, 0x822021, 0x862021, 0xaf440270, +0xaf450274, 0x92020000, 0x30420001, 0x1440000c, +0x2402ffff, 0x8f420278, 0x8f43027c, 0x24630001, +0x2c640001, 0x441021, 0xaf420278, 0xaf43027c, +0x8f420278, 0x8f43027c, 0x1000001c, 0x32c20020, +0x8e030000, 0x1462000f, 0x3402ffff, 0x96030004, +0x1462000c, 0x0, 0x8f420288, 0x8f43028c, +0x24630001, 0x2c640001, 0x441021, 0xaf420288, +0xaf43028c, 0x8f420288, 0x8f43028c, 0x1000000b, +0x32c20020, 0x8f420280, 0x8f430284, 0x24630001, +0x2c640001, 0x441021, 0xaf420280, 0xaf430284, +0x8f420280, 0x8f430284, 0x32c20020, 0x10400005, +0xaf40009c, 0x8f420358, 0x2442ffff, 0xaf420358, +0x8f420358, 0x8e22001c, 0x8f430040, 0x24420001, +0x2463ffff, 0x431024, 0xaf42002c, 0x32420060, +0x14400008, 0x32c20010, 0x8f420034, 0x24420001, +0xaf420034, 0x8c03023c, 0x43102b, 0x14400102, +0x32c20010, 0x10400018, 0x24070008, 0x8f440170, +0x8f450174, 0x8f43002c, 0x8f48000c, 0x8f860120, +0x24020080, 0xafa20010, 0xafa30014, 0xafa80018, +0x8f42010c, 0x40f809, 0x24c6001c, 0x10400047, +0x24020001, 0x8f420300, 0x8f43002c, 0x24420001, +0xaf420300, 0x8f420300, 0x24020001, 0xa34205c1, +0x1000007c, 0xaf430038, 0x8f440170, 0x8f450174, +0x8f43002c, 0x8f48000c, 0x8f860120, 0x24020020, +0xafa20010, 0xafa30014, 0xafa80018, 0x8f42010c, +0x40f809, 0x24c6001c, 0x10400057, 0x24020001, +0x10000065, 0x0, 0x32420012, 0x10400075, +0x32420001, 0x9622000e, 0x8f43009c, 0x621821, +0x32c20020, 0x10400005, 0xaf43009c, 0x8f420358, +0x2442ffff, 0xaf420358, 0x8f420358, 0x8e22001c, +0x8f430040, 0x24420001, 0x2463ffff, 0x431024, +0xaf42002c, 0x32420010, 0x14400008, 0x32c20010, +0x8f420034, 0x24420001, 0xaf420034, 0x8c03023c, +0x43102b, 0x144000bc, 0x32c20010, 0x10400028, +0x24070008, 0x8f440170, 0x8f450174, 0x8f43002c, +0x8f48000c, 0x8f860120, 0x24020080, 0xafa20010, +0xafa30014, 0xafa80018, 0x8f42010c, 0x40f809, +0x24c6001c, 0x14400011, 0x24020001, 0x3c010001, +0x370821, 0xa02240f1, 0x8f820124, 0xafa20010, +0x8f820128, 0x3c040001, 0x248467c4, 0xafa20014, +0x8f46002c, 0x8f870120, 0x3c050009, 0xc002b3b, +0x34a51100, 0x10000036, 0x0, 0x8f420300, +0x8f43002c, 0x24420001, 0xaf420300, 0x8f420300, +0x24020001, 0xa34205c1, 0x10000026, 0xaf430038, +0x8f440170, 0x8f450174, 0x8f43002c, 0x8f48000c, +0x8f860120, 0x24020020, 0xafa20010, 0xafa30014, +0xafa80018, 0x8f42010c, 0x40f809, 0x24c6001c, +0x14400011, 0x24020001, 0x3c010001, 0x370821, +0xa02240f0, 0x8f820124, 0xafa20010, 0x8f820128, +0x3c040001, 0x248467b8, 0xafa20014, 0x8f46002c, +0x8f870120, 0x3c050009, 0xc002b3b, 0x34a50900, +0x1000000f, 0x0, 0x8f420300, 0x24420001, +0xaf420300, 0x8f420300, 0x8f42002c, 0xa34005c1, +0xaf420038, 0x3c010001, 0x370821, 0xa02040f1, +0x3c010001, 0x370821, 0xa02040f0, 0xaf400034, +0x8f420314, 0x24420001, 0xaf420314, 0x10000062, +0x8f420314, 0x10400022, 0x32427000, 0x8e25001c, +0x8f420028, 0xa22023, 0x4810003, 0x0, +0x8f420040, 0x822021, 0x8f420358, 0x8f430000, +0xaf450028, 0x441021, 0x10600007, 0xaf420358, +0xaf80004c, 0x8f82004c, 0x1040fffd, 0x0, +0x10000005, 0x0, 0xaf800048, 0x8f820048, +0x1040fffd, 0x0, 0x8f820060, 0x34420008, +0xaf820060, 0x8f420000, 0x10400003, 0x0, +0x10000041, 0xaf80004c, 0x1000003f, 0xaf800048, +0x1040002f, 0x32421000, 0x1040000c, 0x32424000, +0x8e23001c, 0x8f420050, 0x622023, 0x4820001, +0x24840200, 0x8f42035c, 0x441021, 0xaf42035c, +0x8f420368, 0x1000001a, 0xaf430050, 0x1040000c, +0x32c28000, 0x8e23001c, 0x8f420070, 0x622023, +0x4820001, 0x24840400, 0x8f420364, 0x441021, +0xaf420364, 0x8f420368, 0x1000000d, 0xaf430070, +0x1040000e, 0x3c020800, 0x8e23001c, 0x8f420060, +0x622023, 0x4820001, 0x24840100, 0x8f420360, +0x441021, 0xaf420360, 0x8f420368, 0xaf430060, +0x441021, 0xaf420368, 0x3c020800, 0x2c21024, +0x50400011, 0x36940040, 0x1000000f, 0x0, +0x32420048, 0x10400007, 0x24150001, 0x8e22001c, +0x3c03ffff, 0x43f024, 0x3042ffff, 0x1000fd75, +0xae22001c, 0x32420100, 0x10400003, 0x0, +0xc002bd8, 0x0, 0x8fbf0050, 0x8fbe004c, +0x8fb50048, 0x8fb30044, 0x8fb20040, 0x8fb1003c, +0x8fb00038, 0x3e00008, 0x27bd0058, 0x3e00008, +0x0, 0x0, 0x0, 0x8f8300e4, +0x8f8200e0, 0x2404fff8, 0x441024, 0x621026, +0x2102b, 0x21023, 0x3e00008, 0x621024, +0x3e00008, 0x0, 0x27bdffe0, 0xafbf001c, +0xafb00018, 0x8f8600c4, 0x8f8400e0, 0x8f8500e4, +0x2402fff8, 0x821824, 0x10a30009, 0x27623ff8, +0x14a20002, 0x24a20008, 0x27623000, 0x408021, +0x16030005, 0x30820004, 0x10400004, 0xc02021, +0x10000022, 0x1021, 0x8e040000, 0x8f42011c, +0x14a20003, 0x0, 0x8f420120, 0xaf420114, +0x8ca30000, 0x8f420148, 0x831823, 0x43102b, +0x10400003, 0x0, 0x8f420148, 0x621821, +0x94a20006, 0x24420050, 0x62102b, 0x1440000f, +0xa01021, 0xafa40010, 0xafa30014, 0x8ca60000, +0x8ca70004, 0x3c040001, 0xc002b3b, 0x24846894, +0x8f42020c, 0x24420001, 0xaf42020c, 0x8f42020c, +0x1021, 0xaf9000e8, 0xaf9000e4, 0x8fbf001c, +0x8fb00018, 0x3e00008, 0x27bd0020, 0x3e00008, +0x0, 0x8f8400e0, 0x8f8800c4, 0x8f8300e8, +0x2402fff8, 0x823824, 0xe32023, 0x2c821000, +0x50400001, 0x24841000, 0x420c2, 0x801821, +0x8f440258, 0x8f45025c, 0x1021, 0xa32821, +0xa3302b, 0x822021, 0x862021, 0xaf440258, +0xaf45025c, 0x8f8300c8, 0x8f420148, 0x1032023, +0x82102b, 0x14400004, 0x801821, 0x8f420148, +0x822021, 0x801821, 0x8f440250, 0x8f450254, +0x1021, 0xa32821, 0xa3302b, 0x822021, +0x862021, 0xaf440250, 0xaf450254, 0xaf8800c8, +0xaf8700e4, 0xaf8700e8, 0x3e00008, 0x0, +0x27bdff30, 0x240a0001, 0xafbf00c8, 0xafbe00c4, +0xafb500c0, 0xafb300bc, 0xafb200b8, 0xafb100b4, +0xafb000b0, 0xa3a00097, 0xafa00044, 0xafaa005c, +0x934205c4, 0xa7a0008e, 0x1040000a, 0xa7a00086, +0x8f4b00c4, 0xafab0064, 0x8f4a00c0, 0xafaa006c, +0x8f4b00cc, 0xafab0074, 0x8f4a00c8, 0x10000129, +0xafaa007c, 0x8f420114, 0x40f809, 0x0, +0x403021, 0x10c0034f, 0x0, 0x8cc20000, +0x8cc30004, 0xafa20020, 0xafa30024, 0x8fab0024, +0x8faa0020, 0x3162ffff, 0x2442fffc, 0xafa2006c, +0x3c020006, 0x2c21024, 0xafab007c, 0x14400015, +0xafaa0064, 0x91420000, 0x30420001, 0x10400011, +0x2402ffff, 0x8d430000, 0x14620004, 0x3402ffff, +0x95430004, 0x1062000b, 0x0, 0xc0024bb, +0x8fa40064, 0x304200ff, 0x14400006, 0x0, +0x8f420118, 0x40f809, 0x0, 0x1000032d, +0x0, 0x8fa20024, 0x3c03ffbf, 0x3463ffff, +0x431024, 0x3c03ffff, 0x431824, 0x14600003, +0xafa20024, 0x10000040, 0x1821, 0x3c020080, +0x621024, 0x10400007, 0x0, 0x8f42038c, +0x24420001, 0xaf42038c, 0x8f42038c, 0x10000036, +0x24030001, 0x8f420210, 0x24420001, 0xaf420210, +0x8f420210, 0x3c020001, 0x621024, 0x10400006, +0x3c020002, 0x8f4201c4, 0x24420001, 0xaf4201c4, +0x8f4201c4, 0x3c020002, 0x621024, 0x10400006, +0x3c020004, 0x8f42037c, 0x24420001, 0xaf42037c, +0x8f42037c, 0x3c020004, 0x621024, 0x10400006, +0x3c020008, 0x8f420380, 0x24420001, 0xaf420380, +0x8f420380, 0x3c020008, 0x621024, 0x10400006, +0x3c020010, 0x8f420384, 0x24420001, 0xaf420384, +0x8f420384, 0x3c020010, 0x621024, 0x10400006, +0x3c020020, 0x8f4201c0, 0x24420001, 0xaf4201c0, +0x8f4201c0, 0x3c020020, 0x621024, 0x10400006, +0x24030001, 0x8f420388, 0x24420001, 0xaf420388, +0x8f420388, 0x24030001, 0x8c020260, 0x8fab006c, +0x4b102b, 0x10400014, 0x307000ff, 0x8f4201e8, +0x24420001, 0xaf4201e8, 0x8f4201e8, 0x8faa007c, +0x8f8200e0, 0x354a0100, 0xafaa007c, 0xafa20010, +0x8f8200e4, 0x24100001, 0x3c040001, 0x248468a0, +0xafa20014, 0x8fa60020, 0x8fa70024, 0x3c050007, +0xc002b3b, 0x34a50800, 0x12000010, 0x3c020080, +0x2c21024, 0x1440000e, 0x32c20400, 0x8fab007c, +0x3c020080, 0x34420100, 0x1621024, 0x10400005, +0x0, 0x8f42020c, 0x24420001, 0xaf42020c, +0x8f42020c, 0x100002b0, 0x8fa3006c, 0x32c20400, +0x10400015, 0x34028100, 0x8faa0064, 0x9543000c, +0x14620012, 0x3c020100, 0x240b0200, 0xa7ab008e, +0x9542000e, 0x8d430008, 0x8d440004, 0x8d450000, +0x8faa006c, 0x8fab0064, 0x254afffc, 0xafaa006c, +0xa7a20086, 0xad63000c, 0xad640008, 0xad650004, +0x256b0004, 0xafab0064, 0x3c020100, 0x2c21024, +0x10400004, 0x0, 0x8faa006c, 0x254a0004, +0xafaa006c, 0x8f4200bc, 0x5040000a, 0xafa00074, +0x8fab006c, 0x4b102b, 0x50400006, 0xafa00074, +0x8f4200bc, 0x1621023, 0xafa20074, 0x8f4a00bc, +0xafaa006c, 0x8f420080, 0x8fab006c, 0x4b102b, +0x10400056, 0x32c28000, 0x1040005e, 0x240a0003, +0x32c21000, 0x1040005b, 0xafaa005c, 0x10000058, +0x240b0004, 0x8f420350, 0x2403ffbf, 0x283a024, +0x24420001, 0xaf420350, 0x1000024f, 0x8f420350, +0x2c2b025, 0x2402ffbf, 0x282a024, 0x8f830128, +0x3c040001, 0x248468d0, 0x26620001, 0xafa20014, +0xafa30010, 0x8f860120, 0x8f870124, 0x3c050007, +0xc002b3b, 0x34a52250, 0x1000023f, 0x0, +0x2c2b025, 0x2402ffbf, 0x282a024, 0x8f830128, +0x3c040001, 0x248468d0, 0x24020002, 0xafa20014, +0xafa30010, 0x8f860120, 0x8f870124, 0x3c050007, +0xc002b3b, 0x34a52450, 0x1000022f, 0x0, +0x8ea20000, 0x8ea30004, 0x3c040001, 0x248468e8, +0xafb00010, 0xafbe0014, 0x8ea70018, 0x34a52800, +0xc002b3b, 0x603021, 0x10000223, 0x0, +0xa6b1000a, 0x8f820124, 0x3c040001, 0x248468f0, +0xafbe0014, 0xafa20010, 0x8f460044, 0x8f870120, +0x3c050007, 0xc002b3b, 0x34a53000, 0x10000216, +0x0, 0xa6b1000a, 0xa6b2000e, 0x8f820124, +0x3c040001, 0x248468fc, 0xafbe0014, 0xafa20010, +0x8f460044, 0x8f870120, 0x3c050007, 0xc002b3b, +0x34a53200, 0x10000208, 0x0, 0x8f420084, +0x8faa006c, 0x4a102b, 0x14400007, 0x3c020001, +0x2c21024, 0x10400004, 0x0, 0x240b0002, +0xafab005c, 0x8faa006c, 0x1140021b, 0x27ab0020, +0xafab00a4, 0x3c0a001f, 0x354affff, 0xafaa009c, +0x8fab005c, 0x240a0001, 0x556a0021, 0x240a0002, +0x8f430054, 0x8f420050, 0x1062000b, 0x274b0054, +0x8f5e0054, 0x3403ecc0, 0xafab004c, 0x27c20001, +0x304201ff, 0xafa20054, 0x1e1140, 0x431021, +0x1000006b, 0x2e2a821, 0x8f420044, 0x8faa006c, +0x3c040001, 0x248468ac, 0xafaa0014, 0xafa20010, +0x8f460054, 0x8f470050, 0x3c050007, 0xc002b3b, +0x34a51300, 0x8f430350, 0x2402ffbf, 0x282a024, +0x24630001, 0xaf430350, 0x100001d3, 0x8f420350, +0x156a001d, 0x0, 0x8f430074, 0x8f420070, +0x1062000a, 0x274b0074, 0x8f5e0074, 0xafab004c, +0x27c20001, 0x304203ff, 0xafa20054, 0x1e1140, +0x24426cc0, 0x1000004a, 0x2e2a821, 0x8f420044, +0x8faa006c, 0x3c040001, 0x248468b8, 0x3c050007, +0xafaa0014, 0xafa20010, 0x8f460074, 0x8f470070, +0x34a51500, 0x240b0001, 0xc002b3b, 0xafab005c, +0x1000ffc3, 0x0, 0x8f430064, 0x8f420060, +0x1062001a, 0x274a0064, 0x8f5e0064, 0x8fab005c, +0xafaa004c, 0x27c20001, 0x304200ff, 0xafa20054, +0x24020004, 0x1562000e, 0x1e1140, 0x1e1180, +0x24420cc0, 0x2e21021, 0xafa20044, 0x9442002a, +0x8faa0044, 0x8fab006c, 0x4b102b, 0x10400024, +0x25550020, 0x240a0001, 0x10000021, 0xa3aa0097, +0x24424cc0, 0x1000001e, 0x2e2a821, 0x8f420044, +0x8fab006c, 0x3c040001, 0x248468c4, 0xafab0014, +0xafa20010, 0x8f460064, 0x8f470060, 0x3c050007, +0xc002b3b, 0x34a51800, 0x3c020008, 0x2c21024, +0x1440ff34, 0x0, 0x8f420370, 0x240a0001, +0xafaa005c, 0x24420001, 0xaf420370, 0x1000ff90, +0x8f420370, 0x27a30036, 0x131040, 0x621821, +0x94620000, 0x441021, 0x10000020, 0xa4620000, +0x8fab0064, 0xaeab0018, 0x93a20097, 0x10400072, +0x9821, 0x8faa0044, 0x8fa4006c, 0x8fa300a4, +0x25420020, 0xafa20028, 0x25420008, 0xafa20030, +0x25420010, 0xafaa002c, 0xafa20034, 0x9542002a, +0xa7a20038, 0x95420018, 0xa7a2003a, 0x9542001a, +0xa7a2003c, 0x9542001c, 0xa7a2003e, 0x94620018, +0x24630002, 0x822023, 0x1880ffde, 0x26730001, +0x2e620004, 0x1440fff9, 0x0, 0x8f4200fc, +0x26650001, 0xa2102a, 0x1440002b, 0x24030001, +0x8f83012c, 0x10600023, 0x0, 0x8f820124, +0x431023, 0x22143, 0x58800001, 0x24840040, +0x8f820128, 0x431023, 0x21943, 0x58600001, +0x24630040, 0x64102a, 0x54400001, 0x602021, +0xaf4400fc, 0x8f4200fc, 0xa2102a, 0x10400011, +0x24030001, 0x10000015, 0x306200ff, 0x8fab0064, +0x96070018, 0xafab0010, 0x8e220008, 0x3c040001, +0x248468dc, 0x8c430004, 0x8c420000, 0x34a52400, +0x2403021, 0xc002b3b, 0xafa30014, 0x1000002b, +0x0, 0x8f420334, 0x1821, 0x24420001, +0xaf420334, 0x8f420334, 0x306200ff, 0x5040fedc, +0x3c020800, 0x12600021, 0x9021, 0x8fb100a4, +0x2208021, 0x8e220008, 0x96070018, 0x8fa60064, +0x8c440000, 0x8c450004, 0x240a0001, 0xafaa0010, +0xafbe0014, 0x8f420008, 0xafa20018, 0x8f42010c, +0x40f809, 0x0, 0x1040ffd8, 0x3c050007, +0x96020018, 0x8fab0064, 0x8faa009c, 0x1625821, +0x14b102b, 0x10400004, 0xafab0064, 0x8f420148, +0x1625823, 0xafab0064, 0x26100002, 0x26520001, +0x253102b, 0x1440ffe3, 0x26310004, 0x8fb0006c, +0x10000036, 0x97b10038, 0x8f4200fc, 0x24050002, +0xa2102a, 0x1440001b, 0x24030001, 0x8f83012c, +0x10600013, 0x0, 0x8f820124, 0x431023, +0x22143, 0x58800001, 0x24840040, 0x8f820128, +0x431023, 0x21943, 0x58600001, 0x24630040, +0x64102a, 0x54400001, 0x602021, 0xaf4400fc, +0x8f4200fc, 0xa2102a, 0x14400006, 0x24030001, +0x8f420334, 0x1821, 0x24420001, 0xaf420334, +0x8f420334, 0x306200ff, 0x1040fea5, 0x3c020800, +0x96b1000a, 0x8fb0006c, 0x3223ffff, 0x70102b, +0x54400001, 0x608021, 0x8ea40000, 0x8ea50004, +0x240b0001, 0xafab0010, 0xafbe0014, 0x8f420008, +0x8fa60064, 0xafa20018, 0x8f42010c, 0x40f809, +0x2003821, 0x1040fea2, 0x3c050007, 0x96a3000e, +0x97aa008e, 0x11400007, 0x609021, 0x934205c4, +0x14400004, 0x0, 0x97ab0086, 0x6a1825, +0xa6ab0016, 0x8faa007c, 0x3c02ffff, 0x1421024, +0x10400003, 0xa1402, 0x34630400, 0xa6a20014, +0x8fab006c, 0x560b0072, 0xa6a3000e, 0x34620004, +0xa6a2000e, 0x8faa0074, 0x16a1021, 0xa6a2000a, +0x8f430044, 0x8f4401a0, 0x8f4501a4, 0x34028000, +0xafa20010, 0x8f420044, 0x2a03021, 0x24070020, +0xafa20014, 0x8f42000c, 0x31940, 0x604821, +0xafa20018, 0x8f42010c, 0x4021, 0xa92821, +0xa9182b, 0x882021, 0x40f809, 0x832021, +0x5040fe7f, 0xa6b2000e, 0x8f420368, 0xafa0006c, +0xa34005c4, 0x2442ffff, 0xaf420368, 0x8fab005c, +0x240a0001, 0x8f420368, 0x156a0006, 0x240a0002, +0x8f42035c, 0x2442ffff, 0xaf42035c, 0x1000000c, +0x8f42035c, 0x156a0006, 0x0, 0x8f420364, +0x2442ffff, 0xaf420364, 0x10000005, 0x8f420364, +0x8f420360, 0x2442ffff, 0xaf420360, 0x8f420360, +0x8faa0054, 0x8fab004c, 0xad6a0000, 0x8f420044, +0x8f440088, 0x8f430078, 0x24420001, 0x441024, +0x24630001, 0xaf420044, 0xaf430078, 0x8c020240, +0x62182b, 0x14600075, 0x24070008, 0x8f440168, +0x8f45016c, 0x8f430044, 0x8f48000c, 0x8f860120, +0x24020040, 0xafa20010, 0xafa30014, 0xafa80018, +0x8f42010c, 0x40f809, 0x24c6001c, 0x14400011, +0x240b0001, 0x3c010001, 0x370821, 0xa02b40f2, +0x8f820124, 0xafa20010, 0x8f820128, 0x3c040001, +0x2484688c, 0xafa20014, 0x8f460044, 0x8f870120, +0x3c050009, 0xc002b3b, 0x34a51300, 0x1000000b, +0x0, 0x8f420304, 0x24420001, 0xaf420304, +0x8f420304, 0x8f420044, 0xaf42007c, 0x3c010001, +0x370821, 0xa02040f2, 0xaf400078, 0x8f420318, +0x24420001, 0xaf420318, 0x10000048, 0x8f420318, +0xa6b0000a, 0x8f430044, 0x8f4401a0, 0x8f4501a4, +0x34028000, 0xafa20010, 0x8f420044, 0x2a03021, +0x24070020, 0xafa20014, 0x8f42000c, 0x31940, +0x604821, 0xafa20018, 0x8f42010c, 0x4021, +0xa92821, 0xa9182b, 0x882021, 0x40f809, +0x832021, 0x1040fe1f, 0x240a0001, 0xa34a05c4, +0x8fab006c, 0x8faa0064, 0x1705823, 0xafab006c, +0x8fab009c, 0x1505021, 0x16a102b, 0x10400004, +0xafaa0064, 0x8f420148, 0x1425023, 0xafaa0064, +0x8f420368, 0x2442ffff, 0xaf420368, 0x8faa005c, +0x240b0001, 0x8f420368, 0x154b0006, 0x240b0002, +0x8f42035c, 0x2442ffff, 0xaf42035c, 0x1000000c, +0x8f42035c, 0x114b0006, 0x0, 0x8f420360, +0x2442ffff, 0xaf420360, 0x10000005, 0x8f420360, +0x8f420364, 0x2442ffff, 0xaf420364, 0x8f420364, +0x8fab0054, 0x8faa004c, 0xad4b0000, 0x8f420044, +0x8f440088, 0x8f430078, 0x24420001, 0x441024, +0x24630001, 0xaf420044, 0xaf430078, 0x8faa006c, +0x1540fe0b, 0x0, 0x8fab006c, 0x1160001e, +0x0, 0x934205c4, 0x10400009, 0x0, +0x8faa0064, 0xaf4a00c4, 0xaf4b00c0, 0x8fab007c, +0xaf4b00c8, 0x8faa0074, 0x1000000e, 0xaf4a00cc, +0x97ab008e, 0x1160000b, 0x34038100, 0x8fa20020, +0x8c46000c, 0xa443000c, 0x97aa0086, 0x8c440004, +0x8c450008, 0xa44a000e, 0xac440000, 0xac450004, +0xac460008, 0x8f42034c, 0x24420001, 0xaf42034c, +0x10000010, 0x8f42034c, 0x8fab007c, 0x3164ffff, +0x2484fffc, 0x801821, 0x8f440250, 0x8f450254, +0x8f460118, 0x1021, 0xa32821, 0xa3382b, +0x822021, 0x872021, 0xaf440250, 0xc0f809, +0xaf450254, 0x8fbf00c8, 0x8fbe00c4, 0x8fb500c0, +0x8fb300bc, 0x8fb200b8, 0x8fb100b4, 0x8fb000b0, +0x3e00008, 0x27bd00d0, 0x3e00008, 0x0, +0x27bdff38, 0x240b0001, 0xafbf00c0, 0xafbe00bc, +0xafb500b8, 0xafb300b4, 0xafb200b0, 0xafb100ac, +0xafb000a8, 0xa3a00087, 0xafa00044, 0xafab005c, +0x934205c4, 0xa7a00076, 0x10400007, 0xa7a0007e, +0x8f4c00c0, 0xafac0064, 0x8f4b00c8, 0x8f5e00c4, +0x10000130, 0xafab006c, 0x8f420114, 0x40f809, +0x0, 0x403021, 0x10c002a1, 0x0, +0x8cc20000, 0x8cc30004, 0xafa20020, 0xafa30024, +0x8fac0024, 0x8fbe0020, 0x3182ffff, 0x2442fffc, +0xafa20064, 0x3c020006, 0x2c21024, 0x14400015, +0xafac006c, 0x93c20000, 0x30420001, 0x10400011, +0x2402ffff, 0x8fc30000, 0x14620004, 0x3402ffff, +0x97c30004, 0x1062000b, 0x0, 0xc0024bb, +0x3c02021, 0x304200ff, 0x14400006, 0x0, +0x8f420118, 0x40f809, 0x0, 0x10000280, +0x0, 0x8fa20024, 0x3c03ffbf, 0x3463ffff, +0x431024, 0x3c03ffff, 0x431824, 0x14600003, +0xafa20024, 0x10000040, 0x8021, 0x3c020080, +0x621024, 0x10400007, 0x0, 0x8f42038c, +0x24420001, 0xaf42038c, 0x8f42038c, 0x10000036, +0x24100001, 0x8f420210, 0x24420001, 0xaf420210, +0x8f420210, 0x3c020001, 0x621024, 0x10400006, +0x3c020002, 0x8f4201c4, 0x24420001, 0xaf4201c4, +0x8f4201c4, 0x3c020002, 0x621024, 0x10400006, +0x3c020004, 0x8f42037c, 0x24420001, 0xaf42037c, +0x8f42037c, 0x3c020004, 0x621024, 0x10400006, +0x3c020008, 0x8f420380, 0x24420001, 0xaf420380, +0x8f420380, 0x3c020008, 0x621024, 0x10400006, +0x3c020010, 0x8f420384, 0x24420001, 0xaf420384, +0x8f420384, 0x3c020010, 0x621024, 0x10400006, +0x3c020020, 0x8f4201c0, 0x24420001, 0xaf4201c0, +0x8f4201c0, 0x3c020020, 0x621024, 0x10400006, +0x24100001, 0x8f420388, 0x24420001, 0xaf420388, +0x8f420388, 0x24100001, 0x8c020260, 0x8fab0064, +0x4b102b, 0x10400015, 0x320200ff, 0x8f4201e8, +0x24420001, 0xaf4201e8, 0x8f4201e8, 0x8fac006c, +0x8f8200e0, 0x358c0100, 0xafac006c, 0xafa20010, +0x8f8200e4, 0x24100001, 0x3c040001, 0x248468a0, +0xafa20014, 0x8fa60020, 0x8fa70024, 0x3c050007, +0xc002b3b, 0x34a53600, 0x320200ff, 0x10400010, +0x3c020080, 0x2c21024, 0x1440000e, 0x32c20400, +0x8fab006c, 0x3c020080, 0x34420100, 0x1621024, +0x10400005, 0x0, 0x8f42020c, 0x24420001, +0xaf42020c, 0x8f42020c, 0x10000202, 0x8fa30064, +0x32c20400, 0x10400012, 0x34028100, 0x97c3000c, +0x1462000f, 0x0, 0x240c0200, 0xa7ac0076, +0x97c2000e, 0x8fc30008, 0x8fc40004, 0x8fab0064, +0x8fc50000, 0x256bfffc, 0xafab0064, 0xa7a2007e, +0xafc3000c, 0xafc40008, 0xafc50004, 0x27de0004, +0x8fa70064, 0x320200ff, 0x14400034, 0x3c020100, +0x97c4000c, 0x2c8305dd, 0x38828870, 0x2c420001, +0x621825, 0x10600015, 0x2821, 0x32c20800, +0x10400015, 0x24020800, 0x97c30014, 0x14620012, +0x3402aaaa, 0x97c3000e, 0x14620007, 0x2021, +0x97c30010, 0x24020300, 0x14620004, 0x801021, +0x97c20012, 0x2c440001, 0x801021, 0x54400006, +0x24050016, 0x10000004, 0x0, 0x24020800, +0x50820001, 0x2405000e, 0x10a00013, 0x3c52021, +0x24830009, 0x3c02001f, 0x3442ffff, 0x43102b, +0x10400003, 0x0, 0x8f420148, 0x621823, +0x90620000, 0x38430006, 0x2c630001, 0x38420011, +0x2c420001, 0x621825, 0x10600004, 0x3c020100, +0x94820002, 0x453821, 0x3c020100, 0x2c21024, +0x5040000e, 0xafa70064, 0x8fac0064, 0x10ec0008, +0x3c050007, 0x3c040001, 0x24846908, 0x8fa60064, +0x34a54000, 0xafa00010, 0xc002b3b, 0xafa00014, +0x8fab0064, 0x256b0004, 0xafab0064, 0x8f420080, +0x8fac0064, 0x4c102b, 0x1040002c, 0x32c28000, +0x10400034, 0x240b0003, 0x32c21000, 0x10400031, +0xafab005c, 0x1000002e, 0x240c0004, 0x8f420350, +0x2403ffbf, 0x283a024, 0x24420001, 0xaf420350, +0x10000173, 0x8f420350, 0x3c020800, 0x2c2b025, +0x2402ffbf, 0x282a024, 0x8f830128, 0x3c040001, +0x248468d0, 0x26620001, 0xafa20014, 0xafa30010, +0x8f860120, 0x8f870124, 0x3c050007, 0xc002b3b, +0x34a55300, 0x10000162, 0x0, 0x8ea20000, +0x8ea30004, 0x3c040001, 0x248468e8, 0xafb00010, +0xafb10014, 0x8ea70018, 0x34a55900, 0xc002b3b, +0x603021, 0x10000156, 0x0, 0x8f420084, +0x8fab0064, 0x4b102b, 0x14400007, 0x3c020001, +0x2c21024, 0x10400004, 0x0, 0x240c0002, +0xafac005c, 0x8fab0064, 0x11600166, 0x27ac0020, +0xafac008c, 0x8fab005c, 0x240c0001, 0x556c0021, +0x240c0002, 0x8f430054, 0x8f420050, 0x1062000b, +0x274b0054, 0x8f510054, 0x3403ecc0, 0xafab004c, +0x26220001, 0x304201ff, 0xafa20054, 0x111140, +0x431021, 0x1000006b, 0x2e2a821, 0x8f420044, +0x8fac0064, 0x3c040001, 0x248468ac, 0xafac0014, +0xafa20010, 0x8f460054, 0x8f470050, 0x3c050007, +0xc002b3b, 0x34a54300, 0x8f430350, 0x2402ffbf, +0x282a024, 0x24630001, 0xaf430350, 0x10000124, +0x8f420350, 0x156c001d, 0x0, 0x8f430074, +0x8f420070, 0x1062000a, 0x274b0074, 0x8f510074, +0xafab004c, 0x26220001, 0x304203ff, 0xafa20054, +0x111140, 0x24426cc0, 0x1000004a, 0x2e2a821, +0x8f420044, 0x8fac0064, 0x3c040001, 0x248468b8, +0x3c050007, 0xafac0014, 0xafa20010, 0x8f460074, +0x8f470070, 0x34a54500, 0x240b0001, 0xc002b3b, +0xafab005c, 0x1000ffc3, 0x0, 0x8f430064, +0x8f420060, 0x1062001a, 0x274c0064, 0x8f510064, +0x8fab005c, 0xafac004c, 0x26220001, 0x304200ff, +0xafa20054, 0x24020004, 0x1562000e, 0x111140, +0x111180, 0x24420cc0, 0x2e21021, 0xafa20044, +0x9442002a, 0x8fac0044, 0x8fab0064, 0x4b102b, +0x10400024, 0x25950020, 0x240c0001, 0x10000021, +0xa3ac0087, 0x24424cc0, 0x1000001e, 0x2e2a821, +0x8f420044, 0x8fab0064, 0x3c040001, 0x248468c4, +0xafab0014, 0xafa20010, 0x8f460064, 0x8f470060, +0x3c050007, 0xc002b3b, 0x34a54800, 0x3c020008, +0x2c21024, 0x1440ff61, 0x0, 0x8f420370, +0x240c0001, 0xafac005c, 0x24420001, 0xaf420370, +0x1000ff90, 0x8f420370, 0x27a30036, 0x131040, +0x621821, 0x94620000, 0x441021, 0x1000001f, +0xa4620000, 0xaebe0018, 0x93a20087, 0x10400084, +0x9821, 0x8fab0044, 0x8fa40064, 0x8fa3008c, +0x25620020, 0xafa20028, 0x25620008, 0xafa20030, +0x25620010, 0xafab002c, 0xafa20034, 0x9562002a, +0xa7a20038, 0x95620018, 0xa7a2003a, 0x9562001a, +0xa7a2003c, 0x9562001c, 0xa7a2003e, 0x94620018, +0x24630002, 0x822023, 0x1880ffdf, 0x26730001, +0x2e620004, 0x1440fff9, 0x0, 0x8f4200fc, +0x262102a, 0x14400030, 0x24030001, 0x8f83012c, +0x10600028, 0x0, 0x8f820124, 0x431023, +0x22143, 0x58800001, 0x24840040, 0x8f820128, +0x431023, 0x21943, 0x58600001, 0x24630040, +0x64102a, 0x54400001, 0x602021, 0xaf4400fc, +0x8f4200fc, 0x262102a, 0x10400016, 0x24030001, +0x1000001a, 0x306200ff, 0x8fac008c, 0x101040, +0x4c1021, 0x94470018, 0x101080, 0x4c1021, +0xafbe0010, 0x8c420008, 0x3c040001, 0x248468dc, +0x3c050007, 0x8c430004, 0x8c420000, 0x34a55500, +0x2003021, 0xc002b3b, 0xafa30014, 0x10000039, +0x0, 0x8f420334, 0x1821, 0x24420001, +0xaf420334, 0x8f420334, 0x306200ff, 0x1040ff06, +0x8021, 0x8f430008, 0x2402fbff, 0x1260002d, +0x625024, 0x3c0b4000, 0x22b4025, 0x8fb1008c, +0x2669ffff, 0x2209021, 0x8e420008, 0x96270018, +0x8c440000, 0x8c450004, 0x56090004, 0x240b0001, +0x240c0002, 0x10000002, 0xafac0010, 0xafab0010, +0x16000004, 0xafa80014, 0x8f420008, 0x10000002, +0xafa20018, 0xafaa0018, 0x8f42010c, 0x3c03021, +0xafa80098, 0xafa9009c, 0x40f809, 0xafaa00a0, +0x8fa80098, 0x8fa9009c, 0x8faa00a0, 0x1040ffc2, +0x3c02001f, 0x96230018, 0x3442ffff, 0x3c3f021, +0x5e102b, 0x10400003, 0x26310002, 0x8f420148, +0x3c2f023, 0x26100001, 0x213102b, 0x1440ffda, +0x26520004, 0x8fb00064, 0x1000001a, 0x0, +0x96a3000a, 0x8fb00064, 0x70102b, 0x54400001, +0x608021, 0x8ea40000, 0x8ea50004, 0x8fab005c, +0x240c0002, 0xafac0010, 0x934305c4, 0xb1700, +0x10600003, 0x2223025, 0x3c020800, 0xc23025, +0xafa60014, 0x8f420008, 0xafa20018, 0x8f42010c, +0x3c03021, 0x40f809, 0x2003821, 0x1040fecb, +0x3c050007, 0x97ac0076, 0x11800007, 0x96a3000e, +0x934205c4, 0x14400004, 0x0, 0x97ab007e, +0x6c1825, 0xa6ab0016, 0x8fac006c, 0x3c02ffff, +0x1821024, 0x10400003, 0xc1402, 0x34630400, +0xa6a20014, 0xa6b0000a, 0x8fab0064, 0x560b0006, +0x3d0f021, 0x34620004, 0xafa00064, 0xa6a2000e, +0x1000000d, 0xa34005c4, 0x8fac0064, 0x3c02001f, +0x3442ffff, 0x5e102b, 0x1906023, 0xafac0064, +0xa6a3000e, 0x240b0001, 0x10400003, 0xa34b05c4, +0x8f420148, 0x3c2f023, 0x8fab0054, 0x8fac004c, +0xad8b0000, 0x8fac0064, 0x1580feba, 0x0, +0x8fab0064, 0x1160001b, 0x0, 0x934205c4, +0x10400006, 0x0, 0xaf5e00c4, 0xaf4b00c0, +0x8fac006c, 0x1000000e, 0xaf4c00c8, 0x97ab0076, +0x1160000b, 0x34038100, 0x8fa20020, 0x8c46000c, +0xa443000c, 0x97ac007e, 0x8c440004, 0x8c450008, +0xa44c000e, 0xac440000, 0xac450004, 0xac460008, +0x8f42034c, 0x24420001, 0xaf42034c, 0x10000010, +0x8f42034c, 0x8fab006c, 0x3164ffff, 0x2484fffc, +0x801821, 0x8f440250, 0x8f450254, 0x8f460118, +0x1021, 0xa32821, 0xa3382b, 0x822021, +0x872021, 0xaf440250, 0xc0f809, 0xaf450254, +0x8fbf00c0, 0x8fbe00bc, 0x8fb500b8, 0x8fb300b4, +0x8fb200b0, 0x8fb100ac, 0x8fb000a8, 0x3e00008, +0x27bd00c8, 0x3e00008, 0x0, 0x27bdffd8, +0xafbf0024, 0xafb00020, 0x8f43004c, 0x8f420048, +0x10620034, 0x0, 0x8f430048, 0x8f42004c, +0x622023, 0x4820001, 0x24840200, 0x8f430054, +0x8f42004c, 0x43102b, 0x14400004, 0x24020200, +0x8f43004c, 0x10000005, 0x431023, 0x8f420054, +0x8f43004c, 0x431023, 0x2442ffff, 0x405021, +0x8a102a, 0x54400001, 0x805021, 0x8f49004c, +0x8f48004c, 0x8f440188, 0x8f45018c, 0x8f46004c, +0x24071000, 0xafa70010, 0x84140, 0x1001821, +0x12a4821, 0x313001ff, 0xafb00014, 0x8f470014, +0x1021, 0x63140, 0xafa70018, 0xa32821, +0xa3382b, 0x822021, 0x872021, 0x3402ecc0, +0xc23021, 0x8f420108, 0x2e63021, 0x40f809, +0xa3940, 0x54400001, 0xaf50004c, 0x8f43004c, +0x8f420048, 0x14620018, 0x0, 0x8f420000, +0x10400007, 0x0, 0xaf80004c, 0x8f82004c, +0x1040fffd, 0x0, 0x10000005, 0x0, +0xaf800048, 0x8f820048, 0x1040fffd, 0x0, +0x8f820060, 0x2403fdff, 0x431024, 0xaf820060, +0x8f420000, 0x10400003, 0x0, 0x10000002, +0xaf80004c, 0xaf800048, 0x8fbf0024, 0x8fb00020, +0x3e00008, 0x27bd0028, 0x3e00008, 0x0, +0x27bdffd8, 0xafbf0024, 0xafb00020, 0x8f43005c, +0x8f420058, 0x10620049, 0x0, 0x8f430058, +0x8f42005c, 0x622023, 0x4820001, 0x24840100, +0x8f430064, 0x8f42005c, 0x43102b, 0x14400004, +0x24020100, 0x8f43005c, 0x10000005, 0x431023, +0x8f420064, 0x8f43005c, 0x431023, 0x2442ffff, +0x403821, 0x87102a, 0x54400001, 0x803821, +0x8f42005c, 0x471021, 0x305000ff, 0x32c21000, +0x10400015, 0x24082000, 0x8f49005c, 0x8f440190, +0x8f450194, 0x8f46005c, 0x73980, 0xafa80010, +0xafb00014, 0x8f480014, 0x94980, 0x1201821, +0x1021, 0xa32821, 0xa3482b, 0x822021, +0x892021, 0x63180, 0xafa80018, 0x8f420108, +0x10000014, 0x24c60cc0, 0x8f49005c, 0x8f440190, +0x8f450194, 0x8f46005c, 0x73940, 0xafa80010, +0xafb00014, 0x8f480014, 0x94940, 0x1201821, +0x1021, 0xa32821, 0xa3482b, 0x822021, +0x892021, 0x63140, 0xafa80018, 0x8f420108, +0x24c64cc0, 0x40f809, 0x2e63021, 0x54400001, +0xaf50005c, 0x8f43005c, 0x8f420058, 0x14620018, +0x0, 0x8f420000, 0x10400007, 0x0, +0xaf80004c, 0x8f82004c, 0x1040fffd, 0x0, +0x10000005, 0x0, 0xaf800048, 0x8f820048, +0x1040fffd, 0x0, 0x8f820060, 0x2403feff, +0x431024, 0xaf820060, 0x8f420000, 0x10400003, +0x0, 0x10000002, 0xaf80004c, 0xaf800048, +0x8fbf0024, 0x8fb00020, 0x3e00008, 0x27bd0028, +0x3e00008, 0x0, 0x27bdffd8, 0xafbf0024, +0xafb00020, 0x8f43006c, 0x8f420068, 0x10620033, +0x0, 0x8f430068, 0x8f42006c, 0x622023, +0x4820001, 0x24840400, 0x8f430074, 0x8f42006c, +0x43102b, 0x14400004, 0x24020400, 0x8f43006c, +0x10000005, 0x431023, 0x8f420074, 0x8f43006c, +0x431023, 0x2442ffff, 0x405021, 0x8a102a, +0x54400001, 0x805021, 0x8f49006c, 0x8f48006c, +0x8f440198, 0x8f45019c, 0x8f46006c, 0x24074000, +0xafa70010, 0x84140, 0x1001821, 0x12a4821, +0x313003ff, 0xafb00014, 0x8f470014, 0x1021, +0x63140, 0x24c66cc0, 0xafa70018, 0xa32821, +0xa3382b, 0x822021, 0x872021, 0x8f420108, +0x2e63021, 0x40f809, 0xa3940, 0x54400001, +0xaf50006c, 0x8f43006c, 0x8f420068, 0x14620018, +0x0, 0x8f420000, 0x10400007, 0x0, +0xaf80004c, 0x8f82004c, 0x1040fffd, 0x0, +0x10000005, 0x0, 0xaf800048, 0x8f820048, +0x1040fffd, 0x0, 0x8f820060, 0x2403f7ff, +0x431024, 0xaf820060, 0x8f420000, 0x10400003, +0x0, 0x10000002, 0xaf80004c, 0xaf800048, +0x8fbf0024, 0x8fb00020, 0x3e00008, 0x27bd0028, +0x3e00008, 0x0, 0x8f4200fc, 0x3c030001, +0x8f4400f8, 0x346330c8, 0x24420001, 0xaf4200fc, +0x8f850128, 0x2e31021, 0x54820004, 0x24820008, +0x3c020001, 0x34422ec8, 0x2e21021, 0x401821, +0xaf4300f8, 0xac600000, 0x8f4200f4, 0x14620004, +0x3c020001, 0x24a20020, 0x1000000f, 0xaf820128, +0x8f4300f8, 0x344230c8, 0x2e21021, 0x54620004, +0x24620008, 0x3c020001, 0x34422ec8, 0x2e21021, +0x401821, 0x8c620004, 0x21140, 0xa21021, +0xaf820128, 0xac600000, 0x8ca30018, 0x30620070, +0x1040002d, 0x30620020, 0x10400004, 0x3c020010, +0x2c21024, 0x1040000d, 0x0, 0x30620040, +0x10400004, 0x3c020020, 0x2c21024, 0x10400007, +0x0, 0x30620010, 0x1040001f, 0x3c020040, +0x2c21024, 0x1440001c, 0x0, 0x8f820040, +0x30420001, 0x14400008, 0x2021, 0x8c030104, +0x24020001, 0x50620005, 0x24040001, 0x8c020264, +0x10400003, 0x801021, 0x24040001, 0x801021, +0x10400006, 0x0, 0x8f42030c, 0x24420001, +0xaf42030c, 0x10000008, 0x8f42030c, 0x8f820044, +0x34420004, 0xaf820044, 0x8f420308, 0x24420001, +0xaf420308, 0x8f420308, 0x3e00008, 0x0, +0x3e00008, 0x0, 0x27bdff98, 0xafbf0060, +0xafbe005c, 0xafb50058, 0xafb30054, 0xafb20050, +0xafb1004c, 0xafb00048, 0x8f4200fc, 0x24420001, +0xaf4200fc, 0x8f880128, 0x25020020, 0xaf820128, +0x8d030018, 0x30620070, 0x1040002e, 0x30620020, +0x10400004, 0x3c020010, 0x2c21024, 0x1040000d, +0x0, 0x30620040, 0x10400004, 0x3c020020, +0x2c21024, 0x10400007, 0x0, 0x30620010, +0x104001a9, 0x3c020040, 0x2c21024, 0x144001a6, +0x0, 0x8f820040, 0x30420001, 0x14400008, +0x2021, 0x8c030104, 0x24020001, 0x50620005, +0x24040001, 0x8c020264, 0x10400003, 0x801021, +0x24040001, 0x801021, 0x10400006, 0x0, +0x8f42030c, 0x24420001, 0xaf42030c, 0x10000192, +0x8f42030c, 0x8f820044, 0x34420004, 0xaf820044, +0x8f420308, 0x24420001, 0xaf420308, 0x1000018a, +0x8f420308, 0x30620002, 0x1040014b, 0x3c020800, +0x8d1e001c, 0x1e5702, 0xafaa0034, 0x950a0016, +0x3c22024, 0xafaa0024, 0x8faa0034, 0x24020001, +0x15420006, 0x33deffff, 0x1e1140, 0x3403ecc0, +0x431021, 0x10000010, 0x2e2a821, 0x24020002, +0x15420005, 0x24020003, 0x1e1140, 0x24426cc0, +0x10000009, 0x2e2a821, 0x15420005, 0x1e1180, +0x1e1140, 0x24424cc0, 0x10000003, 0x2e2a821, +0x571021, 0x24550ce0, 0x96a2000e, 0x304afffc, +0x30420400, 0x10400003, 0xafaa002c, 0x100000e1, +0x8821, 0x10800004, 0x8821, 0x97b10026, +0x100000dd, 0xa6b10012, 0x8eb30018, 0x966a000c, +0xa7aa003e, 0x97a5003e, 0x2ca305dd, 0x38a28870, +0x2c420001, 0x621825, 0x10600015, 0x2021, +0x32c20800, 0x10400015, 0x24020800, 0x96630014, +0x14620012, 0x3402aaaa, 0x9663000e, 0x14620007, +0x2821, 0x96630010, 0x24020300, 0x14620004, +0xa01021, 0x96620012, 0x2c450001, 0xa01021, +0x54400006, 0x24040016, 0x10000004, 0x0, +0x24020800, 0x50a20001, 0x2404000e, 0x108000b9, +0x2649021, 0x92420000, 0x3042000f, 0x28080, +0x32c20100, 0x10400020, 0x2501821, 0x3c020020, +0x43102b, 0x1440000e, 0x2402021, 0x2821, +0x94820000, 0x24840002, 0xa22821, 0x83102b, +0x1440fffb, 0x30a2ffff, 0x51c02, 0x622821, +0x51c02, 0x30a2ffff, 0x10000009, 0x622821, +0x8f470148, 0x8f420110, 0x102842, 0x3c060020, +0x40f809, 0xafa80040, 0x3045ffff, 0x8fa80040, +0x50a00001, 0x3405ffff, 0x8faa002c, 0x354a0002, +0x10000002, 0xafaa002c, 0x2821, 0x32c20080, +0x10400090, 0xa6a50010, 0x26430009, 0x3c02001f, +0x3442ffff, 0x43102b, 0x10400003, 0x0, +0x8f420148, 0x621823, 0x90660000, 0x30c200ff, +0x38430006, 0x2c630001, 0x38420011, 0x2c420001, +0x621825, 0x1060007f, 0x24020800, 0x8821, +0x97a3003e, 0x1462000f, 0x2602021, 0x96710000, +0x96620002, 0x96630004, 0x96640006, 0x2228821, +0x2238821, 0x2248821, 0x96620008, 0x9663000a, +0x9664000c, 0x2228821, 0x2238821, 0x10000007, +0x2248821, 0x94820000, 0x24840002, 0x2228821, +0x92102b, 0x1440fffb, 0x0, 0x111c02, +0x3222ffff, 0x628821, 0x111c02, 0x3222ffff, +0x628821, 0x32c20200, 0x10400003, 0x26440006, +0x1000003e, 0x8021, 0x3c05001f, 0x34a5ffff, +0xa4102b, 0x10400003, 0x0, 0x8f420148, +0x822023, 0x94820000, 0x30421fff, 0x10400004, +0x2644000c, 0x96420002, 0x10000030, 0x508023, +0x96420002, 0x26430014, 0x508023, 0x3c020020, +0x43102b, 0x1440000a, 0xd08021, 0x9642000c, +0x2028021, 0x9642000e, 0x96430010, 0x96440012, +0x2028021, 0x2038021, 0x10000020, 0x2048021, +0xa4102b, 0x10400003, 0x0, 0x8f420148, +0x822023, 0x94820000, 0x24840002, 0x2028021, +0xa4102b, 0x10400003, 0x0, 0x8f420148, +0x822023, 0x94820000, 0x24840002, 0x2028021, +0xa4102b, 0x10400003, 0x0, 0x8f420148, +0x822023, 0x94820000, 0x24840002, 0x2028021, +0xa4102b, 0x10400003, 0x0, 0x8f420148, +0x822023, 0x94820000, 0x2028021, 0x3c020100, +0x2c21024, 0x1040000e, 0x0, 0x8faa002c, +0x31420004, 0x1040000a, 0x0, 0x9504000e, +0x2642021, 0xc003eec, 0x2484fffc, 0x3042ffff, +0x2228821, 0x111c02, 0x3222ffff, 0x628821, +0x8faa0024, 0x1518823, 0x111402, 0x2228821, +0x2308821, 0x111402, 0x2228821, 0x3231ffff, +0x52200001, 0x3411ffff, 0x8faa002c, 0x354a0001, +0xafaa002c, 0xa6b10012, 0x97aa002e, 0xa6aa000e, +0x8faa002c, 0x31420004, 0x10400002, 0x24091000, +0x34098000, 0x8f480044, 0x8f4401a0, 0x8f4501a4, +0xafa90010, 0x8f490044, 0x84140, 0x1001821, +0xafa90014, 0x8f48000c, 0x2a03021, 0x24070020, +0xafa80018, 0x8f48010c, 0x1021, 0xa32821, +0xa3482b, 0x822021, 0x100f809, 0x892021, +0x1440000b, 0x0, 0x8f820128, 0x3c040001, +0x24846914, 0xafbe0014, 0xafa20010, 0x8f860124, +0x8f870120, 0x3c050007, 0xc002b3b, 0x34a59920, +0x8f420368, 0x2442ffff, 0xaf420368, 0x8f420044, +0x8f430088, 0x24420001, 0x431024, 0xaf420044, +0x8faa0034, 0x8f440368, 0x24020001, 0x15420006, +0x24020002, 0x8f42035c, 0x2442ffff, 0xaf42035c, +0x10000049, 0x8f42035c, 0x15420006, 0x0, +0x8f420364, 0x2442ffff, 0xaf420364, 0x10000042, +0x8f420364, 0x8f420360, 0x2442ffff, 0xaf420360, +0x1000003d, 0x8f420360, 0x30621000, 0x10400005, +0x30628000, 0x8f420078, 0x24420001, 0x10000036, +0xaf420078, 0x10400034, 0x0, 0x8f420078, +0x24420001, 0xaf420078, 0x8c030240, 0x43102b, +0x1440002d, 0x24070008, 0x8f440168, 0x8f45016c, +0x8f430044, 0x8f48000c, 0x8f860120, 0x24020040, +0xafa20010, 0xafa30014, 0xafa80018, 0x8f42010c, +0x40f809, 0x24c6001c, 0x14400011, 0x24020001, +0x3c010001, 0x370821, 0xa02240f2, 0x8f820124, +0xafa20010, 0x8f820128, 0x3c040001, 0x2484688c, +0xafa20014, 0x8f460044, 0x8f870120, 0x3c050009, +0xc002b3b, 0x34a51300, 0x1000000b, 0x0, +0x8f420304, 0x24420001, 0xaf420304, 0x8f420304, +0x8f420044, 0xaf42007c, 0x3c010001, 0x370821, +0xa02040f2, 0xaf400078, 0x8f420318, 0x24420001, +0xaf420318, 0x8f420318, 0x8fbf0060, 0x8fbe005c, +0x8fb50058, 0x8fb30054, 0x8fb20050, 0x8fb1004c, +0x8fb00048, 0x3e00008, 0x27bd0068, 0x3e00008, +0x0, 0x0, 0x0, 0x8f42013c, +0xaf8200c0, 0x8f42013c, 0xaf8200c4, 0x8f42013c, +0xaf8200c8, 0x8f420138, 0xaf8200d0, 0x8f420138, +0xaf8200d4, 0x8f420138, 0x3e00008, 0xaf8200d8, +0x27bdffe0, 0x27840208, 0x24050200, 0xafbf0018, +0xc002bbf, 0x24060008, 0x8c020204, 0xc004012, +0xaf820210, 0x3c020001, 0x8c426d94, 0x30420002, +0x1040000e, 0x2021, 0x8c060248, 0x24020002, +0x3c010001, 0xac226d98, 0xc005104, 0x24050002, +0x2021, 0x8c060248, 0x24020001, 0x3c010001, +0xac226d98, 0x10000011, 0x24050001, 0x8c060248, +0x24020004, 0x3c010001, 0xac226d98, 0xc005104, +0x24050004, 0x3c020001, 0x8c426d94, 0x30420001, +0x10400008, 0x24020001, 0x3c010001, 0xac226d98, +0x2021, 0x24050001, 0x3c06601b, 0xc005104, +0x0, 0x3c040001, 0x248469d0, 0x8f420150, +0x8f430154, 0x3c050008, 0x8f460158, 0x21640, +0x31940, 0x34630403, 0x431025, 0x633c0, +0x461025, 0xaf82021c, 0xafa00010, 0xafa00014, +0x8f86021c, 0x34a50200, 0xc002b3b, 0x3821, +0x3c010001, 0xac206d90, 0x3c010001, 0xac206da8, +0x8fbf0018, 0x3e00008, 0x27bd0020, 0x27bdffe0, +0x3c050008, 0x34a50300, 0xafbf0018, 0xafa00010, +0xafa00014, 0x8f860200, 0x3c040001, 0x248469dc, +0xc002b3b, 0x3821, 0x8f420410, 0x24420001, +0xaf420410, 0x8f420410, 0x8fbf0018, 0x3e00008, +0x27bd0020, 0x27bdffd8, 0xafbf0020, 0xafb1001c, +0xafb00018, 0x8f4203a4, 0x24420001, 0xaf4203a4, +0x8f4203a4, 0x8f900220, 0x8f8200e0, 0xafa20010, +0x8f8200e4, 0xafa20014, 0x8f8600c4, 0x8f8700c8, +0x3c040001, 0x248469e8, 0xc002b3b, 0x2002821, +0x3c044000, 0x2041024, 0x504000b4, 0x3c040100, +0x8f4203bc, 0x24420001, 0xaf4203bc, 0x8f4203bc, +0x8f8700c4, 0x8f8300c8, 0x8f420148, 0x671823, +0x43102b, 0x10400003, 0x0, 0x8f420148, +0x621821, 0x10600005, 0x0, 0x8f42014c, +0x43102b, 0x1040000b, 0x0, 0x8f8200e0, +0x8f430124, 0xaf42011c, 0xaf430114, 0x8f820220, +0x3c0308ff, 0x3463fffb, 0x431024, 0x100000ce, +0x441025, 0x8f820220, 0x3c0308ff, 0x3463ffff, +0x431024, 0x34420004, 0xaf820220, 0x8f8200e0, +0x8f430124, 0xaf42011c, 0xaf430114, 0x8f8600c8, +0x8f840120, 0x8f830124, 0x10000005, 0x2821, +0x14620002, 0x24620020, 0x27624800, 0x401821, +0x1064000c, 0x30a200ff, 0x8c620018, 0x30420003, +0x1040fff7, 0x27624fe0, 0x8f4203d0, 0x24050001, +0x24420001, 0xaf4203d0, 0x8f4203d0, 0x8c660008, +0x30a200ff, 0x14400058, 0x0, 0x934205c4, +0x14400055, 0x0, 0x8f8700c4, 0x8f8800e0, +0x8f8400e4, 0x2402fff8, 0x1024024, 0x1041023, +0x218c3, 0x4620001, 0x24630200, 0x10600005, +0x24020001, 0x10620009, 0x0, 0x1000001f, +0x0, 0x8f4203c0, 0xe03021, 0x24420001, +0xaf4203c0, 0x10000040, 0x8f4203c0, 0x8f4203c4, +0x24420001, 0xaf4203c4, 0x8c860000, 0x8f420148, +0x8f4303c4, 0xe61823, 0x43102b, 0x10400004, +0x2c62233f, 0x8f420148, 0x621821, 0x2c62233f, +0x14400031, 0x0, 0x8f42020c, 0x24420001, +0xaf42020c, 0x8f42020c, 0xe03021, 0x24820008, +0xaf8200e4, 0x10000028, 0xaf8200e8, 0x8f4203c8, +0x24420001, 0xaf4203c8, 0x8f4203c8, 0x8c850000, +0x8f420148, 0xa71823, 0x43102b, 0x10400003, +0x0, 0x8f420148, 0x621821, 0x8f42014c, +0x43102b, 0x5440000a, 0xa03021, 0x8f42020c, +0x24420001, 0xaf42020c, 0x8f42020c, 0x24820008, +0xaf8200e4, 0x8f8400e4, 0x1488ffec, 0xaf8400e8, +0x1488000d, 0x27623000, 0x14820002, 0x2482fff8, +0x27623ff8, 0x94430006, 0x3c02001f, 0x3442ffff, +0xc33021, 0x46102b, 0x10400003, 0x0, +0x8f420148, 0xc23023, 0xaf8600c8, 0x8f8300c4, +0x8f420148, 0xc31823, 0x43102b, 0x10400003, +0x0, 0x8f420148, 0x621821, 0x10600005, +0x0, 0x8f42014c, 0x43102b, 0x50400008, +0x3c02fdff, 0x8f820220, 0x3c0308ff, 0x3463fffb, +0x431024, 0x3c034000, 0x1000003f, 0x431025, +0x8f4303cc, 0x3442ffff, 0x282a024, 0x24630001, +0xaf4303cc, 0x10000039, 0x8f4203cc, 0x2041024, +0x1040000e, 0x3c110200, 0x8f4203a8, 0x24420001, +0xaf4203a8, 0x8f4203a8, 0x8f820220, 0x3c0308ff, +0x3463ffff, 0x431024, 0x441025, 0xc003daf, +0xaf820220, 0x10000029, 0x0, 0x2111024, +0x50400008, 0x3c110400, 0x8f4203ac, 0x24420001, +0xaf4203ac, 0xc003daf, 0x8f4203ac, 0x10000019, +0x0, 0x2111024, 0x1040001c, 0x0, +0x8f830224, 0x24021402, 0x14620009, 0x3c050008, +0x3c040001, 0x248469f4, 0xafa00010, 0xafa00014, +0x8f860224, 0x34a50500, 0xc002b3b, 0x3821, +0x8f4203b0, 0x24420001, 0xaf4203b0, 0x8f4203b0, +0x8f820220, 0x2002021, 0x34420002, 0xc004e9c, +0xaf820220, 0x8f820220, 0x3c0308ff, 0x3463ffff, +0x431024, 0x511025, 0xaf820220, 0x8fbf0020, +0x8fb1001c, 0x8fb00018, 0x3e00008, 0x27bd0028, +0x3e00008, 0x0, 0x3c020001, 0x8c426da8, +0x27bdffb0, 0xafbf0048, 0xafbe0044, 0xafb50040, +0xafb3003c, 0xafb20038, 0xafb10034, 0x1040000f, +0xafb00030, 0x3c040001, 0x24846a00, 0x3c050008, +0xafa00010, 0xafa00014, 0x8f860220, 0x34a50600, +0x24020001, 0x3c010001, 0xac206da8, 0x3c010001, +0xac226d9c, 0xc002b3b, 0x3821, 0x3c037fff, +0x8c020268, 0x3463ffff, 0x3c04fdff, 0x431024, +0xac020268, 0x8f420004, 0x3484ffff, 0x30420002, +0x10400092, 0x284a024, 0x3c040600, 0x34842000, +0x8f420004, 0x2821, 0x2403fffd, 0x431024, +0xaf420004, 0xafa40020, 0x8f5e0018, 0x27aa0020, +0x240200ff, 0x13c20002, 0xafaa002c, 0x27c50001, +0x8c020228, 0xa09021, 0x1642000e, 0x1e38c0, +0x8f42033c, 0x24420001, 0xaf42033c, 0x8f42033c, +0x8c020228, 0x3c040001, 0x24846998, 0x3c050009, +0xafa00014, 0xafa20010, 0x8fa60020, 0x1000006d, +0x34a50500, 0xf71021, 0x8fa30020, 0x8fa40024, +0xac4304c0, 0xac4404c4, 0x8f830054, 0x8f820054, +0x247003e8, 0x2021023, 0x2c4203e9, 0x1040001b, +0x9821, 0xe08821, 0x263504c0, 0x8f440178, +0x8f45017c, 0x2201821, 0x240a0004, 0xafaa0010, +0xafb20014, 0x8f48000c, 0x1021, 0x2f53021, +0xafa80018, 0x8f48010c, 0x24070008, 0xa32821, +0xa3482b, 0x822021, 0x100f809, 0x892021, +0x54400006, 0x24130001, 0x8f820054, 0x2021023, +0x2c4203e9, 0x1440ffe9, 0x0, 0x326200ff, +0x54400017, 0xaf520018, 0x8f420378, 0x24420001, +0xaf420378, 0x8f420378, 0x8f820120, 0x8faa002c, +0xafa20010, 0x8f820124, 0x3c040001, 0x248469a4, +0x3c050009, 0xafa20014, 0x8d460000, 0x10000035, +0x34a50600, 0x8f420308, 0x24130001, 0x24420001, +0xaf420308, 0x8f420308, 0x1000001e, 0x326200ff, +0x8f830054, 0x8f820054, 0x247003e8, 0x2021023, +0x2c4203e9, 0x10400016, 0x9821, 0x3c150020, +0x24110010, 0x8f42000c, 0x8f440160, 0x8f450164, +0x8f860120, 0xafb10010, 0xafb20014, 0x551025, +0xafa20018, 0x8f42010c, 0x24070008, 0x40f809, +0x24c6001c, 0x1440ffe3, 0x0, 0x8f820054, +0x2021023, 0x2c4203e9, 0x1440ffee, 0x0, +0x326200ff, 0x14400011, 0x0, 0x8f420378, +0x24420001, 0xaf420378, 0x8f420378, 0x8f820120, +0x8faa002c, 0xafa20010, 0x8f820124, 0x3c040001, +0x248469ac, 0x3c050009, 0xafa20014, 0x8d460000, +0x34a50700, 0xc002b3b, 0x3c03821, 0x8f4202ec, +0x24420001, 0xaf4202ec, 0x8f4202ec, 0x8fbf0048, +0x8fbe0044, 0x8fb50040, 0x8fb3003c, 0x8fb20038, +0x8fb10034, 0x8fb00030, 0x3e00008, 0x27bd0050, +0x3c020001, 0x8c426da8, 0x27bdffe0, 0x1440000d, +0xafbf0018, 0x3c040001, 0x24846a0c, 0x3c050008, +0xafa00010, 0xafa00014, 0x8f860220, 0x34a50700, +0x24020001, 0x3c010001, 0xac226da8, 0xc002b3b, +0x3821, 0x3c020004, 0x2c21024, 0x10400007, +0x0, 0x8f820220, 0x3c0308ff, 0x3463ffff, +0x431024, 0x34420008, 0xaf820220, 0x3c050001, +0x8ca56d98, 0x24020001, 0x14a20007, 0x2021, +0xc00529b, 0x24050001, 0xac02026c, 0x8c03026c, +0x10000006, 0x3c020007, 0xc00529b, 0x2021, +0xac020268, 0x8c030268, 0x3c020007, 0x621824, +0x3c020002, 0x5062000d, 0x3c0205f5, 0x43102b, +0x14400006, 0x3c020004, 0x3c020001, 0x10620009, +0x3c020098, 0x1000000b, 0x0, 0x14620009, +0x3c023b9a, 0x10000004, 0x3442ca00, 0x10000002, +0x3442e100, 0x34429680, 0xaf4201fc, 0x8f4201fc, +0xaee20064, 0x8fbf0018, 0x3e00008, 0x27bd0020, +0x0, 0x0, 0x0, 0x86102b, +0x50400001, 0x872023, 0xc41023, 0x24843, +0x125102b, 0x1040001b, 0x91040, 0x824021, +0x88102b, 0x10400007, 0x1821, 0x94820000, +0x24840002, 0x621821, 0x88102b, 0x1440fffb, +0x0, 0x602021, 0xc73023, 0xa91023, +0x21040, 0xc22821, 0xc5102b, 0x10400007, +0x1821, 0x94c20000, 0x24c60002, 0x621821, +0xc5102b, 0x1440fffb, 0x0, 0x1000000d, +0x832021, 0x51040, 0x822821, 0x85102b, +0x10400007, 0x1821, 0x94820000, 0x24840002, +0x621821, 0x85102b, 0x1440fffb, 0x0, +0x602021, 0x41c02, 0x3082ffff, 0x622021, +0x41c02, 0x3082ffff, 0x622021, 0x3e00008, +0x3082ffff, 0x3e00008, 0x0, 0x802821, +0x30a20001, 0x1040002b, 0x3c03001f, 0x3463ffff, +0x24a20004, 0x62102b, 0x54400007, 0x65102b, +0x90a20001, 0x90a40003, 0x90a30000, 0x90a50002, +0x1000002a, 0x441021, 0x10400003, 0x0, +0x8f420148, 0xa22823, 0x90a40000, 0x24a50001, +0x65102b, 0x10400003, 0x0, 0x8f420148, +0xa22823, 0x90a20000, 0x24a50001, 0x21200, +0x822021, 0x65102b, 0x10400003, 0x0, +0x8f420148, 0xa22823, 0x90a20000, 0x24a50001, +0x822021, 0x65102b, 0x10400003, 0x0, +0x8f420148, 0xa22823, 0x90a20000, 0x1000002d, +0x21200, 0x3463ffff, 0x24a20004, 0x62102b, +0x5440000a, 0x65102b, 0x90a20000, 0x90a40002, +0x90a30001, 0x90a50003, 0x441021, 0x21200, +0x651821, 0x10000020, 0x432021, 0x10400003, +0x0, 0x8f420148, 0xa22823, 0x90a20000, +0x24a50001, 0x22200, 0x65102b, 0x10400003, +0x0, 0x8f420148, 0xa22823, 0x90a20000, +0x24a50001, 0x822021, 0x65102b, 0x10400003, +0x0, 0x8f420148, 0xa22823, 0x90a20000, +0x24a50001, 0x21200, 0x822021, 0x65102b, +0x10400003, 0x0, 0x8f420148, 0xa22823, +0x90a20000, 0x822021, 0x41c02, 0x3082ffff, +0x622021, 0x41c02, 0x3082ffff, 0x622021, +0x3e00008, 0x3082ffff, 0x0, 0x8f820220, +0x34420002, 0xaf820220, 0x3c020002, 0x8c428ff8, +0x30424000, 0x10400054, 0x24040001, 0x8f820200, +0x24067fff, 0x8f830200, 0x30450002, 0x2402fffd, +0x621824, 0xaf830200, 0xaf840204, 0x8f830054, +0x8f820054, 0x10000002, 0x24630001, 0x8f820054, +0x621023, 0x2c420002, 0x1440fffc, 0x0, +0x8f820224, 0x1444004d, 0x42040, 0xc4102b, +0x1040fff1, 0x0, 0x8f820200, 0x451025, +0xaf820200, 0x8f820220, 0x34428000, 0xaf820220, +0x8f830054, 0x8f820054, 0x10000002, 0x24630001, +0x8f820054, 0x621023, 0x2c420002, 0x1440fffc, +0x0, 0x8f820220, 0x3c030004, 0x431024, +0x1440000f, 0x0, 0x8f820220, 0x3c03ffff, +0x34637fff, 0x431024, 0xaf820220, 0x8f830054, +0x8f820054, 0x10000002, 0x24630001, 0x8f820054, +0x621023, 0x2c420002, 0x1440fffc, 0x0, +0x8f820220, 0x3c030004, 0x431024, 0x1440000d, +0x0, 0x8f820220, 0x34428000, 0xaf820220, +0x8f830054, 0x8f820054, 0x10000002, 0x24630001, +0x8f820054, 0x621023, 0x2c420002, 0x1440fffc, +0x0, 0x8f820220, 0x3c030004, 0x431024, +0x1040001b, 0x1021, 0x8f830220, 0x24020001, +0x10000015, 0x3c04f700, 0x8f820220, 0x3c04f700, +0x441025, 0xaf820220, 0x8f820220, 0x2403fffd, +0x431024, 0xaf820220, 0x8f820220, 0x3c030300, +0x431024, 0x14400003, 0x0, 0x10000008, +0x1021, 0x8f820220, 0x34420002, 0xaf820220, +0x8f830220, 0x24020001, 0x641825, 0xaf830220, +0x3e00008, 0x0, 0x2021, 0x3c050100, +0x24020001, 0xaf80021c, 0xaf820200, 0xaf820220, +0x27625000, 0xaf8200c0, 0x27625000, 0xaf8200c4, +0x27625000, 0xaf8200c8, 0x27625000, 0xaf8200d0, +0x27625000, 0xaf8200d4, 0x27625000, 0xaf8200d8, +0x27623000, 0xaf8200e0, 0x27623000, 0xaf8200e4, +0x27623000, 0xaf8200e8, 0x27622800, 0xaf8200f0, +0x27622800, 0xaf8200f4, 0x27622800, 0xaf8200f8, +0x418c0, 0x24840001, 0x3631021, 0xac453004, +0x3631021, 0xac403000, 0x28820200, 0x1440fff9, +0x418c0, 0x2021, 0x418c0, 0x24840001, +0x3631021, 0xac402804, 0x3631021, 0xac402800, +0x28820100, 0x1440fff9, 0x418c0, 0xaf80023c, +0x24030080, 0x24040100, 0xac600000, 0x24630004, +0x64102b, 0x5440fffd, 0xac600000, 0x8f830040, +0x3c02f000, 0x621824, 0x3c025000, 0x1062000c, +0x43102b, 0x14400006, 0x3c026000, 0x3c024000, +0x10620008, 0x24020800, 0x10000008, 0x0, +0x10620004, 0x24020800, 0x10000004, 0x0, +0x24020700, 0x3c010001, 0xac226dac, 0x3e00008, +0x0, 0x3c020001, 0x8c426dbc, 0x27bdffd0, +0xafbf002c, 0xafb20028, 0xafb10024, 0xafb00020, +0x3c010001, 0x10400005, 0xac206d94, 0xc004d9e, +0x0, 0x3c010001, 0xac206dbc, 0x8f830054, +0x8f820054, 0x10000002, 0x24630064, 0x8f820054, +0x621023, 0x2c420065, 0x1440fffc, 0x0, +0xc004db9, 0x0, 0x24040001, 0x2821, +0x27a60018, 0x34028000, 0xc0045be, 0xa7a20018, +0x8f830054, 0x8f820054, 0x10000002, 0x24630064, +0x8f820054, 0x621023, 0x2c420065, 0x1440fffc, +0x24040001, 0x24050001, 0xc00457c, 0x27a60018, +0x8f830054, 0x8f820054, 0x10000002, 0x24630064, +0x8f820054, 0x621023, 0x2c420065, 0x1440fffc, +0x24040001, 0x24050001, 0xc00457c, 0x27a60018, +0x8f830054, 0x8f820054, 0x10000002, 0x24630064, +0x8f820054, 0x621023, 0x2c420065, 0x1440fffc, +0x24040001, 0x3c060001, 0x24c66f24, 0xc00457c, +0x24050002, 0x8f830054, 0x8f820054, 0x10000002, +0x24630064, 0x8f820054, 0x621023, 0x2c420065, +0x1440fffc, 0x24040001, 0x24050003, 0x3c100001, +0x26106f26, 0xc00457c, 0x2003021, 0x97a60018, +0x3c070001, 0x94e76f24, 0x3c040001, 0x24846ae0, +0xafa00014, 0x96020000, 0x3c05000d, 0x34a50100, +0xc002b3b, 0xafa20010, 0x97a20018, 0x1040004d, +0x24036040, 0x96020000, 0x3042fff0, 0x1443000c, +0x24020020, 0x3c030001, 0x94636f24, 0x1462000b, +0x24027830, 0x24020003, 0x3c010001, 0xac226d94, +0x24020005, 0x3c010001, 0x1000003f, 0xac226f34, +0x3c030001, 0x94636f24, 0x24027830, 0x1462000c, +0x24030010, 0x3c020001, 0x94426f26, 0x3042fff0, +0x14430007, 0x24020003, 0x3c010001, 0xac226d94, +0x24020006, 0x3c010001, 0x1000002f, 0xac226f34, +0x3c020001, 0x8c426d94, 0x3c030001, 0x94636f24, +0x34420001, 0x3c010001, 0xac226d94, 0x24020015, +0x1462000b, 0x0, 0x3c020001, 0x94426f26, +0x3042fff0, 0x3843f420, 0x2c630001, 0x3842f430, +0x2c420001, 0x621825, 0x1460001b, 0x24020003, +0x3c030001, 0x94636f24, 0x24027810, 0x14620016, +0x24020002, 0x3c020001, 0x94426f26, 0x3042fff0, +0x14400011, 0x24020002, 0x1000000f, 0x24020004, +0x3c020001, 0x8c426d94, 0x34420008, 0x3c010001, +0xac226d94, 0x1000005e, 0x24020004, 0x3c020001, +0x8c426d94, 0x34420004, 0x3c010001, 0x100000af, +0xac226d94, 0x24020001, 0x3c010001, 0xac226f40, +0x3c020001, 0x8c426d94, 0x30420002, 0x144000b2, +0x3c09fff0, 0x24020e00, 0xaf820238, 0x8f840054, +0x8f820054, 0x24030008, 0x3c010001, 0xac236d98, +0x10000002, 0x248401f4, 0x8f820054, 0x821023, +0x2c4201f5, 0x1440fffc, 0x3c0200c8, 0x344201fb, +0xaf820238, 0x8f830054, 0x8f820054, 0x10000002, +0x246301f4, 0x8f820054, 0x621023, 0x2c4201f5, +0x1440fffc, 0x8021, 0x24120001, 0x24110009, +0xc004482, 0x0, 0x3c010001, 0xac326db4, +0xc004547, 0x0, 0x3c020001, 0x8c426db4, +0x1451fffb, 0x3c0200c8, 0x344201f6, 0xaf820238, +0x8f830054, 0x8f820054, 0x10000002, 0x2463000a, +0x8f820054, 0x621023, 0x2c42000b, 0x1440fffc, +0x0, 0x8f820220, 0x24040001, 0x34420002, +0xaf820220, 0x8f830200, 0x24057fff, 0x2402fffd, +0x621824, 0xaf830200, 0xaf840204, 0x8f830054, +0x8f820054, 0x10000002, 0x24630001, 0x8f820054, +0x621023, 0x2c420002, 0x1440fffc, 0x0, +0x8f820224, 0x14440005, 0x34028000, 0x42040, +0xa4102b, 0x1040fff0, 0x34028000, 0x1082ffa0, +0x26100001, 0x2e020014, 0x1440ffcd, 0x24020004, +0x3c010001, 0xac226d98, 0x8021, 0x24120009, +0x3c11ffff, 0x36313f7f, 0xc004482, 0x0, +0x24020001, 0x3c010001, 0xac226db4, 0xc004547, +0x0, 0x3c020001, 0x8c426db4, 0x1452fffb, +0x0, 0x8f820044, 0x511024, 0x34425080, +0xaf820044, 0x8f830054, 0x8f820054, 0x10000002, +0x2463000a, 0x8f820054, 0x621023, 0x2c42000b, +0x1440fffc, 0x0, 0x8f820044, 0x511024, +0x3442f080, 0xaf820044, 0x8f830054, 0x8f820054, +0x10000002, 0x2463000a, 0x8f820054, 0x621023, +0x2c42000b, 0x1440fffc, 0x0, 0x8f820220, +0x3c03f700, 0x431025, 0xaf820220, 0x8f830054, +0x8f820054, 0x10000002, 0x24630064, 0x8f820054, +0x621023, 0x2c420065, 0x1440fffc, 0x0, +0x8f820220, 0x24040001, 0x34420002, 0xaf820220, +0x8f830200, 0x24057fff, 0x2402fffd, 0x621824, +0xaf830200, 0xaf840204, 0x8f830054, 0x8f820054, +0x10000002, 0x24630001, 0x8f820054, 0x621023, +0x2c420002, 0x1440fffc, 0x0, 0x8f820224, +0x14440005, 0x34028000, 0x42040, 0xa4102b, +0x1040fff0, 0x34028000, 0x1082ff50, 0x26100001, +0x2e020064, 0x1440ffb0, 0x0, 0x3c020001, +0x8c426d94, 0x30420004, 0x14400007, 0x3c09fff0, +0x8f820044, 0x3c03ffff, 0x34633f7f, 0x431024, +0xaf820044, 0x3c09fff0, 0x3529bdc0, 0x3c060001, +0x8cc66d94, 0x3c040001, 0x24846ae0, 0x24020001, +0x3c010001, 0xac226d9c, 0x8f820054, 0x3c070001, +0x8ce76f40, 0x3c030001, 0x94636f24, 0x3c080001, +0x95086f26, 0x3c05000d, 0x34a50100, 0x3c010001, +0xac206d98, 0x491021, 0x3c010001, 0xac226f30, +0xafa30010, 0xc002b3b, 0xafa80014, 0x8fbf002c, +0x8fb20028, 0x8fb10024, 0x8fb00020, 0x3e00008, +0x27bd0030, 0x27bdffe8, 0x3c050001, 0x8ca56d98, +0x24060004, 0x24020001, 0x14a20014, 0xafbf0010, +0x3c020002, 0x8c428ffc, 0x30428000, 0x10400005, +0x3c04000f, 0x3c030001, 0x8c636f40, 0x10000005, +0x34844240, 0x3c040004, 0x3c030001, 0x8c636f40, +0x348493e0, 0x24020005, 0x14620016, 0x0, +0x3c04003d, 0x10000013, 0x34840900, 0x3c020002, +0x8c428ff8, 0x30428000, 0x10400005, 0x3c04001e, +0x3c030001, 0x8c636f40, 0x10000005, 0x34848480, +0x3c04000f, 0x3c030001, 0x8c636f40, 0x34844240, +0x24020005, 0x14620003, 0x0, 0x3c04007a, +0x34841200, 0x3c020001, 0x8c426f30, 0x8f830054, +0x441021, 0x431023, 0x44102b, 0x1440004c, +0x0, 0x3c020001, 0x8c426da0, 0x14400048, +0x0, 0x3c010001, 0x10c00025, 0xac206db0, +0x3c090001, 0x8d296d94, 0x24070001, 0x3c044000, +0x3c080002, 0x25088ffc, 0x250afffc, 0x52842, +0x14a00002, 0x24c6ffff, 0x24050008, 0xa91024, +0x10400010, 0x0, 0x14a70008, 0x0, +0x8d020000, 0x441024, 0x1040000a, 0x0, +0x3c010001, 0x10000007, 0xac256db0, 0x8d420000, +0x441024, 0x10400003, 0x0, 0x3c010001, +0xac276db0, 0x3c020001, 0x8c426db0, 0x6182b, +0x2c420001, 0x431024, 0x5440ffe5, 0x52842, +0x8f820054, 0x3c030001, 0x8c636db0, 0x3c010001, +0xac226f30, 0x1060003b, 0x24020005, 0x3c030001, +0x8c636f40, 0x3c010001, 0xac256d98, 0x14620012, +0x24020001, 0x3c020002, 0x8c428ff8, 0x3c032000, +0x34635000, 0x431024, 0x14400006, 0x24020001, +0x3c010001, 0xac206f1c, 0x3c010001, 0xac226d98, +0x24020001, 0x3c010001, 0xac226e24, 0x3c010001, +0xac226da4, 0x24020001, 0x3c010001, 0xac226d9c, +0x3c020001, 0x8c426db0, 0x1040001e, 0x0, +0x3c020001, 0x8c426d9c, 0x10400008, 0x24020001, +0x3c010001, 0xac206d9c, 0xaee204b8, 0x3c010001, +0xac206e1c, 0x3c010001, 0xac226dd4, 0x8ee304b8, +0x24020008, 0x10620005, 0x24020001, 0xc004239, +0x0, 0x1000000b, 0x0, 0x3c030001, +0x8c636d98, 0x10620007, 0x2402000e, 0x3c030002, +0x8c638f90, 0x10620003, 0x0, 0xc004e9c, +0x8f840220, 0x8fbf0010, 0x3e00008, 0x27bd0018, +0x27bdffe0, 0x3c03fdff, 0x3c040001, 0x8c846d98, +0x3c020001, 0x8c426dc0, 0x3463ffff, 0x283a024, +0x14820006, 0xafbf0018, 0x8ee304b8, 0x3c020001, +0x8c426dc4, 0x10620006, 0x0, 0x8ee204b8, +0x3c010001, 0xac246dc0, 0x3c010001, 0xac226dc4, +0x3c030001, 0x8c636d98, 0x24020002, 0x1062019c, +0x2c620003, 0x10400005, 0x24020001, 0x1062000a, +0x0, 0x10000226, 0x0, 0x24020004, +0x106200b6, 0x24020008, 0x1062010a, 0x24020001, +0x1000021f, 0x0, 0x8ee204b8, 0x2443ffff, +0x2c620008, 0x1040021c, 0x31080, 0x3c010001, +0x220821, 0x8c226af8, 0x400008, 0x0, +0x3c030001, 0x8c636f40, 0x24020005, 0x14620010, +0x0, 0x3c020001, 0x8c426da4, 0x10400008, +0x24020003, 0xc004482, 0x0, 0x24020002, +0xaee204b8, 0x3c010001, 0x10000002, 0xac206da4, +0xaee204b8, 0x3c010001, 0x10000203, 0xac206d30, +0xc004482, 0x0, 0x3c020001, 0x8c426da4, +0x3c010001, 0xac206d30, 0x1440017a, 0x24020002, +0x1000019d, 0x24020007, 0x3c030001, 0x8c636f40, +0x24020005, 0x14620003, 0x24020001, 0x3c010001, +0xac226dd0, 0xc0045ff, 0x0, 0x3c030001, +0x8c636dd0, 0x10000174, 0x24020011, 0x3c050001, +0x8ca56d98, 0x3c060002, 0x8cc68ffc, 0xc005104, +0x2021, 0x24020005, 0x3c010001, 0xac206da4, +0x100001e1, 0xaee204b8, 0x3c040001, 0x24846aec, +0x3c05000f, 0x34a50100, 0x3021, 0x3821, +0xafa00010, 0xc002b3b, 0xafa00014, 0x100001d6, +0x0, 0x8f820220, 0x3c030004, 0x431024, +0x14400175, 0x24020007, 0x8f830054, 0x3c020001, +0x8c426f28, 0x2463d8f0, 0x431023, 0x2c422710, +0x14400003, 0x24020001, 0x3c010001, 0xac226d9c, +0x3c020002, 0x8c428ffc, 0x30425000, 0x104001c2, +0x0, 0x8f820220, 0x30428000, 0x1040017d, +0x0, 0x10000175, 0x0, 0x3c050001, +0x8ca56d98, 0xc00529b, 0x2021, 0xc00551b, +0x2021, 0x3c030002, 0x8c638ff4, 0x46101b0, +0x24020001, 0x3c020008, 0x621024, 0x10400006, +0x0, 0x8f820214, 0x3c03ffff, 0x431024, +0x10000005, 0x3442251f, 0x8f820214, 0x3c03ffff, +0x431024, 0x3442241f, 0xaf820214, 0x8f820220, +0x3c030200, 0x34420002, 0xaf820220, 0x24020008, +0xaee204b8, 0x8f820220, 0x283a025, 0x3c030004, +0x431024, 0x14400016, 0x0, 0x3c020002, +0x8c428ffc, 0x30425000, 0x1040000d, 0x0, +0x8f820220, 0x30428000, 0x10400006, 0x0, +0x8f820220, 0x3c03ffff, 0x34637fff, 0x10000003, +0x431024, 0x8f820220, 0x34428000, 0xaf820220, +0x8f820220, 0x3c03f700, 0x431025, 0xaf820220, +0x3c030001, 0x8c636f40, 0x24020005, 0x1462000a, +0x0, 0x3c020001, 0x94426f26, 0x24429fbc, +0x2c420004, 0x10400004, 0x24040018, 0x24050002, +0xc004ddb, 0x24060020, 0xc003e6d, 0x0, +0x3c010001, 0x10000170, 0xac206e20, 0x8ee204b8, +0x2443ffff, 0x2c620008, 0x1040016b, 0x31080, +0x3c010001, 0x220821, 0x8c226b18, 0x400008, +0x0, 0xc004547, 0x0, 0x3c030001, +0x8c636db4, 0x100000e8, 0x24020009, 0x3c020002, +0x8c428ff8, 0x30424000, 0x10400004, 0x0, +0x8f820044, 0x10000006, 0x3442f080, 0x8f820044, +0x3c03ffff, 0x34633f7f, 0x431024, 0x3442a080, +0xaf820044, 0x8f830054, 0x100000ea, 0x24020004, +0x8f830054, 0x3c020001, 0x8c426f28, 0x2463d8f0, +0x431023, 0x2c422710, 0x14400147, 0x24020005, +0x100000d8, 0x0, 0x8f820220, 0x3c03f700, +0x431025, 0xaf820220, 0xaf800204, 0x3c010002, +0x100000d6, 0xac208fe0, 0x8f830054, 0x3c020001, +0x8c426f28, 0x2463fff6, 0x431023, 0x2c42000a, +0x14400135, 0x24020007, 0x100000d7, 0x0, +0xc003f50, 0x0, 0x1040012d, 0x24020001, +0x8f820214, 0x3c03ffff, 0x3c040001, 0x8c846f1c, +0x431024, 0x3442251f, 0xaf820214, 0x24020008, +0x10800005, 0xaee204b8, 0x3c020001, 0x8c426e44, +0x10400064, 0x24020001, 0x8f820220, 0x3c030008, +0x431024, 0x1040006a, 0x3c020200, 0x10000078, +0x0, 0x8ee204b8, 0x2443ffff, 0x2c620007, +0x10400115, 0x31080, 0x3c010001, 0x220821, +0x8c226b38, 0x400008, 0x0, 0xc003daf, +0x0, 0x3c010001, 0xac206d9c, 0xaf800204, +0x3c010002, 0xc004482, 0xac208fe0, 0x24020001, +0x3c010001, 0xac226db4, 0x24020002, 0x10000102, +0xaee204b8, 0xc004547, 0x0, 0x3c030001, +0x8c636db4, 0x10000084, 0x24020009, 0x3c020002, +0x8c428ff8, 0x30424000, 0x10400003, 0x3c0200c8, +0x10000002, 0x344201f6, 0x344201fe, 0xaf820238, +0x8f830054, 0x1000008b, 0x24020004, 0x8f830054, +0x3c020001, 0x8c426f28, 0x2463d8f0, 0x431023, +0x2c422710, 0x144000e8, 0x24020005, 0x10000079, +0x0, 0x8f820220, 0x3c03f700, 0x431025, +0xaf820220, 0xaf800204, 0x3c010002, 0x10000077, +0xac208fe0, 0x8f830054, 0x3c020001, 0x8c426f28, +0x2463fff6, 0x431023, 0x2c42000a, 0x144000d6, +0x24020007, 0x10000078, 0x0, 0xc003f50, +0x0, 0x104000ce, 0x24020001, 0x8f820214, +0x3c03ffff, 0x3c040001, 0x8c846f1c, 0x431024, +0x3442251f, 0xaf820214, 0x24020008, 0x1080000f, +0xaee204b8, 0x3c020001, 0x8c426e44, 0x1440000b, +0x0, 0x8f820220, 0x34420002, 0xaf820220, +0x24020001, 0x3c010002, 0xac228f90, 0xc004e9c, +0x8f840220, 0x10000016, 0x0, 0x8f820220, +0x3c030008, 0x431024, 0x14400011, 0x3c020200, +0x282a025, 0x2402000e, 0x3c010002, 0xac228f90, +0xc00551b, 0x2021, 0x8f820220, 0x34420002, +0xc003e6d, 0xaf820220, 0x3c050001, 0x8ca56d98, +0xc00529b, 0x2021, 0x100000a3, 0x0, +0x3c020001, 0x8c426e44, 0x1040009f, 0x0, +0x3c020001, 0x8c426e40, 0x2442ffff, 0x3c010001, +0xac226e40, 0x14400098, 0x24020002, 0x3c010001, +0xac206e44, 0x3c010001, 0x10000093, 0xac226e40, +0x8ee204b8, 0x2443ffff, 0x2c620007, 0x1040008e, +0x31080, 0x3c010001, 0x220821, 0x8c226b58, +0x400008, 0x0, 0x3c020001, 0x8c426da4, +0x10400018, 0x24020005, 0xc004482, 0x0, +0x24020002, 0xaee204b8, 0x3c010001, 0x1000007e, +0xac206da4, 0xc004963, 0x0, 0x3c030001, +0x8c636dd4, 0x24020006, 0x14620077, 0x24020003, +0x10000075, 0xaee204b8, 0x3c050001, 0x8ca56d98, +0x3c060002, 0x8cc68ff8, 0xc005104, 0x2021, +0x24020005, 0x1000006c, 0xaee204b8, 0x8f820220, +0x3c03f700, 0x431025, 0xaf820220, 0x8f830054, +0x24020006, 0xaee204b8, 0x3c010001, 0x10000062, +0xac236f28, 0x8f820220, 0x3c030004, 0x431024, +0x10400003, 0x24020007, 0x1000005b, 0xaee204b8, +0x8f830054, 0x3c020001, 0x8c426f28, 0x2463d8f0, +0x431023, 0x2c422710, 0x14400003, 0x24020001, +0x3c010001, 0xac226d9c, 0x3c020002, 0x8c428ff8, +0x30425000, 0x1040004c, 0x0, 0x8f820220, +0x30428000, 0x10400007, 0x0, 0x8f820220, +0x3c03ffff, 0x34637fff, 0x431024, 0x10000042, +0xaf820220, 0x8f820220, 0x34428000, 0x1000003e, +0xaf820220, 0x3c050001, 0x8ca56d98, 0xc00529b, +0x2021, 0xc00551b, 0x2021, 0x3c020002, +0x8c428ff0, 0x4410032, 0x24020001, 0x8f820214, +0x3c03ffff, 0x431024, 0x3442251f, 0xaf820214, +0x24020008, 0xaee204b8, 0x8f820220, 0x34420002, +0xaf820220, 0x8f820220, 0x3c030004, 0x431024, +0x14400016, 0x0, 0x3c020002, 0x8c428ff8, +0x30425000, 0x1040000d, 0x0, 0x8f820220, +0x30428000, 0x10400006, 0x0, 0x8f820220, +0x3c03ffff, 0x34637fff, 0x10000003, 0x431024, +0x8f820220, 0x34428000, 0xaf820220, 0x8f820220, +0x3c03f700, 0x431025, 0xaf820220, 0x3c020001, +0x94426f26, 0x24429fbc, 0x2c420004, 0x10400004, +0x24040018, 0x24050002, 0xc004ddb, 0x24060020, +0xc003e6d, 0x0, 0x10000003, 0x0, +0x3c010001, 0xac226d9c, 0x8fbf0018, 0x3e00008, +0x27bd0020, 0x8f820200, 0x8f820220, 0x8f820220, +0x34420004, 0xaf820220, 0x8f820200, 0x3c050001, +0x8ca56d98, 0x34420004, 0xaf820200, 0x24020002, +0x10a2004b, 0x2ca20003, 0x10400005, 0x24020001, +0x10a2000a, 0x0, 0x100000b1, 0x0, +0x24020004, 0x10a20072, 0x24020008, 0x10a20085, +0x3c02f0ff, 0x100000aa, 0x0, 0x8f830050, +0x3c02f0ff, 0x3442ffff, 0x3c040001, 0x8c846f40, +0x621824, 0x3c020700, 0x621825, 0x24020e00, +0x2484fffb, 0x2c840002, 0xaf830050, 0xaf850200, +0xaf850220, 0x14800006, 0xaf820238, 0x8f820044, +0x3c03ffff, 0x34633f7f, 0x431024, 0xaf820044, +0x3c030001, 0x8c636f40, 0x24020005, 0x14620004, +0x0, 0x8f820044, 0x34425000, 0xaf820044, +0x3c020001, 0x8c426d88, 0x3c030001, 0x8c636f40, +0x34420022, 0x2463fffc, 0x2c630002, 0x1460000c, +0xaf820200, 0x3c020001, 0x8c426dac, 0x3c030001, +0x8c636d90, 0x3c040001, 0x8c846d8c, 0x34428000, +0x621825, 0x641825, 0x1000000a, 0x34620002, +0x3c020001, 0x8c426d90, 0x3c030001, 0x8c636dac, +0x3c040001, 0x8c846d8c, 0x431025, 0x441025, +0x34420002, 0xaf820220, 0x1000002f, 0x24020001, +0x24020e01, 0xaf820238, 0x8f830050, 0x3c02f0ff, +0x3442ffff, 0x3c040001, 0x8c846f1c, 0x621824, +0x3c020d00, 0x621825, 0x24020001, 0xaf830050, +0xaf820200, 0xaf820220, 0x10800005, 0x3c033f00, +0x3c020001, 0x8c426d80, 0x10000004, 0x34630070, +0x3c020001, 0x8c426d80, 0x34630072, 0x431025, +0xaf820200, 0x3c030001, 0x8c636d84, 0x3c02f700, +0x621825, 0x3c020001, 0x8c426d90, 0x3c040001, +0x8c846dac, 0x3c050001, 0x8ca56f40, 0x431025, +0x441025, 0xaf820220, 0x24020005, 0x14a20006, +0x24020001, 0x8f820044, 0x2403afff, 0x431024, +0xaf820044, 0x24020001, 0x1000003d, 0xaf820238, +0x8f830050, 0x3c02f0ff, 0x3442ffff, 0x3c040001, +0x8c846f1c, 0x621824, 0x3c020a00, 0x621825, +0x24020001, 0xaf830050, 0xaf820200, 0x1080001e, +0xaf820220, 0x3c020001, 0x8c426e44, 0x1440001a, +0x3c033f00, 0x3c020001, 0x8c426d80, 0x1000001a, +0x346300e0, 0x8f830050, 0x3c040001, 0x8c846f1c, +0x3442ffff, 0x621824, 0x1080000f, 0xaf830050, +0x3c020001, 0x8c426e44, 0x1440000b, 0x3c043f00, +0x3c030001, 0x8c636d80, 0x348400e0, 0x24020001, +0xaf820200, 0xaf820220, 0x641825, 0xaf830200, +0x10000008, 0x3c05f700, 0x3c020001, 0x8c426d80, +0x3c033f00, 0x346300e2, 0x431025, 0xaf820200, +0x3c05f700, 0x34a58000, 0x3c030001, 0x8c636d84, +0x3c020001, 0x8c426d90, 0x3c040001, 0x8c846dac, +0x651825, 0x431025, 0x441025, 0xaf820220, +0x3e00008, 0x0, 0x3c030001, 0x8c636db4, +0x3c020001, 0x8c426db8, 0x10620003, 0x24020002, +0x3c010001, 0xac236db8, 0x1062001d, 0x2c620003, +0x10400025, 0x24020001, 0x14620023, 0x24020004, +0x3c030001, 0x8c636d98, 0x10620006, 0x24020008, +0x1462000c, 0x3c0200c8, 0x344201fb, 0x10000009, +0xaf820238, 0x24020e01, 0xaf820238, 0x8f820044, +0x3c03ffff, 0x34633f7f, 0x431024, 0x34420080, +0xaf820044, 0x8f830054, 0x24020002, 0x3c010001, +0xac226db4, 0x3c010001, 0x1000000b, 0xac236f2c, +0x8f830054, 0x3c020001, 0x8c426f2c, 0x2463d8f0, +0x431023, 0x2c422710, 0x14400003, 0x24020009, +0x3c010001, 0xac226db4, 0x3e00008, 0x0, +0x0, 0x0, 0x0, 0x27bdffd8, +0xafb20018, 0x809021, 0xafb3001c, 0xa09821, +0xafb10014, 0xc08821, 0xafb00010, 0x8021, +0xafbf0020, 0xa6200000, 0xc004d78, 0x24040001, +0x26100001, 0x2e020020, 0x1440fffb, 0x0, +0xc004d78, 0x2021, 0xc004d78, 0x24040001, +0xc004d78, 0x24040001, 0xc004d78, 0x2021, +0x24100010, 0x2501024, 0x10400002, 0x2021, +0x24040001, 0xc004d78, 0x108042, 0x1600fffa, +0x2501024, 0x24100010, 0x2701024, 0x10400002, +0x2021, 0x24040001, 0xc004d78, 0x108042, +0x1600fffa, 0x2701024, 0xc004db9, 0x34108000, +0xc004db9, 0x0, 0xc004d58, 0x0, +0x50400005, 0x108042, 0x96220000, 0x501025, +0xa6220000, 0x108042, 0x1600fff7, 0x0, +0xc004db9, 0x0, 0x8fbf0020, 0x8fb3001c, +0x8fb20018, 0x8fb10014, 0x8fb00010, 0x3e00008, +0x27bd0028, 0x27bdffd8, 0xafb10014, 0x808821, +0xafb20018, 0xa09021, 0xafb3001c, 0xc09821, +0xafb00010, 0x8021, 0xafbf0020, 0xc004d78, +0x24040001, 0x26100001, 0x2e020020, 0x1440fffb, +0x0, 0xc004d78, 0x2021, 0xc004d78, +0x24040001, 0xc004d78, 0x2021, 0xc004d78, +0x24040001, 0x24100010, 0x2301024, 0x10400002, +0x2021, 0x24040001, 0xc004d78, 0x108042, +0x1600fffa, 0x2301024, 0x24100010, 0x2501024, +0x10400002, 0x2021, 0x24040001, 0xc004d78, +0x108042, 0x1600fffa, 0x2501024, 0xc004d78, +0x24040001, 0xc004d78, 0x2021, 0x34108000, +0x96620000, 0x501024, 0x10400002, 0x2021, +0x24040001, 0xc004d78, 0x108042, 0x1600fff8, +0x0, 0xc004db9, 0x0, 0x8fbf0020, +0x8fb3001c, 0x8fb20018, 0x8fb10014, 0x8fb00010, +0x3e00008, 0x27bd0028, 0x3c040001, 0x8c846dd0, +0x3c020001, 0x8c426e18, 0x27bdffd8, 0xafbf0020, +0xafb1001c, 0x10820003, 0xafb00018, 0x3c010001, +0xac246e18, 0x3c030001, 0x8c636f40, 0x24020005, +0x14620005, 0x2483ffff, 0xc004963, 0x0, +0x1000034c, 0x0, 0x2c620013, 0x10400349, +0x31080, 0x3c010001, 0x220821, 0x8c226b80, +0x400008, 0x0, 0xc004db9, 0x8021, +0x34028000, 0xa7a20010, 0x27b10010, 0xc004d78, +0x24040001, 0x26100001, 0x2e020020, 0x1440fffb, +0x0, 0xc004d78, 0x2021, 0xc004d78, +0x24040001, 0xc004d78, 0x2021, 0xc004d78, +0x24040001, 0x24100010, 0x32020001, 0x10400002, +0x2021, 0x24040001, 0xc004d78, 0x108042, +0x1600fffa, 0x32020001, 0x24100010, 0xc004d78, +0x2021, 0x108042, 0x1600fffc, 0x0, +0xc004d78, 0x24040001, 0xc004d78, 0x2021, +0x34108000, 0x96220000, 0x501024, 0x10400002, +0x2021, 0x24040001, 0xc004d78, 0x108042, +0x1600fff8, 0x0, 0xc004db9, 0x0, +0x1000030e, 0x24020002, 0x27b10010, 0xa7a00010, +0x8021, 0xc004d78, 0x24040001, 0x26100001, +0x2e020020, 0x1440fffb, 0x0, 0xc004d78, +0x2021, 0xc004d78, 0x24040001, 0xc004d78, +0x24040001, 0xc004d78, 0x2021, 0x24100010, +0x32020001, 0x10400002, 0x2021, 0x24040001, +0xc004d78, 0x108042, 0x1600fffa, 0x32020001, +0x24100010, 0xc004d78, 0x2021, 0x108042, +0x1600fffc, 0x0, 0xc004db9, 0x34108000, +0xc004db9, 0x0, 0xc004d58, 0x0, +0x50400005, 0x108042, 0x96220000, 0x501025, +0xa6220000, 0x108042, 0x1600fff7, 0x0, +0xc004db9, 0x0, 0x97a20010, 0x30428000, +0x144002dc, 0x24020003, 0x100002d8, 0x0, +0x24021200, 0xa7a20010, 0x27b10010, 0x8021, +0xc004d78, 0x24040001, 0x26100001, 0x2e020020, +0x1440fffb, 0x0, 0xc004d78, 0x2021, +0xc004d78, 0x24040001, 0xc004d78, 0x2021, +0xc004d78, 0x24040001, 0x24100010, 0x32020001, +0x10400002, 0x2021, 0x24040001, 0xc004d78, +0x108042, 0x1600fffa, 0x32020001, 0x24100010, +0xc004d78, 0x2021, 0x108042, 0x1600fffc, +0x0, 0xc004d78, 0x24040001, 0xc004d78, +0x2021, 0x34108000, 0x96220000, 0x501024, +0x10400002, 0x2021, 0x24040001, 0xc004d78, +0x108042, 0x1600fff8, 0x0, 0xc004db9, +0x0, 0x8f830054, 0x10000296, 0x24020004, +0x8f830054, 0x3c020001, 0x8c426f3c, 0x2463ff9c, +0x431023, 0x2c420064, 0x1440029e, 0x24020002, +0x3c030001, 0x8c636f40, 0x10620297, 0x2c620003, +0x14400296, 0x24020011, 0x24020003, 0x10620005, +0x24020004, 0x10620291, 0x2402000f, 0x1000028f, +0x24020011, 0x1000028d, 0x24020005, 0x24020014, +0xa7a20010, 0x27b10010, 0x8021, 0xc004d78, +0x24040001, 0x26100001, 0x2e020020, 0x1440fffb, +0x0, 0xc004d78, 0x2021, 0xc004d78, +0x24040001, 0xc004d78, 0x2021, 0xc004d78, +0x24040001, 0x24100010, 0x32020001, 0x10400002, +0x2021, 0x24040001, 0xc004d78, 0x108042, +0x1600fffa, 0x32020001, 0x24100010, 0x32020012, +0x10400002, 0x2021, 0x24040001, 0xc004d78, +0x108042, 0x1600fffa, 0x32020012, 0xc004d78, +0x24040001, 0xc004d78, 0x2021, 0x34108000, +0x96220000, 0x501024, 0x10400002, 0x2021, +0x24040001, 0xc004d78, 0x108042, 0x1600fff8, +0x0, 0xc004db9, 0x0, 0x8f830054, +0x10000248, 0x24020006, 0x8f830054, 0x3c020001, +0x8c426f3c, 0x2463ff9c, 0x431023, 0x2c420064, +0x14400250, 0x24020007, 0x1000024c, 0x0, +0x24020006, 0xa7a20010, 0x27b10010, 0x8021, +0xc004d78, 0x24040001, 0x26100001, 0x2e020020, +0x1440fffb, 0x0, 0xc004d78, 0x2021, +0xc004d78, 0x24040001, 0xc004d78, 0x2021, +0xc004d78, 0x24040001, 0x24100010, 0x32020001, +0x10400002, 0x2021, 0x24040001, 0xc004d78, +0x108042, 0x1600fffa, 0x32020001, 0x24100010, +0x32020013, 0x10400002, 0x2021, 0x24040001, +0xc004d78, 0x108042, 0x1600fffa, 0x32020013, +0xc004d78, 0x24040001, 0xc004d78, 0x2021, +0x34108000, 0x96220000, 0x501024, 0x10400002, +0x2021, 0x24040001, 0xc004d78, 0x108042, +0x1600fff8, 0x0, 0xc004db9, 0x0, +0x8f830054, 0x10000207, 0x24020008, 0x8f830054, +0x3c020001, 0x8c426f3c, 0x2463ff9c, 0x431023, +0x2c420064, 0x1440020f, 0x24020009, 0x1000020b, +0x0, 0x27b10010, 0xa7a00010, 0x8021, +0xc004d78, 0x24040001, 0x26100001, 0x2e020020, +0x1440fffb, 0x0, 0xc004d78, 0x2021, +0xc004d78, 0x24040001, 0xc004d78, 0x24040001, +0xc004d78, 0x2021, 0x24100010, 0x32020001, +0x10400002, 0x2021, 0x24040001, 0xc004d78, +0x108042, 0x1600fffa, 0x32020001, 0x24100010, +0x32020018, 0x10400002, 0x2021, 0x24040001, +0xc004d78, 0x108042, 0x1600fffa, 0x32020018, +0xc004db9, 0x34108000, 0xc004db9, 0x0, +0xc004d58, 0x0, 0x50400005, 0x108042, +0x96220000, 0x501025, 0xa6220000, 0x108042, +0x1600fff7, 0x0, 0xc004db9, 0x8021, +0x97a20010, 0x27b10010, 0x34420001, 0xa7a20010, +0xc004d78, 0x24040001, 0x26100001, 0x2e020020, +0x1440fffb, 0x0, 0xc004d78, 0x2021, +0xc004d78, 0x24040001, 0xc004d78, 0x2021, +0xc004d78, 0x24040001, 0x24100010, 0x32020001, +0x10400002, 0x2021, 0x24040001, 0xc004d78, +0x108042, 0x1600fffa, 0x32020001, 0x24100010, +0x32020018, 0x10400002, 0x2021, 0x24040001, +0xc004d78, 0x108042, 0x1600fffa, 0x32020018, +0xc004d78, 0x24040001, 0xc004d78, 0x2021, +0x34108000, 0x96220000, 0x501024, 0x10400002, +0x2021, 0x24040001, 0xc004d78, 0x108042, +0x1600fff8, 0x0, 0xc004db9, 0x0, +0x8f830054, 0x10000193, 0x2402000a, 0x8f830054, +0x3c020001, 0x8c426f3c, 0x2463ff9c, 0x431023, +0x2c420064, 0x1440019b, 0x2402000b, 0x10000197, +0x0, 0x27b10010, 0xa7a00010, 0x8021, +0xc004d78, 0x24040001, 0x26100001, 0x2e020020, +0x1440fffb, 0x0, 0xc004d78, 0x2021, +0xc004d78, 0x24040001, 0xc004d78, 0x24040001, +0xc004d78, 0x2021, 0x24100010, 0x32020001, +0x10400002, 0x2021, 0x24040001, 0xc004d78, +0x108042, 0x1600fffa, 0x32020001, 0x24100010, +0x32020017, 0x10400002, 0x2021, 0x24040001, +0xc004d78, 0x108042, 0x1600fffa, 0x32020017, +0xc004db9, 0x34108000, 0xc004db9, 0x0, +0xc004d58, 0x0, 0x50400005, 0x108042, +0x96220000, 0x501025, 0xa6220000, 0x108042, +0x1600fff7, 0x0, 0xc004db9, 0x8021, +0x97a20010, 0x27b10010, 0x34420700, 0xa7a20010, +0xc004d78, 0x24040001, 0x26100001, 0x2e020020, +0x1440fffb, 0x0, 0xc004d78, 0x2021, +0xc004d78, 0x24040001, 0xc004d78, 0x2021, +0xc004d78, 0x24040001, 0x24100010, 0x32020001, +0x10400002, 0x2021, 0x24040001, 0xc004d78, +0x108042, 0x1600fffa, 0x32020001, 0x24100010, +0x32020017, 0x10400002, 0x2021, 0x24040001, +0xc004d78, 0x108042, 0x1600fffa, 0x32020017, +0xc004d78, 0x24040001, 0xc004d78, 0x2021, +0x34108000, 0x96220000, 0x501024, 0x10400002, +0x2021, 0x24040001, 0xc004d78, 0x108042, +0x1600fff8, 0x0, 0xc004db9, 0x0, +0x8f830054, 0x1000011f, 0x2402000c, 0x8f830054, +0x3c020001, 0x8c426f3c, 0x2463ff9c, 0x431023, +0x2c420064, 0x14400127, 0x24020012, 0x10000123, +0x0, 0x27b10010, 0xa7a00010, 0x8021, +0xc004d78, 0x24040001, 0x26100001, 0x2e020020, +0x1440fffb, 0x0, 0xc004d78, 0x2021, +0xc004d78, 0x24040001, 0xc004d78, 0x24040001, +0xc004d78, 0x2021, 0x24100010, 0x32020001, +0x10400002, 0x2021, 0x24040001, 0xc004d78, +0x108042, 0x1600fffa, 0x32020001, 0x24100010, +0x32020014, 0x10400002, 0x2021, 0x24040001, +0xc004d78, 0x108042, 0x1600fffa, 0x32020014, +0xc004db9, 0x34108000, 0xc004db9, 0x0, +0xc004d58, 0x0, 0x50400005, 0x108042, +0x96220000, 0x501025, 0xa6220000, 0x108042, +0x1600fff7, 0x0, 0xc004db9, 0x8021, +0x97a20010, 0x27b10010, 0x34420010, 0xa7a20010, +0xc004d78, 0x24040001, 0x26100001, 0x2e020020, +0x1440fffb, 0x0, 0xc004d78, 0x2021, +0xc004d78, 0x24040001, 0xc004d78, 0x2021, +0xc004d78, 0x24040001, 0x24100010, 0x32020001, +0x10400002, 0x2021, 0x24040001, 0xc004d78, +0x108042, 0x1600fffa, 0x32020001, 0x24100010, +0x32020014, 0x10400002, 0x2021, 0x24040001, +0xc004d78, 0x108042, 0x1600fffa, 0x32020014, +0xc004d78, 0x24040001, 0xc004d78, 0x2021, +0x34108000, 0x96220000, 0x501024, 0x10400002, +0x2021, 0x24040001, 0xc004d78, 0x108042, +0x1600fff8, 0x0, 0xc004db9, 0x0, +0x8f830054, 0x100000ab, 0x24020013, 0x8f830054, +0x3c020001, 0x8c426f3c, 0x2463ff9c, 0x431023, +0x2c420064, 0x144000b3, 0x2402000d, 0x100000af, +0x0, 0x27b10010, 0xa7a00010, 0x8021, +0xc004d78, 0x24040001, 0x26100001, 0x2e020020, +0x1440fffb, 0x0, 0xc004d78, 0x2021, +0xc004d78, 0x24040001, 0xc004d78, 0x24040001, +0xc004d78, 0x2021, 0x24100010, 0x32020001, +0x10400002, 0x2021, 0x24040001, 0xc004d78, +0x108042, 0x1600fffa, 0x32020001, 0x24100010, +0x32020018, 0x10400002, 0x2021, 0x24040001, +0xc004d78, 0x108042, 0x1600fffa, 0x32020018, +0xc004db9, 0x34108000, 0xc004db9, 0x0, +0xc004d58, 0x0, 0x50400005, 0x108042, +0x96220000, 0x501025, 0xa6220000, 0x108042, +0x1600fff7, 0x0, 0xc004db9, 0x8021, +0x97a20010, 0x27b10010, 0x3042fffe, 0xa7a20010, +0xc004d78, 0x24040001, 0x26100001, 0x2e020020, +0x1440fffb, 0x0, 0xc004d78, 0x2021, +0xc004d78, 0x24040001, 0xc004d78, 0x2021, +0xc004d78, 0x24040001, 0x24100010, 0x32020001, +0x10400002, 0x2021, 0x24040001, 0xc004d78, +0x108042, 0x1600fffa, 0x32020001, 0x24100010, +0x32020018, 0x10400002, 0x2021, 0x24040001, +0xc004d78, 0x108042, 0x1600fffa, 0x32020018, +0xc004d78, 0x24040001, 0xc004d78, 0x2021, +0x34108000, 0x96220000, 0x501024, 0x10400002, +0x2021, 0x24040001, 0xc004d78, 0x108042, +0x1600fff8, 0x0, 0xc004db9, 0x0, +0x8f830054, 0x10000037, 0x2402000e, 0x24020840, +0xa7a20010, 0x27b10010, 0x8021, 0xc004d78, +0x24040001, 0x26100001, 0x2e020020, 0x1440fffb, +0x0, 0xc004d78, 0x2021, 0xc004d78, +0x24040001, 0xc004d78, 0x2021, 0xc004d78, +0x24040001, 0x24100010, 0x32020001, 0x10400002, +0x2021, 0x24040001, 0xc004d78, 0x108042, +0x1600fffa, 0x32020001, 0x24100010, 0x32020013, +0x10400002, 0x2021, 0x24040001, 0xc004d78, +0x108042, 0x1600fffa, 0x32020013, 0xc004d78, +0x24040001, 0xc004d78, 0x2021, 0x34108000, +0x96220000, 0x501024, 0x10400002, 0x2021, +0x24040001, 0xc004d78, 0x108042, 0x1600fff8, +0x0, 0xc004db9, 0x0, 0x8f830054, +0x24020010, 0x3c010001, 0xac226dd0, 0x3c010001, +0x1000000c, 0xac236f3c, 0x8f830054, 0x3c020001, +0x8c426f3c, 0x2463ff9c, 0x431023, 0x2c420064, +0x14400004, 0x0, 0x24020011, 0x3c010001, +0xac226dd0, 0x8fbf0020, 0x8fb1001c, 0x8fb00018, +0x3e00008, 0x27bd0028, 0x3c030001, 0x8c636d98, +0x27bdffc8, 0x24020002, 0xafbf0034, 0xafb20030, +0xafb1002c, 0x14620004, 0xafb00028, 0x3c120002, +0x10000003, 0x8e528ff8, 0x3c120002, 0x8e528ffc, +0x3c030001, 0x8c636dd4, 0x3c020001, 0x8c426e1c, +0x50620004, 0x2463ffff, 0x3c010001, 0xac236e1c, +0x2463ffff, 0x2c620006, 0x10400377, 0x31080, +0x3c010001, 0x220821, 0x8c226bd8, 0x400008, +0x0, 0x2021, 0x2821, 0xc004ddb, +0x34068000, 0x24040010, 0x24050002, 0x24060002, +0x24020002, 0xc004ddb, 0xa7a20018, 0x24020002, +0x3c010001, 0x10000364, 0xac226dd4, 0x27b10018, +0xa7a00018, 0x8021, 0xc004d78, 0x24040001, +0x26100001, 0x2e020020, 0x1440fffb, 0x0, +0xc004d78, 0x2021, 0xc004d78, 0x24040001, +0xc004d78, 0x24040001, 0xc004d78, 0x2021, +0x24100010, 0x32020001, 0x10400002, 0x2021, +0x24040001, 0xc004d78, 0x108042, 0x1600fffa, +0x32020001, 0x24100010, 0xc004d78, 0x2021, +0x108042, 0x1600fffc, 0x0, 0xc004db9, +0x34108000, 0xc004db9, 0x0, 0xc004d58, +0x0, 0x50400005, 0x108042, 0x96220000, +0x501025, 0xa6220000, 0x108042, 0x1600fff7, +0x0, 0xc004db9, 0x0, 0x97a20018, +0x30428000, 0x14400004, 0x24020003, 0x3c010001, +0xac226dd4, 0x24020003, 0x3c010001, 0x1000032a, +0xac226dd4, 0x24040010, 0x24050002, 0x24060002, +0x24020002, 0xc004ddb, 0xa7a20018, 0x3c030001, +0x8c636e20, 0x24020001, 0x146201e1, 0x8021, +0x27b10018, 0xa7a00018, 0xc004d78, 0x24040001, +0x26100001, 0x2e020020, 0x1440fffb, 0x0, +0xc004d78, 0x2021, 0xc004d78, 0x24040001, +0xc004d78, 0x24040001, 0xc004d78, 0x2021, +0x24100010, 0x32020001, 0x10400002, 0x2021, +0x24040001, 0xc004d78, 0x108042, 0x1600fffa, +0x32020001, 0x24100010, 0x32020018, 0x10400002, +0x2021, 0x24040001, 0xc004d78, 0x108042, +0x1600fffa, 0x32020018, 0xc004db9, 0x34108000, +0xc004db9, 0x0, 0xc004d58, 0x0, +0x50400005, 0x108042, 0x96220000, 0x501025, +0xa6220000, 0x108042, 0x1600fff7, 0x0, +0xc004db9, 0x8021, 0x27b10018, 0xa7a00018, +0xc004d78, 0x24040001, 0x26100001, 0x2e020020, +0x1440fffb, 0x0, 0xc004d78, 0x2021, +0xc004d78, 0x24040001, 0xc004d78, 0x24040001, +0xc004d78, 0x2021, 0x24100010, 0x32020001, +0x10400002, 0x2021, 0x24040001, 0xc004d78, +0x108042, 0x1600fffa, 0x32020001, 0x24100010, +0x32020018, 0x10400002, 0x2021, 0x24040001, +0xc004d78, 0x108042, 0x1600fffa, 0x32020018, +0xc004db9, 0x34108000, 0xc004db9, 0x0, +0xc004d58, 0x0, 0x50400005, 0x108042, +0x96220000, 0x501025, 0xa6220000, 0x108042, +0x1600fff7, 0x0, 0xc004db9, 0x8021, +0x24040018, 0x2821, 0xc004ddb, 0x24060404, +0xa7a0001a, 0xc004d78, 0x24040001, 0x26100001, +0x2e020020, 0x1440fffb, 0x0, 0xc004d78, +0x2021, 0xc004d78, 0x24040001, 0xc004d78, +0x24040001, 0xc004d78, 0x2021, 0x24100010, +0x32020001, 0x10400002, 0x2021, 0x24040001, +0xc004d78, 0x108042, 0x1600fffa, 0x32020001, +0x24100010, 0x32020018, 0x10400002, 0x2021, +0x24040001, 0xc004d78, 0x108042, 0x1600fffa, +0x32020018, 0xc004db9, 0x34108000, 0xc004db9, +0x0, 0xc004d58, 0x0, 0x50400005, +0x108042, 0x97a2001a, 0x501025, 0xa7a2001a, +0x108042, 0x1600fff7, 0x0, 0xc004db9, +0x8021, 0xa7a0001a, 0xc004d78, 0x24040001, +0x26100001, 0x2e020020, 0x1440fffb, 0x0, +0xc004d78, 0x2021, 0xc004d78, 0x24040001, +0xc004d78, 0x24040001, 0xc004d78, 0x2021, +0x24100010, 0x32020001, 0x10400002, 0x2021, +0x24040001, 0xc004d78, 0x108042, 0x1600fffa, +0x32020001, 0x24100010, 0x32020018, 0x10400002, +0x2021, 0x24040001, 0xc004d78, 0x108042, +0x1600fffa, 0x32020018, 0xc004db9, 0x34108000, +0xc004db9, 0x0, 0xc004d58, 0x0, +0x50400005, 0x108042, 0x97a2001a, 0x501025, +0xa7a2001a, 0x108042, 0x1600fff7, 0x0, +0xc004db9, 0x8021, 0xa7a0001c, 0xc004d78, +0x24040001, 0x26100001, 0x2e020020, 0x1440fffb, +0x0, 0xc004d78, 0x2021, 0xc004d78, +0x24040001, 0xc004d78, 0x24040001, 0xc004d78, +0x2021, 0x24100010, 0xc004d78, 0x2021, +0x108042, 0x1600fffc, 0x0, 0x24100010, +0x3202001e, 0x10400002, 0x2021, 0x24040001, +0xc004d78, 0x108042, 0x1600fffa, 0x3202001e, +0xc004db9, 0x34108000, 0xc004db9, 0x0, +0xc004d58, 0x0, 0x50400005, 0x108042, +0x97a2001c, 0x501025, 0xa7a2001c, 0x108042, +0x1600fff7, 0x0, 0xc004db9, 0x8021, +0xa7a0001c, 0xc004d78, 0x24040001, 0x26100001, +0x2e020020, 0x1440fffb, 0x0, 0xc004d78, +0x2021, 0xc004d78, 0x24040001, 0xc004d78, +0x24040001, 0xc004d78, 0x2021, 0x24100010, +0xc004d78, 0x2021, 0x108042, 0x1600fffc, +0x0, 0x24100010, 0x3202001e, 0x10400002, +0x2021, 0x24040001, 0xc004d78, 0x108042, +0x1600fffa, 0x3202001e, 0xc004db9, 0x34108000, +0xc004db9, 0x0, 0xc004d58, 0x0, +0x50400005, 0x108042, 0x97a2001c, 0x501025, +0xa7a2001c, 0x108042, 0x1600fff7, 0x0, +0xc004db9, 0x8021, 0x24020002, 0xa7a2001e, +0xc004d78, 0x24040001, 0x26100001, 0x2e020020, +0x1440fffb, 0x0, 0xc004d78, 0x2021, +0xc004d78, 0x24040001, 0xc004d78, 0x2021, +0xc004d78, 0x24040001, 0x24100010, 0xc004d78, +0x2021, 0x108042, 0x1600fffc, 0x0, +0x24100010, 0x3202001e, 0x10400002, 0x2021, +0x24040001, 0xc004d78, 0x108042, 0x1600fffa, +0x3202001e, 0xc004d78, 0x24040001, 0xc004d78, +0x2021, 0x34108000, 0x97a2001e, 0x501024, +0x10400002, 0x2021, 0x24040001, 0xc004d78, +0x108042, 0x1600fff8, 0x0, 0xc004db9, +0x8021, 0xa7a00020, 0xc004d78, 0x24040001, +0x26100001, 0x2e020020, 0x1440fffb, 0x0, +0xc004d78, 0x2021, 0xc004d78, 0x24040001, +0xc004d78, 0x24040001, 0xc004d78, 0x2021, +0x24100010, 0xc004d78, 0x2021, 0x108042, +0x1600fffc, 0x0, 0x24100010, 0x3202001e, +0x10400002, 0x2021, 0x24040001, 0xc004d78, +0x108042, 0x1600fffa, 0x3202001e, 0xc004db9, +0x34108000, 0xc004db9, 0x0, 0xc004d58, +0x0, 0x50400005, 0x108042, 0x97a20020, +0x501025, 0xa7a20020, 0x108042, 0x1600fff7, +0x0, 0xc004db9, 0x8021, 0xa7a00020, +0xc004d78, 0x24040001, 0x26100001, 0x2e020020, +0x1440fffb, 0x0, 0xc004d78, 0x2021, +0xc004d78, 0x24040001, 0xc004d78, 0x24040001, +0xc004d78, 0x2021, 0x24100010, 0xc004d78, +0x2021, 0x108042, 0x1600fffc, 0x0, +0x24100010, 0x3202001e, 0x10400002, 0x2021, +0x24040001, 0xc004d78, 0x108042, 0x1600fffa, +0x3202001e, 0xc004db9, 0x34108000, 0xc004db9, +0x0, 0xc004d58, 0x0, 0x50400005, +0x108042, 0x97a20020, 0x501025, 0xa7a20020, +0x108042, 0x1600fff7, 0x0, 0xc004db9, +0x8021, 0xa7a00022, 0xc004d78, 0x24040001, +0x26100001, 0x2e020020, 0x1440fffb, 0x0, +0xc004d78, 0x2021, 0xc004d78, 0x24040001, +0xc004d78, 0x2021, 0xc004d78, 0x24040001, +0x24100010, 0xc004d78, 0x2021, 0x108042, +0x1600fffc, 0x0, 0x24100010, 0xc004d78, +0x2021, 0x108042, 0x1600fffc, 0x0, +0xc004d78, 0x24040001, 0xc004d78, 0x2021, +0x34108000, 0x97a20022, 0x501024, 0x10400002, +0x2021, 0x24040001, 0xc004d78, 0x108042, +0x1600fff8, 0x0, 0xc004db9, 0x0, +0x24040018, 0x24050002, 0xc004ddb, 0x24060004, +0x3c100001, 0x8e106e24, 0x24020001, 0x1602011d, +0x0, 0x3c020001, 0x94426f26, 0x3c010001, +0xac206e24, 0x24429fbc, 0x2c420004, 0x1040000c, +0x24040009, 0x24050001, 0xc004ddb, 0x24060400, +0x24040018, 0x24050001, 0xc004ddb, 0x24060020, +0x24040018, 0x24050001, 0xc004ddb, 0x24062000, +0x3c024000, 0x2421024, 0x10400123, 0x3c022000, +0x2421024, 0x10400004, 0x0, 0x3c010001, +0x10000003, 0xac306f1c, 0x3c010001, 0xac206f1c, +0x3c030001, 0x8c636f34, 0x24020005, 0x146200f9, +0x0, 0x3c020001, 0x8c426f1c, 0x10400067, +0x3c020004, 0x2421024, 0x10400011, 0xa7a00018, +0x3c020008, 0x2421024, 0x10400002, 0x24020200, +0xa7a20018, 0x3c020010, 0x2421024, 0x10400004, +0x0, 0x97a20018, 0x34420100, 0xa7a20018, +0x97a60018, 0x24040009, 0x10000004, 0x2821, +0x24040009, 0x2821, 0x3021, 0xc004ddb, +0x0, 0x24020001, 0xa7a2001a, 0x3c020008, +0x2421024, 0x1040000c, 0x3c020002, 0x2421024, +0x10400002, 0x24020101, 0xa7a2001a, 0x3c020001, +0x2421024, 0x10400005, 0x3c020010, 0x97a2001a, +0x34420040, 0xa7a2001a, 0x3c020010, 0x2421024, +0x1040000e, 0x3c020002, 0x2421024, 0x10400005, +0x3c020001, 0x97a2001a, 0x34420080, 0xa7a2001a, +0x3c020001, 0x2421024, 0x10400005, 0x3c0300a0, +0x97a2001a, 0x34420020, 0xa7a2001a, 0x3c0300a0, +0x2431024, 0x54430004, 0x3c020020, 0x97a2001a, +0x1000000c, 0x34420400, 0x2421024, 0x50400004, +0x3c020080, 0x97a2001a, 0x10000006, 0x34420800, +0x2421024, 0x10400004, 0x0, 0x97a2001a, +0x34420c00, 0xa7a2001a, 0x97a6001a, 0x24040004, +0xc004ddb, 0x2821, 0x3c020004, 0x2421024, +0x10400004, 0xa7a0001c, 0x32425000, 0x14400004, +0x0, 0x32424000, 0x10400005, 0x2021, +0xc004cf9, 0x2402021, 0x10000096, 0x0, +0x97a6001c, 0x2821, 0x34c61200, 0xc004ddb, +0xa7a6001c, 0x1000008f, 0x0, 0x2421024, +0x10400004, 0xa7a00018, 0x32425000, 0x14400004, +0x0, 0x32424000, 0x10400005, 0x3c020010, +0xc004cf9, 0x2402021, 0x10000019, 0xa7a0001a, +0x2421024, 0x10400004, 0x0, 0x97a20018, +0x10000004, 0xa7a20018, 0x97a20018, 0x34420100, +0xa7a20018, 0x3c020001, 0x2421024, 0x10400004, +0x0, 0x97a20018, 0x10000004, 0xa7a20018, +0x97a20018, 0x34422000, 0xa7a20018, 0x97a60018, +0x2021, 0xc004ddb, 0x2821, 0xa7a0001a, +0x8021, 0xc004d78, 0x24040001, 0x26100001, +0x2e020020, 0x1440fffb, 0x0, 0xc004d78, +0x2021, 0xc004d78, 0x24040001, 0xc004d78, +0x24040001, 0xc004d78, 0x2021, 0x24100010, +0x32020001, 0x10400002, 0x2021, 0x24040001, +0xc004d78, 0x108042, 0x1600fffa, 0x32020001, +0x24100010, 0xc004d78, 0x2021, 0x108042, +0x1600fffc, 0x0, 0xc004db9, 0x34108000, +0xc004db9, 0x0, 0xc004d58, 0x0, +0x50400005, 0x108042, 0x97a2001a, 0x501025, +0xa7a2001a, 0x108042, 0x1600fff7, 0x0, +0xc004db9, 0x8021, 0xa7a0001a, 0xc004d78, +0x24040001, 0x26100001, 0x2e020020, 0x1440fffb, +0x0, 0xc004d78, 0x2021, 0xc004d78, +0x24040001, 0xc004d78, 0x24040001, 0xc004d78, +0x2021, 0x24100010, 0x32020001, 0x10400002, +0x2021, 0x24040001, 0xc004d78, 0x108042, +0x1600fffa, 0x32020001, 0x24100010, 0xc004d78, +0x2021, 0x108042, 0x1600fffc, 0x0, +0xc004db9, 0x34108000, 0xc004db9, 0x0, +0xc004d58, 0x0, 0x50400005, 0x108042, +0x97a2001a, 0x501025, 0xa7a2001a, 0x108042, +0x1600fff7, 0x0, 0xc004db9, 0x0, +0x3c040001, 0x24846bcc, 0x97a60018, 0x97a7001a, +0x3c020001, 0x8c426d98, 0x3c030001, 0x8c636f1c, +0x3c05000d, 0x34a50205, 0xafa20010, 0xc002b3b, +0xafa30014, 0x8f830054, 0x24020004, 0x3c010001, +0xac226dd4, 0x3c010001, 0x10000017, 0xac236f38, +0x8f830054, 0x3c020001, 0x8c426f38, 0x2463ff9c, +0x431023, 0x2c420064, 0x1440000f, 0x0, +0x8f820220, 0x24030005, 0x3c010001, 0xac236dd4, +0x3c03f700, 0x431025, 0x10000007, 0xaf820220, +0x24020006, 0x3c010001, 0xac226dd4, 0x24020011, +0x3c010001, 0xac226dd0, 0x8fbf0034, 0x8fb20030, +0x8fb1002c, 0x8fb00028, 0x3e00008, 0x27bd0038, +0x27bdffd8, 0xafb00018, 0x808021, 0xafb1001c, +0x8821, 0x32024000, 0x10400013, 0xafbf0020, +0x3c020010, 0x2021024, 0x2c420001, 0x21023, +0x30434100, 0x3c020001, 0x2021024, 0x14400006, +0x34714000, 0x3c020002, 0x2021024, 0x14400002, +0x34716000, 0x34714040, 0x2021, 0x2821, +0x10000036, 0x2203021, 0x32021000, 0x10400035, +0x2021, 0x2821, 0xc004ddb, 0x24060040, +0x24040018, 0x2821, 0xc004ddb, 0x24060c00, +0x24040017, 0x2821, 0xc004ddb, 0x24060400, +0x24040016, 0x2821, 0xc004ddb, 0x24060006, +0x24040017, 0x2821, 0xc004ddb, 0x24062500, +0x24040016, 0x2821, 0xc004ddb, 0x24060006, +0x24040017, 0x2821, 0xc004ddb, 0x24064600, +0x24040016, 0x2821, 0xc004ddb, 0x24060006, +0x24040017, 0x2821, 0xc004ddb, 0x24066700, +0x24040016, 0x2821, 0xc004ddb, 0x24060006, +0x2404001f, 0x2821, 0xc004ddb, 0x24060010, +0x24040009, 0x2821, 0xc004ddb, 0x24061500, +0x24040009, 0x2821, 0x24061d00, 0xc004ddb, +0x0, 0x3c040001, 0x24846bf0, 0x3c05000e, +0x34a50100, 0x2003021, 0x2203821, 0xafa00010, +0xc002b3b, 0xafa00014, 0x8fbf0020, 0x8fb1001c, +0x8fb00018, 0x3e00008, 0x27bd0028, 0x8f850044, +0x8f820044, 0x3c030001, 0x431025, 0x3c030008, +0xaf820044, 0x8f840054, 0x8f820054, 0xa32824, +0x10000002, 0x24840001, 0x8f820054, 0x821023, +0x2c420002, 0x1440fffc, 0x0, 0x8f820044, +0x3c03fffe, 0x3463ffff, 0x431024, 0xaf820044, +0x8f830054, 0x8f820054, 0x10000002, 0x24630001, +0x8f820054, 0x621023, 0x2c420002, 0x1440fffc, +0x0, 0x3e00008, 0xa01021, 0x8f830044, +0x3c02fff0, 0x3442ffff, 0x42480, 0x621824, +0x3c020002, 0x822025, 0x641825, 0xaf830044, +0x8f820044, 0x3c03fffe, 0x3463ffff, 0x431024, +0xaf820044, 0x8f830054, 0x8f820054, 0x10000002, +0x24630001, 0x8f820054, 0x621023, 0x2c420002, +0x1440fffc, 0x0, 0x8f820044, 0x3c030001, +0x431025, 0xaf820044, 0x8f830054, 0x8f820054, +0x10000002, 0x24630001, 0x8f820054, 0x621023, +0x2c420002, 0x1440fffc, 0x0, 0x3e00008, +0x0, 0x8f820044, 0x2403ff7f, 0x431024, +0xaf820044, 0x8f830054, 0x8f820054, 0x10000002, +0x24630001, 0x8f820054, 0x621023, 0x2c420002, +0x1440fffc, 0x0, 0x8f820044, 0x34420080, +0xaf820044, 0x8f830054, 0x8f820054, 0x10000002, +0x24630001, 0x8f820054, 0x621023, 0x2c420002, +0x1440fffc, 0x0, 0x3e00008, 0x0, +0x8f820044, 0x3c03fff0, 0x3463ffff, 0x431024, +0xaf820044, 0x8f820044, 0x3c030001, 0x431025, +0xaf820044, 0x8f830054, 0x8f820054, 0x10000002, +0x24630001, 0x8f820054, 0x621023, 0x2c420002, +0x1440fffc, 0x0, 0x8f820044, 0x3c03fffe, +0x3463ffff, 0x431024, 0xaf820044, 0x8f830054, +0x8f820054, 0x10000002, 0x24630001, 0x8f820054, +0x621023, 0x2c420002, 0x1440fffc, 0x0, +0x3e00008, 0x0, 0x27bdffc8, 0xafb30024, +0x809821, 0xafbe002c, 0xa0f021, 0xafb20020, +0xc09021, 0x33c2ffff, 0xafbf0030, 0xafb50028, +0xafb1001c, 0xafb00018, 0x14400034, 0xa7b20010, +0x3271ffff, 0x27b20010, 0x8021, 0xc004d78, +0x24040001, 0x26100001, 0x2e020020, 0x1440fffb, +0x0, 0xc004d78, 0x2021, 0xc004d78, +0x24040001, 0xc004d78, 0x2021, 0xc004d78, +0x24040001, 0x24100010, 0x32020001, 0x10400002, +0x2021, 0x24040001, 0xc004d78, 0x108042, +0x1600fffa, 0x32020001, 0x24100010, 0x2301024, +0x10400002, 0x2021, 0x24040001, 0xc004d78, +0x108042, 0x1600fffa, 0x2301024, 0xc004d78, +0x24040001, 0xc004d78, 0x2021, 0x34108000, +0x96420000, 0x501024, 0x10400002, 0x2021, +0x24040001, 0xc004d78, 0x108042, 0x12000075, +0x0, 0x1000fff6, 0x0, 0x3275ffff, +0x27b10010, 0xa7a00010, 0x8021, 0xc004d78, +0x24040001, 0x26100001, 0x2e020020, 0x1440fffb, +0x0, 0xc004d78, 0x2021, 0xc004d78, +0x24040001, 0xc004d78, 0x24040001, 0xc004d78, +0x2021, 0x24100010, 0x32020001, 0x10400002, +0x2021, 0x24040001, 0xc004d78, 0x108042, +0x1600fffa, 0x32020001, 0x24100010, 0x2b01024, +0x10400002, 0x2021, 0x24040001, 0xc004d78, +0x108042, 0x1600fffa, 0x2b01024, 0xc004db9, +0x34108000, 0xc004db9, 0x0, 0xc004d58, +0x0, 0x50400005, 0x108042, 0x96220000, +0x501025, 0xa6220000, 0x108042, 0x1600fff7, +0x0, 0xc004db9, 0x0, 0x33c5ffff, +0x24020001, 0x54a20004, 0x24020002, 0x97a20010, +0x10000006, 0x521025, 0x14a20006, 0x3271ffff, +0x97a20010, 0x121827, 0x431024, 0xa7a20010, +0x3271ffff, 0x27b20010, 0x8021, 0xc004d78, +0x24040001, 0x26100001, 0x2e020020, 0x1440fffb, +0x0, 0xc004d78, 0x2021, 0xc004d78, +0x24040001, 0xc004d78, 0x2021, 0xc004d78, +0x24040001, 0x24100010, 0x32020001, 0x10400002, +0x2021, 0x24040001, 0xc004d78, 0x108042, +0x1600fffa, 0x32020001, 0x24100010, 0x2301024, +0x10400002, 0x2021, 0x24040001, 0xc004d78, +0x108042, 0x1600fffa, 0x2301024, 0xc004d78, +0x24040001, 0xc004d78, 0x2021, 0x34108000, +0x96420000, 0x501024, 0x10400002, 0x2021, +0x24040001, 0xc004d78, 0x108042, 0x1600fff8, +0x0, 0xc004db9, 0x0, 0x8fbf0030, +0x8fbe002c, 0x8fb50028, 0x8fb30024, 0x8fb20020, +0x8fb1001c, 0x8fb00018, 0x3e00008, 0x27bd0038, +0x0, 0x0, 0x0, 0x27bdffe8, +0xafbf0010, 0x8ee304b8, 0x24020008, 0x146201e0, +0x0, 0x3c020001, 0x8c426f1c, 0x14400005, +0x0, 0xc003daf, 0x8f840224, 0x100001d8, +0x0, 0x8f820220, 0x3c030008, 0x431024, +0x10400026, 0x24020001, 0x8f840224, 0x8f820220, +0x3c030400, 0x431024, 0x10400006, 0x0, +0x3c010002, 0xac208fa0, 0x3c010002, 0x1000000b, +0xac208fc0, 0x3c030002, 0x24638fa0, 0x8c620000, +0x24420001, 0xac620000, 0x2c420002, 0x14400003, +0x24020001, 0x3c010002, 0xac228fc0, 0x3c020002, +0x8c428fc0, 0x10400006, 0x30820040, 0x10400004, +0x24020001, 0x3c010002, 0x10000003, 0xac228fc4, +0x3c010002, 0xac208fc4, 0x3c010002, 0xac248f9c, +0x3c010002, 0x1000000b, 0xac208fd0, 0x3c010002, +0xac228fd0, 0x3c010002, 0xac208fc0, 0x3c010002, +0xac208fa0, 0x3c010002, 0xac208fc4, 0x3c010002, +0xac208f9c, 0x3c030002, 0x8c638f90, 0x3c020002, +0x8c428f94, 0x50620004, 0x2463ffff, 0x3c010002, +0xac238f94, 0x2463ffff, 0x2c62000e, 0x10400194, +0x31080, 0x3c010001, 0x220821, 0x8c226c00, +0x400008, 0x0, 0x24020002, 0x3c010002, +0xac208fc0, 0x3c010002, 0xac208fa0, 0x3c010002, +0xac208f9c, 0x3c010002, 0xac208fc4, 0x3c010002, +0xac208fb8, 0x3c010002, 0xac208fb0, 0xaf800224, +0x3c010002, 0xac228f90, 0x3c020002, 0x8c428fd0, +0x1440004f, 0x3c02fdff, 0x3442ffff, 0xc003daf, +0x282a024, 0xaf800204, 0x8f820200, 0x2403fffd, +0x431024, 0xaf820200, 0x3c010002, 0xac208fe0, +0x8f830054, 0x3c020002, 0x8c428fb8, 0x24040001, +0x3c010002, 0xac248fcc, 0x24420001, 0x3c010002, +0xac228fb8, 0x2c420004, 0x3c010002, 0xac238fb4, +0x14400006, 0x24020003, 0x3c010001, 0xac246d9c, +0x3c010002, 0x1000015e, 0xac208fb8, 0x3c010002, +0x1000015b, 0xac228f90, 0x8f830054, 0x3c020002, +0x8c428fb4, 0x2463d8f0, 0x431023, 0x2c422710, +0x14400003, 0x24020004, 0x3c010002, 0xac228f90, +0x3c020002, 0x8c428fd0, 0x14400021, 0x3c02fdff, +0x3442ffff, 0x1000014a, 0x282a024, 0x3c040001, +0x8c846f20, 0x3c010002, 0xc005084, 0xac208fa8, +0x3c020002, 0x8c428fdc, 0xaf820204, 0x3c020002, +0x8c428fd0, 0x14400012, 0x3c03fdff, 0x8f820204, +0x3463ffff, 0x30420030, 0x1440012f, 0x283a024, +0x3c030002, 0x8c638fdc, 0x24020005, 0x3c010002, +0xac228f90, 0x3c010002, 0x10000131, 0xac238fe0, +0x3c020002, 0x8c428fd0, 0x10400010, 0x3c02fdff, +0x3c020001, 0x8c426e3c, 0x24420001, 0x3c010001, +0xac226e3c, 0x2c420002, 0x14400125, 0x24020001, +0x3c010001, 0xac226e44, 0x3c010001, 0xac206e3c, +0x3c010001, 0x1000011e, 0xac226d9c, 0x3c030002, +0x8c638fc0, 0x3442ffff, 0x10600119, 0x282a024, +0x3c020002, 0x8c428f9c, 0x10400115, 0x0, +0x3c010002, 0xac228fc8, 0x24020003, 0x3c010002, +0xac228fa0, 0x100000b8, 0x24020006, 0x3c010002, +0xac208fa8, 0x8f820204, 0x34420040, 0xaf820204, +0x3c020002, 0x8c428fe0, 0x24030007, 0x3c010002, +0xac238f90, 0x34420040, 0x3c010002, 0xac228fe0, +0x3c020002, 0x8c428fc0, 0x10400005, 0x0, +0x3c020002, 0x8c428f9c, 0x104000f0, 0x24020002, +0x3c050002, 0x24a58fa0, 0x8ca20000, 0x2c424e21, +0x104000ea, 0x24020002, 0x3c020002, 0x8c428fc4, +0x104000ef, 0x2404ffbf, 0x3c020002, 0x8c428f9c, +0x3c030002, 0x8c638fc8, 0x441024, 0x641824, +0x10430004, 0x24020001, 0x3c010002, 0x100000e4, +0xac228f90, 0x24020003, 0xaca20000, 0x24020008, +0x3c010002, 0xac228f90, 0x3c020002, 0x8c428fcc, +0x1040000c, 0x24020001, 0x3c040002, 0xc005091, +0x8c848f9c, 0x3c020002, 0x8c428fe8, 0x14400005, +0x24020001, 0x3c020002, 0x8c428fe4, 0x10400006, +0x24020001, 0x3c010001, 0xac226d9c, 0x3c010002, +0x100000cb, 0xac208fb8, 0x3c020002, 0x8c428fb0, +0x3c030002, 0x8c638f9c, 0x2c420001, 0x210c0, +0x30630008, 0x3c010002, 0xac228fb0, 0x3c010002, +0xac238fac, 0x8f830054, 0x24020009, 0x3c010002, +0xac228f90, 0x3c010002, 0x100000b9, 0xac238fb4, +0x8f830054, 0x3c020002, 0x8c428fb4, 0x2463d8f0, +0x431023, 0x2c422710, 0x1440009f, 0x0, +0x3c020002, 0x8c428fc0, 0x10400005, 0x0, +0x3c020002, 0x8c428f9c, 0x104000a0, 0x24020002, +0x3c030002, 0x24638fa0, 0x8c620000, 0x2c424e21, +0x1040009a, 0x24020002, 0x3c020002, 0x8c428fcc, +0x1040000e, 0x0, 0x3c020002, 0x8c428f9c, +0x3c010002, 0xac208fcc, 0x30420080, 0x1040002f, +0x2402000c, 0x8f820204, 0x30420080, 0x1440000c, +0x24020003, 0x10000029, 0x2402000c, 0x3c020002, +0x8c428f9c, 0x30420080, 0x14400005, 0x24020003, +0x8f820204, 0x30420080, 0x1040001f, 0x24020003, +0xac620000, 0x2402000a, 0x3c010002, 0xac228f90, +0x3c040002, 0x24848fd8, 0x8c820000, 0x3c030002, +0x8c638fb0, 0x431025, 0xaf820204, 0x8c830000, +0x3c040002, 0x8c848fb0, 0x2402000b, 0x3c010002, +0xac228f90, 0x641825, 0x3c010002, 0xac238fe0, +0x3c050002, 0x24a58fa0, 0x8ca20000, 0x2c424e21, +0x10400066, 0x24020002, 0x3c020002, 0x8c428fd0, +0x10400005, 0x0, 0x2402000c, 0x3c010002, +0x10000067, 0xac228f90, 0x3c020002, 0x8c428fc0, +0x10400063, 0x0, 0x3c040002, 0x8c848f9c, +0x10800055, 0x30820008, 0x3c030002, 0x8c638fac, +0x1062005b, 0x24020003, 0x3c010002, 0xac248fc8, +0xaca20000, 0x24020006, 0x3c010002, 0x10000054, +0xac228f90, 0x8f820200, 0x34420002, 0xaf820200, +0x8f830054, 0x2402000d, 0x3c010002, 0xac228f90, +0x3c010002, 0xac238fb4, 0x8f830054, 0x3c020002, +0x8c428fb4, 0x2463d8f0, 0x431023, 0x2c422710, +0x14400031, 0x0, 0x3c020002, 0x8c428fd0, +0x10400020, 0x2402000e, 0x3c030002, 0x8c638fe4, +0x3c010002, 0x14600015, 0xac228f90, 0xc003e6d, +0x0, 0x3c050001, 0x8ca56d98, 0xc00529b, +0x2021, 0x3c030001, 0x8c636d98, 0x24020004, +0x14620005, 0x2403fffb, 0x3c020001, 0x8c426d94, +0x10000003, 0x2403fff7, 0x3c020001, 0x8c426d94, +0x431024, 0x3c010001, 0xac226d94, 0x8f830224, +0x3c020200, 0x3c010002, 0xac238fec, 0x10000020, +0x282a025, 0x3c020002, 0x8c428fc0, 0x10400005, +0x0, 0x3c020002, 0x8c428f9c, 0x1040000f, +0x24020002, 0x3c020002, 0x8c428fa0, 0x2c424e21, +0x1040000a, 0x24020002, 0x3c020002, 0x8c428fc0, +0x1040000f, 0x0, 0x3c020002, 0x8c428f9c, +0x1440000b, 0x0, 0x24020002, 0x3c010002, +0x10000007, 0xac228f90, 0x3c020002, 0x8c428fc0, +0x10400003, 0x0, 0xc003daf, 0x0, +0x8f820220, 0x3c03f700, 0x431025, 0xaf820220, +0x8fbf0010, 0x3e00008, 0x27bd0018, 0x3c030002, +0x24638fe8, 0x8c620000, 0x10400005, 0x34422000, +0x3c010002, 0xac228fdc, 0x10000003, 0xac600000, +0x3c010002, 0xac248fdc, 0x3e00008, 0x0, +0x27bdffe0, 0x30820030, 0xafbf0018, 0x3c010002, +0xac228fe4, 0x14400067, 0x3c02ffff, 0x34421f0e, +0x821024, 0x14400061, 0x24020030, 0x30822000, +0x1040005d, 0x30838000, 0x31a02, 0x30820001, +0x21200, 0x3c040001, 0x8c846f20, 0x621825, +0x331c2, 0x3c030001, 0x24636e48, 0x30828000, +0x21202, 0x30840001, 0x42200, 0x441025, +0x239c2, 0x61080, 0x431021, 0x471021, +0x90430000, 0x24020001, 0x10620025, 0x0, +0x10600007, 0x24020002, 0x10620013, 0x24020003, +0x1062002c, 0x3c05000f, 0x10000037, 0x0, +0x8f820200, 0x2403feff, 0x431024, 0xaf820200, +0x8f820220, 0x3c03fffe, 0x3463ffff, 0x431024, +0xaf820220, 0x3c010002, 0xac209004, 0x3c010002, +0x10000034, 0xac20900c, 0x8f820200, 0x34420100, +0xaf820200, 0x8f820220, 0x3c03fffe, 0x3463ffff, +0x431024, 0xaf820220, 0x24020100, 0x3c010002, +0xac229004, 0x3c010002, 0x10000026, 0xac20900c, +0x8f820200, 0x2403feff, 0x431024, 0xaf820200, +0x8f820220, 0x3c030001, 0x431025, 0xaf820220, +0x3c010002, 0xac209004, 0x3c010002, 0x10000019, +0xac23900c, 0x8f820200, 0x34420100, 0xaf820200, +0x8f820220, 0x3c030001, 0x431025, 0xaf820220, +0x24020100, 0x3c010002, 0xac229004, 0x3c010002, +0x1000000c, 0xac23900c, 0x34a5ffff, 0x3c040001, +0x24846c38, 0xafa30010, 0xc002b3b, 0xafa00014, +0x10000004, 0x0, 0x24020030, 0x3c010002, +0xac228fe8, 0x8fbf0018, 0x3e00008, 0x27bd0020, +0x0, 0x0, 0x0, 0x27bdffc8, +0xafb20028, 0x809021, 0xafb3002c, 0xa09821, +0xafb00020, 0xc08021, 0x3c040001, 0x24846c50, +0x3c050009, 0x3c020001, 0x8c426d98, 0x34a59001, +0x2403021, 0x2603821, 0xafbf0030, 0xafb10024, +0xa7a0001a, 0xafb00014, 0xc002b3b, 0xafa20010, +0x24020002, 0x12620083, 0x2e620003, 0x10400005, +0x24020001, 0x1262000a, 0x0, 0x10000173, +0x0, 0x24020004, 0x126200f8, 0x24020008, +0x126200f7, 0x3c02ffec, 0x1000016c, 0x0, +0x3c020001, 0x8c426d94, 0x30420002, 0x14400004, +0x128940, 0x3c02fffb, 0x3442ffff, 0x2028024, +0x3c010002, 0x310821, 0xac308ffc, 0x3c024000, +0x2021024, 0x1040004e, 0x1023c2, 0x30840030, +0x101382, 0x3042001c, 0x3c030001, 0x24636dd8, +0x431021, 0x823821, 0x3c020020, 0x2021024, +0x10400006, 0x24020100, 0x3c010002, 0x310821, +0xac229000, 0x10000005, 0x3c020080, 0x3c010002, +0x310821, 0xac209000, 0x3c020080, 0x2021024, +0x10400006, 0x121940, 0x3c020001, 0x3c010002, +0x230821, 0x10000005, 0xac229008, 0x121140, +0x3c010002, 0x220821, 0xac209008, 0x94e40000, +0x3c030001, 0x8c636f40, 0x24020005, 0x10620010, +0xa7a40018, 0x32024000, 0x10400002, 0x34824000, +0xa7a20018, 0x24040001, 0x94e20002, 0x24050004, +0x24e60002, 0x34420001, 0xc0045be, 0xa4e20002, +0x24040001, 0x2821, 0xc0045be, 0x27a60018, +0x3c020001, 0x8c426d98, 0x24110001, 0x3c010001, +0xac316da4, 0x14530004, 0x32028000, 0xc003daf, +0x0, 0x32028000, 0x1040011c, 0x0, +0xc003daf, 0x0, 0x3c030001, 0x8c636f40, +0x24020005, 0x10620115, 0x24020002, 0x3c010001, +0xac316d9c, 0x3c010001, 0x10000110, 0xac226d98, +0x24040001, 0x24050004, 0x27b0001a, 0xc0045be, +0x2003021, 0x24040001, 0x2821, 0xc0045be, +0x2003021, 0x3c020002, 0x511021, 0x8c428ff4, +0x3c040001, 0x8c846d98, 0x3c03bfff, 0x3463ffff, +0x3c010001, 0xac336da4, 0x431024, 0x3c010002, +0x310821, 0x109300f7, 0xac228ff4, 0x100000f7, +0x0, 0x3c022000, 0x2021024, 0x10400005, +0x24020001, 0x3c010001, 0xac226f1c, 0x10000004, +0x128940, 0x3c010001, 0xac206f1c, 0x128940, +0x3c010002, 0x310821, 0xac308ff8, 0x3c024000, +0x2021024, 0x14400014, 0x0, 0x3c020001, +0x8c426f1c, 0x10400006, 0x24040004, 0x24050001, +0xc004ddb, 0x24062000, 0x24020001, 0xaee204b8, +0x3c020002, 0x511021, 0x8c428ff0, 0x3c03bfff, +0x3463ffff, 0x431024, 0x3c010002, 0x310821, +0x100000d0, 0xac228ff0, 0x3c020001, 0x8c426f1c, +0x10400028, 0x3c0300a0, 0x2031024, 0x5443000d, +0x3c020020, 0x3c020001, 0x8c426f20, 0x24030100, +0x3c010002, 0x310821, 0xac239004, 0x3c030001, +0x3c010002, 0x310821, 0xac23900c, 0x10000015, +0x34420400, 0x2021024, 0x10400008, 0x24030100, +0x3c020001, 0x8c426f20, 0x3c010002, 0x310821, +0xac239004, 0x1000000b, 0x34420800, 0x3c020080, +0x2021024, 0x1040002e, 0x3c030001, 0x3c020001, +0x8c426f20, 0x3c010002, 0x310821, 0xac23900c, +0x34420c00, 0x3c010001, 0xac226f20, 0x10000025, +0x24040001, 0x3c020020, 0x2021024, 0x10400006, +0x24020100, 0x3c010002, 0x310821, 0xac229004, +0x10000005, 0x3c020080, 0x3c010002, 0x310821, +0xac209004, 0x3c020080, 0x2021024, 0x10400007, +0x121940, 0x3c020001, 0x3c010002, 0x230821, +0xac22900c, 0x10000006, 0x24040001, 0x121140, +0x3c010002, 0x220821, 0xac20900c, 0x24040001, +0x2821, 0x27b0001e, 0xc00457c, 0x2003021, +0x24040001, 0x2821, 0xc00457c, 0x2003021, +0x24040001, 0x24050001, 0x27b0001c, 0xc00457c, +0x2003021, 0x24040001, 0x24050001, 0xc00457c, +0x2003021, 0x10000077, 0x0, 0x3c02ffec, +0x3442ffff, 0x2028024, 0x3c020008, 0x2028025, +0x121140, 0x3c010002, 0x220821, 0xac308ff8, +0x3c022000, 0x2021024, 0x10400009, 0x0, +0x3c020001, 0x8c426e44, 0x14400005, 0x24020001, +0x3c010001, 0xac226f1c, 0x10000004, 0x3c024000, +0x3c010001, 0xac206f1c, 0x3c024000, 0x2021024, +0x1440001d, 0x24020e01, 0x3c030001, 0x8c636f1c, +0xaf820238, 0x3c010001, 0xac206db0, 0x10600005, +0x24022020, 0x3c010001, 0xac226f20, 0x24020001, +0xaee204b8, 0x3c04bfff, 0x121940, 0x3c020002, +0x431021, 0x8c428ff0, 0x3c050001, 0x8ca56d98, +0x3484ffff, 0x441024, 0x3c010002, 0x230821, +0xac228ff0, 0x24020001, 0x10a20044, 0x0, +0x10000040, 0x0, 0x3c020001, 0x8c426f1c, +0x1040001c, 0x24022000, 0x3c010001, 0xac226f20, +0x3c0300a0, 0x2031024, 0x14430005, 0x121140, +0x3402a000, 0x3c010001, 0x1000002d, 0xac226f20, +0x3c030002, 0x621821, 0x8c638ff8, 0x3c020020, +0x621024, 0x10400004, 0x24022001, 0x3c010001, +0x10000023, 0xac226f20, 0x3c020080, 0x621024, +0x1040001f, 0x3402a001, 0x3c010001, 0x1000001c, +0xac226f20, 0x3c020020, 0x2021024, 0x10400007, +0x121940, 0x24020100, 0x3c010002, 0x230821, +0xac229004, 0x10000006, 0x3c020080, 0x121140, +0x3c010002, 0x220821, 0xac209004, 0x3c020080, +0x2021024, 0x10400006, 0x121940, 0x3c020001, +0x3c010002, 0x230821, 0x10000005, 0xac22900c, +0x121140, 0x3c010002, 0x220821, 0xac20900c, +0x3c030001, 0x8c636d98, 0x24020001, 0x10620003, +0x0, 0xc003daf, 0x0, 0x8fbf0030, +0x8fb3002c, 0x8fb20028, 0x8fb10024, 0x8fb00020, +0x3e00008, 0x27bd0038, 0x27bdffb0, 0xafb3003c, +0x9821, 0xafb50040, 0xa821, 0xafb10034, +0x8821, 0x24020002, 0xafbf0048, 0xafbe0044, +0xafb20038, 0xafb00030, 0xafa4002c, 0xa7a0001a, +0xa7a00018, 0xa7a00020, 0xa7a0001e, 0xa7a00022, +0x10a20130, 0xa7a0001c, 0x2ca20003, 0x10400005, +0x24020001, 0x10a2000a, 0x3c024000, 0x1000025d, +0x2201021, 0x24020004, 0x10a2020a, 0x24020008, +0x10a20208, 0x2201021, 0x10000256, 0x0, +0x8fa8002c, 0x88140, 0x3c030002, 0x701821, +0x8c638ffc, 0x621024, 0x14400009, 0x24040001, +0x3c027fff, 0x3442ffff, 0x628824, 0x3c010002, +0x300821, 0xac318ff4, 0x10000246, 0x2201021, +0x24050001, 0xc00457c, 0x27a60018, 0x24040001, +0x24050001, 0xc00457c, 0x27a60018, 0x97a20018, +0x30420004, 0x104000d9, 0x3c114000, 0x3c020001, +0x8c426f40, 0x2443ffff, 0x2c620006, 0x104000d9, +0x31080, 0x3c010001, 0x220821, 0x8c226c68, +0x400008, 0x0, 0x24040001, 0x24050011, +0x27b0001a, 0xc00457c, 0x2003021, 0x24040001, +0x24050011, 0xc00457c, 0x2003021, 0x97a3001a, +0x30624000, 0x10400002, 0x3c150010, 0x3c150008, +0x30628000, 0x104000aa, 0x3c130001, 0x100000a8, +0x3c130002, 0x24040001, 0x24050014, 0x27b0001a, +0xc00457c, 0x2003021, 0x24040001, 0x24050014, +0xc00457c, 0x2003021, 0x97a3001a, 0x30621000, +0x10400002, 0x3c150010, 0x3c150008, 0x30620800, +0x10400097, 0x3c130001, 0x10000095, 0x3c130002, +0x24040001, 0x24050019, 0x27b0001c, 0xc00457c, +0x2003021, 0x24040001, 0x24050019, 0xc00457c, +0x2003021, 0x97a2001c, 0x30430700, 0x24020400, +0x10620027, 0x28620401, 0x1040000e, 0x24020200, +0x1062001f, 0x28620201, 0x10400005, 0x24020100, +0x5062001e, 0x3c130001, 0x1000001e, 0x24040001, +0x24020300, 0x50620019, 0x3c130002, 0x10000019, +0x24040001, 0x24020600, 0x1062000d, 0x28620601, +0x10400005, 0x24020500, 0x5062000b, 0x3c130002, +0x10000010, 0x24040001, 0x24020700, 0x1462000d, +0x24040001, 0x3c130004, 0x1000000a, 0x3c150008, +0x10000006, 0x3c130004, 0x10000005, 0x3c150008, +0x3c130001, 0x10000002, 0x3c150008, 0x3c150010, +0x24040001, 0x24050018, 0x27b0001e, 0xc00457c, +0x2003021, 0x24040001, 0x24050018, 0xc00457c, +0x2003021, 0x8fa8002c, 0x97a7001e, 0x81140, +0x3c060002, 0xc23021, 0x8cc68ff4, 0x97a20022, +0x3c100001, 0x26106c5c, 0x2002021, 0xafa20010, +0x97a2001c, 0x3c05000c, 0x34a50303, 0xc002b3b, +0xafa20014, 0x3c020004, 0x16620010, 0x3c020001, +0x8f840054, 0x24030001, 0x24020002, 0x3c010001, +0xac236d9c, 0x3c010001, 0xac226d98, 0x3c010001, +0xac236da4, 0x3c010001, 0xac236e24, 0x3c010001, +0xac246f30, 0x1000004f, 0x2b38825, 0x16620039, +0x3c028000, 0x3c020001, 0x8c426e20, 0x1440001e, +0x24040018, 0x2021, 0x2821, 0xc004ddb, +0x34068000, 0x8f830054, 0x8f820054, 0x2b38825, +0x10000002, 0x24630032, 0x8f820054, 0x621023, +0x2c420033, 0x1440fffc, 0x0, 0x8f830054, +0x24020001, 0x3c010001, 0xac226e20, 0x3c010001, +0xac226d9c, 0x3c010001, 0xac226d98, 0x3c010001, +0xac226da4, 0x3c010001, 0xac226e24, 0x3c010001, +0x1000002c, 0xac236f30, 0x2821, 0xc004ddb, +0x24060404, 0x2021, 0x2405001e, 0x27a60018, +0x24020002, 0xc0045be, 0xa7a20018, 0x2021, +0x2821, 0x27a60018, 0xc0045be, 0xa7a00018, +0x24040018, 0x24050002, 0xc004ddb, 0x24060004, +0x3c028000, 0x2221025, 0x2b31825, 0x10000015, +0x438825, 0x2221025, 0x2751825, 0x438825, +0x2002021, 0x97a6001c, 0x3c070001, 0x8ce76d98, +0x3c05000c, 0x34a50326, 0xafb30010, 0xc002b3b, +0xafb10014, 0x10000007, 0x0, 0x3c110002, +0x2308821, 0x8e318ffc, 0x3c027fff, 0x3442ffff, +0x2228824, 0x3c020001, 0x8c426da8, 0x1040001e, +0x0, 0x3c020001, 0x8c426f1c, 0x10400002, +0x3c022000, 0x2228825, 0x8fa8002c, 0x81140, +0x3c010002, 0x220821, 0x8c229000, 0x10400003, +0x3c020020, 0x10000005, 0x2228825, 0x3c02ffdf, +0x3442ffff, 0x2228824, 0x8fa8002c, 0x81140, +0x3c010002, 0x220821, 0x8c229008, 0x10400003, +0x3c020080, 0x10000004, 0x2228825, 0x3c02ff7f, +0x3442ffff, 0x2228824, 0x8fa8002c, 0x81140, +0x3c010002, 0x220821, 0xac318ff4, 0x10000135, +0x2201021, 0x8fa8002c, 0x8f140, 0x3c030002, +0x7e1821, 0x8c638ff8, 0x3c024000, 0x621024, +0x14400009, 0x24040001, 0x3c027fff, 0x3442ffff, +0x628824, 0x3c010002, 0x3e0821, 0xac318ff0, +0x10000124, 0x2201021, 0x2821, 0xc00457c, +0x27a60018, 0x24040001, 0x2821, 0xc00457c, +0x27a60018, 0x24040001, 0x24050001, 0x27b20020, +0xc00457c, 0x2403021, 0x24040001, 0x24050001, +0xc00457c, 0x2403021, 0x24040001, 0x24050004, +0x27b1001e, 0xc00457c, 0x2203021, 0x24040001, +0x24050004, 0xc00457c, 0x2203021, 0x24040001, +0x24050005, 0x27b00022, 0xc00457c, 0x2003021, +0x24040001, 0x24050005, 0xc00457c, 0x2003021, +0x24040001, 0x24050010, 0xc00457c, 0x27a60018, +0x24040001, 0x24050010, 0xc00457c, 0x27a60018, +0x24040001, 0x2405000a, 0xc00457c, 0x2403021, +0x24040001, 0x2405000a, 0xc00457c, 0x2403021, +0x24040001, 0x24050018, 0xc00457c, 0x2203021, +0x24040001, 0x24050018, 0xc00457c, 0x2203021, +0x24040001, 0x24050001, 0xc00457c, 0x27a60018, +0x24040001, 0x24050001, 0xc00457c, 0x27a60018, +0x97a20018, 0x30420004, 0x10400066, 0x3c114000, +0x3c030001, 0x8c636f34, 0x24020005, 0x14620067, +0x24040001, 0x24050019, 0x27b0001c, 0xc00457c, +0x2003021, 0x24040001, 0x24050019, 0xc00457c, +0x2003021, 0x97a2001c, 0x30430700, 0x24020400, +0x10620027, 0x28620401, 0x1040000e, 0x24020200, +0x1062001f, 0x28620201, 0x10400005, 0x24020100, +0x5062001e, 0x3c130001, 0x1000001e, 0x3c020004, +0x24020300, 0x50620019, 0x3c130002, 0x10000019, +0x3c020004, 0x24020600, 0x1062000d, 0x28620601, +0x10400005, 0x24020500, 0x5062000b, 0x3c130002, +0x10000010, 0x3c020004, 0x24020700, 0x1462000d, +0x3c020004, 0x3c130004, 0x1000000a, 0x3c150008, +0x10000006, 0x3c130004, 0x10000005, 0x3c150008, +0x3c130001, 0x10000002, 0x3c150008, 0x3c150010, +0x3c020004, 0x12620017, 0x3c028000, 0x8f820054, +0x24100001, 0x3c010001, 0xac306d9c, 0x3c010001, +0xac306d98, 0x3c010001, 0xac306da4, 0x3c010001, +0xac306e24, 0x3c010001, 0xac226f30, 0x3c020001, +0x16620022, 0x2758825, 0x2021, 0x2821, +0xc004ddb, 0x34068000, 0x3c010001, 0x1000001b, +0xac306e20, 0x2221025, 0x2b31825, 0x438825, +0x97a6001c, 0x3c020001, 0x8c426f1c, 0x3c070001, +0x8ce76d98, 0x3c040001, 0x24846c5c, 0xafa20010, +0x97a2001e, 0x3c05000c, 0x34a50323, 0x3c010001, +0xac206e20, 0xc002b3b, 0xafa20014, 0x10000007, +0x0, 0x3c110002, 0x23e8821, 0x8e318ff0, +0x3c027fff, 0x3442ffff, 0x2228824, 0x3c020001, +0x8c426da8, 0x10400069, 0x0, 0x3c020001, +0x8c426f1c, 0x10400002, 0x3c022000, 0x2228825, +0x8fa8002c, 0x81140, 0x3c010002, 0x220821, +0x8c229004, 0x10400003, 0x3c020020, 0x10000005, +0x2228825, 0x3c02ffdf, 0x3442ffff, 0x2228824, +0x8fa8002c, 0x81140, 0x3c010002, 0x220821, +0x8c22900c, 0x10400003, 0x3c020080, 0x1000004f, +0x2228825, 0x3c02ff7f, 0x3442ffff, 0x1000004b, +0x2228824, 0x8fa8002c, 0x82940, 0x3c030002, +0x651821, 0x8c638ff8, 0x3c024000, 0x621024, +0x14400008, 0x3c027fff, 0x3442ffff, 0x628824, +0x3c010002, 0x250821, 0xac318ff0, 0x10000041, +0x2201021, 0x3c020001, 0x8c426da8, 0x10400034, +0x3c11c00c, 0x3c020001, 0x8c426e44, 0x3c04c00c, +0x34842000, 0x3c030001, 0x8c636f1c, 0x2102b, +0x21023, 0x441024, 0x10600003, 0x518825, +0x3c022000, 0x2228825, 0x3c020002, 0x451021, +0x8c429004, 0x10400003, 0x3c020020, 0x10000004, +0x2228825, 0x3c02ffdf, 0x3442ffff, 0x2228824, +0x8fa8002c, 0x81140, 0x3c010002, 0x220821, +0x8c22900c, 0x10400003, 0x3c020080, 0x10000004, +0x2228825, 0x3c02ff7f, 0x3442ffff, 0x2228824, +0x3c020001, 0x8c426e30, 0x10400002, 0x3c020800, +0x2228825, 0x3c020001, 0x8c426e34, 0x10400002, +0x3c020400, 0x2228825, 0x3c020001, 0x8c426e38, +0x10400006, 0x3c020100, 0x10000004, 0x2228825, +0x3c027fff, 0x3442ffff, 0x628824, 0x8fa8002c, +0x81140, 0x3c010002, 0x220821, 0xac318ff0, +0x2201021, 0x8fbf0048, 0x8fbe0044, 0x8fb50040, +0x8fb3003c, 0x8fb20038, 0x8fb10034, 0x8fb00030, +0x3e00008, 0x27bd0050, 0x27bdffd0, 0xafb20028, +0x809021, 0xafbf002c, 0xafb10024, 0xafb00020, +0x8f840200, 0x3c100001, 0x8e106d98, 0x8f860220, +0x24020002, 0x1202005c, 0x2e020003, 0x10400005, +0x24020001, 0x1202000a, 0x121940, 0x1000010c, +0x0, 0x24020004, 0x120200bf, 0x24020008, +0x120200be, 0x128940, 0x10000105, 0x0, +0x3c050002, 0xa32821, 0x8ca58ffc, 0x3c100002, +0x2038021, 0x8e108ff4, 0x3c024000, 0xa21024, +0x10400038, 0x3c020008, 0x2021024, 0x10400020, +0x34840002, 0x3c020002, 0x431021, 0x8c429000, +0x10400005, 0x34840020, 0x34840100, 0x3c020020, +0x10000006, 0x2028025, 0x2402feff, 0x822024, +0x3c02ffdf, 0x3442ffff, 0x2028024, 0x121140, +0x3c010002, 0x220821, 0x8c229008, 0x10400005, +0x3c020001, 0xc23025, 0x3c020080, 0x10000016, +0x2028025, 0x3c02fffe, 0x3442ffff, 0xc23024, +0x3c02ff7f, 0x3442ffff, 0x1000000f, 0x2028024, +0x2402fedf, 0x822024, 0x3c02fffe, 0x3442ffff, +0xc23024, 0x3c02ff5f, 0x3442ffff, 0x2028024, +0x3c010002, 0x230821, 0xac209000, 0x3c010002, +0x230821, 0xac209008, 0xaf840200, 0xaf860220, +0x8f820220, 0x34420002, 0xaf820220, 0x1000000a, +0x121140, 0x3c02bfff, 0x3442ffff, 0x8f830200, +0x2028024, 0x2402fffd, 0x621824, 0xc003daf, +0xaf830200, 0x121140, 0x3c010002, 0x220821, +0x100000b7, 0xac308ff4, 0x3c020001, 0x8c426f1c, +0x10400069, 0x24050004, 0x24040001, 0xc00457c, +0x27a60018, 0x24040001, 0x24050005, 0xc00457c, +0x27a6001a, 0x97a30018, 0x97a2001a, 0x3c040001, +0x24846e48, 0x30630c00, 0x31a82, 0x30420c00, +0x21282, 0xa7a2001a, 0x21080, 0x441021, +0x431021, 0xa7a30018, 0x90480000, 0x24020001, +0x3103ffff, 0x10620029, 0x28620002, 0x10400005, +0x0, 0x10600009, 0x0, 0x1000003d, +0x0, 0x10700013, 0x24020003, 0x1062002c, +0x0, 0x10000037, 0x0, 0x8f820200, +0x2403feff, 0x431024, 0xaf820200, 0x8f820220, +0x3c03fffe, 0x3463ffff, 0x431024, 0xaf820220, +0x3c010002, 0xac209004, 0x3c010002, 0x10000032, +0xac20900c, 0x8f820200, 0x34420100, 0xaf820200, +0x8f820220, 0x3c03fffe, 0x3463ffff, 0x431024, +0xaf820220, 0x24020100, 0x3c010002, 0xac229004, +0x3c010002, 0x10000024, 0xac20900c, 0x8f820200, +0x2403feff, 0x431024, 0xaf820200, 0x8f820220, +0x3c030001, 0x431025, 0xaf820220, 0x3c010002, +0xac209004, 0x3c010002, 0x10000017, 0xac23900c, +0x8f820200, 0x34420100, 0xaf820200, 0x8f820220, +0x3c030001, 0x431025, 0xaf820220, 0x24020100, +0x3c010002, 0xac229004, 0x3c010002, 0x1000000a, +0xac23900c, 0x3c040001, 0x24846c80, 0x97a6001a, +0x97a70018, 0x3c050001, 0x34a5ffff, 0xafa80010, +0xc002b3b, 0xafa00014, 0x8f820200, 0x34420002, +0x1000004b, 0xaf820200, 0x128940, 0x3c050002, +0xb12821, 0x8ca58ff8, 0x3c100002, 0x2118021, +0x8e108ff0, 0x3c024000, 0xa21024, 0x14400010, +0x0, 0x3c020001, 0x8c426f1c, 0x14400005, +0x3c02bfff, 0x8f820200, 0x34420002, 0xaf820200, +0x3c02bfff, 0x3442ffff, 0xc003daf, 0x2028024, +0x3c010002, 0x310821, 0x10000031, 0xac308ff0, +0x3c020001, 0x8c426f1c, 0x10400005, 0x3c020020, +0x3c020001, 0x8c426e44, 0x10400025, 0x3c020020, +0xa21024, 0x10400007, 0x34840020, 0x24020100, +0x3c010002, 0x310821, 0xac229004, 0x10000006, +0x34840100, 0x3c010002, 0x310821, 0xac209004, +0x2402feff, 0x822024, 0x3c020080, 0xa21024, +0x10400007, 0x121940, 0x3c020001, 0x3c010002, +0x230821, 0xac22900c, 0x10000008, 0xc23025, +0x121140, 0x3c010002, 0x220821, 0xac20900c, +0x3c02fffe, 0x3442ffff, 0xc23024, 0xaf840200, +0xaf860220, 0x8f820220, 0x34420002, 0xaf820220, +0x121140, 0x3c010002, 0x220821, 0xac308ff0, +0x8fbf002c, 0x8fb20028, 0x8fb10024, 0x8fb00020, +0x3e00008, 0x27bd0030, 0x0, 0x1821, +0x308400ff, 0x2405ffdf, 0x2406ffbf, 0x641007, +0x30420001, 0x10400004, 0x0, 0x8f820044, +0x10000003, 0x34420040, 0x8f820044, 0x461024, +0xaf820044, 0x8f820044, 0x34420020, 0xaf820044, +0x8f820044, 0x451024, 0xaf820044, 0x24630001, +0x28620008, 0x5440ffee, 0x641007, 0x3e00008, +0x0, 0x2c820008, 0x1040001b, 0x0, +0x2405ffdf, 0x2406ffbf, 0x41880, 0x3c020001, +0x24426e60, 0x621821, 0x24640004, 0x90620000, +0x10400004, 0x0, 0x8f820044, 0x10000003, +0x34420040, 0x8f820044, 0x461024, 0xaf820044, +0x8f820044, 0x34420020, 0xaf820044, 0x8f820044, +0x451024, 0xaf820044, 0x24630001, 0x64102b, +0x1440ffee, 0x0, 0x3e00008, 0x0, +0x0, 0x0, 0x0, 0x8f8400c4, +0x8f8600e0, 0x8f8700e4, 0x2402fff8, 0xc22824, +0x10e5001a, 0x27623ff8, 0x14e20002, 0x24e80008, +0x27683000, 0x55050004, 0x8d0a0000, 0x30c20004, +0x14400012, 0x805021, 0x8ce90000, 0x8f42013c, +0x1494823, 0x49182b, 0x94eb0006, 0x10600002, +0x25630050, 0x494821, 0x123182b, 0x50400003, +0x8f4201fc, 0x3e00008, 0xe01021, 0xaf8800e8, +0x24420001, 0xaf4201fc, 0xaf8800e4, 0x3e00008, +0x1021, 0x3e00008, 0x0, 0x8f8300e4, +0x27623ff8, 0x10620004, 0x24620008, 0xaf8200e8, +0x3e00008, 0xaf8200e4, 0x27623000, 0xaf8200e8, +0x3e00008, 0xaf8200e4, 0x3e00008, 0x0, +0x0, 0x0, 0x0, 0x8f880120, +0x27624fe0, 0x8f830128, 0x15020002, 0x25090020, +0x27694800, 0x11230012, 0x8fa20010, 0xad040000, +0xad050004, 0xad060008, 0xa507000e, 0x8fa30014, +0xad020018, 0x8fa20018, 0xad03001c, 0x25030016, +0xad020010, 0xad030014, 0xaf890120, 0x8f4300fc, +0x24020001, 0x2463ffff, 0x3e00008, 0xaf4300fc, +0x8f430324, 0x1021, 0x24630001, 0x3e00008, +0xaf430324, 0x3e00008, 0x0, 0x8f880100, +0x276247e0, 0x8f830108, 0x15020002, 0x25090020, +0x27694000, 0x1123000f, 0x8fa20010, 0xad040000, +0xad050004, 0xad060008, 0xa507000e, 0x8fa30014, +0xad020018, 0x8fa20018, 0xad03001c, 0x25030016, +0xad020010, 0xad030014, 0xaf890100, 0x3e00008, +0x24020001, 0x8f430328, 0x1021, 0x24630001, +0x3e00008, 0xaf430328, 0x3e00008, 0x0, +0x0, 0x0, 0x0, 0x0 }; +static int tigon2FwRodata[/*(MAX_RODATA_LEN/4) + 1*/] = { +0x24486561, 0x6465723a, 0x202f7072, +0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, +0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, +0x6e2f6677, 0x6d61696e, 0x2e632c76, 0x20312e31, +0x2e322e34, 0x35203139, 0x39392f30, 0x312f3234, +0x2030303a, 0x31303a35, 0x35207368, 0x75616e67, +0x20457870, 0x20240000, 0x65767452, 0x6e674600, +0x51657674, 0x46000000, 0x51657674, 0x505f4600, +0x4d657674, 0x526e6746, 0x0, 0x4d516576, +0x74460000, 0x4d516576, 0x505f4600, 0x5173436f, +0x6e495f46, 0x0, 0x5173436f, 0x6e734600, +0x51725072, 0x6f644600, 0x6261644d, 0x656d537a, +0x0, 0x68775665, 0x72000000, 0x62616448, +0x77566572, 0x0, 0x2a2a4441, 0x574e5f41, +0x0, 0x74785278, 0x4266537a, 0x0, +0x62664174, 0x6e4d726b, 0x0, 0x7265645a, +0x6f6e6531, 0x0, 0x70636943, 0x6f6e6600, +0x67656e43, 0x6f6e6600, 0x2a646d61, 0x5244666c, +0x0, 0x2a50414e, 0x49432a00, 0x2e2e2f2e, +0x2e2f2e2e, 0x2f2e2e2f, 0x2e2e2f73, 0x72632f6e, +0x69632f66, 0x77322f63, 0x6f6d6d6f, 0x6e2f6677, +0x6d61696e, 0x2e630000, 0x72636246, 0x6c616773, +0x0, 0x62616452, 0x78526362, 0x0, +0x676c6f62, 0x466c6773, 0x0, 0x2b5f6469, +0x73705f6c, 0x6f6f7000, 0x2b65765f, 0x68616e64, +0x6c657200, 0x63616e74, 0x31446d61, 0x0, +0x2b715f64, 0x6d615f74, 0x6f5f6e69, 0x635f636b, +0x73756d00, 0x2b685f73, 0x656e645f, 0x64617461, +0x5f726561, 0x64795f63, 0x6b73756d, 0x0, +0x2b685f64, 0x6d615f72, 0x645f6173, 0x73697374, +0x5f636b73, 0x756d0000, 0x74436b73, 0x6d4f6e00, +0x2b715f64, 0x6d615f74, 0x6f5f6e69, 0x63000000, +0x2b685f73, 0x656e645f, 0x64617461, 0x5f726561, +0x64790000, 0x2b685f64, 0x6d615f72, 0x645f6173, +0x73697374, 0x0, 0x74436b73, 0x6d4f6666, +0x0, 0x2b685f73, 0x656e645f, 0x62645f72, +0x65616479, 0x0, 0x68737453, 0x52696e67, +0x0, 0x62616453, 0x52696e67, 0x0, +0x6e696353, 0x52696e67, 0x0, 0x77446d61, +0x416c6c41, 0x0, 0x2b715f64, 0x6d615f74, +0x6f5f686f, 0x73745f63, 0x6b73756d, 0x0, +0x2b685f6d, 0x61635f72, 0x785f636f, 0x6d705f63, +0x6b73756d, 0x0, 0x2b685f64, 0x6d615f77, +0x725f6173, 0x73697374, 0x5f636b73, 0x756d0000, +0x72436b73, 0x6d4f6e00, 0x2b715f64, 0x6d615f74, +0x6f5f686f, 0x73740000, 0x2b685f6d, 0x61635f72, +0x785f636f, 0x6d700000, 0x2b685f64, 0x6d615f77, +0x725f6173, 0x73697374, 0x0, 0x72436b73, +0x6d4f6666, 0x0, 0x2b685f72, 0x6563765f, +0x62645f72, 0x65616479, 0x0, 0x2b685f72, +0x6563765f, 0x6a756d62, 0x6f5f6264, 0x5f726561, +0x64790000, 0x2b685f72, 0x6563765f, 0x6d696e69, +0x5f62645f, 0x72656164, 0x79000000, 0x2b6d685f, +0x636f6d6d, 0x616e6400, 0x2b685f74, 0x696d6572, +0x0, 0x2b685f64, 0x6f5f7570, 0x64617465, +0x5f74785f, 0x636f6e73, 0x0, 0x2b685f64, +0x6f5f7570, 0x64617465, 0x5f72785f, 0x70726f64, +0x0, 0x2b636b73, 0x756d3136, 0x0, +0x2b706565, 0x6b5f6d61, 0x635f7278, 0x5f776100, +0x2b706565, 0x6b5f6d61, 0x635f7278, 0x0, +0x2b646571, 0x5f6d6163, 0x5f727800, 0x2b685f6d, +0x61635f72, 0x785f6174, 0x746e0000, 0x62616452, +0x6574537a, 0x0, 0x72784264, 0x4266537a, +0x0, 0x2b6e756c, 0x6c5f6861, 0x6e646c65, +0x72000000, 0x66774f70, 0x4661696c, 0x0, +0x2b685f75, 0x70646174, 0x655f6c65, 0x64340000, +0x2b685f75, 0x70646174, 0x655f6c65, 0x64360000, +0x2b685f75, 0x70646174, 0x655f6c65, 0x64320000, +0x696e7453, 0x74617465, 0x0, 0x2a2a696e, +0x69744370, 0x0, 0x23736372, 0x65616d00, +0x69537461, 0x636b4572, 0x0, 0x70726f62, +0x654d656d, 0x0, 0x2a2a4441, 0x574e5f42, +0x0, 0x2b73775f, 0x646d615f, 0x61737369, +0x73745f70, 0x6c75735f, 0x74696d65, 0x72000000, +0x2b267072, 0x656c6f61, 0x645f7772, 0x5f646573, +0x63720000, 0x2b267072, 0x656c6f61, 0x645f7264, +0x5f646573, 0x63720000, 0x2b685f68, 0x665f7469, +0x6d657200, 0x24486561, 0x6465723a, 0x202f7072, +0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, +0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, +0x6e2f7469, 0x6d65722e, 0x632c7620, 0x312e312e, +0x322e3335, 0x20313939, 0x392f3031, 0x2f323720, +0x31393a30, 0x393a3530, 0x20686179, 0x65732045, +0x78702024, 0x0, 0x65767452, 0x6e674600, +0x51657674, 0x46000000, 0x51657674, 0x505f4600, +0x4d657674, 0x526e6746, 0x0, 0x4d516576, +0x74460000, 0x4d516576, 0x505f4600, 0x5173436f, +0x6e495f46, 0x0, 0x5173436f, 0x6e734600, +0x51725072, 0x6f644600, 0x542d446d, 0x61526432, +0x0, 0x542d446d, 0x61526431, 0x0, +0x542d446d, 0x61526442, 0x0, 0x542d446d, +0x61577232, 0x0, 0x542d446d, 0x61577231, +0x0, 0x542d446d, 0x61577242, 0x0, +0x0, 0x24486561, 0x6465723a, 0x202f7072, +0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, +0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, +0x6e2f636f, 0x6d6d616e, 0x642e632c, 0x7620312e, +0x312e322e, 0x32382031, 0x3939392f, 0x30312f32, +0x30203139, 0x3a34393a, 0x34392073, 0x6875616e, +0x67204578, 0x70202400, 0x65767452, 0x6e674600, +0x51657674, 0x46000000, 0x51657674, 0x505f4600, +0x4d657674, 0x526e6746, 0x0, 0x4d516576, +0x74460000, 0x4d516576, 0x505f4600, 0x5173436f, +0x6e495f46, 0x0, 0x5173436f, 0x6e734600, +0x51725072, 0x6f644600, 0x3f48636d, 0x644d6278, +0x0, 0x3f636d64, 0x48737453, 0x0, +0x3f636d64, 0x4d634d64, 0x0, 0x3f636d64, +0x50726f6d, 0x0, 0x3f636d64, 0x4c696e6b, +0x0, 0x3f636d64, 0x45727200, 0x86ac, +0x8e5c, 0x8e5c, 0x8de4, 0x8b78, +0x8e30, 0x8e5c, 0x8790, 0x8800, +0x8990, 0x8a68, 0x8a34, 0x8e5c, +0x8870, 0x8b24, 0x8e5c, 0x8b34, +0x87b4, 0x8824, 0x0, 0x0, +0x0, 0x24486561, 0x6465723a, 0x202f7072, +0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, +0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, +0x6e2f6d63, 0x6173742e, 0x632c7620, 0x312e312e, +0x322e3820, 0x31393938, 0x2f31322f, 0x30382030, +0x323a3336, 0x3a333620, 0x73687561, 0x6e672045, +0x78702024, 0x0, 0x65767452, 0x6e674600, +0x51657674, 0x46000000, 0x51657674, 0x505f4600, +0x4d657674, 0x526e6746, 0x0, 0x4d516576, +0x74460000, 0x4d516576, 0x505f4600, 0x5173436f, +0x6e495f46, 0x0, 0x5173436f, 0x6e734600, +0x51725072, 0x6f644600, 0x6164644d, 0x63447570, +0x0, 0x6164644d, 0x6346756c, 0x0, +0x64656c4d, 0x634e6f45, 0x0, 0x0, +0x0, 0x24486561, 0x6465723a, 0x202f7072, +0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, +0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, +0x6e2f646d, 0x612e632c, 0x7620312e, 0x312e322e, +0x32342031, 0x3939382f, 0x31322f32, 0x31203030, +0x3a33333a, 0x30392073, 0x6875616e, 0x67204578, +0x70202400, 0x65767452, 0x6e674600, 0x51657674, +0x46000000, 0x51657674, 0x505f4600, 0x4d657674, +0x526e6746, 0x0, 0x4d516576, 0x74460000, +0x4d516576, 0x505f4600, 0x5173436f, 0x6e495f46, +0x0, 0x5173436f, 0x6e734600, 0x51725072, +0x6f644600, 0x7377446d, 0x614f6666, 0x0, +0x31446d61, 0x4f6e0000, 0x7377446d, 0x614f6e00, +0x2372446d, 0x6141544e, 0x0, 0x72446d61, +0x41544e30, 0x0, 0x72446d61, 0x41544e31, +0x0, 0x72446d61, 0x34476200, 0x2a50414e, +0x49432a00, 0x2e2e2f2e, 0x2e2f2e2e, 0x2f2e2e2f, +0x2e2e2f73, 0x72632f6e, 0x69632f66, 0x77322f63, +0x6f6d6d6f, 0x6e2f646d, 0x612e6300, 0x2377446d, +0x6141544e, 0x0, 0x77446d61, 0x41544e30, +0x0, 0x77446d61, 0x41544e31, 0x0, +0x77446d61, 0x34476200, 0x0, 0x0, +0x0, 0x24486561, 0x6465723a, 0x202f7072, +0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, +0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, +0x6e2f7472, 0x6163652e, 0x632c7620, 0x312e312e, +0x322e3520, 0x31393938, 0x2f30392f, 0x33302031, +0x383a3530, 0x3a323820, 0x73687561, 0x6e672045, +0x78702024, 0x0, 0x0, 0x0, +0x0, 0x24486561, 0x6465723a, 0x202f7072, +0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, +0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, +0x6e2f6461, 0x74612e63, 0x2c762031, 0x2e312e32, +0x2e313220, 0x31393939, 0x2f30312f, 0x32302031, +0x393a3439, 0x3a353120, 0x73687561, 0x6e672045, +0x78702024, 0x0, 0x46575f56, 0x45525349, +0x4f4e3a20, 0x23312046, 0x72692041, 0x70722037, +0x2031373a, 0x35373a35, 0x32205044, 0x54203230, +0x30300000, 0x46575f43, 0x4f4d5049, 0x4c455f54, +0x494d453a, 0x2031373a, 0x35373a35, 0x32000000, +0x46575f43, 0x4f4d5049, 0x4c455f42, 0x593a2064, +0x65767263, 0x73000000, 0x46575f43, 0x4f4d5049, +0x4c455f48, 0x4f53543a, 0x20636f6d, 0x70757465, +0x0, 0x46575f43, 0x4f4d5049, 0x4c455f44, +0x4f4d4149, 0x4e3a2065, 0x6e672e61, 0x6374656f, +0x6e2e636f, 0x6d000000, 0x46575f43, 0x4f4d5049, +0x4c45523a, 0x20676363, 0x20766572, 0x73696f6e, +0x20322e37, 0x2e320000, 0x0, 0x12041100, +0x0, 0x24486561, 0x6465723a, 0x202f7072, +0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, +0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, +0x6e2f6d65, 0x6d2e632c, 0x7620312e, 0x312e322e, +0x35203139, 0x39382f30, 0x392f3330, 0x2031383a, +0x35303a30, 0x38207368, 0x75616e67, 0x20457870, +0x20240000, 0x24486561, 0x6465723a, 0x202f7072, +0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, +0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, +0x6e2f7365, 0x6e642e63, 0x2c762031, 0x2e312e32, +0x2e343420, 0x31393938, 0x2f31322f, 0x32312030, +0x303a3333, 0x3a313820, 0x73687561, 0x6e672045, +0x78702024, 0x0, 0x65767452, 0x6e674600, +0x51657674, 0x46000000, 0x51657674, 0x505f4600, +0x4d657674, 0x526e6746, 0x0, 0x4d516576, +0x74460000, 0x4d516576, 0x505f4600, 0x5173436f, +0x6e495f46, 0x0, 0x5173436f, 0x6e734600, +0x51725072, 0x6f644600, 0x69736e74, 0x54637055, +0x0, 0x24486561, 0x6465723a, 0x202f7072, +0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, +0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, +0x6e2f7265, 0x63762e63, 0x2c762031, 0x2e312e32, +0x2e353320, 0x31393939, 0x2f30312f, 0x31362030, +0x323a3535, 0x3a343320, 0x73687561, 0x6e672045, +0x78702024, 0x0, 0x65767452, 0x6e674600, +0x51657674, 0x46000000, 0x51657674, 0x505f4600, +0x4d657674, 0x526e6746, 0x0, 0x4d516576, +0x74460000, 0x4d516576, 0x505f4600, 0x5173436f, +0x6e495f46, 0x0, 0x5173436f, 0x6e734600, +0x51725072, 0x6f644600, 0x724d6163, 0x43686b30, +0x0, 0x72784672, 0x6d324c67, 0x0, +0x72784e6f, 0x53744264, 0x0, 0x72784e6f, +0x4d694264, 0x0, 0x72784e6f, 0x4a6d4264, +0x0, 0x7278436b, 0x446d6146, 0x0, +0x72785144, 0x6d457846, 0x0, 0x72785144, +0x6d614600, 0x72785144, 0x4c426446, 0x0, +0x72785144, 0x6d426446, 0x0, 0x72784372, +0x63506164, 0x0, 0x72536d51, 0x446d6146, +0x0, 0x24486561, 0x6465723a, 0x202f7072, +0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, +0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, +0x6e2f6d61, 0x632e632c, 0x7620312e, 0x312e322e, +0x32322031, 0x3939382f, 0x31322f30, 0x38203032, +0x3a33363a, 0x33302073, 0x6875616e, 0x67204578, +0x70202400, 0x65767452, 0x6e674600, 0x51657674, +0x46000000, 0x51657674, 0x505f4600, 0x4d657674, +0x526e6746, 0x0, 0x4d516576, 0x74460000, +0x4d516576, 0x505f4600, 0x5173436f, 0x6e495f46, +0x0, 0x5173436f, 0x6e734600, 0x51725072, +0x6f644600, 0x6d616354, 0x68726573, 0x0, +0x23744d61, 0x6341544e, 0x0, 0x23724d61, +0x6341544e, 0x0, 0x72656d41, 0x73737274, +0x0, 0x6c696e6b, 0x444f574e, 0x0, +0x6c696e6b, 0x55500000, 0x0, 0x0, +0x0, 0x24486561, 0x6465723a, 0x202f7072, +0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, +0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, +0x6e2f636b, 0x73756d2e, 0x632c7620, 0x312e312e, +0x322e3920, 0x31393939, 0x2f30312f, 0x31342030, +0x303a3033, 0x3a343820, 0x73687561, 0x6e672045, +0x78702024, 0x0, 0x65767452, 0x6e674600, +0x51657674, 0x46000000, 0x51657674, 0x505f4600, +0x4d657674, 0x526e6746, 0x0, 0x4d516576, +0x74460000, 0x4d516576, 0x505f4600, 0x5173436f, +0x6e495f46, 0x0, 0x5173436f, 0x6e734600, +0x51725072, 0x6f644600, 0x0, 0x0, +0x0, 0x50726f62, 0x65506879, 0x0, +0x6c6e6b41, 0x53535254, 0x0, 0x109a4, +0x10a1c, 0x10a50, 0x10a7c, 0x11050, +0x10aa8, 0x10b10, 0x111fc, 0x10dc0, +0x10c68, 0x10c80, 0x10cc4, 0x10cec, +0x10d0c, 0x10d34, 0x111fc, 0x10dc0, +0x10df8, 0x10e10, 0x10e40, 0x10e68, +0x10e88, 0x10eb0, 0x0, 0x10fdc, +0x11008, 0x1102c, 0x111fc, 0x11050, +0x11078, 0x11108, 0x0, 0x0, +0x0, 0x1186c, 0x1193c, 0x11a14, +0x11ae4, 0x11b40, 0x11c1c, 0x11c44, +0x11d20, 0x11d48, 0x11ef0, 0x11f18, +0x120c0, 0x122b8, 0x1254c, 0x12460, +0x1254c, 0x12578, 0x120e8, 0x12290, +0x7273745f, 0x676d6969, 0x0, 0x12608, +0x12640, 0x12728, 0x13374, 0x133b4, +0x133cc, 0x7365746c, 0x6f6f7000, 0x0, +0x0, 0x13bbc, 0x13bfc, 0x13c8c, +0x13cd0, 0x13d34, 0x13dc0, 0x13df4, +0x13e7c, 0x13f14, 0x13fe4, 0x14024, +0x140a8, 0x140cc, 0x141dc, 0x646f4261, +0x73655067, 0x0, 0x0, 0x0, +0x0, 0x73746d61, 0x634c4e4b, 0x0, +0x6765746d, 0x636c6e6b, 0x0, 0x14ed8, +0x14ed8, 0x14b8c, 0x14bd8, 0x14c24, +0x14ed8, 0x7365746d, 0x61636163, 0x74000000, +0x0, 0x0 }; +static int tigon2FwData[/*(MAX_DATA_LEN/4) + 1*/] = { +0x1, +0x1, 0x1, 0xc001fc, 0x3ffc, +0xc00000, 0x416c7465, 0x6f6e2041, 0x63654e49, +0x43205600, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x416c7465, +0x6f6e2041, 0x63654e49, 0x43205600, 0x42424242, +0x0, 0x0, 0x0, 0x1ffffc, +0x1fff7c, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x60cf00, +0x60, 0xcf000000, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x3, 0x0, +0x1, 0x0, 0x0, 0x0, +0x1, 0x0, 0x1, 0x0, +0x0, 0x0, 0x0, 0x1, +0x1, 0x0, 0x0, 0x0, +0x0, 0x0, 0x1000000, 0x21000000, +0x12000140, 0x0, 0x0, 0x20000000, +0x120000a0, 0x0, 0x12000060, 0x12000180, +0x120001e0, 0x0, 0x0, 0x0, +0x1, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x2, +0x0, 0x0, 0x30001, 0x1, +0x30201, 0x0, 0x0, 0x1010101, +0x1010100, 0x10100, 0x1010001, 0x10001, +0x1000101, 0x101, 0x0, 0x0 }; diff --git a/os/pc/etherif.h b/os/pc/etherif.h new file mode 100644 index 00000000..d80e06c9 --- /dev/null +++ b/os/pc/etherif.h @@ -0,0 +1,39 @@ +enum { + MaxEther = 24, + Ntypes = 8, +}; + +typedef struct Ether Ether; +struct Ether { + ISAConf; /* hardware info */ + + int ctlrno; + int tbdf; /* type+busno+devno+funcno */ + int minmtu; + int maxmtu; + uchar ea[Eaddrlen]; + + void (*attach)(Ether*); /* filled in by reset routine */ + void (*detach)(Ether*); + void (*transmit)(Ether*); + void (*interrupt)(Ureg*, void*); + long (*ifstat)(Ether*, void*, long, ulong); + long (*ctl)(Ether*, void*, long); /* custom ctl messages */ + void (*power)(Ether*, int); /* power on/off */ + void (*shutdown)(Ether*); /* shutdown hardware before reboot */ + void *ctlr; + + Queue* oq; + + Netif; +}; + +extern Block* etheriq(Ether*, Block*, int); +extern void addethercard(char*, int(*)(Ether*)); +extern ulong ethercrc(uchar*, int); +extern int parseether(uchar*, char*); + +#define NEXT(x, l) (((x)+1)%(l)) +#define PREV(x, l) (((x) == 0) ? (l)-1: (x)-1) +#define HOWMANY(x, y) (((x)+((y)-1))/(y)) +#define ROUNDUP(x, y) (HOWMANY((x), (y))*(y)) diff --git a/os/pc/etherigbe.c b/os/pc/etherigbe.c new file mode 100644 index 00000000..a8c54267 --- /dev/null +++ b/os/pc/etherigbe.c @@ -0,0 +1,1968 @@ +/* + * Intel 8254[340]NN Gigabit Ethernet Controller + * as found on the Intel PRO/1000 series of adapters: + * 82543GC Intel PRO/1000 T + * 82544EI Intel PRO/1000 XT + * 82540EM Intel PRO/1000 MT + * 82541[GP]I + * 82547GI + * 82546GB + * 82546EB + * To Do: + * finish autonegotiation code; + * integrate fiber stuff back in (this ONLY handles + * the CAT5 cards at the moment); + * add checksum-offload; + * add tuning control via ctl file; + * this driver is little-endian specific. + */ +#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/netif.h" + +#include "etherif.h" +#include "ethermii.h" + +enum { + i82542 = (0x1000<<16)|0x8086, + i82543gc = (0x1004<<16)|0x8086, + i82544ei = (0x1008<<16)|0x8086, + i82547ei = (0x1019<<16)|0x8086, + i82540em = (0x100E<<16)|0x8086, + i82540eplp = (0x101E<<16)|0x8086, + i82545gmc = (0x1026<<16)|0x8086, + i82547gi = (0x1075<<16)|0x8086, + i82541gi = (0x1076<<16)|0x8086, + i82546gb = (0x1079<<16)|0x8086, + i82541pi = (0x107c<<16)|0x8086, + i82546eb = (0x1010<<16)|0x8086, +}; + +enum { + Ctrl = 0x00000000, /* Device Control */ + Ctrldup = 0x00000004, /* Device Control Duplicate */ + Status = 0x00000008, /* Device Status */ + Eecd = 0x00000010, /* EEPROM/Flash Control/Data */ + Ctrlext = 0x00000018, /* Extended Device Control */ + Mdic = 0x00000020, /* MDI Control */ + Fcal = 0x00000028, /* Flow Control Address Low */ + Fcah = 0x0000002C, /* Flow Control Address High */ + Fct = 0x00000030, /* Flow Control Type */ + Icr = 0x000000C0, /* Interrupt Cause Read */ + Ics = 0x000000C8, /* Interrupt Cause Set */ + Ims = 0x000000D0, /* Interrupt Mask Set/Read */ + Imc = 0x000000D8, /* Interrupt mask Clear */ + Rctl = 0x00000100, /* Receive Control */ + Fcttv = 0x00000170, /* Flow Control Transmit Timer Value */ + Txcw = 0x00000178, /* Transmit Configuration Word */ + Rxcw = 0x00000180, /* Receive Configuration Word */ + Tctl = 0x00000400, /* Transmit Control */ + Tipg = 0x00000410, /* Transmit IPG */ + Tbt = 0x00000448, /* Transmit Burst Timer */ + Ait = 0x00000458, /* Adaptive IFS Throttle */ + Fcrtl = 0x00002160, /* Flow Control RX Threshold Low */ + Fcrth = 0x00002168, /* Flow Control Rx Threshold High */ + Rdfh = 0x00002410, /* Receive data fifo head */ + Rdft = 0x00002418, /* Receive data fifo tail */ + Rdfhs = 0x00002420, /* Receive data fifo head saved */ + Rdfts = 0x00002428, /* Receive data fifo tail saved */ + Rdfpc = 0x00002430, /* Receive data fifo packet count */ + Rdbal = 0x00002800, /* Rd Base Address Low */ + Rdbah = 0x00002804, /* Rd Base Address High */ + Rdlen = 0x00002808, /* Receive Descriptor Length */ + Rdh = 0x00002810, /* Receive Descriptor Head */ + Rdt = 0x00002818, /* Receive Descriptor Tail */ + Rdtr = 0x00002820, /* Receive Descriptor Timer Ring */ + Rxdctl = 0x00002828, /* Receive Descriptor Control */ + Radv = 0x0000282C, /* Receive Interrupt Absolute Delay Timer */ + Txdmac = 0x00003000, /* Transfer DMA Control */ + Ett = 0x00003008, /* Early Transmit Control */ + Tdfh = 0x00003410, /* Transmit data fifo head */ + Tdft = 0x00003418, /* Transmit data fifo tail */ + Tdfhs = 0x00003420, /* Transmit data Fifo Head saved */ + Tdfts = 0x00003428, /* Transmit data fifo tail saved */ + Tdfpc = 0x00003430, /* Trasnmit data Fifo packet count */ + Tdbal = 0x00003800, /* Td Base Address Low */ + Tdbah = 0x00003804, /* Td Base Address High */ + Tdlen = 0x00003808, /* Transmit Descriptor Length */ + Tdh = 0x00003810, /* Transmit Descriptor Head */ + Tdt = 0x00003818, /* Transmit Descriptor Tail */ + Tidv = 0x00003820, /* Transmit Interrupt Delay Value */ + Txdctl = 0x00003828, /* Transmit Descriptor Control */ + Tadv = 0x0000382C, /* Transmit Interrupt Absolute Delay Timer */ + + Statistics = 0x00004000, /* Start of Statistics Area */ + Gorcl = 0x88/4, /* Good Octets Received Count */ + Gotcl = 0x90/4, /* Good Octets Transmitted Count */ + Torl = 0xC0/4, /* Total Octets Received */ + Totl = 0xC8/4, /* Total Octets Transmitted */ + Nstatistics = 64, + + Rxcsum = 0x00005000, /* Receive Checksum Control */ + Mta = 0x00005200, /* Multicast Table Array */ + Ral = 0x00005400, /* Receive Address Low */ + Rah = 0x00005404, /* Receive Address High */ + Manc = 0x00005820, /* Management Control */ +}; + +enum { /* Ctrl */ + Bem = 0x00000002, /* Big Endian Mode */ + Prior = 0x00000004, /* Priority on the PCI bus */ + Lrst = 0x00000008, /* Link Reset */ + Asde = 0x00000020, /* Auto-Speed Detection Enable */ + Slu = 0x00000040, /* Set Link Up */ + Ilos = 0x00000080, /* Invert Loss of Signal (LOS) */ + SspeedMASK = 0x00000300, /* Speed Selection */ + SspeedSHIFT = 8, + Sspeed10 = 0x00000000, /* 10Mb/s */ + Sspeed100 = 0x00000100, /* 100Mb/s */ + Sspeed1000 = 0x00000200, /* 1000Mb/s */ + Frcspd = 0x00000800, /* Force Speed */ + Frcdplx = 0x00001000, /* Force Duplex */ + SwdpinsloMASK = 0x003C0000, /* Software Defined Pins - lo nibble */ + SwdpinsloSHIFT = 18, + SwdpioloMASK = 0x03C00000, /* Software Defined Pins - I or O */ + SwdpioloSHIFT = 22, + Devrst = 0x04000000, /* Device Reset */ + Rfce = 0x08000000, /* Receive Flow Control Enable */ + Tfce = 0x10000000, /* Transmit Flow Control Enable */ + Vme = 0x40000000, /* VLAN Mode Enable */ +}; + +enum { /* Status */ + Lu = 0x00000002, /* Link Up */ + Tckok = 0x00000004, /* Transmit clock is running */ + Rbcok = 0x00000008, /* Receive clock is running */ + Txoff = 0x00000010, /* Transmission Paused */ + Tbimode = 0x00000020, /* TBI Mode Indication */ + LspeedMASK = 0x000000C0, /* Link Speed Setting */ + LspeedSHIFT = 6, + Lspeed10 = 0x00000000, /* 10Mb/s */ + Lspeed100 = 0x00000040, /* 100Mb/s */ + Lspeed1000 = 0x00000080, /* 1000Mb/s */ + Mtxckok = 0x00000400, /* MTX clock is running */ + Pci66 = 0x00000800, /* PCI Bus speed indication */ + Bus64 = 0x00001000, /* PCI Bus width indication */ + Pcixmode = 0x00002000, /* PCI-X mode */ + PcixspeedMASK = 0x0000C000, /* PCI-X bus speed */ + PcixspeedSHIFT = 14, + Pcix66 = 0x00000000, /* 50-66MHz */ + Pcix100 = 0x00004000, /* 66-100MHz */ + Pcix133 = 0x00008000, /* 100-133MHz */ +}; + +enum { /* Ctrl and Status */ + Fd = 0x00000001, /* Full-Duplex */ + AsdvMASK = 0x00000300, + AsdvSHIFT = 8, + Asdv10 = 0x00000000, /* 10Mb/s */ + Asdv100 = 0x00000100, /* 100Mb/s */ + Asdv1000 = 0x00000200, /* 1000Mb/s */ +}; + +enum { /* Eecd */ + Sk = 0x00000001, /* Clock input to the EEPROM */ + Cs = 0x00000002, /* Chip Select */ + Di = 0x00000004, /* Data Input to the EEPROM */ + Do = 0x00000008, /* Data Output from the EEPROM */ + Areq = 0x00000040, /* EEPROM Access Request */ + Agnt = 0x00000080, /* EEPROM Access Grant */ + Eesz256 = 0x00000200, /* EEPROM is 256 words not 64 */ + Spi = 0x00002000, /* EEPROM is SPI not Microwire */ +}; + +enum { /* Ctrlext */ + Gpien = 0x0000000F, /* General Purpose Interrupt Enables */ + SwdpinshiMASK = 0x000000F0, /* Software Defined Pins - hi nibble */ + SwdpinshiSHIFT = 4, + SwdpiohiMASK = 0x00000F00, /* Software Defined Pins - I or O */ + SwdpiohiSHIFT = 8, + Asdchk = 0x00001000, /* ASD Check */ + Eerst = 0x00002000, /* EEPROM Reset */ + Ips = 0x00004000, /* Invert Power State */ + Spdbyps = 0x00008000, /* Speed Select Bypass */ +}; + +enum { /* EEPROM content offsets */ + Ea = 0x00, /* Ethernet Address */ + Cf = 0x03, /* Compatibility Field */ + Pba = 0x08, /* Printed Board Assembly number */ + Icw1 = 0x0A, /* Initialization Control Word 1 */ + Sid = 0x0B, /* Subsystem ID */ + Svid = 0x0C, /* Subsystem Vendor ID */ + Did = 0x0D, /* Device ID */ + Vid = 0x0E, /* Vendor ID */ + Icw2 = 0x0F, /* Initialization Control Word 2 */ +}; + +enum { /* Mdic */ + MDIdMASK = 0x0000FFFF, /* Data */ + MDIdSHIFT = 0, + MDIrMASK = 0x001F0000, /* PHY Register Address */ + MDIrSHIFT = 16, + MDIpMASK = 0x03E00000, /* PHY Address */ + MDIpSHIFT = 21, + MDIwop = 0x04000000, /* Write Operation */ + MDIrop = 0x08000000, /* Read Operation */ + MDIready = 0x10000000, /* End of Transaction */ + MDIie = 0x20000000, /* Interrupt Enable */ + MDIe = 0x40000000, /* Error */ +}; + +enum { /* Icr, Ics, Ims, Imc */ + Txdw = 0x00000001, /* Transmit Descriptor Written Back */ + Txqe = 0x00000002, /* Transmit Queue Empty */ + Lsc = 0x00000004, /* Link Status Change */ + Rxseq = 0x00000008, /* Receive Sequence Error */ + Rxdmt0 = 0x00000010, /* Rd Minimum Threshold Reached */ + Rxo = 0x00000040, /* Receiver Overrun */ + Rxt0 = 0x00000080, /* Receiver Timer Interrupt */ + Mdac = 0x00000200, /* MDIO Access Completed */ + Rxcfg = 0x00000400, /* Receiving /C/ ordered sets */ + Gpi0 = 0x00000800, /* General Purpose Interrupts */ + Gpi1 = 0x00001000, + Gpi2 = 0x00002000, + Gpi3 = 0x00004000, +}; + +/* + * The Mdic register isn't implemented on the 82543GC, + * the software defined pins are used instead. + * These definitions work for the Intel PRO/1000 T Server Adapter. + * The direction pin bits are read from the EEPROM. + */ +enum { + Mdd = ((1<<2)<<SwdpinsloSHIFT), /* data */ + Mddo = ((1<<2)<<SwdpioloSHIFT), /* pin direction */ + Mdc = ((1<<3)<<SwdpinsloSHIFT), /* clock */ + Mdco = ((1<<3)<<SwdpioloSHIFT), /* pin direction */ + Mdr = ((1<<0)<<SwdpinshiSHIFT), /* reset */ + Mdro = ((1<<0)<<SwdpiohiSHIFT), /* pin direction */ +}; + +enum { /* Txcw */ + TxcwFd = 0x00000020, /* Full Duplex */ + TxcwHd = 0x00000040, /* Half Duplex */ + TxcwPauseMASK = 0x00000180, /* Pause */ + TxcwPauseSHIFT = 7, + TxcwPs = (1<<TxcwPauseSHIFT), /* Pause Supported */ + TxcwAs = (2<<TxcwPauseSHIFT), /* Asymmetric FC desired */ + TxcwRfiMASK = 0x00003000, /* Remote Fault Indication */ + TxcwRfiSHIFT = 12, + TxcwNpr = 0x00008000, /* Next Page Request */ + TxcwConfig = 0x40000000, /* Transmit COnfig Control */ + TxcwAne = 0x80000000, /* Auto-Negotiation Enable */ +}; + +enum { /* Rxcw */ + Rxword = 0x0000FFFF, /* Data from auto-negotiation process */ + Rxnocarrier = 0x04000000, /* Carrier Sense indication */ + Rxinvalid = 0x08000000, /* Invalid Symbol during configuration */ + Rxchange = 0x10000000, /* Change to the Rxword indication */ + Rxconfig = 0x20000000, /* /C/ order set reception indication */ + Rxsync = 0x40000000, /* Lost bit synchronization indication */ + Anc = 0x80000000, /* Auto Negotiation Complete */ +}; + +enum { /* Rctl */ + Rrst = 0x00000001, /* Receiver Software Reset */ + Ren = 0x00000002, /* Receiver Enable */ + Sbp = 0x00000004, /* Store Bad Packets */ + Upe = 0x00000008, /* Unicast Promiscuous Enable */ + Mpe = 0x00000010, /* Multicast Promiscuous Enable */ + Lpe = 0x00000020, /* Long Packet Reception Enable */ + LbmMASK = 0x000000C0, /* Loopback Mode */ + LbmOFF = 0x00000000, /* No Loopback */ + LbmTBI = 0x00000040, /* TBI Loopback */ + LbmMII = 0x00000080, /* GMII/MII Loopback */ + LbmXCVR = 0x000000C0, /* Transceiver Loopback */ + RdtmsMASK = 0x00000300, /* Rd Minimum Threshold Size */ + RdtmsHALF = 0x00000000, /* Threshold is 1/2 Rdlen */ + RdtmsQUARTER = 0x00000100, /* Threshold is 1/4 Rdlen */ + RdtmsEIGHTH = 0x00000200, /* Threshold is 1/8 Rdlen */ + MoMASK = 0x00003000, /* Multicast Offset */ + Mo47b36 = 0x00000000, /* bits [47:36] of received address */ + Mo46b35 = 0x00001000, /* bits [46:35] of received address */ + Mo45b34 = 0x00002000, /* bits [45:34] of received address */ + Mo43b32 = 0x00003000, /* bits [43:32] of received address */ + Bam = 0x00008000, /* Broadcast Accept Mode */ + BsizeMASK = 0x00030000, /* Receive Buffer Size */ + Bsize2048 = 0x00000000, /* Bsex = 0 */ + Bsize1024 = 0x00010000, /* Bsex = 0 */ + Bsize512 = 0x00020000, /* Bsex = 0 */ + Bsize256 = 0x00030000, /* Bsex = 0 */ + Bsize16384 = 0x00010000, /* Bsex = 1 */ + Vfe = 0x00040000, /* VLAN Filter Enable */ + Cfien = 0x00080000, /* Canonical Form Indicator Enable */ + Cfi = 0x00100000, /* Canonical Form Indicator value */ + Dpf = 0x00400000, /* Discard Pause Frames */ + Pmcf = 0x00800000, /* Pass MAC Control Frames */ + Bsex = 0x02000000, /* Buffer Size Extension */ + Secrc = 0x04000000, /* Strip CRC from incoming packet */ +}; + +enum { /* Tctl */ + Trst = 0x00000001, /* Transmitter Software Reset */ + Ten = 0x00000002, /* Transmit Enable */ + Psp = 0x00000008, /* Pad Short Packets */ + CtMASK = 0x00000FF0, /* Collision Threshold */ + CtSHIFT = 4, + ColdMASK = 0x003FF000, /* Collision Distance */ + ColdSHIFT = 12, + Swxoff = 0x00400000, /* Sofware XOFF Transmission */ + Pbe = 0x00800000, /* Packet Burst Enable */ + Rtlc = 0x01000000, /* Re-transmit on Late Collision */ + Nrtu = 0x02000000, /* No Re-transmit on Underrrun */ +}; + +enum { /* [RT]xdctl */ + PthreshMASK = 0x0000003F, /* Prefetch Threshold */ + PthreshSHIFT = 0, + HthreshMASK = 0x00003F00, /* Host Threshold */ + HthreshSHIFT = 8, + WthreshMASK = 0x003F0000, /* Writeback Threshold */ + WthreshSHIFT = 16, + Gran = 0x01000000, /* Granularity */ + LthreshMASK = 0xFE000000, /* Low Threshold */ + LthreshSHIFT = 25, +}; + +enum { /* Rxcsum */ + PcssMASK = 0x000000FF, /* Packet Checksum Start */ + PcssSHIFT = 0, + Ipofl = 0x00000100, /* IP Checksum Off-load Enable */ + Tuofl = 0x00000200, /* TCP/UDP Checksum Off-load Enable */ +}; + +enum { /* Manc */ + Arpen = 0x00002000, /* Enable ARP Request Filtering */ +}; + +enum { /* Receive Delay Timer Ring */ + DelayMASK = 0x0000FFFF, /* delay timer in 1.024nS increments */ + DelaySHIFT = 0, + Fpd = 0x80000000, /* Flush partial Descriptor Block */ +}; + +typedef struct Rd { /* Receive Descriptor */ + uint addr[2]; + ushort length; + ushort checksum; + uchar status; + uchar errors; + ushort special; +} Rd; + +enum { /* Rd status */ + Rdd = 0x01, /* Descriptor Done */ + Reop = 0x02, /* End of Packet */ + Ixsm = 0x04, /* Ignore Checksum Indication */ + Vp = 0x08, /* Packet is 802.1Q (matched VET) */ + Tcpcs = 0x20, /* TCP Checksum Calculated on Packet */ + Ipcs = 0x40, /* IP Checksum Calculated on Packet */ + Pif = 0x80, /* Passed in-exact filter */ +}; + +enum { /* Rd errors */ + Ce = 0x01, /* CRC Error or Alignment Error */ + Se = 0x02, /* Symbol Error */ + Seq = 0x04, /* Sequence Error */ + Cxe = 0x10, /* Carrier Extension Error */ + Tcpe = 0x20, /* TCP/UDP Checksum Error */ + Ipe = 0x40, /* IP Checksum Error */ + Rxe = 0x80, /* RX Data Error */ +}; + +typedef struct Td Td; +struct Td { /* Transmit Descriptor */ + union { + uint addr[2]; /* Data */ + struct { /* Context */ + uchar ipcss; + uchar ipcso; + ushort ipcse; + uchar tucss; + uchar tucso; + ushort tucse; + }; + }; + uint control; + uint status; +}; + +enum { /* Td control */ + LenMASK = 0x000FFFFF, /* Data/Packet Length Field */ + LenSHIFT = 0, + DtypeCD = 0x00000000, /* Data Type 'Context Descriptor' */ + DtypeDD = 0x00100000, /* Data Type 'Data Descriptor' */ + PtypeTCP = 0x01000000, /* TCP/UDP Packet Type (CD) */ + Teop = 0x01000000, /* End of Packet (DD) */ + PtypeIP = 0x02000000, /* IP Packet Type (CD) */ + Ifcs = 0x02000000, /* Insert FCS (DD) */ + Tse = 0x04000000, /* TCP Segmentation Enable */ + Rs = 0x08000000, /* Report Status */ + Rps = 0x10000000, /* Report Status Sent */ + Dext = 0x20000000, /* Descriptor Extension */ + Vle = 0x40000000, /* VLAN Packet Enable */ + Ide = 0x80000000, /* Interrupt Delay Enable */ +}; + +enum { /* Td status */ + Tdd = 0x00000001, /* Descriptor Done */ + Ec = 0x00000002, /* Excess Collisions */ + Lc = 0x00000004, /* Late Collision */ + Tu = 0x00000008, /* Transmit Underrun */ + Iixsm = 0x00000100, /* Insert IP Checksum */ + Itxsm = 0x00000200, /* Insert TCP/UDP Checksum */ + HdrlenMASK = 0x0000FF00, /* Header Length (Tse) */ + HdrlenSHIFT = 8, + VlanMASK = 0x0FFF0000, /* VLAN Identifier */ + VlanSHIFT = 16, + Tcfi = 0x10000000, /* Canonical Form Indicator */ + PriMASK = 0xE0000000, /* User Priority */ + PriSHIFT = 29, + MssMASK = 0xFFFF0000, /* Maximum Segment Size (Tse) */ + MssSHIFT = 16, +}; + +enum { + Nrd = 256, /* multiple of 8 */ + Ntd = 64, /* multiple of 8 */ + Nrb = 1024, /* private receive buffers per Ctlr */ + Rbsz = 2048, +}; + +typedef struct Ctlr Ctlr; +typedef struct Ctlr { + int port; + Pcidev* pcidev; + Ctlr* next; + int active; + int started; + int id; + int cls; + ushort eeprom[0x40]; + + QLock alock; /* attach */ + void* alloc; /* receive/transmit descriptors */ + int nrd; + int ntd; + int nrb; /* how many this Ctlr has in the pool */ + + int* nic; + Lock imlock; + int im; /* interrupt mask */ + + Mii* mii; + Rendez lrendez; + int lim; + + int link; + + QLock slock; + uint statistics[Nstatistics]; + uint lsleep; + uint lintr; + uint rsleep; + uint rintr; + uint txdw; + uint tintr; + uint ixsm; + uint ipcs; + uint tcpcs; + + uchar ra[Eaddrlen]; /* receive address */ + ulong mta[128]; /* multicast table array */ + + Rendez rrendez; + int rim; + int rdfree; + Rd* rdba; /* receive descriptor base address */ + Block** rb; /* receive buffers */ + int rdh; /* receive descriptor head */ + int rdt; /* receive descriptor tail */ + int rdtr; /* receive delay timer ring value */ + + Lock tlock; + int tbusy; + int tdfree; + Td* tdba; /* transmit descriptor base address */ + Block** tb; /* transmit buffers */ + int tdh; /* transmit descriptor head */ + int tdt; /* transmit descriptor tail */ + + int txcw; + int fcrtl; + int fcrth; +} Ctlr; + +#define csr32r(c, r) (*((c)->nic+((r)/4))) +#define csr32w(c, r, v) (*((c)->nic+((r)/4)) = (v)) + +static Ctlr* igbectlrhead; +static Ctlr* igbectlrtail; + +static Lock igberblock; /* free receive Blocks */ +static Block* igberbpool; + +static char* statistics[Nstatistics] = { + "CRC Error", + "Alignment Error", + "Symbol Error", + "RX Error", + "Missed Packets", + "Single Collision", + "Excessive Collisions", + "Multiple Collision", + "Late Collisions", + nil, + "Collision", + "Transmit Underrun", + "Defer", + "Transmit - No CRS", + "Sequence Error", + "Carrier Extension Error", + "Receive Error Length", + nil, + "XON Received", + "XON Transmitted", + "XOFF Received", + "XOFF Transmitted", + "FC Received Unsupported", + "Packets Received (64 Bytes)", + "Packets Received (65-127 Bytes)", + "Packets Received (128-255 Bytes)", + "Packets Received (256-511 Bytes)", + "Packets Received (512-1023 Bytes)", + "Packets Received (1024-1522 Bytes)", + "Good Packets Received", + "Broadcast Packets Received", + "Multicast Packets Received", + "Good Packets Transmitted", + nil, + "Good Octets Received", + nil, + "Good Octets Transmitted", + nil, + nil, + nil, + "Receive No Buffers", + "Receive Undersize", + "Receive Fragment", + "Receive Oversize", + "Receive Jabber", + nil, + nil, + nil, + "Total Octets Received", + nil, + "Total Octets Transmitted", + nil, + "Total Packets Received", + "Total Packets Transmitted", + "Packets Transmitted (64 Bytes)", + "Packets Transmitted (65-127 Bytes)", + "Packets Transmitted (128-255 Bytes)", + "Packets Transmitted (256-511 Bytes)", + "Packets Transmitted (512-1023 Bytes)", + "Packets Transmitted (1024-1522 Bytes)", + "Multicast Packets Transmitted", + "Broadcast Packets Transmitted", + "TCP Segmentation Context Transmitted", + "TCP Segmentation Context Fail", +}; + +static long +igbeifstat(Ether* edev, void* a, long n, ulong offset) +{ + Ctlr *ctlr; + char *p, *s; + int i, l, r; + uvlong tuvl, ruvl; + + ctlr = edev->ctlr; + qlock(&ctlr->slock); + p = malloc(2*READSTR); + l = 0; + for(i = 0; i < Nstatistics; i++){ + r = csr32r(ctlr, Statistics+i*4); + if((s = statistics[i]) == nil) + continue; + switch(i){ + case Gorcl: + case Gotcl: + case Torl: + case Totl: + ruvl = r; + ruvl += ((uvlong)csr32r(ctlr, Statistics+(i+1)*4))<<32; + tuvl = ruvl; + tuvl += ctlr->statistics[i]; + tuvl += ((uvlong)ctlr->statistics[i+1])<<32; + if(tuvl == 0) + continue; + ctlr->statistics[i] = tuvl; + ctlr->statistics[i+1] = tuvl>>32; + l += snprint(p+l, 2*READSTR-l, "%s: %llud %llud\n", + s, tuvl, ruvl); + i++; + break; + + default: + ctlr->statistics[i] += r; + if(ctlr->statistics[i] == 0) + continue; + l += snprint(p+l, 2*READSTR-l, "%s: %ud %ud\n", + s, ctlr->statistics[i], r); + break; + } + } + + l += snprint(p+l, 2*READSTR-l, "lintr: %ud %ud\n", + ctlr->lintr, ctlr->lsleep); + l += snprint(p+l, 2*READSTR-l, "rintr: %ud %ud\n", + ctlr->rintr, ctlr->rsleep); + l += snprint(p+l, 2*READSTR-l, "tintr: %ud %ud\n", + ctlr->tintr, ctlr->txdw); + l += snprint(p+l, 2*READSTR-l, "ixcs: %ud %ud %ud\n", + ctlr->ixsm, ctlr->ipcs, ctlr->tcpcs); + l += snprint(p+l, 2*READSTR-l, "rdtr: %ud\n", ctlr->rdtr); + l += snprint(p+l, 2*READSTR-l, "Ctrlext: %08x\n", csr32r(ctlr, Ctrlext)); + + l += snprint(p+l, 2*READSTR-l, "eeprom:"); + for(i = 0; i < 0x40; i++){ + if(i && ((i & 0x07) == 0)) + l += snprint(p+l, 2*READSTR-l, "\n "); + l += snprint(p+l, 2*READSTR-l, " %4.4uX", ctlr->eeprom[i]); + } + l += snprint(p+l, 2*READSTR-l, "\n"); + + if(ctlr->mii != nil && ctlr->mii->curphy != nil){ + l += snprint(p+l, 2*READSTR, "phy: "); + for(i = 0; i < NMiiPhyr; i++){ + if(i && ((i & 0x07) == 0)) + l += snprint(p+l, 2*READSTR-l, "\n "); + r = miimir(ctlr->mii, i); + l += snprint(p+l, 2*READSTR-l, " %4.4uX", r); + } + snprint(p+l, 2*READSTR-l, "\n"); + } + n = readstr(offset, a, n, p); + free(p); + qunlock(&ctlr->slock); + + return n; +} + +enum { + CMrdtr, +}; + +static Cmdtab igbectlmsg[] = { + CMrdtr, "rdtr", 2, +}; + +static long +igbectl(Ether* edev, void* buf, long n) +{ + int v; + char *p; + Ctlr *ctlr; + Cmdbuf *cb; + Cmdtab *ct; + + if((ctlr = edev->ctlr) == nil) + error(Enonexist); + + cb = parsecmd(buf, n); + if(waserror()){ + free(cb); + nexterror(); + } + + ct = lookupcmd(cb, igbectlmsg, nelem(igbectlmsg)); + switch(ct->index){ + case CMrdtr: + v = strtol(cb->f[1], &p, 0); + if(v < 0 || p == cb->f[1] || v > 0xFFFF) + error(Ebadarg); + ctlr->rdtr = v;; + csr32w(ctlr, Rdtr, Fpd|v); + break; + } + free(cb); + poperror(); + + return n; +} + +static void +igbepromiscuous(void* arg, int on) +{ + int rctl; + Ctlr *ctlr; + Ether *edev; + + edev = arg; + ctlr = edev->ctlr; + + rctl = csr32r(ctlr, Rctl); + rctl &= ~MoMASK; + rctl |= Mo47b36; + if(on) + rctl |= Upe|Mpe; + else + rctl &= ~(Upe|Mpe); + csr32w(ctlr, Rctl, rctl); +} + +static void +igbemulticast(void* arg, uchar* addr, int on) +{ + int bit, x; + Ctlr *ctlr; + Ether *edev; + + edev = arg; + ctlr = edev->ctlr; + + x = addr[5]>>1; + bit = ((addr[5] & 1)<<4)|(addr[4]>>4); + if(on) + ctlr->mta[x] |= 1<<bit; + else + ctlr->mta[x] &= ~(1<<bit); + + csr32w(ctlr, Mta+x*4, ctlr->mta[x]); +} + +static Block* +igberballoc(void) +{ + Block *bp; + + ilock(&igberblock); + if((bp = igberbpool) != nil){ + igberbpool = bp->next; + bp->next = nil; + } + iunlock(&igberblock); + + return bp; +} + +static void +igberbfree(Block* bp) +{ + bp->rp = bp->lim - Rbsz; + bp->wp = bp->rp; + + ilock(&igberblock); + bp->next = igberbpool; + igberbpool = bp; + iunlock(&igberblock); +} + +static void +igbeim(Ctlr* ctlr, int im) +{ + ilock(&ctlr->imlock); + ctlr->im |= im; + csr32w(ctlr, Ims, ctlr->im); + iunlock(&ctlr->imlock); +} + +static int +igbelim(void* ctlr) +{ + return ((Ctlr*)ctlr)->lim != 0; +} + +static void +igbelproc(void* arg) +{ + Ctlr *ctlr; + Ether *edev; + MiiPhy *phy; + int ctrl, r; + + edev = arg; + ctlr = edev->ctlr; + for(;;){ + if(ctlr->mii == nil || ctlr->mii->curphy == nil) + continue; + + /* + * To do: + * logic to manage status change, + * this is incomplete but should work + * one time to set up the hardware. + * + * MiiPhy.speed, etc. should be in Mii. + */ + if(miistatus(ctlr->mii) < 0) + //continue; + goto enable; + + phy = ctlr->mii->curphy; + ctrl = csr32r(ctlr, Ctrl); + + switch(ctlr->id){ + case i82543gc: + case i82544ei: + default: + if(!(ctrl & Asde)){ + ctrl &= ~(SspeedMASK|Ilos|Fd); + ctrl |= Frcdplx|Frcspd; + if(phy->speed == 1000) + ctrl |= Sspeed1000; + else if(phy->speed == 100) + ctrl |= Sspeed100; + if(phy->fd) + ctrl |= Fd; + } + break; + + case i82540em: + case i82540eplp: + case i82547gi: + case i82541gi: + case i82541pi: + break; + } + + /* + * Collision Distance. + */ + r = csr32r(ctlr, Tctl); + r &= ~ColdMASK; + if(phy->fd) + r |= 64<<ColdSHIFT; + else + r |= 512<<ColdSHIFT; + csr32w(ctlr, Tctl, r); + + /* + * Flow control. + */ + if(phy->rfc) + ctrl |= Rfce; + if(phy->tfc) + ctrl |= Tfce; + csr32w(ctlr, Ctrl, ctrl); + +enable: + ctlr->lim = 0; + igbeim(ctlr, Lsc); + + ctlr->lsleep++; + sleep(&ctlr->lrendez, igbelim, ctlr); + } +} + +static void +igbetxinit(Ctlr* ctlr) +{ + int i, r; + Block *bp; + + csr32w(ctlr, Tctl, (0x0F<<CtSHIFT)|Psp|(66<<ColdSHIFT)); + switch(ctlr->id){ + default: + r = 6; + break; + case i82543gc: + case i82544ei: + case i82547ei: + case i82540em: + case i82540eplp: + case i82541gi: + case i82541pi: + case i82545gmc: + case i82546gb: + case i82546eb: + case i82547gi: + r = 8; + break; + } + csr32w(ctlr, Tipg, (6<<20)|(8<<10)|r); + csr32w(ctlr, Ait, 0); + csr32w(ctlr, Txdmac, 0); + + csr32w(ctlr, Tdbal, PCIWADDR(ctlr->tdba)); + csr32w(ctlr, Tdbah, 0); + csr32w(ctlr, Tdlen, ctlr->ntd*sizeof(Td)); + ctlr->tdh = PREV(0, ctlr->ntd); + csr32w(ctlr, Tdh, 0); + ctlr->tdt = 0; + csr32w(ctlr, Tdt, 0); + + for(i = 0; i < ctlr->ntd; i++){ + if((bp = ctlr->tb[i]) != nil){ + ctlr->tb[i] = nil; + freeb(bp); + } + memset(&ctlr->tdba[i], 0, sizeof(Td)); + } + ctlr->tdfree = ctlr->ntd; + + csr32w(ctlr, Tidv, 128); + r = (4<<WthreshSHIFT)|(4<<HthreshSHIFT)|(8<<PthreshSHIFT); + + switch(ctlr->id){ + default: + break; + case i82540em: + case i82540eplp: + case i82547gi: + case i82545gmc: + case i82546gb: + case i82546eb: + case i82541gi: + case i82541pi: + r = csr32r(ctlr, Txdctl); + r &= ~WthreshMASK; + r |= Gran|(4<<WthreshSHIFT); + + csr32w(ctlr, Tadv, 64); + break; + } + + csr32w(ctlr, Txdctl, r); + + r = csr32r(ctlr, Tctl); + r |= Ten; + csr32w(ctlr, Tctl, r); +} + +static void +igbetransmit(Ether* edev) +{ + Td *td; + Block *bp; + Ctlr *ctlr; + int tdh, tdt; + + ctlr = edev->ctlr; + + ilock(&ctlr->tlock); + + /* + * Free any completed packets + */ + tdh = ctlr->tdh; + while(NEXT(tdh, ctlr->ntd) != csr32r(ctlr, Tdh)){ + if((bp = ctlr->tb[tdh]) != nil){ + ctlr->tb[tdh] = nil; + freeb(bp); + } + memset(&ctlr->tdba[tdh], 0, sizeof(Td)); + tdh = NEXT(tdh, ctlr->ntd); + } + ctlr->tdh = tdh; + + /* + * Try to fill the ring back up. + */ + tdt = ctlr->tdt; + while(NEXT(tdt, ctlr->ntd) != tdh){ + if((bp = qget(edev->oq)) == nil) + break; + td = &ctlr->tdba[tdt]; + td->addr[0] = PCIWADDR(bp->rp); + td->control = ((BLEN(bp) & LenMASK)<<LenSHIFT); + td->control |= Dext|Ifcs|Teop|DtypeDD; + ctlr->tb[tdt] = bp; + tdt = NEXT(tdt, ctlr->ntd); + if(NEXT(tdt, ctlr->ntd) == tdh){ + td->control |= Rs; + ctlr->txdw++; + ctlr->tdt = tdt; + csr32w(ctlr, Tdt, tdt); + igbeim(ctlr, Txdw); + break; + } + ctlr->tdt = tdt; + csr32w(ctlr, Tdt, tdt); + } + + iunlock(&ctlr->tlock); +} + +static void +igbereplenish(Ctlr* ctlr) +{ + Rd *rd; + int rdt; + Block *bp; + + rdt = ctlr->rdt; + while(NEXT(rdt, ctlr->nrd) != ctlr->rdh){ + rd = &ctlr->rdba[rdt]; + if(ctlr->rb[rdt] == nil){ + bp = igberballoc(); + if(bp == nil){ + iprint("no available buffers\n"); + break; + } + ctlr->rb[rdt] = bp; + rd->addr[0] = PCIWADDR(bp->rp); + rd->addr[1] = 0; + } + coherence(); + rd->status = 0; + rdt = NEXT(rdt, ctlr->nrd); + ctlr->rdfree++; + } + ctlr->rdt = rdt; + csr32w(ctlr, Rdt, rdt); +} + +static void +igberxinit(Ctlr* ctlr) +{ + int i; + Block *bp; + + csr32w(ctlr, Rctl, Dpf|Bsize2048|Bam|RdtmsHALF); + + csr32w(ctlr, Rdbal, PCIWADDR(ctlr->rdba)); + csr32w(ctlr, Rdbah, 0); + csr32w(ctlr, Rdlen, ctlr->nrd*sizeof(Rd)); + ctlr->rdh = 0; + csr32w(ctlr, Rdh, 0); + ctlr->rdt = 0; + csr32w(ctlr, Rdt, 0); + ctlr->rdtr = 0; + csr32w(ctlr, Rdtr, Fpd|0); + + for(i = 0; i < ctlr->nrd; i++){ + if((bp = ctlr->rb[i]) != nil){ + ctlr->rb[i] = nil; + freeb(bp); + } + } + igbereplenish(ctlr); + + switch(ctlr->id){ + case i82540em: + case i82540eplp: + case i82541gi: + case i82541pi: + case i82545gmc: + case i82546gb: + case i82546eb: + case i82547gi: + csr32w(ctlr, Radv, 64); + break; + } + csr32w(ctlr, Rxdctl, (8<<WthreshSHIFT)|(8<<HthreshSHIFT)|4); + + /* + * Enable checksum offload. + */ + csr32w(ctlr, Rxcsum, Tuofl|Ipofl|(ETHERHDRSIZE<<PcssSHIFT)); +} + +static int +igberim(void* ctlr) +{ + return ((Ctlr*)ctlr)->rim != 0; +} + +static void +igberproc(void* arg) +{ + Rd *rd; + Block *bp; + Ctlr *ctlr; + int r, rdh; + Ether *edev; + + edev = arg; + ctlr = edev->ctlr; + + igberxinit(ctlr); + r = csr32r(ctlr, Rctl); + r |= Ren; + csr32w(ctlr, Rctl, r); + + for(;;){ + ctlr->rim = 0; + igbeim(ctlr, Rxt0|Rxo|Rxdmt0|Rxseq); + ctlr->rsleep++; + sleep(&ctlr->rrendez, igberim, ctlr); + + rdh = ctlr->rdh; + for(;;){ + rd = &ctlr->rdba[rdh]; + + if(!(rd->status & Rdd)) + break; + + /* + * Accept eop packets with no errors. + * With no errors and the Ixsm bit set, + * the descriptor status Tpcs and Ipcs bits give + * an indication of whether the checksums were + * calculated and valid. + */ + if((rd->status & Reop) && rd->errors == 0){ + bp = ctlr->rb[rdh]; + ctlr->rb[rdh] = nil; + bp->wp += rd->length; + bp->next = nil; + if(!(rd->status & Ixsm)){ + ctlr->ixsm++; + if(rd->status & Ipcs){ + /* + * IP checksum calculated + * (and valid as errors == 0). + */ + ctlr->ipcs++; + bp->flag |= Bipck; + } + if(rd->status & Tcpcs){ + /* + * TCP/UDP checksum calculated + * (and valid as errors == 0). + */ + ctlr->tcpcs++; + bp->flag |= Btcpck|Budpck; + } + bp->checksum = rd->checksum; + bp->flag |= Bpktck; + } + etheriq(edev, bp, 1); + } + else if(ctlr->rb[rdh] != nil){ + freeb(ctlr->rb[rdh]); + ctlr->rb[rdh] = nil; + } + + memset(rd, 0, sizeof(Rd)); + coherence(); + ctlr->rdfree--; + rdh = NEXT(rdh, ctlr->nrd); + } + ctlr->rdh = rdh; + + if(ctlr->rdfree < ctlr->nrd/2 || (ctlr->rim & Rxdmt0)) + igbereplenish(ctlr); + } +} + +static void +igbeattach(Ether* edev) +{ + Block *bp; + Ctlr *ctlr; + char name[KNAMELEN]; + + ctlr = edev->ctlr; + qlock(&ctlr->alock); + if(ctlr->alloc != nil){ + qunlock(&ctlr->alock); + return; + } + + ctlr->nrd = ROUND(Nrd, 8); + ctlr->ntd = ROUND(Ntd, 8); + ctlr->alloc = malloc(ctlr->nrd*sizeof(Rd)+ctlr->ntd*sizeof(Td) + 127); + if(ctlr->alloc == nil){ + qunlock(&ctlr->alock); + return; + } + ctlr->rdba = (Rd*)ROUNDUP((ulong)ctlr->alloc, 128); + ctlr->tdba = (Td*)(ctlr->rdba+ctlr->nrd); + + ctlr->rb = malloc(ctlr->nrd*sizeof(Block*)); + ctlr->tb = malloc(ctlr->ntd*sizeof(Block*)); + + if(waserror()){ + while(ctlr->nrb > 0){ + bp = igberballoc(); + bp->free = nil; + freeb(bp); + ctlr->nrb--; + } + free(ctlr->tb); + ctlr->tb = nil; + free(ctlr->rb); + ctlr->rb = nil; + free(ctlr->alloc); + ctlr->alloc = nil; + qunlock(&ctlr->alock); + nexterror(); + } + + for(ctlr->nrb = 0; ctlr->nrb < Nrb; ctlr->nrb++){ + if((bp = allocb(Rbsz)) == nil) + break; + bp->free = igberbfree; + freeb(bp); + } + + snprint(name, KNAMELEN, "#l%dlproc", edev->ctlrno); + kproc(name, igbelproc, edev, 0); + + snprint(name, KNAMELEN, "#l%drproc", edev->ctlrno); + kproc(name, igberproc, edev, 0); + + igbetxinit(ctlr); + + qunlock(&ctlr->alock); + poperror(); +} + +static void +igbeinterrupt(Ureg*, void* arg) +{ + Ctlr *ctlr; + Ether *edev; + int icr, im, txdw; + + edev = arg; + ctlr = edev->ctlr; + + ilock(&ctlr->imlock); + csr32w(ctlr, Imc, ~0); + im = ctlr->im; + txdw = 0; + + while((icr = csr32r(ctlr, Icr) & ctlr->im) != 0){ + if(icr & Lsc){ + im &= ~Lsc; + ctlr->lim = icr & Lsc; + wakeup(&ctlr->lrendez); + ctlr->lintr++; + } + if(icr & (Rxt0|Rxo|Rxdmt0|Rxseq)){ + im &= ~(Rxt0|Rxo|Rxdmt0|Rxseq); + ctlr->rim = icr & (Rxt0|Rxo|Rxdmt0|Rxseq); + wakeup(&ctlr->rrendez); + ctlr->rintr++; + } + if(icr & Txdw){ + im &= ~Txdw; + txdw++; + ctlr->tintr++; + } + } + + ctlr->im = im; + csr32w(ctlr, Ims, im); + iunlock(&ctlr->imlock); + + if(txdw) + igbetransmit(edev); +} + +static int +i82543mdior(Ctlr* ctlr, int n) +{ + int ctrl, data, i, r; + + /* + * Read n bits from the Management Data I/O Interface. + */ + ctrl = csr32r(ctlr, Ctrl); + r = (ctrl & ~Mddo)|Mdco; + data = 0; + for(i = n-1; i >= 0; i--){ + if(csr32r(ctlr, Ctrl) & Mdd) + data |= (1<<i); + csr32w(ctlr, Ctrl, Mdc|r); + csr32w(ctlr, Ctrl, r); + } + csr32w(ctlr, Ctrl, ctrl); + + return data; +} + +static int +i82543mdiow(Ctlr* ctlr, int bits, int n) +{ + int ctrl, i, r; + + /* + * Write n bits to the Management Data I/O Interface. + */ + ctrl = csr32r(ctlr, Ctrl); + r = Mdco|Mddo|ctrl; + for(i = n-1; i >= 0; i--){ + if(bits & (1<<i)) + r |= Mdd; + else + r &= ~Mdd; + csr32w(ctlr, Ctrl, Mdc|r); + csr32w(ctlr, Ctrl, r); + } + csr32w(ctlr, Ctrl, ctrl); + + return 0; +} + +static int +i82543miimir(Mii* mii, int pa, int ra) +{ + int data; + Ctlr *ctlr; + + ctlr = mii->ctlr; + + /* + * MII Management Interface Read. + * + * Preamble; + * ST+OP+PHYAD+REGAD; + * TA + 16 data bits. + */ + i82543mdiow(ctlr, 0xFFFFFFFF, 32); + i82543mdiow(ctlr, 0x1800|(pa<<5)|ra, 14); + data = i82543mdior(ctlr, 18); + + if(data & 0x10000) + return -1; + + return data & 0xFFFF; +} + +static int +i82543miimiw(Mii* mii, int pa, int ra, int data) +{ + Ctlr *ctlr; + + ctlr = mii->ctlr; + + /* + * MII Management Interface Write. + * + * Preamble; + * ST+OP+PHYAD+REGAD+TA + 16 data bits; + * Z. + */ + i82543mdiow(ctlr, 0xFFFFFFFF, 32); + data &= 0xFFFF; + data |= (0x05<<(5+5+2+16))|(pa<<(5+2+16))|(ra<<(2+16))|(0x02<<16); + i82543mdiow(ctlr, data, 32); + + return 0; +} + +static int +igbemiimir(Mii* mii, int pa, int ra) +{ + Ctlr *ctlr; + int mdic, timo; + + ctlr = mii->ctlr; + + csr32w(ctlr, Mdic, MDIrop|(pa<<MDIpSHIFT)|(ra<<MDIrSHIFT)); + mdic = 0; + for(timo = 64; timo; timo--){ + mdic = csr32r(ctlr, Mdic); + if(mdic & (MDIe|MDIready)) + break; + microdelay(1); + } + + if((mdic & (MDIe|MDIready)) == MDIready) + return mdic & 0xFFFF; + return -1; +} + +static int +igbemiimiw(Mii* mii, int pa, int ra, int data) +{ + Ctlr *ctlr; + int mdic, timo; + + ctlr = mii->ctlr; + + data &= MDIdMASK; + csr32w(ctlr, Mdic, MDIwop|(pa<<MDIpSHIFT)|(ra<<MDIrSHIFT)|data); + mdic = 0; + for(timo = 64; timo; timo--){ + mdic = csr32r(ctlr, Mdic); + if(mdic & (MDIe|MDIready)) + break; + microdelay(1); + } + if((mdic & (MDIe|MDIready)) == MDIready) + return 0; + return -1; +} + +static int +igbemii(Ctlr* ctlr) +{ + MiiPhy *phy; + int ctrl, p, r; + + r = csr32r(ctlr, Status); + if(r & Tbimode) + return -1; + if((ctlr->mii = malloc(sizeof(Mii))) == nil) + return -1; + ctlr->mii->ctlr = ctlr; + + ctrl = csr32r(ctlr, Ctrl); + ctrl |= Slu; + + switch(ctlr->id){ + case i82543gc: + ctrl |= Frcdplx|Frcspd; + csr32w(ctlr, Ctrl, ctrl); + + /* + * The reset pin direction (Mdro) should already + * be set from the EEPROM load. + * If it's not set this configuration is unexpected + * so bail. + */ + r = csr32r(ctlr, Ctrlext); + if(!(r & Mdro)) + return -1; + csr32w(ctlr, Ctrlext, r); + delay(20); + r = csr32r(ctlr, Ctrlext); + r &= ~Mdr; + csr32w(ctlr, Ctrlext, r); + delay(20); + r = csr32r(ctlr, Ctrlext); + r |= Mdr; + csr32w(ctlr, Ctrlext, r); + delay(20); + + ctlr->mii->mir = i82543miimir; + ctlr->mii->miw = i82543miimiw; + break; + case i82544ei: + case i82547ei: + case i82540em: + case i82540eplp: + case i82547gi: + case i82541gi: + case i82541pi: + case i82545gmc: + case i82546gb: + case i82546eb: + ctrl &= ~(Frcdplx|Frcspd); + csr32w(ctlr, Ctrl, ctrl); + ctlr->mii->mir = igbemiimir; + ctlr->mii->miw = igbemiimiw; + break; + default: + free(ctlr->mii); + ctlr->mii = nil; + return -1; + } + + if(mii(ctlr->mii, ~0) == 0 || (phy = ctlr->mii->curphy) == nil){ + free(ctlr->mii); + ctlr->mii = nil; + return -1; + } + print("oui %X phyno %d\n", phy->oui, phy->phyno); + + /* + * 8254X-specific PHY registers not in 802.3: + * 0x10 PHY specific control + * 0x14 extended PHY specific control + * Set appropriate values then reset the PHY to have + * changes noted. + */ + switch(ctlr->id){ + case i82547gi: + case i82541gi: + case i82541pi: + case i82545gmc: + case i82546gb: + case i82546eb: + break; + default: + r = miimir(ctlr->mii, 16); + r |= 0x0800; /* assert CRS on Tx */ + r |= 0x0060; /* auto-crossover all speeds */ + r |= 0x0002; /* polarity reversal enabled */ + miimiw(ctlr->mii, 16, r); + + r = miimir(ctlr->mii, 20); + r |= 0x0070; /* +25MHz clock */ + r &= ~0x0F00; + r |= 0x0100; /* 1x downshift */ + miimiw(ctlr->mii, 20, r); + + miireset(ctlr->mii); + p = 0; + if(ctlr->txcw & TxcwPs) + p |= AnaP; + if(ctlr->txcw & TxcwAs) + p |= AnaAP; + miiane(ctlr->mii, ~0, p, ~0); + break; + } + return 0; +} + +static int +at93c46io(Ctlr* ctlr, char* op, int data) +{ + char *lp, *p; + int i, loop, eecd, r; + + eecd = csr32r(ctlr, Eecd); + + r = 0; + loop = -1; + lp = nil; + for(p = op; *p != '\0'; p++){ + switch(*p){ + default: + return -1; + case ' ': + continue; + case ':': /* start of loop */ + loop = strtol(p+1, &lp, 0)-1; + lp--; + if(p == lp) + loop = 7; + p = lp; + continue; + case ';': /* end of loop */ + if(lp == nil) + return -1; + loop--; + if(loop >= 0) + p = lp; + else + lp = nil; + continue; + case 'C': /* assert clock */ + eecd |= Sk; + break; + case 'c': /* deassert clock */ + eecd &= ~Sk; + break; + case 'D': /* next bit in 'data' byte */ + if(loop < 0) + return -1; + if(data & (1<<loop)) + eecd |= Di; + else + eecd &= ~Di; + break; + case 'O': /* collect data output */ + i = (csr32r(ctlr, Eecd) & Do) != 0; + if(loop >= 0) + r |= (i<<loop); + else + r = i; + continue; + case 'I': /* assert data input */ + eecd |= Di; + break; + case 'i': /* deassert data input */ + eecd &= ~Di; + break; + case 'S': /* enable chip select */ + eecd |= Cs; + break; + case 's': /* disable chip select */ + eecd &= ~Cs; + break; + } + csr32w(ctlr, Eecd, eecd); + microdelay(1); + } + if(loop >= 0) + return -1; + return r; +} + +static int +at93c46r(Ctlr* ctlr) +{ + ushort sum; + char rop[20]; + int addr, areq, bits, data, eecd, i; + + eecd = csr32r(ctlr, Eecd); + if(eecd & Spi){ + print("igbe: SPI EEPROM access not implemented\n"); + return 0; + } + if(eecd & Eesz256) + bits = 8; + else + bits = 6; + snprint(rop, sizeof(rop), "S :%dDCc;", bits+3); + + sum = 0; + + switch(ctlr->id){ + default: + areq = 0; + break; + case i82540em: + case i82540eplp: + case i82541gi: + case i82541pi: + case i82547gi: + case i82545gmc: + case i82546gb: + case i82546eb: + areq = 1; + csr32w(ctlr, Eecd, eecd|Areq); + for(i = 0; i < 1000; i++){ + if((eecd = csr32r(ctlr, Eecd)) & Agnt) + break; + microdelay(5); + } + if(!(eecd & Agnt)){ + print("igbe: not granted EEPROM access\n"); + goto release; + } + break; + } + + for(addr = 0; addr < 0x40; addr++){ + /* + * Read a word at address 'addr' from the Atmel AT93C46 + * 3-Wire Serial EEPROM or compatible. The EEPROM access is + * controlled by 4 bits in Eecd. See the AT93C46 datasheet + * for protocol details. + */ + if(at93c46io(ctlr, rop, (0x06<<bits)|addr) != 0){ + print("igbe: can't set EEPROM address 0x%2.2X\n", addr); + goto release; + } + data = at93c46io(ctlr, ":16COc;", 0); + at93c46io(ctlr, "sic", 0); + ctlr->eeprom[addr] = data; + sum += data; + } + +release: + if(areq) + csr32w(ctlr, Eecd, eecd & ~Areq); + return sum; +} + +static int +igbedetach(Ctlr* ctlr) +{ + int r, timeo; + + /* + * Perform a device reset to get the chip back to the + * power-on state, followed by an EEPROM reset to read + * the defaults for some internal registers. + */ + csr32w(ctlr, Imc, ~0); + csr32w(ctlr, Rctl, 0); + csr32w(ctlr, Tctl, 0); + + delay(10); + + csr32w(ctlr, Ctrl, Devrst); + delay(1); + for(timeo = 0; timeo < 1000; timeo++){ + if(!(csr32r(ctlr, Ctrl) & Devrst)) + break; + delay(1); + } + if(csr32r(ctlr, Ctrl) & Devrst) + return -1; + r = csr32r(ctlr, Ctrlext); + csr32w(ctlr, Ctrlext, r|Eerst); + delay(1); + for(timeo = 0; timeo < 1000; timeo++){ + if(!(csr32r(ctlr, Ctrlext) & Eerst)) + break; + delay(1); + } + if(csr32r(ctlr, Ctrlext) & Eerst) + return -1; + + switch(ctlr->id){ + default: + break; + case i82540em: + case i82540eplp: + case i82541gi: + case i82541pi: + case i82547gi: + case i82545gmc: + case i82546gb: + case i82546eb: + r = csr32r(ctlr, Manc); + r &= ~Arpen; + csr32w(ctlr, Manc, r); + break; + } + + csr32w(ctlr, Imc, ~0); + delay(1); + for(timeo = 0; timeo < 1000; timeo++){ + if(!csr32r(ctlr, Icr)) + break; + delay(1); + } + if(csr32r(ctlr, Icr)) + return -1; + + return 0; +} + +static void +igbeshutdown(Ether* ether) +{ + igbedetach(ether->ctlr); +} + +static int +igbereset(Ctlr* ctlr) +{ + int ctrl, i, pause, r, swdpio, txcw; + + if(igbedetach(ctlr)) + return -1; + + /* + * Read the EEPROM, validate the checksum + * then get the device back to a power-on state. + */ + if((r = at93c46r(ctlr)) != 0xBABA){ + print("igbe: bad EEPROM checksum - 0x%4.4uX\n", r); + return -1; + } + + /* + * Snarf and set up the receive addresses. + * There are 16 addresses. The first should be the MAC address. + * The others are cleared and not marked valid (MS bit of Rah). + */ + if ((ctlr->id == i82546gb || ctlr->id == i82546eb) && BUSFNO(ctlr->pcidev->tbdf) == 1) + ctlr->eeprom[Ea+2] += 0x100; // second interface + for(i = Ea; i < Eaddrlen/2; i++){ + ctlr->ra[2*i] = ctlr->eeprom[i]; + ctlr->ra[2*i+1] = ctlr->eeprom[i]>>8; + } + r = (ctlr->ra[3]<<24)|(ctlr->ra[2]<<16)|(ctlr->ra[1]<<8)|ctlr->ra[0]; + csr32w(ctlr, Ral, r); + r = 0x80000000|(ctlr->ra[5]<<8)|ctlr->ra[4]; + csr32w(ctlr, Rah, r); + for(i = 1; i < 16; i++){ + csr32w(ctlr, Ral+i*8, 0); + csr32w(ctlr, Rah+i*8, 0); + } + + /* + * Clear the Multicast Table Array. + * It's a 4096 bit vector accessed as 128 32-bit registers. + */ + memset(ctlr->mta, 0, sizeof(ctlr->mta)); + for(i = 0; i < 128; i++) + csr32w(ctlr, Mta+i*4, 0); + + /* + * Just in case the Eerst didn't load the defaults + * (doesn't appear to fully on the 8243GC), do it manually. + */ + if (ctlr->id == i82543gc) { // 82543 + txcw = csr32r(ctlr, Txcw); + txcw &= ~(TxcwAne|TxcwPauseMASK|TxcwFd); + ctrl = csr32r(ctlr, Ctrl); + ctrl &= ~(SwdpioloMASK|Frcspd|Ilos|Lrst|Fd); + + if(ctlr->eeprom[Icw1] & 0x0400){ + ctrl |= Fd; + txcw |= TxcwFd; + } + if(ctlr->eeprom[Icw1] & 0x0200) + ctrl |= Lrst; + if(ctlr->eeprom[Icw1] & 0x0010) + ctrl |= Ilos; + if(ctlr->eeprom[Icw1] & 0x0800) + ctrl |= Frcspd; + swdpio = (ctlr->eeprom[Icw1] & 0x01E0)>>5; + ctrl |= swdpio<<SwdpioloSHIFT; + csr32w(ctlr, Ctrl, ctrl); + + ctrl = csr32r(ctlr, Ctrlext); + ctrl &= ~(Ips|SwdpiohiMASK); + swdpio = (ctlr->eeprom[Icw2] & 0x00F0)>>4; + if(ctlr->eeprom[Icw1] & 0x1000) + ctrl |= Ips; + ctrl |= swdpio<<SwdpiohiSHIFT; + csr32w(ctlr, Ctrlext, ctrl); + + if(ctlr->eeprom[Icw2] & 0x0800) + txcw |= TxcwAne; + pause = (ctlr->eeprom[Icw2] & 0x3000)>>12; + txcw |= pause<<TxcwPauseSHIFT; + switch(pause){ + default: + ctlr->fcrtl = 0x00002000; + ctlr->fcrth = 0x00004000; + txcw |= TxcwAs|TxcwPs; + break; + case 0: + ctlr->fcrtl = 0x00002000; + ctlr->fcrth = 0x00004000; + break; + case 2: + ctlr->fcrtl = 0; + ctlr->fcrth = 0; + txcw |= TxcwAs; + break; + } + ctlr->txcw = txcw; + csr32w(ctlr, Txcw, txcw); + } + + + /* + * Flow control - values from the datasheet. + */ + csr32w(ctlr, Fcal, 0x00C28001); + csr32w(ctlr, Fcah, 0x00000100); + csr32w(ctlr, Fct, 0x00008808); + csr32w(ctlr, Fcttv, 0x00000100); + + csr32w(ctlr, Fcrtl, ctlr->fcrtl); + csr32w(ctlr, Fcrth, ctlr->fcrth); + + if(!(csr32r(ctlr, Status) & Tbimode) && igbemii(ctlr) < 0) + return -1; + + return 0; +} + +static void +igbepci(void) +{ + int port, cls; + Pcidev *p; + Ctlr *ctlr; + + p = nil; + while(p = pcimatch(p, 0, 0)){ + if(p->ccrb != 0x02 || p->ccru != 0) + continue; + + switch((p->did<<16)|p->vid){ + default: + continue; + case i82543gc: + case i82544ei: + case i82547ei: + case i82540em: + case i82540eplp: + case i82547gi: + case i82541gi: + case i82541pi: + case i82545gmc: + case i82546gb: + case i82546eb: + break; + } + + port = upamalloc(p->mem[0].bar & ~0x0F, p->mem[0].size, 0); + if(port == 0){ + print("igbe: can't map %8.8luX\n", p->mem[0].bar); + continue; + } + cls = pcicfgr8(p, PciCLS); + switch(cls){ + default: + print("igbe: unexpected CLS - %d\n", cls*4); + break; + case 0x00: + case 0xFF: + print("igbe: unusable CLS\n"); + continue; + case 0x08: + case 0x10: + break; + } + ctlr = malloc(sizeof(Ctlr)); + ctlr->port = port; + ctlr->pcidev = p; + ctlr->id = (p->did<<16)|p->vid; + ctlr->cls = cls*4; + ctlr->nic = KADDR(ctlr->port); + + if(igbereset(ctlr)){ + free(ctlr); + continue; + } + pcisetbme(p); + + if(igbectlrhead != nil) + igbectlrtail->next = ctlr; + else + igbectlrhead = ctlr; + igbectlrtail = ctlr; + } +} + +static int +igbepnp(Ether* edev) +{ + Ctlr *ctlr; + + if(igbectlrhead == nil) + igbepci(); + + /* + * Any adapter matches if no edev->port is supplied, + * otherwise the ports must match. + */ + for(ctlr = igbectlrhead; ctlr != nil; ctlr = ctlr->next){ + if(ctlr->active) + continue; + if(edev->port == 0 || edev->port == ctlr->port){ + ctlr->active = 1; + break; + } + } + if(ctlr == nil) + return -1; + + edev->ctlr = ctlr; + edev->port = ctlr->port; + edev->irq = ctlr->pcidev->intl; + edev->tbdf = ctlr->pcidev->tbdf; + edev->mbps = 1000; + memmove(edev->ea, ctlr->ra, Eaddrlen); + + /* + * Linkage to the generic ethernet driver. + */ + edev->attach = igbeattach; + edev->transmit = igbetransmit; + edev->interrupt = igbeinterrupt; + edev->ifstat = igbeifstat; + edev->ctl = igbectl; + + edev->arg = edev; + edev->promiscuous = igbepromiscuous; + edev->shutdown = igbeshutdown; + edev->multicast = igbemulticast; + + return 0; +} + +void +etherigbelink(void) +{ + addethercard("i82543", igbepnp); + addethercard("igbe", igbepnp); +} diff --git a/os/pc/etherrhine.c b/os/pc/etherrhine.c new file mode 100644 index 00000000..5d25a6f0 --- /dev/null +++ b/os/pc/etherrhine.c @@ -0,0 +1,734 @@ + /* + Via Rhine driver, written for VT6102. + Uses the ethermii to control PHY. + + Currently always copies on both, tx and rx. + rx side could be copy-free, and tx-side might be made + (almost) copy-free by using (possibly) two descriptors (if it allows + arbitrary tx lengths, which it should..): first for alignment and + second for rest of the frame. Rx-part should be worth doing. +*/ + +#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/netif.h" +#include "etherif.h" + +#include "ethermii.h" + +typedef struct Desc Desc; +typedef struct Ctlr Ctlr; + +enum { + Ntxd = 16, + Nrxd = 64, + Nwait = 50, + Ntxstats = 9, + Nrxstats = 8, + BIGSTR = 8192, +}; + +struct Desc { + ulong stat; + ulong size; + ulong addr; + ulong next; + char *buf; + ulong pad[3]; +}; + +struct Ctlr { + Pcidev *pci; + int attached; + int txused; + int txhead; + int txtail; + int rxtail; + ulong port; + + Mii mii; + + ulong txstats[Ntxstats]; + ulong rxstats[Nrxstats]; + + Desc *txd; /* wants to be aligned on 16-byte boundary */ + Desc *rxd; + + QLock attachlck; + Lock lock; +}; + +#define ior8(c, r) (inb((c)->port+(r))) +#define ior16(c, r) (ins((c)->port+(r))) +#define ior32(c, r) (inl((c)->port+(r))) +#define iow8(c, r, b) (outb((c)->port+(r), (int)(b))) +#define iow16(c, r, w) (outs((c)->port+(r), (ushort)(w))) +#define iow32(c, r, l) (outl((c)->port+(r), (ulong)(l))) + +enum Regs { + Eaddr = 0x0, + Rcr = 0x6, + Tcr = 0x7, + Cr = 0x8, + Isr = 0xc, + Imr = 0xe, + McastAddr = 0x10, + RxdAddr = 0x18, + TxdAddr = 0x1C, + Bcr = 0x6e, + RhineMiiPhy = 0x6C, + RhineMiiSr = 0x6D, + RhineMiiCr = 0x70, + RhineMiiAddr = 0x71, + RhineMiiData = 0x72, + Eecsr = 0x74, + ConfigB = 0x79, + ConfigD = 0x7B, + MiscCr = 0x80, + HwSticky = 0x83, + MiscIsr = 0x84, + MiscImr = 0x86, + WolCrSet = 0xA0, + WolCfgSet = 0xA1, + WolCgSet = 0xA3, + WolCrClr = 0xA4, + PwrCfgClr = 0xA5, + WolCgClr = 0xA7, +}; + +enum Rcrbits { + RxErrX = 1<<0, + RxSmall = 1<<1, + RxMcast = 1<<2, + RxBcast = 1<<3, + RxProm = 1<<4, + RxFifo64 = 0<<5, RxFifo32 = 1<<5, RxFifo128 = 2<<5, RxFifo256 = 3<<5, + RxFifo512 = 4<<5, RxFifo768 = 5<<5, RxFifo1024 = 6<<5, + RxFifoStoreForward = 7<<5, +}; + +enum Tcrbits { + TxLoopback0 = 1<<1, + TxLoopback1 = 1<<2, + TxBackoff = 1<<3, + TxFifo128 = 0<<5, TxFifo256 = 1<<5, TxFifo512 = 2<<5, TxFifo1024 = 3<<5, + TxFifoStoreForward = 7<<5, +}; + +enum Crbits { + Init = 1<<0, + Start = 1<<1, + Stop = 1<<2, + RxOn = 1<<3, + TxOn = 1<<4, + Tdmd = 1<<5, + Rdmd = 1<<6, + EarlyRx = 1<<8, + Reserved0 = 1<<9, + FullDuplex = 1<<10, + NoAutoPoll = 1<<11, + Reserved1 = 1<<12, + Tdmd1 = 1<<13, + Rdmd1 = 1<<14, + Reset = 1<<15, +}; + +enum Isrbits { + RxOk = 1<<0, + TxOk = 1<<1, + RxErr = 1<<2, + TxErr = 1<<3, + TxBufUdf = 1<<4, + RxBufLinkErr = 1<<5, + BusErr = 1<<6, + CrcOvf = 1<<7, + EarlyRxInt = 1<<8, + TxFifoUdf = 1<<9, + RxFifoOvf = 1<<10, + TxPktRace = 1<<11, + NoRxbuf = 1<<12, + TxCollision = 1<<13, + PortCh = 1<<14, + GPInt = 1<<15 +}; + +enum Bcrbits { + Dma32 = 0<<0, Dma64 = 1<<0, Dma128 = 2<<0, + Dma256 = 3<<0, Dma512 = 4<<0, Dma1024 = 5<<0, + DmaStoreForward = 7<<0, + DupRxFifo0 = 1<<3, DupRxFifo1 = 1<<4, DupRxFifo2 = 1<<5, + ExtraLed = 1<<6, + MediumSelect = 1<<7, + PollTimer0 = 1<<8, PollTimer1 = 1<<9, PollTimer2 = 1<<10, + DupTxFifo0 = 1<<11, DupTxFifo1 = 1<<12, DupTxFifo2 = 1<<13, +}; + +enum Eecsrbits { + EeAutoLoad = 1<<5, +}; + +enum MiscCrbits { + Timer0Enable= 1<<0, + Timer0Suspend = 1<<1, + HalfDuplexFlowControl = 1<<2, + FullDuplexFlowControl = 1<<3, + Timer1Enable = 1<<8, + ForceSoftReset = 1<<14, +}; + +enum HwStickybits { + StickyDS0 = 1<<0, + StickyDS1 = 1<<1, + WOLEna = 1<<2, + WOLStat = 1<<3, +}; + +enum WolCgbits { + PmeOvr = 1<<7, +}; + +enum Descbits { + OwnNic = 1<<31, /* stat */ + TxAbort = 1<<8, /* stat */ + TxError = 1<<15, /* stat */ + RxChainbuf = 1<<10, /* stat */ + RxChainStart = 1<<9, /* stat */ + RxChainEnd = 1<<8, /* stat */ + Chainbuf = 1<<15, /* size rx & tx*/ + TxDisableCrc = 1<<16, /* size */ + TxChainStart = 1<<21, /* size */ + TxChainEnd = 1<<22, /* size */ + TxInt = 1<<23, /* size */ +}; + +enum ConfigDbits { + BackoffOptional = 1<<0, + BackoffAMD = 1<<1, + BackoffDEC = 1<<2, + BackoffRandom = 1<<3, + PmccTestMode = 1<<4, + PciReadlineCap = 1<<5, + DiagMode = 1<<6, + MmioEnable = 1<<7, +}; + +enum ConfigBbits { + LatencyTimer = 1<<0, + WriteWaitState = 1<<1, + ReadWaitState = 1<<2, + RxArbit = 1<<3, + TxArbit = 1<<4, + NoMemReadline = 1<<5, + NoParity = 1<<6, + NoTxQueuing = 1<<7, +}; + +enum RhineMiiCrbits { + Mdc = 1<<0, + Mdi = 1<<1, + Mdo = 1<<2, + Mdout = 1<<3, + Mdpm = 1<<4, + Wcmd = 1<<5, + Rcmd = 1<<6, + Mauto = 1<<7, +}; + +enum RhineMiiSrbits { + Speed10M = 1<<0, + LinkFail = 1<<1, + PhyError = 1<<3, + DefaultPhy = 1<<4, + ResetPhy = 1<<7, +}; + +enum RhineMiiAddrbits { + Mdone = 1<<5, + Msrcen = 1<<6, + Midle = 1<<7, +}; + +static char * +txstatnames[Ntxstats] = { + "aborts (excess collisions)", + "out of window collisions", + "carrier sense losses", + "fifo underflows", + "invalid descriptor format or underflows", + "system errors", + "reserved", + "transmit errors", + "collisions", +}; + +static char * +rxstatnames[Nrxstats] = { + "receiver errors", + "crc errors", + "frame alignment errors", + "fifo overflows", + "long packets", + "run packets", + "system errors", + "buffer underflows", +}; + +static void +attach(Ether *edev) +{ + Ctlr *ctlr; + Desc *txd, *rxd, *td, *rd; + Mii *mi; + MiiPhy *phy; + int i, s; + + ctlr = edev->ctlr; + qlock(&ctlr->attachlck); + if (ctlr->attached == 0) { + txd = ctlr->txd; + rxd = ctlr->rxd; + for (i = 0; i < Ntxd; ++i) { + td = &txd[i]; + td->next = PCIWADDR(&txd[(i+1) % Ntxd]); + td->buf = xspanalloc(sizeof(Etherpkt)+4, 4, 0); + td->addr = PCIWADDR(td->buf); + td->size = 0; + coherence(); + td->stat = 0; + } + for (i = 0; i < Nrxd; ++i) { + rd = &rxd[i]; + rd->next = PCIWADDR(&rxd[(i+1) % Nrxd]); + rd->buf = xspanalloc(sizeof(Etherpkt)+4, 4, 0); + rd->addr = PCIWADDR(rd->buf); + rd->size = sizeof(Etherpkt)+4; + coherence(); + rd->stat = OwnNic; + } + + ctlr->txhead = ctlr->txtail = ctlr->rxtail = 0; + mi = &ctlr->mii; + miistatus(mi); + phy = mi->curphy; + s = splhi(); + iow32(ctlr, TxdAddr, PCIWADDR(&txd[0])); + iow32(ctlr, RxdAddr, PCIWADDR(&rxd[0])); + iow16(ctlr, Cr, (phy->fd ? FullDuplex : 0) | NoAutoPoll | TxOn | RxOn | Start | Rdmd); + iow16(ctlr, Isr, 0xFFFF); + iow16(ctlr, Imr, 0xFFFF); + iow8(ctlr, MiscIsr, 0xFF); + iow8(ctlr, MiscImr, ~(3<<5)); + splx(s); + } + ctlr->attached++; + qunlock(&ctlr->attachlck); +} + +static void +txstart(Ether *edev) +{ + Ctlr *ctlr; + Desc *txd, *td; + Block *b; + int i, txused, n; + ulong size; + + ctlr = edev->ctlr; + + txd = ctlr->txd; + i = ctlr->txhead; + txused = ctlr->txused; + n = 0; + while (txused < Ntxd) { + if ((b = qget(edev->oq)) == nil) + break; + + td = &txd[i]; + + size = BLEN(b); + memmove(td->buf, b->rp, size); + freeb(b); + td->size = size | TxChainStart | TxChainEnd | TxInt; /* could reduce number of ints here */ + coherence(); + td->stat = OwnNic; + i = (i + 1) % Ntxd; + txused++; + n++; + } + if (n) + iow16(ctlr, Cr, ior16(ctlr, Cr) | Tdmd); + + ctlr->txhead = i; + ctlr->txused = txused; +} + +static void +transmit(Ether *edev) +{ + Ctlr *ctlr; + ctlr = edev->ctlr; + ilock(&ctlr->lock); + txstart(edev); + iunlock(&ctlr->lock); +} + +static void +txcomplete(Ether *edev) +{ + Ctlr *ctlr; + Desc *txd, *td; + int i, txused, j; + ulong stat; + + ctlr = edev->ctlr; + txd = ctlr->txd; + txused = ctlr->txused; + i = ctlr->txtail; + while (txused > 0) { + td = &txd[i]; + stat = td->stat; + + if (stat & OwnNic) + break; + + ctlr->txstats[Ntxstats-1] += stat & 0xF; + for (j = 0; j < Ntxstats-1; ++j) + if (stat & (1<<(j+8))) + ctlr->txstats[j]++; + + i = (i + 1) % Ntxd; + txused--; + } + ctlr->txused = txused; + ctlr->txtail = i; + + if (txused <= Ntxd/2) + txstart(edev); +} + +static void +interrupt(Ureg *, void *arg) +{ + Ether *edev; + Ctlr *ctlr; + ushort isr, misr; + ulong stat; + Desc *rxd, *rd; + int i, n, j; + + edev = (Ether*)arg; + ctlr = edev->ctlr; + iow16(ctlr, Imr, 0); + isr = ior16(ctlr, Isr); + iow16(ctlr, Isr, 0xFFFF); + misr = ior16(ctlr, MiscIsr) & ~(3<<5); /* don't care about used defined ints */ + + if (isr & RxOk) { + Block *b; + int size; + rxd = ctlr->rxd; + i = ctlr->rxtail; + + n = 0; + while ((rxd[i].stat & OwnNic) == 0) { + rd = &rxd[i]; + stat = rd->stat; + for (j = 0; j < Nrxstats; ++j) + if (stat & (1<<j)) + ctlr->rxstats[j]++; + + if (stat & 0xFF) + iprint("rx: %lux\n", stat & 0xFF); + + size = ((rd->stat>>16) & 2047) - 4; + b = iallocb(sizeof(Etherpkt)); + memmove(b->wp, rd->buf, size); + b->wp += size; + etheriq(edev, b, 1); + rd->size = sizeof(Etherpkt)+4; + coherence(); + rd->stat = OwnNic; + i = (i + 1) % Nrxd; + n++; + } + if (n) + iow16(ctlr, Cr, ior16(ctlr, Cr) | Rdmd); + ctlr->rxtail = i; + isr &= ~RxOk; + } + if (isr & TxOk) { + txcomplete(edev); + isr &= ~TxOk; + } + if (isr | misr) + iprint("etherrhine: unhandled irq(s). isr:%x misr:%x\n", isr, misr); + + iow16(ctlr, Imr, 0xFFFF); +} + +static void +promiscuous(void *arg, int enable) +{ + Ether *edev; + Ctlr *ctlr; + + edev = arg; + ctlr = edev->ctlr; + ilock(&ctlr->lock); + iow8(ctlr, Rcr, (ior8(ctlr, Rcr) & ~(RxProm|RxBcast)) | + (enable ? RxProm : RxBcast)); + iunlock(&ctlr->lock); +} + +static int +miiread(Mii *mii, int phy, int reg) +{ + Ctlr *ctlr; + int n; + + ctlr = mii->ctlr; + + n = Nwait; + while (n-- && ior8(ctlr, RhineMiiCr) & (Rcmd | Wcmd)) + microdelay(1); + if (n == Nwait) + iprint("etherrhine: miiread: timeout\n"); + + iow8(ctlr, RhineMiiCr, 0); + iow8(ctlr, RhineMiiPhy, phy); + iow8(ctlr, RhineMiiAddr, reg); + iow8(ctlr, RhineMiiCr, Rcmd); + + n = Nwait; + while (n-- && ior8(ctlr, RhineMiiCr) & Rcmd) + microdelay(1); + if (n == Nwait) + iprint("etherrhine: miiread: timeout\n"); + + n = ior16(ctlr, RhineMiiData); + + return n; +} + +static int +miiwrite(Mii *mii, int phy, int reg, int data) +{ + int n; + Ctlr *ctlr; + + ctlr = mii->ctlr; + + n = Nwait; + while (n-- && ior8(ctlr, RhineMiiCr) & (Rcmd | Wcmd)) + microdelay(1); + if (n == Nwait) + iprint("etherrhine: miiwrite: timeout\n"); + + iow8(ctlr, RhineMiiCr, 0); + iow8(ctlr, RhineMiiPhy, phy); + iow8(ctlr, RhineMiiAddr, reg); + iow16(ctlr, RhineMiiData, data); + iow8(ctlr, RhineMiiCr, Wcmd); + + n = Nwait; + while (n-- && ior8(ctlr, RhineMiiCr) & Wcmd) + microdelay(1); + if (n == Nwait) + iprint("etherrhine: miiwrite: timeout\n"); + + return 0; +} + +/* multicast already on, don't need to do anything */ +static void +multicast(void*, uchar*, int) +{ +} + +static void +shutdown(Ether *edev) +{ + int i; + Ctlr *ctlr = edev->ctlr; + + ilock(&ctlr->lock); + pcisetbme(ctlr->pci); + + iow16(ctlr, Cr, ior16(ctlr, Cr) | Stop); + iow16(ctlr, Cr, ior16(ctlr, Cr) | Reset); + + for (i = 0; i < Nwait; ++i) { + if ((ior16(ctlr, Cr) & Reset) == 0) + break; + delay(5); + } + if (i == Nwait) + iprint("etherrhine: reset timeout\n"); + iunlock(&ctlr->lock); +} + +static void +init(Ether *edev) +{ + Ctlr *ctlr; + MiiPhy *phy; + int i; + + shutdown(edev); + + ctlr = edev->ctlr; + ilock(&ctlr->lock); + iow8(ctlr, Eecsr, ior8(ctlr, Eecsr) | EeAutoLoad); + for (i = 0; i < Nwait; ++i) { + if ((ior8(ctlr, Eecsr) & EeAutoLoad) == 0) + break; + delay(5); + } + if (i == Nwait) + iprint("etherrhine: eeprom autoload timeout\n"); + + for (i = 0; i < Eaddrlen; ++i) + edev->ea[i] = ior8(ctlr, Eaddr + i); + + ctlr->mii.mir = miiread; + ctlr->mii.miw = miiwrite; + ctlr->mii.ctlr = ctlr; + + if(mii(&ctlr->mii, ~0) == 0 || ctlr->mii.curphy == nil){ + iprint("etherrhine: init mii failure\n"); + return; + } + for (i = 0; i < NMiiPhy; ++i) + if (ctlr->mii.phy[i]) + if (ctlr->mii.phy[i]->oui != 0xFFFFF) + ctlr->mii.curphy = ctlr->mii.phy[i]; + + miistatus(&ctlr->mii); + phy = ctlr->mii.curphy; + edev->mbps = phy->speed; + + iow16(ctlr, Imr, 0); + iow16(ctlr, Cr, ior16(ctlr, Cr) | Stop); + iow8(ctlr, Rcr, ior8(ctlr, Rcr) | RxMcast); + + iunlock(&ctlr->lock); +} + +static Pcidev * +rhinematch(ulong) +{ + static int nrhines = 0; + int nfound = 0; + Pcidev *p = nil; + + while (p = pcimatch(p, 0x1106, 0)) + if (p->did == 0x3065) + if (++nfound > nrhines) { + nrhines++; + break; + } + return p; +} +static long +ifstat(Ether* edev, void* a, long n, ulong offset) +{ + int l = 0, i; + char *p; + Ctlr *ctlr; + ctlr = edev->ctlr; + p = malloc(BIGSTR); + + for (i = 0; i < Ntxstats; ++i) + if (txstatnames[i]) + l += snprint(p+l, BIGSTR - l, "tx: %s: %lud\n", txstatnames[i], ctlr->txstats[i]); + + for (i = 0; i < Nrxstats; ++i) + if (rxstatnames[i]) + l += snprint(p+l, BIGSTR - l, "rx: %s: %lud\n", rxstatnames[i], ctlr->rxstats[i]); + +/* + for (i = 0; i < NMiiPhyr; ++i) { + if ((i % 8) == 0) + l += snprint(p + l, BIGSTR - l, "\nmii 0x%02x:", i); + reg=miimir(&ctlr->mii, i); + reg=miimir(&ctlr->mii, i); + l += snprint(p + l, BIGSTR - l, " %4ux", reg); + } + + for (i = 0; i < 0x100; i+=1) { + if ((i % 16) == 0) + l += snprint(p + l, BIGSTR - l, "\nreg 0x%02x:", i); + else if ((i % 2) == 0) + l += snprint(p + l, BIGSTR - l, " "); + reg=ior8(ctlr, i); + l += snprint(p + l, BIGSTR - l, "%02x", reg); + } + l += snprint(p + l, BIGSTR - l, " \n"); +*/ + + + n = readstr(offset, a, n, p); + free(p); + + return n; +} + +static int +pnp(Ether *edev) +{ + Pcidev *p; + Ctlr *ctlr; + ulong port; + ulong size; + + p = rhinematch(edev->port); + if (p == nil) + return -1; + + port = p->mem[0].bar & ~1; + size = p->mem[0].size; + if (ioalloc(port, size, 0, "rhine") < 0) { + print("etherrhine: couldn't allocate port %lud\n", port); + return -1; + } + + if ((ctlr = malloc(sizeof(Ctlr))) == nil) { + print("etherrhine: couldn't allocate memory for ctlr\n"); + return -1; + } + memset(ctlr, 0, sizeof(Ctlr)); + ctlr->txd = xspanalloc(sizeof(Desc) * Ntxd, 16, 0); + ctlr->rxd = xspanalloc(sizeof(Desc) * Nrxd, 16, 0); + + ctlr->pci = p; + ctlr->port = port; + + edev->ctlr = ctlr; + edev->port = ctlr->port; + edev->irq = p->intl; + edev->tbdf = p->tbdf; + + init(edev); + + edev->interrupt = interrupt; + edev->arg = edev; + + edev->attach = attach; + edev->transmit = transmit; + edev->ifstat = ifstat; + edev->promiscuous = promiscuous; + edev->multicast = multicast; + edev->shutdown = shutdown; + return 0; +} + +void +etherrhinelink(void) +{ + addethercard("rhine", pnp); +} diff --git a/os/pc/ethersmc.c b/os/pc/ethersmc.c new file mode 100644 index 00000000..ddd3f729 --- /dev/null +++ b/os/pc/ethersmc.c @@ -0,0 +1,780 @@ +/* + * SMC EtherEZ (SMC91cXX chip) PCMCIA card support. + */ + +#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/netif.h" +#include "etherif.h" + +enum { + IoSize = 0x10, /* port pool size */ + TxTimeout = 150, +}; + +enum { /* PCMCIA related */ + TupleFunce = 0x22, + TfNodeId = 0x04, +}; + +enum { /* bank 0 registers */ + Tcr = 0x0000, /* transmit control */ + Eph = 0x0002, /* ethernet protocol handler */ + Rcr = 0x0004, /* receiver control */ + Counter = 0x0006, /* statistics counter */ + MemInfo = 0x0008, + MemCfg = 0x000A, +}; + +enum { /* bank 1 registers */ + Config = 0x0000, + BaseAddr = 0x0002, + Addr0 = 0x0004, /* ethernet address */ + Addr1 = 0x0006, + Addr2 = 0x0008, + General = 0x000A, + Control = 0x000C, +}; + +enum { /* bank 2 registers */ + MmuCmd = 0x0000, + PktNo = 0x0002, + AllocRes = 0x0003, + FifoPorts = 0x0004, + Pointer = 0x0006, + Data1 = 0x0008, + Interrupt = 0x000C, + IntrMask = 0x000D, +}; + +enum { /* bank 3 registers */ + Mcast0 = 0x0000, + Mcast2 = 0x0002, + Mcast4 = 0x0004, + Mcast6 = 0x0006, + Revision = 0x000A, +}; + +enum { + BankSelect = 0x000E /* bank select register */ +}; + +enum { + BsrMask = 0xFF00, /* mask for chip identification */ + BsrId = 0x3300, +}; + + +enum { /* Tcr values */ + TcrClear = 0x0000, + TcrEnable = 0x0001, /* enable transmit */ + TcrLoop = 0x0002, /* enable internal analogue loopback */ + TcrForceCol = 0x0004, /* force collision on next tx */ + TcrPadEn = 0x0080, /* pad short packets to 64 bytes */ + TcrNoCrc = 0x0100, /* do not append CRC */ + TcrMonCns = 0x0400, /* monitor carrier status */ + TcrFduplx = 0x0800, + TcrStpSqet = 0x1000, + TcrEphLoop = 0x2000, + TcrNormal = TcrEnable, +}; + +enum { /* Eph values */ + EphTxOk = 0x0001, + Eph1Col = 0x0002, /* single collision */ + EphMCol = 0x0004, /* multiple collisions */ + EphTxMcast = 0x0008, /* multicast transmit */ + Eph16Col = 0x0010, /* 16 collisions, tx disabled */ + EphSqet = 0x0020, /* SQE test failed, tx disabled */ + EphTxBcast = 0x0040, /* broadcast tx */ + EphDefr = 0x0080, /* deffered tx */ + EphLatCol = 0x0200, /* late collision, tx disabled */ + EphLostCarr = 0x0400, /* lost carrier, tx disabled */ + EphExcDefr = 0x0800, /* excessive defferals */ + EphCntRol = 0x1000, /* ECR counter(s) rolled over */ + EphRxOvrn = 0x2000, /* receiver overrun, packets dropped */ + EphLinkOk = 0x4000, + EphTxUnrn = 0x8000, /* tx underrun */ +}; + +enum { /* Rcr values */ + RcrClear = 0x0000, + RcrPromisc = 0x0002, + RcrAllMcast = 0x0004, + RcrEnable = 0x0100, + RcrStripCrc = 0x0200, + RcrSoftReset = 0x8000, + RcrNormal = RcrStripCrc | RcrEnable, +}; + +enum { /* Counter value masks */ + CntColMask = 0x000F, /* collisions */ + CntMColMask = 0x00F0, /* multiple collisions */ + CntDtxMask = 0x0F00, /* deferred transmits */ + CntExDtxMask = 0xF000, /* excessively deferred transmits */ + + CntColShr = 1, + CntMColShr = 4, + CntDtxShr = 8, +}; + +enum { /* MemInfo value masks */ + MirTotalMask = 0x00FF, + MirFreeMask = 0xFF00, +}; + +enum { /* Config values */ + CfgIrqSel0 = 0x0002, + CfgIrqSel1 = 0x0004, + CfgDisLink = 0x0040, /* disable 10BaseT link test */ + Cfg16Bit = 0x0080, + CfgAuiSelect = 0x0100, + CfgSetSqlch = 0x0200, + CfgFullStep = 0x0400, + CfgNoWait = 0x1000, + CfgMiiSelect = 0x8000, +}; + +enum { /* Control values */ + CtlStore = 0x0001, /* store to EEPROM */ + CtlReload = 0x0002, /* reload EEPROM into registers */ + CtlEeSelect = 0x0004, /* select registers for reload/store */ + CtlTeEnable = 0x0020, /* tx error detection via eph irq */ + CtlCrEnable = 0x0040, /* counter rollover via eph irq */ + CtlLeEnable = 0x0080, /* link error detection via eph irq*/ + CtlAutoRls = 0x0800, /* auto release mode */ + CtlPowerDn = 0x2000, +}; + +enum { /* MmuCmd values */ + McBusy = 0x0001, + McAlloc = 0x0020, /* | with number of 256 byte packets - 1 */ + McReset = 0x0040, + McRelease = 0x0080, /* dequeue (but not free) current rx packet */ + McFreePkt = 0x00A0, /* dequeue and free current rx packet */ + McEnqueue = 0x00C0, /* enqueue the packet for tx */ + McTxReset = 0x00E0, /* reset transmit queues */ +}; + +enum { /* AllocRes values */ + ArFailed = 0x80, +}; + +enum { /* FifoPorts values */ + FpTxEmpty = 0x0080, + FpRxEmpty = 0x8000, + FpTxMask = 0x007F, + FpRxMask = 0x7F00, +}; + +enum { /* Pointer values */ + PtrRead = 0x2000, + PtrAutoInc = 0x4000, + PtrRcv = 0x8000, +}; + +enum { /* Interrupt values */ + IntRcv = 0x0001, + IntTxError = 0x0002, + IntTxEmpty = 0x0004, + IntAlloc = 0x0008, + IntRxOvrn = 0x0010, + IntEph = 0x0020, +}; + +enum { /* transmit status bits */ + TsSuccess = 0x0001, + Ts16Col = 0x00A0, + TsLatCol = 0x0200, + TsLostCar = 0x0400, +}; + +enum { /* receive status bits */ + RsMcast = 0x0001, + RsTooShort = 0x0400, + RsTooLong = 0x0800, + RsOddFrame = 0x1000, + RsBadCrc = 0x2000, + RsAlgnErr = 0x8000, + RsError = RsAlgnErr | RsBadCrc | RsTooLong | RsTooShort, +}; + +enum { + RxLenMask = 0x07FF, /* significant rx len bits */ + HdrSize = 6, /* packet header length */ + PageSize = 256, /* page length */ +}; + +typedef struct Smc91xx Smc91xx; +struct Smc91xx { + Lock; + ushort rev; + int attached; + Block *txbp; + ulong txtime; + + ulong rovrn; + ulong lcar; + ulong col; + ulong scol; + ulong mcol; + ulong lcol; + ulong dfr; +}; + +#define SELECT_BANK(x) outs(port + BankSelect, x) + +static int +readnodeid(int slot, Ether* ether) +{ + uchar data[Eaddrlen + 1]; + int len; + + len = sizeof(data); + if (pcmcistuple(slot, TupleFunce, TfNodeId, data, len) != len) + return -1; + + if (data[0] != Eaddrlen) + return -1; + + memmove(ether->ea, &data[1], Eaddrlen); + return 0; +} + +static void +chipreset(Ether* ether) +{ + int port; + int i; + + port = ether->port; + + /* reset the chip */ + SELECT_BANK(0); + outs(port + Rcr, RcrSoftReset); + delay(1); + outs(port + Rcr, RcrClear); + outs(port + Tcr, TcrClear); + SELECT_BANK(1); + outs(port + Control, CtlAutoRls | CtlTeEnable | + CtlCrEnable); + + for(i = 0; i < 6; i++) { + outb(port + Addr0 + i, ether->ea[i]); + } + + SELECT_BANK(2); + outs(port + MmuCmd, McReset); +} + +static void +chipenable(Ether* ether) +{ + int port; + + port = ether->port; + SELECT_BANK(0); + outs(port + Tcr, TcrNormal); + outs(port + Rcr, RcrNormal); + SELECT_BANK(2); + outb(port + IntrMask, IntEph | IntRxOvrn | IntRcv); +} + +static void +attach(Ether *ether) +{ + Smc91xx* ctlr; + + ctlr = ether->ctlr; + ilock(ctlr); + + if (ctlr->attached) { + iunlock(ctlr); + return; + } + + chipenable(ether); + ctlr->attached = 1; + iunlock(ctlr); +} + +static void +txstart(Ether* ether) +{ + int port; + Smc91xx* ctlr; + Block* bp; + int len, npages; + int pno; + + /* assumes ctlr is locked and bank 2 is selected */ + /* leaves bank 2 selected on return */ + port = ether->port; + ctlr = ether->ctlr; + + if (ctlr->txbp) { + bp = ctlr->txbp; + ctlr->txbp = 0; + } else { + bp = qget(ether->oq); + if (bp == 0) + return; + + len = BLEN(bp); + npages = (len + HdrSize) / PageSize; + outs(port + MmuCmd, McAlloc | npages); + } + + pno = inb(port + AllocRes); + if (pno & ArFailed) { + outb(port + IntrMask, inb(port + IntrMask) | IntAlloc); + ctlr->txbp = bp; + ctlr->txtime = MACHP(0)->ticks; + return; + } + + outb(port + PktNo, pno); + outs(port + Pointer, PtrAutoInc); + + len = BLEN(bp); + outs(port + Data1, 0); + outb(port + Data1, (len + HdrSize) & 0xFF); + outb(port + Data1, (len + HdrSize) >> 8); + outss(port + Data1, bp->rp, len / 2); + if ((len & 1) == 0) { + outs(port + Data1, 0); + } else { + outb(port + Data1, bp->rp[len - 1]); + outb(port + Data1, 0x20); /* no info what 0x20 means */ + } + + outb(port + IntrMask, inb(port + IntrMask) | + IntTxError | IntTxEmpty); + + outs(port + MmuCmd, McEnqueue); + freeb(bp); +} + +static void +receive(Ether* ether) +{ + int port; + Block* bp; + int pktno, status, len; + + /* assumes ctlr is locked and bank 2 is selected */ + /* leaves bank 2 selected on return */ + port = ether->port; + + pktno = ins(port + FifoPorts); + if (pktno & FpRxEmpty) { + return; + } + + outs(port + Pointer, PtrRead | PtrRcv | PtrAutoInc); + status = ins(port + Data1); + len = ins(port + Data1) & RxLenMask - HdrSize; + + if (status & RsOddFrame) + len++; + + if ((status & RsError) || (bp = iallocb(len)) == 0) { + + if (status & RsAlgnErr) + ether->frames++; + if (status & (RsTooShort | RsTooLong)) + ether->buffs++; + if (status & RsBadCrc) + ether->crcs++; + + outs(port + MmuCmd, McRelease); + return; + } + + /* packet length is padded to word */ + inss(port + Data1, bp->rp, len / 2); + bp->wp = bp->rp + (len & ~1); + + if (len & 1) { + *bp->wp = inb(port + Data1); + bp->wp++; + } + + etheriq(ether, bp, 1); + ether->inpackets++; + outs(port + MmuCmd, McRelease); +} + +static void +txerror(Ether* ether) +{ + int port; + Smc91xx* ctlr; + int save_pkt; + int pktno, status; + + /* assumes ctlr is locked and bank 2 is selected */ + /* leaves bank 2 selected on return */ + port = ether->port; + ctlr = ether->ctlr; + + save_pkt = inb(port + PktNo); + + pktno = ins(port + FifoPorts) & FpTxMask; + outb(port + PktNo, pktno); + outs(port + Pointer, PtrAutoInc | PtrRead); + status = ins(port + Data1); + + if (status & TsLostCar) + ctlr->lcar++; + + if (status & TsLatCol) + ctlr->lcol++; + + if (status & Ts16Col) + ctlr->scol++; + + ether->oerrs++; + + SELECT_BANK(0); + outs(port + Tcr, ins(port + Tcr) | TcrEnable); + + SELECT_BANK(2); + outs(port + MmuCmd, McFreePkt); + + outb(port + PktNo, save_pkt); +} + +static void +eph_irq(Ether* ether) +{ + int port; + Smc91xx* ctlr; + ushort status; + int n; + + /* assumes ctlr is locked and bank 2 is selected */ + /* leaves bank 2 selected on return */ + port = ether->port; + ctlr = ether->ctlr; + + SELECT_BANK(0); + status = ins(port + Eph); + + if (status & EphCntRol) { + /* read the counter register even if we don't need it */ + /* otherwise we will keep getting this interrupt */ + n = ins(port + Counter); + ctlr->col += (n & CntColMask) >> CntColShr; + ctlr->mcol += (n & CntMColMask) >> CntMColShr; + ctlr->dfr += (n & CntDtxMask) >> CntDtxShr; + } + + /* if there was a transmit error, Tcr is disabled */ + outs(port + Tcr, ins(port + Tcr) | TcrEnable); + + /* clear a link error interrupt */ + SELECT_BANK(1); + outs(port + Control, CtlAutoRls); + outs(port + Control, CtlAutoRls | CtlTeEnable | CtlCrEnable); + + SELECT_BANK(2); +} + +static void +transmit(Ether* ether) +{ + Smc91xx* ctlr; + int port, n; + + ctlr = ether->ctlr; + port = ether->port; + ilock(ctlr); + + if (ctlr->txbp) { + n = TK2MS(MACHP(0)->ticks - ctlr->txtime); + if (n > TxTimeout) { + chipreset(ether); + chipenable(ether); + freeb(ctlr->txbp); + ctlr->txbp = 0; + } + iunlock(ctlr); + return; + } + + SELECT_BANK(2); + txstart(ether); + iunlock(ctlr); +} + +static void +interrupt(Ureg*, void *arg) +{ + int port; + Smc91xx* ctlr; + Ether* ether; + int save_bank; + int save_pointer; + int mask, status; + + ether = arg; + port = ether->port; + ctlr = ether->ctlr; + + ilock(ctlr); + save_bank = ins(port + BankSelect); + SELECT_BANK(2); + save_pointer = ins(port + Pointer); + + mask = inb(port + IntrMask); + outb(port + IntrMask, 0); + + while ((status = inb(port + Interrupt) & mask) != 0) { + if (status & IntRcv) { + receive(ether); + } + + if (status & IntTxError) { + txerror(ether); + } + + if (status & IntTxEmpty) { + outb(port + Interrupt, IntTxEmpty); + outb(port + IntrMask, mask & ~IntTxEmpty); + txstart(ether); + mask = inb(port + IntrMask); + } + + if (status & IntAlloc) { + outb(port + IntrMask, mask & ~IntAlloc); + txstart(ether);; + mask = inb(port + IntrMask); + } + + if (status & IntRxOvrn) { + ctlr->rovrn++; + ether->misses++; + outb(port + Interrupt,IntRxOvrn); + } + + if (status & IntEph) + eph_irq(ether); + } + + outb(port + IntrMask, mask); + outs(port + Pointer, save_pointer); + outs(port + BankSelect, save_bank); + iunlock(ctlr); +} + +static void +promiscuous(void* arg, int on) +{ + int port; + Smc91xx *ctlr; + Ether* ether; + ushort x; + + ether = arg; + port = ether->port; + ctlr = ether->ctlr; + + ilock(ctlr); + SELECT_BANK(0); + x = ins(port + Rcr); + if (on) + x |= RcrPromisc; + else + x &= ~RcrPromisc; + + outs(port + Rcr, x); + iunlock(ctlr); +} + +static void +multicast(void* arg, uchar *addr, int on) +{ + int port; + Smc91xx*ctlr; + Ether *ether; + ushort x; + + USED(addr, on); + + ether = arg; + port = ether->port; + ctlr = ether->ctlr; + ilock(ctlr); + + SELECT_BANK(0); + x = ins(port + Rcr); + + if (ether->nmaddr) + x |= RcrAllMcast; + else + x &= ~RcrAllMcast; + + outs(port + Rcr, x); + iunlock(ctlr); +} + +static long +ifstat(Ether* ether, void* a, long n, ulong offset) +{ + static char *chiprev[] = { + [3] "92", + [5] "95", + [7] "100", + [8] "100-FD", + [9] "110", + }; + + Smc91xx* ctlr; + char* p; + int r, len; + char* s; + + if (n == 0) + return 0; + + ctlr = ether->ctlr; + p = malloc(READSTR); + + s = 0; + if (ctlr->rev > 0) { + r = ctlr->rev >> 4; + if (r < nelem(chiprev)) + s = chiprev[r]; + + if (r == 4) { + if ((ctlr->rev & 0x0F) >= 6) + s = "96"; + else + s = "94"; + } + } + + len = snprint(p, READSTR, "rev: 91c%s\n", (s) ? s : "???"); + len += snprint(p + len, READSTR - len, "rxovrn: %uld\n", ctlr->rovrn); + len += snprint(p + len, READSTR - len, "lcar: %uld\n", ctlr->lcar); + len += snprint(p + len, READSTR - len, "col: %uld\n", ctlr->col); + len += snprint(p + len, READSTR - len, "16col: %uld\n", ctlr->scol); + len += snprint(p + len, READSTR - len, "mcol: %uld\n", ctlr->mcol); + len += snprint(p + len, READSTR - len, "lcol: %uld\n", ctlr->lcol); + len += snprint(p + len, READSTR - len, "dfr: %uld\n", ctlr->dfr); + USED(len); + + n = readstr(offset, a, n, p); + free(p); + + return n; +} + +static int +reset(Ether* ether) +{ + int port; + int i, x; + char* type; + Smc91xx* ctlr; + int slot; + uchar ea[Eaddrlen]; + + if (ether->irq == 0) + ether->irq = 9; + + if (ether->port == 0) + ether->port = 0x100; + + type = "8020"; + for(i = 0; i < ether->nopt; i++) { + if (cistrncmp(ether->opt[i], "id=", 3)) + continue; + type = ðer->opt[i][3]; + break; + } + + if ((slot = pcmspecial(type, ether)) < 0) + return -1; + + if (ioalloc(ether->port, IoSize, 0, "smc91cXX") < 0) { + pcmspecialclose(slot); + return -1; + } + + ether->ctlr = malloc(sizeof(Smc91xx)); + ctlr = ether->ctlr; + if (ctlr == 0) { + iofree(ether->port); + pcmspecialclose(slot); + } + + ilock(ctlr); + ctlr->rev = 0; + ctlr->txbp = nil; + ctlr->attached = 0; + ctlr->rovrn = 0; + ctlr->lcar = 0; + ctlr->col = 0; + ctlr->scol = 0; + ctlr->mcol = 0; + ctlr->lcol = 0; + ctlr->dfr = 0; + + port = ether->port; + + SELECT_BANK(1); + if ((ins(port + BankSelect) & BsrMask) != BsrId) { + outs(port + Control, 0); /* try powering up the chip */ + delay(55); + } + + outs(port + Config, ins(port + Config) | Cfg16Bit); + x = ins(port + BaseAddr); + + if (((ins(port + BankSelect) & BsrMask) != BsrId) || + ((x >> 8) == (x & 0xFF))) { + iunlock(ctlr); + iofree(port); + pcmspecialclose(slot); + return -1; + } + + SELECT_BANK(3); + ctlr->rev = ins(port + Revision) & 0xFF; + + memset(ea, 0, Eaddrlen); + if (memcmp(ea, ether->ea, Eaddrlen) == 0) { + if (readnodeid(slot, ether) < 0) { + print("Smc91cXX: cannot find ethernet address\n"); + iunlock(ctlr); + iofree(port); + pcmspecialclose(slot); + return -1; + } + } + + chipreset(ether); + + ether->attach = attach; + ether->transmit = transmit; + ether->interrupt = interrupt; + ether->ifstat = ifstat; + ether->promiscuous = promiscuous; + ether->multicast = multicast; + ether->arg = ether; + iunlock(ctlr); + return 0; +} + +void +ethersmclink(void) +{ + addethercard("smc91cXX", reset); +} diff --git a/os/pc/etherwavelan.c b/os/pc/etherwavelan.c new file mode 100644 index 00000000..6fa9f250 --- /dev/null +++ b/os/pc/etherwavelan.c @@ -0,0 +1,196 @@ +/* Pci/pcmcia code for wavelan.c */ + +#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/netif.h" +#include "etherif.h" + +#include "wavelan.h" + +static int +wavelanpcmciareset(Ether *ether) +{ + int i; + char *p; + Ctlr *ctlr; + + if((ctlr = malloc(sizeof(Ctlr))) == nil) + return -1; + + ilock(ctlr); + ctlr->ctlrno = ether->ctlrno; + + if (ether->port==0) + ether->port=WDfltIOB; + ctlr->iob = ether->port; + + if (ether->irq==0) + ether->irq=WDfltIRQ; + + if (ioalloc(ether->port,WIOLen,0,"wavelan")<0){ + // print("#l%d: port 0x%lx in use\n", + // ether->ctlrno, ether->port); + goto abort1; + } + + /* + * If id= is specified, card must match. Otherwise try generic. + */ + ctlr->slot = -1; + for(i=0; i<ether->nopt; i++){ + if(cistrncmp(ether->opt[i], "id=", 3) == 0){ + if((ctlr->slot = pcmspecial(ðer->opt[i][3], ether)) < 0) + goto abort; + break; + } + } + if(ctlr->slot == -1){ + for (i=0; wavenames[i]; i++) + if((ctlr->slot = pcmspecial(wavenames[i], ether))>=0) + break; + if(!wavenames[i]){ + DEBUG("no wavelan found\n"); + goto abort; + } + } + + // DEBUG("#l%d: port=0x%lx irq=%ld\n", + // ether->ctlrno, ether->port, ether->irq); + + if(wavelanreset(ether, ctlr) < 0){ + abort: + iofree(ether->port); + abort1: + iunlock(ctlr); + free(ctlr); + ether->ctlr = nil; + return -1; + } + + for(i = 0; i < ether->nopt; i++){ + if(p = strchr(ether->opt[i], '=')) + *p = ' '; + w_option(ctlr, ether->opt[i], strlen(ether->opt[i])); + } + + iunlock(ctlr); + return 0; +} + +static struct { + int vid; + int did; +} wavelanpci[] = { + 0x1260, 0x3873, /* Intersil Prism2.5 */ +}; + +static Ctlr *ctlrhead, *ctlrtail; + +static void +wavelanpciscan(void) +{ + int i; + ulong pa; + Pcidev *p; + Ctlr *ctlr; + + p = nil; + while(p = pcimatch(p, 0, 0)){ + for(i=0; i<nelem(wavelanpci); i++) + if(p->vid == wavelanpci[i].vid && p->did == wavelanpci[i].did) + break; + if(i==nelem(wavelanpci)) + continue; + + /* + * On the Prism, bar[0] is the memory-mapped register address (4KB), + */ + if(p->mem[0].size != 4096){ + print("wavelanpci: %.4ux %.4ux: unlikely mmio size\n", p->vid, p->did); + continue; + } + + ctlr = malloc(sizeof(Ctlr)); + ctlr->pcidev = p; + pa = upamalloc(p->mem[0].bar&~0xF, p->mem[0].size, 0); + if(pa == 0){ + print("wavelanpci: %.4ux %.4ux: upamalloc 0x%.8lux %d failed\n", p->vid, p->did, p->mem[0].bar&~0xF, p->mem[0].size); + free(ctlr); + continue; + } + ctlr->mmb = (ushort*)KADDR(pa); + if(ctlrhead != nil) + ctlrtail->next = ctlr; + else + ctlrhead = ctlr; + ctlrtail = ctlr; + pcisetbme(p); + } +} + +static int +wavelanpcireset(Ether *ether) +{ + int i; + char *p; + Ctlr *ctlr; + + if(ctlrhead == nil) + wavelanpciscan(); + + /* + * Allow plan9.ini to set vid, did? + */ + for(ctlr=ctlrhead; ctlr!=nil; ctlr=ctlr->next) + if(ctlr->active == 0) + break; + if(ctlr == nil) + return -1; + + ctlr->active = 1; + ilock(ctlr); + ether->irq = ctlr->pcidev->intl; + ether->tbdf = ctlr->pcidev->tbdf; + + /* + * Really hard reset. + */ + csr_outs(ctlr, WR_PciCor, 0x0080); + delay(250); + csr_outs(ctlr, WR_PciCor, 0x0000); + delay(500); + for(i=0; i<2*10; i++){ + if(!(csr_ins(ctlr, WR_Cmd)&WCmdBusy)) + break; + delay(100); + } + if(i >= 2*10) + print("wavelan pci %.4ux %.4ux: reset timeout %.4ux\n", + ctlr->pcidev->vid, ctlr->pcidev->did, csr_ins(ctlr, WR_Cmd)); + + if(wavelanreset(ether, ctlr) < 0){ + iunlock(ctlr); + ether->ctlr = nil; + return -1; + } + + for(i = 0; i < ether->nopt; i++){ + if(p = strchr(ether->opt[i], '=')) + *p = ' '; + w_option(ctlr, ether->opt[i], strlen(ether->opt[i])); + } + iunlock(ctlr); + return 0; +} + +void +etherwavelanlink(void) +{ + addethercard("wavelan", wavelanpcmciareset); + addethercard("wavelanpci", wavelanpcireset); +} diff --git a/os/pc/flashif.h b/os/pc/flashif.h new file mode 100644 index 00000000..b5391798 --- /dev/null +++ b/os/pc/flashif.h @@ -0,0 +1,82 @@ +typedef struct Flash Flash; +typedef struct Flashpart Flashpart; +typedef struct Flashregion Flashregion; + +/* + * logical partitions + */ +enum { + Maxflashpart = 8 +}; + +struct Flashpart { + char* name; + ulong start; + ulong end; +}; + +enum { + Maxflashregion = 8 +}; + +/* + * physical erase block regions + */ +struct Flashregion { + int n; /* number of blocks in region */ + ulong start; /* physical base address (allowing for banks) */ + ulong end; + ulong erasesize; +}; + +/* + * structure defining a flash memory card + */ +struct Flash { + QLock; /* interlock on flash operations */ + Flash* next; + + /* the following are filled in by devflash before Flash.reset called */ + char* name; + void* addr; + ulong size; + int (*reset)(Flash*); + + /* the following are filled in by the reset routine */ + int (*eraseall)(Flash*); + int (*erasezone)(Flash*, int); + int (*write)(Flash*, ulong, void*, long); /* writes of correct width and alignment */ + int (*suspend)(Flash*); + int (*resume)(Flash*); + + /* the following might be filled in by either archflashreset or the reset routine */ + int nr; + Flashregion regions[Maxflashregion]; + + uchar id; /* flash manufacturer ID */ + uchar devid; /* flash device ID */ + int width; /* bytes per flash line */ + int erasesize; /* size of erasable unit (accounting for width) */ + void* data; /* flash type routines' private storage, or nil */ + ulong unusable; /* bit mask of unusable sections */ + Flashpart part[Maxflashpart]; /* logical partitions */ + int protect; /* software protection */ +}; + +/* + * called by link routine of driver for specific flash type: arguments are + * conventional name for card type/model, and card driver's reset routine. + */ +void addflashcard(char*, int (*)(Flash*)); + +/* + * called by devflash.c:/^flashreset; if flash exists, + * sets type, address, and size in bytes of flash + * and returns 0; returns -1 if flash doesn't exist + */ +int archflashreset(char*, void**, long*); + +/* + * enable/disable write protect + */ +void archflashwp(int); diff --git a/os/pc/flashzpc.c b/os/pc/flashzpc.c new file mode 100644 index 00000000..c360f7bf --- /dev/null +++ b/os/pc/flashzpc.c @@ -0,0 +1,371 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#include "flashif.h" + +#define FLASHMEM 0xfff80000 +#define FLASHPGSZ 0x40000 +#define FLASHBKSZ (FLASHPGSZ>>2) +#define LOG2FPGSZ 18 +#define FLASHEND (FLASHMEM+FLASHPGSZ) +#define SYSREG0 0x78 +#define SYSREG1 0x878 + +/* Intel28F016SA flash memory family (8SA and (DD)32SA as well) in byte mode */ + +/* + * word mode does not work - a 2 byte write to a location results in the lower address + * byte being unchanged (4 byte writes are even stranger) and no indication of error. + * Perhaps the bridge is interfering with the address lines. + * Looks like the BIOS code doesn't use it either but that's not certain. + */ + +/* + * When port 0x78 bit 2 is set to 1 (flash device 1) + * 0xfff80000-0xfffbffff seems to be free but has dos block headers + * 0xfffc0000-0xfffdffff seems to be the DOS P: drive + * 0xfffe0000-0xffffffff is the BIOS + * When port 0x78 bit 2 is set to 0 (flash device 0) + * 0xfff80000-0xffffffff is a mixture of used and unused DOS blocks and apparently + * many copies of the BIOS + * + * In the absence of information from Ziatech and to preserve the BIOS and DOS sections, + * this driver only uses the first range for a total of 8 x 0x40000 = 2Mb + */ + +enum { + DQ7 = 0x80, + DQ6 = 0x40, + DQ5 = 0x20, + DQ4 = 0x10, + DQ3 = 0x08, + DQ2 = 0x04, + DQ1 = 0x02, + DQ0 = 0x01, +}; + +enum { + FLRDM = 0xFF, /* read */ + FLWTM = 0x10, /* write/program */ + FLCLR = 0x50, /* clear SR */ + FLBE1 = 0x20, /* block erase */ + FLBE2 = 0xD0, /* block erase */ + FLRSR = 0x70, /* read SR */ + FLDID = 0x90, /* read id */ +}; + +#define DPRINT if(0)print +#define EPRINT if(1)print + +static int +zpcwait(uchar *p, ulong ticks) +{ + uchar csr; + + ticks += m->ticks+1; + while((*p & DQ7) != DQ7){ + sched(); + if(m->ticks >= ticks){ + EPRINT("flash: timed out: %8.8lux\n", (ulong)*p); + return -1; + } + } + csr = *p; + if(csr & (DQ5|DQ4|DQ3)){ + EPRINT("flash: DQ5 error: %8.8lux %8.8lux\n", p, (ulong)csr); + return 0; + } + return 1; +} + +static int +eraseall(Flash *f) +{ + uchar r; + uchar *p; + int i, j, s; + + DPRINT("flash: erase all\n"); + for (i = 0; i < 8; i++) { /* page */ + /* set page */ + r = inb(SYSREG0); + r &= 0x8f; + r |= i<<4; + outb(SYSREG0, r); + p = (uchar *)f->addr; + for (j = 0; j < 4; j++) { /* block within page */ + DPRINT("erasing page %d block %d addr %lux\n", i, j, p); + s = splhi(); + *p = FLBE1; + *p = FLBE2; + splx(s); + if(zpcwait(p, MS2TK(16*1000)) <= 0){ + *p = FLCLR; /* clr SR */ + *p = FLRDM; /* read mode */ + f->unusable = ~0; + return -1; + } + *p = FLCLR; + *p = FLRDM; + p += FLASHPGSZ>>2; + } + } + return 0; +} + +static int +erasezone(Flash *f, int zone) +{ + uchar r; + uchar *p; + int s, pg, blk; + + DPRINT("flash: erase zone %d\n", zone); + if(zone & ~31) { + EPRINT("flash: bad erasezone %d\n", zone); + return -1; /* bad zone */ + } + pg = zone>>2; + blk = zone&3; + /* set page */ + r = inb(SYSREG0); + r &= 0x8f; + r |= pg<<4; + outb(SYSREG0, r); + p = (uchar *)f->addr + blk*(FLASHPGSZ>>2); + DPRINT("erasing zone %d pg %d blk %d addr %lux\n", zone, pg, blk, p); + s = splhi(); + *p = FLBE1; + *p = FLBE2; + splx(s); + if(zpcwait(p, MS2TK(8*1000)) <= 0){ + *p = FLCLR; + *p = FLRDM; /* reset */ + f->unusable |= 1<<zone; + return -1; + } + *p = FLCLR; + *p = FLRDM; + return 0; +} + +static int +readx(Flash *f, ulong offset, void *buf, long n) +{ + uchar r; + ulong pg, o; + long m; + uchar *p = buf; + + pg = offset>>LOG2FPGSZ; + o = offset&(FLASHPGSZ-1); + while (n > 0) { + if (pg < 0 || pg > 7) { + EPRINT("flash: bad read %ld %ld\n", offset, n); + return -1; + } + /* set page */ + r = inb(SYSREG0); + r &= 0x8f; + r |= pg<<4; + outb(SYSREG0, r); + if (o+n > FLASHPGSZ) + m = FLASHPGSZ-o; + else + m = n; + DPRINT("flash: read page %ld offset %lux buf %lux n %ld\n", pg, o, p-(uchar*)buf, m); + memmove(p, (uchar *)f->addr + o, m); + p += m; + n -= m; + pg++; + o = 0; + } + return 0; +} + +static int +writex(Flash *f, ulong offset, void *buf, long n) +{ + int i, s; + uchar r; + ulong pg, o; + long m; + uchar *a, *v = buf; + + DPRINT("flash: writex\n"); + pg = offset>>LOG2FPGSZ; + o = offset&(FLASHPGSZ-1); + while (n > 0) { + if (pg < 0 || pg > 7) { + EPRINT("flash: bad write %ld %ld\n", offset, n); + return -1; + } + /* set page */ + r = inb(SYSREG0); + r &= 0x8f; + r |= pg<<4; + outb(SYSREG0, r); + if (o+n > FLASHPGSZ) + m = FLASHPGSZ-o; + else + m = n; + a = (uchar *)f->addr + o; + DPRINT("flash: write page %ld offset %lux buf %lux n %ld\n", pg, o, v-(uchar*)buf, m); + for (i = 0; i < m; i++, v++, a++) { + if (~*a & *v) { + EPRINT("flash: bad write: %lux %lux -> %lux\n", (ulong)a, (ulong)*a, (ulong)*v); + return -1; + } + if (*a == *v) + continue; + s = splhi(); + *a = FLWTM; /* program */ + *a = *v; + splx(s); + microdelay(8); + if(zpcwait(a, 5) <= 0){ + *a = FLCLR; /* clr SR */ + *a = FLRDM; /* read mode */ + f->unusable = ~0; + return -1; + } + *a = FLCLR; + *a = FLRDM; + if (*a != *v) { + EPRINT("flash: write %lux %lux -> %lux failed\n", (ulong)a, (ulong)*a, (ulong)*v); + return -1; + } + } + n -= m; + pg++; + o = 0; + } + return 0; +} + +#ifdef ZERO +/* search the whole of flash */ +static void +flashsearch(Flash *f) +{ + int d, m, p, b, n, i; + uchar r, buf[64]; + + for (d = 0; d < 2; d++) { /* flash device */ + r = inb(SYSREG0); + r &= 0xfb; + r |= (d<<2); + outb(SYSREG0, r); + for (m = 0; m < 2; m++) { /* lower/upper mem */ + if (m == 0) + f->addr = (void *)FLASHMEM; + else + f->addr = (void *)FLASHEND; + for (p = 0; p < 8; p++) { /* page */ + for (b = 0; b < 4; b++) { /* block */ + n = readx(f, (4*p+b)*FLASHBKSZ, buf, 64); + if (n != 0) { + print("bad read in search %d\n", n); + goto end; + } + print("%d %d %d %d : ", d, m, p, b); + if (buf[0] == 0x5a && buf[1] == 0x54) { /* DOS block */ + n = 0; + for (i = 0; i < 64; i++) { + if (buf[i] == 0xff) + n++; + } + if (n == 64-28) + print("un"); + print("used dos\n"); + } + else if (buf[0] == 0x55 && buf[1] == 0xaa) + print("bios start\n"); + else + print("bios ?\n"); + } + } + } + } +end: + r = inb(SYSREG0); + r |= 4; + outb(SYSREG0, r); + f->addr = (void *)FLASHMEM; +} +#endif + +static int +reset(Flash *f) +{ + uchar r; + int s; + ulong pa; + Pcidev *bridge; + + /* get bridge device */ + bridge = pcimatch(nil, 0x8086, 0x7000); /* Intel PIIX3 ISA bridge device */ + if (bridge == nil) { + EPRINT("flash : failed to find bridge device\n"); + return 1; + } + /* enable extended BIOS and read/write */ + s = splhi(); + r = pcicfgr8(bridge, 0x4e); + r |= 0x84; + pcicfgw8(bridge, 0x4e, r); + splx(s); + /* set system register bits */ + r = inb(SYSREG0); + r |= 0x86; /* chip enable, non-BIOS part, set r/w */ + outb(SYSREG0, r); + /* + * might have to grab memory starting at PADDR(FLASHMEM) ie 0x7ff80000 + * because if this is mapped via virtual address FLASHMEM we would get a + * a kernel panic in mmukmap(). + * va = 0xfff80000 pa = 0xfff80000 for flash + * va = 0xfff80000 pa = 0x7ff80000 if lower memory grabbed by anything + */ + /* + * upafree(FLASHMEM, FLASHPGSZ); + * pa = upamalloc(FLASHMEM, FLASHPGSZ, 0); + * if (pa != FLASHMEM) + * error + */ + pa = mmukmap(FLASHMEM, FLASHMEM, FLASHPGSZ); + if (pa != FLASHEND) { + EPRINT("failed to map flash memory"); + return 1; + } +/* + pa = mmukmap(FLASHEND, FLASHEND, FLASHPGSZ); + if (pa != 0) { + EPRINT("failed to map flash memory"); + return 1; + } +*/ + f->id = 0x0089; /* can't use autoselect: might be running in flash */ + f->devid = 0x66a0; + f->read = readx; + f->write = writex; + f->eraseall = eraseall; + f->erasezone = erasezone; + f->suspend = nil; + f->resume = nil; + f->width = 1; /* must be 1 since devflash.c must not read directly */ + f->erasesize = 64*1024; + *(uchar*)f->addr = FLCLR; /* clear status registers */ + *(uchar*)f->addr = FLRDM; /* reset to read mode */ + return 0; +} + +void +flashzpclink(void) +{ + addflashcard("DD28F032SA", reset); +} diff --git a/os/pc/floppy.h b/os/pc/floppy.h new file mode 100644 index 00000000..f84c9eae --- /dev/null +++ b/os/pc/floppy.h @@ -0,0 +1,183 @@ +typedef struct FController FController; +typedef struct FDrive FDrive; +typedef struct FType FType; + +static void floppyintr(Ureg*); +static int floppyon(FDrive*); +static void floppyoff(FDrive*); +static void floppysetdef(FDrive*); + +/* + * a floppy drive + */ +struct FDrive +{ + FType *t; /* floppy type */ + int dt; /* drive type */ + int dev; + + ulong lasttouched; /* time last touched */ + int cyl; /* current arm position */ + int confused; /* needs to be recalibrated */ + int vers; + int maxtries; /* max read attempts before Eio */ + + int tcyl; /* target cylinder */ + int thead; /* target head */ + int tsec; /* target sector */ + long len; /* size of xfer */ + + uchar *cache; /* track cache */ + int ccyl; + int chead; +}; + +/* + * controller for 4 floppys + */ +struct FController +{ + QLock; /* exclusive access to the contoller */ + + int ndrive; + FDrive *d; /* the floppy drives */ + FDrive *selected; + int rate; /* current rate selected */ + uchar cmd[14]; /* command */ + int ncmd; /* # command bytes */ + uchar stat[14]; /* command status */ + int nstat; /* # status bytes */ + int confused; /* controler needs to be reset */ + Rendez r; /* wait here for command termination */ + int motor; /* bit mask of spinning disks */ +}; + +/* + * floppy types (all MFM encoding) + */ +struct FType +{ + char *name; + int dt; /* compatible drive type */ + int bytes; /* bytes/sector */ + int sectors; /* sectors/track */ + int heads; /* number of heads */ + int steps; /* steps per cylinder */ + int tracks; /* tracks/disk */ + int gpl; /* intersector gap length for read/write */ + int fgpl; /* intersector gap length for format */ + int rate; /* rate code */ + + /* + * these depend on previous entries and are set filled in + * by floppyinit + */ + int bcode; /* coded version of bytes for the controller */ + long cap; /* drive capacity in bytes */ + long tsize; /* track size in bytes */ +}; +/* bits in the registers */ +enum +{ + /* status registers a & b */ + Psra= 0x3f0, + Psrb= 0x3f1, + + /* digital output register */ + Pdor= 0x3f2, + Fintena= 0x8, /* enable floppy interrupt */ + Fena= 0x4, /* 0 == reset controller */ + + /* main status register */ + Pmsr= 0x3f4, + Fready= 0x80, /* ready to be touched */ + Ffrom= 0x40, /* data from controller */ + Ffloppybusy= 0x10, /* operation not over */ + + /* data register */ + Pfdata= 0x3f5, + Frecal= 0x07, /* recalibrate cmd */ + Fseek= 0x0f, /* seek cmd */ + Fsense= 0x08, /* sense cmd */ + Fread= 0x66, /* read cmd */ + Freadid= 0x4a, /* read track id */ + Fspec= 0x03, /* set hold times */ + Fwrite= 0x45, /* write cmd */ + Fformat= 0x4d, /* format cmd */ + Fmulti= 0x80, /* or'd with Fread or Fwrite for multi-head */ + Fdumpreg= 0x0e, /* dump internal registers */ + + /* digital input register */ + Pdir= 0x3F7, /* disk changed port (read only) */ + Pdsr= 0x3F7, /* data rate select port (write only) */ + Fchange= 0x80, /* disk has changed */ + + /* status 0 byte */ + Drivemask= 3<<0, + Seekend= 1<<5, + Codemask= (3<<6)|(3<<3), + Cmdexec= 1<<6, + + /* status 1 byte */ + Overrun= 0x10, +}; + + +static void +pcfloppyintr(Ureg *ur, void *a) +{ + USED(a); + + floppyintr(ur); +} + +void +floppysetup0(FController *fl) +{ + fl->ndrive = 0; + if(ioalloc(Psra, 6, 0, "floppy") < 0) + return; + if(ioalloc(Pdir, 1, 0, "floppy") < 0){ + iofree(Psra); + return; + } + fl->ndrive = 2; +} + +void +floppysetup1(FController *fl) +{ + uchar equip; + + /* + * read nvram for types of floppies 0 & 1 + */ + equip = nvramread(0x10); + if(fl->ndrive > 0){ + fl->d[0].dt = (equip >> 4) & 0xf; + floppysetdef(&fl->d[0]); + } + if(fl->ndrive > 1){ + fl->d[1].dt = equip & 0xf; + floppysetdef(&fl->d[1]); + } + intrenable(IrqFLOPPY, pcfloppyintr, fl, BUSUNKNOWN, "floppy"); +} + +/* + * eject disk ( unknown on safari ) + */ +void +floppyeject(FDrive *dp) +{ + floppyon(dp); + dp->vers++; + floppyoff(dp); +} + +int +floppyexec(char *a, long b, int c) +{ + USED(a, b, c); + return b; +} diff --git a/os/pc/fns.h b/os/pc/fns.h new file mode 100644 index 00000000..028162ac --- /dev/null +++ b/os/pc/fns.h @@ -0,0 +1,162 @@ +#include "../port/portfns.h" +void aamloop(int); +Dirtab* addarchfile(char*, int, long(*)(Chan*,void*,long,vlong), long(*)(Chan*,void*,long,vlong)); +void archinit(void); +void bootargs(ulong); +int cistrcmp(char*, char*); +int cistrncmp(char*, char*, int); +#define clearmmucache() /* x86 doesn't have one */ +void clockintr(Ureg*, void*); +void (*coherence)(void); +void cpuid(char*, int*, int*); +int cpuidentify(void); +void cpuidprint(void); +void (*cycles)(uvlong*); +void delay(int); +int dmacount(int); +int dmadone(int); +void dmaend(int); +int dmainit(int, int); +long dmasetup(int, void*, long, int); +void dumpregs(Ureg*); +#define evenaddr(x) /* x86 doesn't care */ +void fpinit(void); +void fpoff(void); +void fprestore(FPU*); +void fpsave(FPU*); +ulong fpstatus(void); +ulong getcr0(void); +ulong getcr2(void); +ulong getcr3(void); +ulong getcr4(void); +char* getconf(char*); +void guesscpuhz(int); +int i8042auxcmd(int); +int i8042auxcmdval(int); +void i8042auxenable(void (*)(int, int)); +int i8042auxdetect(void); +void i8042reset(void); +void i8250console(void); +void i8253enable(void); +void i8253init(void); +void i8253link(void); +uvlong i8253read(uvlong*); +void i8253timerset(uvlong); +void i8259init(void); +int i8259isr(int); +int i8259enable(Vctl*); +int i8259vecno(int); +int i8259disable(int); +void idle(void); +#define idlehands() /* nothing to do in the runproc */ +int inb(int); +void insb(int, void*, int); +ushort ins(int); +void inss(int, void*, int); +ulong inl(int); +void insl(int, void*, int); +int intrdisable(int, void (*)(Ureg *, void *), void*, int, char*); +void intrenable(int, void (*)(Ureg*, void*), void*, int, char*); +void iofree(int); +void ioinit(void); +int iounused(int, int); +int ioalloc(int, int, int, char*); +int ioreserve(int, int, int, char*); +int iprint(char*, ...); +int isaconfig(char*, int, ISAConf*); +int isvalid_va(void*); +void kbdenable(void); +void kbdinit(void); +void kdbenable(void); +#define kmapinval() +void lapicclock(Ureg*, void*); +void lapictimerset(uvlong); +void lgdt(ushort[3]); +void lidt(ushort[3]); +void links(void); +void ltr(ulong); +void mach0init(void); +void machinit(void); +void mathinit(void); +void mb386(void); +void mb586(void); +void meminit(ulong); +#define mmuflushtlb(pdb) putcr3(pdb) +void mmuinit(void); +ulong mmukmap(ulong, ulong, int); +int mmukmapsync(ulong); +ulong* mmuwalk(ulong*, ulong, int, int); +uchar nvramread(int); +void nvramwrite(int, uchar); +void outb(int, int); +void outsb(int, void*, int); +void outs(int, ushort); +void outss(int, void*, int); +void outl(int, ulong); +void outsl(int, void*, int); +int pciscan(int, Pcidev**); +ulong pcibarsize(Pcidev*, int); +int pcicfgr8(Pcidev*, int); +int pcicfgr16(Pcidev*, int); +int pcicfgr32(Pcidev*, int); +void pcicfgw8(Pcidev*, int, int); +void pcicfgw16(Pcidev*, int, int); +void pcicfgw32(Pcidev*, int, int); +void pciclrbme(Pcidev*); +void pciclrioe(Pcidev*); +int pcigetpms(Pcidev*); +void pcihinv(Pcidev*); +uchar pciipin(Pcidev*, uchar); +Pcidev* pcimatch(Pcidev*, int, int); +Pcidev* pcimatchtbdf(int); +void pcireset(void); +void pcisetbme(Pcidev*); +void pcisetioe(Pcidev*); +int pcisetpms(Pcidev*, int); +void pcmcisread(PCMslot*); +int pcmcistuple(int, int, int, void*, int); +PCMmap* pcmmap(int, ulong, int, int); +int pcmspecial(char*, ISAConf*); +int (*_pcmspecial)(char *, ISAConf *); +void pcmspecialclose(int); +void (*_pcmspecialclose)(int); +void pcmunmap(int, PCMmap*); +void poolinit(void); +void poolsizeinit(void); +void procsave(Proc*); +void procsetup(Proc*); +void putcr3(ulong); +void putcr4(ulong); +void rdmsr(int, vlong*); +ulong rdtsc32(void); +void screeninit(void); +int screenprint(char*, ...); /* debugging */ +void (*screenputs)(char*, int); +#define segflush(a,n) +void syncclock(void); +uvlong tscticks(uvlong*); +void trapenable(int, void (*)(Ureg*, void*), void*, char*); +void trapinit(void); +ulong _tas(ulong*); +ulong umbmalloc(ulong, int, int); +void umbfree(ulong, int); +ulong umbrwmalloc(ulong, int, int); +void umbrwfree(ulong, int); +ulong upamalloc(ulong, int, int); +void upafree(ulong, int); +void vectortable(void); +void wrmsr(ulong, ulong); +int xchgw(ushort*, int); +ulong kzeromap(ulong, ulong, int); +void nmiscreen(void); +int kbdinready(void); + +#define waserror() (up->nerrlab++, setlabel(&up->errlab[up->nerrlab-1])) +#define getcallerpc(x) (((ulong*)(x))[-1]) +#define KADDR(a) ((void*)((ulong)(a)|KZERO)) +#define PADDR(a) ((ulong)(a)&~KZERO) + +#define dcflush(a, b) +#define clockcheck(); +#define dumplongs(x, y, z) +#define setpanic() diff --git a/os/pc/fpi.h b/os/pc/fpi.h new file mode 100644 index 00000000..7a368d18 --- /dev/null +++ b/os/pc/fpi.h @@ -0,0 +1,61 @@ +typedef int Word; +typedef unsigned long Single; +typedef struct { + unsigned long l; + unsigned long h; +} Double; + +enum { + FractBits = 28, + CarryBit = 0x10000000, + HiddenBit = 0x08000000, + MsBit = HiddenBit, + NGuardBits = 3, + GuardMask = 0x07, + LsBit = (1<<NGuardBits), + + SingleExpBias = 127, + SingleExpMax = 255, + DoubleExpBias = 1023, + DoubleExpMax = 2047, + + ExpBias = DoubleExpBias, + ExpInfinity = DoubleExpMax, +}; + +typedef struct { + unsigned char s; + short e; + long l; /* 0000FFFFFFFFFFFFFFFFFFFFFFFFFGGG */ + long h; /* 0000HFFFFFFFFFFFFFFFFFFFFFFFFFFF */ +} Internal; + +#define IsWeird(n) ((n)->e >= ExpInfinity) +#define IsInfinity(n) (IsWeird(n) && (n)->h == HiddenBit && (n)->l == 0) +#define SetInfinity(n) ((n)->e = ExpInfinity, (n)->h = HiddenBit, (n)->l = 0) +#define IsNaN(n) (IsWeird(n) && (((n)->h & ~HiddenBit) || (n)->l)) +#define SetQNaN(n) ((n)->s = 0, (n)->e = ExpInfinity, \ + (n)->h = HiddenBit|(LsBit<<1), (n)->l = 0) +#define IsZero(n) ((n)->e == 1 && (n)->h == 0 && (n)->l == 0) +#define SetZero(n) ((n)->e = 1, (n)->h = 0, (n)->l = 0) + +/* + * fpi.c + */ +extern void fpiround(Internal *); +extern void fpiadd(Internal *, Internal *, Internal *); +extern void fpisub(Internal *, Internal *, Internal *); +extern void fpimul(Internal *, Internal *, Internal *); +extern void fpidiv(Internal *, Internal *, Internal *); +extern int fpicmp(Internal *, Internal *); +extern void fpinormalise(Internal*); + +/* + * fpimem.c + */ +extern void fpis2i(Internal *, void *); +extern void fpid2i(Internal *, void *); +extern void fpiw2i(Internal *, void *); +extern void fpii2s(void *, Internal *); +extern void fpii2d(void *, Internal *); +extern void fpii2w(Word *, Internal *); diff --git a/os/pc/fpi387.c b/os/pc/fpi387.c new file mode 100644 index 00000000..d516bcf5 --- /dev/null +++ b/os/pc/fpi387.c @@ -0,0 +1,743 @@ +/* + * Copyright © 1999 Vita Nuova Limited + * + * this doesn't attempt to implement 387 floating-point properties + * that aren't visible in the Inferno environment. in particular, + * all arithmetic is done in double precision, not extended precision. + * furthermore, the FP trap status isn't updated. + */ + +#ifdef TEST +#include <u.h> +#include <libc.h> +#include <ureg.h> +#include "fpi.h" +#include "tst.h" +#else +#include <u.h> +#include "ureg.h" +#include "fpi.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#endif + +#define fabs Fabs + +typedef struct FPI FPI; + +struct FPI { + char* name; + void (*f)(Ureg*, int, void*, Internal*, Internal*); + int dstf; +}; + +enum { + RndNearest = 0, + RndDown, + RndUp, + Rnd0, + + C0 = 1<<8, + C1 = 1<<9, + C2 = 1<<10, + C3 = 1<<14, +}; + + +int fpemudebug = 0; + +static Internal fpconst[7] = { /* indexed by op&7 */ + /* s, e, l, h */ + {0, 0x3FF, 0x00000000, 0x08000000}, /* 1 */ + {0, 0x400, 0x0BCD1B8A, 0x0D49A784}, /* l2t */ + {0, 0x3FF, 0x095C17F0, 0x0B8AA3B2}, /* l2e */ + {0, 0x400, 0x022168C2, 0x0C90FDAA}, /* pi */ + {0, 0x3FD, 0x04FBCFF7, 0x09A209A8}, /* lg2 */ + {0, 0x3FE, 0x07D1CF79, 0x0B17217F}, /* ln2 */ + {0, 0x1, 0x00000000, 0x00000000}, /* z */ +}; + +static Internal *fpstk(int i); +#define ST(x) (*fpstk((x))) + +#define I387 (up->env->fpu) + +/* BUG: check fetch (not worthwhile in Inferno) */ +#define getubyte(a) (*(uchar*)(a)) +#define getuword(a) (*(ushort*)(a)) +#define getulong(a) (*(ulong*)(a)) + +static void +popfp(void) +{ + ushort *s; + + s = &I387.status; + *s = (*s & ~0x3800) | ((*s + 0x0800) & 0x3800); +} + +static void +pushfp(void) +{ + ushort *s; + + s = &I387.status; + *s = (*s & ~0x3800) | ((*s + 0x3800) & 0x3800); +} + +static Internal * +fpstk(int i) +{ + return (Internal*)I387.istack[(i+(I387.status>>11))&7]; +} + +static void +fldc(Ureg*, int op, void*, Internal*, Internal *d) +{ + *d = fpconst[op&7]; +} + +static void +fabs(Ureg*, int, void*, Internal*, Internal *d) +{ + d->s = 0; +} + +static void +fchs(Ureg*, int, void*, Internal*, Internal *d) +{ + d->s ^= 1; +} + +static void +fadd(Ureg*, int, void*, Internal *s, Internal *d) +{ + Internal l, r; + + l = *s; + r = *d; + (l.s == r.s? fpiadd: fpisub)(&l, &r, d); +} + +static void +fsub(Ureg*, int, void*, Internal *s, Internal *d) +{ + Internal l, r; + + l = *s; + r = *d; + l.s ^= 1; + (l.s == r.s? fpiadd: fpisub)(&l, &r, d); +} + +static void +fsubr(Ureg*, int, void*, Internal *s, Internal *d) +{ + Internal l, r; + + l = *s; + r = *d; + r.s ^= 1; + (l.s == r.s? fpiadd: fpisub)(&r, &l, d); +} + +static void +fmul(Ureg*, int, void*, Internal *s, Internal *d) +{ + Internal l, r; + + l = *s; + r = *d; + fpimul(&l, &r, d); +} + +static void +fdiv(Ureg*, int, void*, Internal *s, Internal *d) +{ + Internal l, r; + + l = *s; + r = *d; + fpidiv(&l, &r, d); +} + +static void +fdivr(Ureg*, int, void*, Internal *s, Internal *d) +{ + Internal l, r; + + l = *s; + r = *d; + fpidiv(&r, &l, d); +} + +static void +fcom(Ureg*, int, void*, Internal *s, Internal *d) +{ + int i; + ushort *p; + + p = &I387.status; + if(IsWeird(s) || IsWeird(d)){ + *p |= C0|C2|C3; + /* BUG: should trap if not masked */ + return; + } + *p &= ~(C0|C2|C3); + i = fpicmp(d, s); + if(i < 0) + *p |= C0; + else if(i == 0) + *p |= C3; +} + +static void +fpush(Ureg*, int op, void*, Internal*, Internal*) +{ + Internal *p; + + p = &ST(op & 7); + pushfp(); + ST(0) = *p; +} + +static void +fmov(Ureg*, int, void*, Internal *s, Internal *d) +{ + *d = *s; +} + +static void +fmovr(Ureg*, int, void*, Internal *s, Internal *d) +{ + *s = *d; +} + +static void +fxch(Ureg*, int, void*, Internal *s, Internal *d) +{ + Internal t; + + t = *s; *s = *d; *d = t; +} + +static void +frstor(Ureg*, int, void *s, Internal*, Internal*) +{ + validaddr(s, 108, 0); + memmove(&I387, s, 108); +} + +static void +fsave(Ureg*, int, void *d, Internal*, Internal*) +{ + validaddr(d, 108, 1); + memmove(d, &I387, 108); + I387.control = 0x037F; + I387.status = 0; + I387.tag = 0; +} + +static void +fstsw(Ureg*, int, void *d, Internal*, Internal*) +{ + validaddr(d, 2, 1); + *(short*)d = I387.status; +} + +static void +fldenv(Ureg*, int, void *s, Internal*, Internal*) +{ + validaddr(s, 28, 0); + memmove(&I387, s, 28); +} + +static void +fldcw(Ureg*, int, void *s, Internal*, Internal*) +{ + validaddr(s, 2, 0); + I387.control = *(short*)s; +} + +static void +fstenv(Ureg*, int, void *d, Internal*, Internal*) +{ + validaddr(d, 4*7, 1); + memmove(d, &I387, 4*7); +} + +static void +fstcw(Ureg*, int, void *d, Internal*, Internal*) +{ + validaddr(d, 2, 1); + *(short*)d = I387.control; +} + +static void +fincstp(Ureg*, int, void*, Internal*, Internal*) +{ + popfp(); +} + +static void +fdecstp(Ureg*, int, void*, Internal*, Internal*) +{ + pushfp(); +} + +static void +fscale(Ureg*, int, void*, Internal *s, Internal *d) +{ + Word w; + + fpii2w(&w, s); /* should truncate towards zero ... */ + d->e += w; +} + +static void +fstswax(Ureg *ur, int, void*, Internal*, Internal*) +{ + ur->ax = (ur->ax & ~0xFFFF) | (I387.status & 0xFFFF); +} + +static void +ftst(Ureg*, int, void*, Internal*, Internal *d) +{ + ushort *p; + + p = &I387.status; + if(IsWeird(d)){ + *p |= C0|C2|C3; + return; + } + *p &= ~(C0|C2|C3); + fpinormalise(d); + if(IsZero(d)) + *p |= C3; + else if(d->s) + *p |=C0; +} + +static void +frndint(Ureg*, int, void*, Internal*, Internal *d) +{ + fpiround(d); /* BUG: doesn't look at rounding mode */ +} + +static void +fnop(Ureg*, int, void*, Internal*, Internal*) +{ +} + +enum { + Fpop1= 1<<0, + Fpop2 = 1<<1, + Fload = 1<<2, +}; + +/* + * %e - effective address - Mod R/M value + * %f - floating point register F0-F7 - from Mod R/M register + */ + +static void fload(Ureg*, int, void*, Internal*, Internal*); +static void fstore(Ureg*, int, void*, Internal*, Internal*); + +#define X(a,b) (((a)<<2)|(b)) + +static FPI optab1[4][4] = { /* normal mod r/m operand */ +[0] { + [0] {"FLDENV %e", fldenv, 0}, + [1] {"FLDCW %e", fldcw, 0}, + [2] {"FSTENV %e", fstenv, 0}, + [3] {"FSTCW %e", fstcw, 0}, + }, +[1] { + [1] {"FMOVX %e,F0", nil, Fload}, + [3] {"FMOVXP F0,%e", nil, Fpop1}, + }, +[2] { + [0] {"FRSTOR %e", frstor, 0}, + [2] {"FSAVE %e", fsave, 0}, + [3] {"FSTSW %e", fstsw, 0}, + }, +[3] { + [0] {"FMOVB %e", nil, 0}, + [1] {"FMOVV %e,F0", nil, Fload}, + [2] {"FMOVBP %e", nil, Fpop1}, + [3] {"FMOVVP F0,%e", nil, Fpop1}, + }, +}; + +#undef X + +static FPI optab2a[1<<3] = { /* A=0 */ +[0] {"FADDx %e,F0", fadd, 0}, +[1] {"FMULx %e,F0", fmul, 0}, +[2] {"FCOMx %e,F0", fcom, 0}, +[3] {"FCOMxP %e,F0", fcom, Fpop1}, +[4] {"FSUBx %e,F0", fsub, 0}, +[5] {"FSUBRx %e,F0", fsubr, 0}, /* ?? */ +[6] {"FDIVx %e,F0", fdiv, 0}, +[7] {"FDIVRx %e,F0", fdivr, 0}, /* ?? */ +}; + +static FPI optab2b[1<<2] = { /* A=1, B=0,2,3 */ +[0] {"FMOVx %e,F0", fload, Fload}, +[2] {"FMOVx F0,%e", fstore, 0}, +[3] {"FMOVxP F0,%e", fstore, Fpop1}, +}; + +#define X(d,P,B) ((d<<4)|(P<<3)|B) + +static FPI optab3a[1<<5] = { /* A=0 */ +[X(0,0,0)] {"FADDD %f,F0", fadd, 0}, +[X(1,0,0)] {"FADDD F0,%f", fadd, 0}, +[X(1,1,0)] {"FADDDP F0,%f", fadd, Fpop1}, +[X(0,0,1)] {"FMULD %f,F0", fmul, 0}, +[X(1,0,1)] {"FMULD F0,%f", fmul, 0}, +[X(1,1,1)] {"FMULDP F0,%f", fmul, Fpop1}, +[X(0,0,2)] {"FCOMD %f,F0", fcom, 0}, +[X(0,0,3)] {"FCOMDP %f,F0", fcom, Fpop1}, +[X(1,1,3)] {"FCOMDPP", fcom, Fpop1|Fpop2}, +[X(0,0,4)] {"FSUBD %f,F0", fsub, 0}, +[X(1,0,4)] {"FSUBRD F0,%f", fsubr, 0}, +[X(1,1,4)] {"FSUBRDP F0,%f", fsubr, Fpop1}, +[X(0,0,5)] {"FSUBRD %f,F0", fsubr, 0}, +[X(1,0,5)] {"FSUBD F0,%f", fsub, 0}, +[X(1,1,5)] {"FSUBDP F0,%f", fsub, Fpop1}, +[X(0,1,5)] {"FUCOMPP", fcom, Fpop1|Fpop2}, +[X(0,0,6)] {"FDIVD %f,F0", fdiv, 0}, +[X(1,0,6)] {"FDIVRD F0,%f", fdivr, 0}, +[X(1,1,6)] {"FDIVRDP F0,%f", fdivr, Fpop1}, +[X(0,0,7)] {"FDIVRD %f,F0", fdivr, 0}, +[X(1,0,7)] {"FDIVD F0,%f", fdiv, 0}, +[X(1,1,7)] {"FDIVDP F0,%f", fdiv, Fpop1}, +}; + +static FPI optab3b[1<<5] = { /* A=1 */ +[X(0,0,0)] {"FMOVD %f,F0", fmov, Fload}, +[X(0,0,1)] {"FXCHD %f,F0", fxch, 0}, +[X(0,0,2)] {"FNOP", fnop, 0}, /* F0 only */ +[X(1,0,0)] {"FFREED %f", fnop, 0}, +[X(1,0,2)] {"FMOVD F0,%f", fmovr, 0}, +[X(1,0,3)] {"FMOVDP F0,%f", fmovr, Fpop1}, +[X(1,1,4)] {"FSTSW AX", fstswax, 0}, +[X(1,0,4)] {"FUCOMD %f,F0", fcom, 0}, +[X(1,0,5)] {"FUCOMDP %f,F0", fcom, Fpop1}, +}; + +#undef X + +static FPI optab4[1<<6] = { +[0x00] {"FCHS", fchs, 0}, +[0x01] {"FABS", fabs, 0}, +[0x04] {"FTST", ftst, 0}, +[0x05] {"FXAM", nil, 0}, +[0x08] {"FLD1", fldc, Fload}, +[0x09] {"FLDL2T", fldc, Fload}, +[0x0a] {"FLDL2E", fldc, Fload}, +[0x0b] {"FLDPI", fldc, Fload}, +[0x0c] {"FLDLG2", fldc, Fload}, +[0x0d] {"FLDLN2", fldc, Fload}, +[0x0e] {"FLDZ", fldc, Fload}, +[0x10] {"F2XM1", nil, 0}, +[0x11] {"FYL2X", nil, 0}, +[0x12] {"FPTAN", nil, 0}, +[0x13] {"FPATAN", nil, 0}, +[0x14] {"FXTRACT", nil, 0}, +[0x15] {"FPREM1", nil, 0}, +[0x16] {"FDECSTP", fdecstp, 0}, +[0x17] {"FINCSTP", fincstp, 0}, +[0x18] {"FPREM", nil, 0}, +[0x19] {"FYL2XP1", nil, 0}, +[0x1a] {"FSQRT", nil, 0}, +[0x1b] {"FSINCOS", nil, 0}, +[0x1c] {"FRNDINT", frndint, 0}, +[0x1d] {"FSCALE", fscale, 0}, +[0x1e] {"FSIN", nil, 0}, +[0x1f] {"FCOS", nil, 0}, +}; + +static void +loadr32(void *s, Internal *d) +{ + validaddr(s, 4, 0); + fpis2i(d, s); +} + +static void +loadi32(void *s, Internal *d) +{ + validaddr(s, 4, 0); + fpiw2i(d, s); +} + +static void +loadr64(void *s, Internal *d) +{ + validaddr(s, 8, 0); + fpid2i(d, s); +} + +static void +loadi16(void *s, Internal *d) +{ + Word w; + + validaddr(s, 2, 0); + w = *(short*)s; + fpiw2i(d, &w); +} + +static void (*loadf[4])(void*, Internal*) ={ + loadr32, loadi32, loadr64, loadi16 +}; + +static void +storer32(Internal s, void *d) +{ + validaddr(d, 4, 1); + fpii2s(d, &s); +} + +static void +storei32(Internal s, void *d) +{ + validaddr(d, 4, 1); + fpii2w(d, &s); +} + +static void +storer64(Internal s, void *d) +{ + validaddr(d, 8, 1); + fpii2d(d, &s); +} + +static void +storei16(Internal s, void *d) +{ + Word w; + + validaddr(d, 2, 1); + fpii2w(&w, &s); + if((short)w != w) + ; /* overflow */ + *(short*)d = w; +} + +static void (*storef[4])(Internal, void*) ={ + storer32, storei32, storer64, storei16 +}; + +static void +fload(Ureg*, int op, void *mem, Internal*, Internal *d) +{ + (*loadf[(op>>9)&3])(mem, d); +} + +static void +fstore(Ureg*, int op, void *mem, Internal *s, Internal*) +{ + (*storef[(op>>9)&3])(*s, mem); +} + +#define REG(x) (*(ulong*)(((char*)ur)+roff[(x)])) +#define offsetof(X) ((ulong)&((Ureg*)0)->X) + +static int roff[] = { + offsetof(ax), + offsetof(cx), + offsetof(dx), + offsetof(bx), + offsetof(ecode), /* ksp */ + offsetof(bp), + offsetof(si), + offsetof(di), +}; + +static long +getdisp(Ureg *ur, int mod, int rm) +{ + uchar c; + long disp; + + if(mod > 2) + return 0; + disp = 0; + if(mod == 1) { + c = getubyte(ur->pc++); + if(c&0x80) + disp = c|(~0<<8); + else + disp = c; + } else if(mod == 2 || rm == 5) { + disp = getulong(ur->pc); + ur->pc += 4; + } + if(mod || rm != 5) + disp += REG(rm); /* base */ + return disp; +} + +static ulong +modrm(Ureg *ur, uchar c) +{ + uchar rm, mod; + int reg; + ulong base; + + mod = (c>>6)&3; + rm = c&7; + if(mod == 3) /* register */ + error("sys: fpemu: invalid addr mode"); + /* no 16-bit mode */ + if(rm == 4) { /* scummy sib byte */ + c = getubyte(ur->pc++); + reg = (c>>3)&0x07; /* index */ + base = getdisp(ur, mod, c&7); + if(reg != 4) + base += (REG(reg) << (c>>6)); /* index */ + if(fpemudebug>1) + print("ur=#%lux sib=#%x reg=%d mod=%d base=%d basev=#%lux sp=%lux\n", ur, c, reg, mod, c&7, base, ur->usp); + return base; + } + if(rm == 5 && mod == 0){ + ur->pc += 4; + return getulong(ur->pc-4); + } + return getdisp(ur, mod, rm); +} + +static void * +ea(Ureg *ur, uchar op) +{ + ulong addr; + + addr = modrm(ur, op); + I387.operand = addr; + if(fpemudebug>1) + print("EA=#%lux\n", addr); + return (void*)addr; +} + +void +fpi387(Ureg *ur) +{ + int op, i; + ulong pc; + FPenv *ufp; + FPI *fp; + Internal tmp, *s, *d; + void *mem; + char buf[60]; + + ur->ecode = (ulong)&ur->sp; /* BUG: TEMPORARY compensation for incorrect Ureg for kernel mode */ + ufp = &up->env->fpu; /* because all the state is in Osenv, it need not be saved/restored */ + if(ufp->fpistate != FPACTIVE) { + ufp->fpistate = FPACTIVE; + ufp->control = 0x037f; + ufp->status = 0; + ufp->tag = 0; + ufp->oselector = 0x17; + } + while((op = getubyte(ur->pc)) >= 0xd8 && op <= 0xdf || op == 0x9B){ + if(op == 0x9B){ /* WAIT */ + ur->pc++; + continue; + } + if(ufp->control & ufp->status & 0x3F) + ufp->status |= 0x8000; + else + ufp->status &= 0x7FFF; + pc = ur->pc; + op = (op<<8) | getubyte(pc+1); + ufp->selector = ur->cs; + ufp->r4 = op-0xD800; + ur->pc += 2; + mem = nil; + s = nil; + d = nil; + /* decode op, following table 10.2.4 in i486 handbook */ + i = op & 0xFFE0; + if(i == 0xD9E0){ + fp = &optab4[op&0x1F]; + s = &ST(0); + if(fp->dstf & Fload) + pushfp(); + d = &ST(0); + } else if(i == 0xDBE0){ + i = op & 0x1F; + if(i == 2){ /* FCLEX */ + ufp->status &= 0x7f00; + continue; + } else if(i == 3){ /* FINIT */ + ufp->control = 0x037f; + ufp->status = 0; + ufp->tag = 0; + continue; + } + fp = nil; + } else if((op & 0xF8C0) == 0xD8C0){ + i = ((op>>6)&030)|((op>>3)&7); + if(op & (1<<8)){ + fp = &optab3b[i]; + s = &ST(op&7); + if(fp->dstf & Fload) + pushfp(); + d = &ST(0); + } else { + fp = &optab3a[i]; + i = op & 7; + if(op & (1<<10)){ + s = &ST(0); + d = &ST(i); + }else{ + s = &ST(i); + d = &ST(0); + } + } + } else if((op & 0xF920) == 0xD920){ + mem = ea(ur, op&0xFF); + fp = &optab1[(op>>9)&3][(op>>3)&3]; + } else { + mem = ea(ur, op&0xFF); + if(op & (1<<8)){ + /* load/store */ + fp = &optab2b[(op>>3)&7]; + if(fp->dstf & Fload){ + pushfp(); + d = &ST(0); + } else + s = &ST(0); + } else { + /* mem OP reg */ + fp = &optab2a[(op>>3)&7]; + (*loadf[(op>>9)&3])(mem, &tmp); + s = &tmp; + d = &ST(0); + } + } + if(fp == nil || fp->f == nil){ + if(fp == nil || fp->name == nil) + snprint(buf, sizeof(buf), "sys: fp: pc=%lux invalid fp 0x%.4x", pc, op); + else + snprint(buf, sizeof(buf), "sys: fp: pc=%lux unimp fp 0x%.4x (%s)", pc, op, fp->name); + error(buf); + } + if(fpemudebug) + print("%8.8lux %.4x %s\n", pc, op, fp->name); + (*fp->f)(ur, op, mem, s, d); + if(fp->dstf & Fpop1){ + popfp(); + if(fp->dstf & Fpop2) + popfp(); + } + if(anyhigher()) + sched(); + } +} diff --git a/os/pc/fpsave.s b/os/pc/fpsave.s new file mode 100644 index 00000000..8bf9448e --- /dev/null +++ b/os/pc/fpsave.s @@ -0,0 +1,9 @@ +TEXT FPsave(SB), 1, $0 /* save FPU environment without waiting */ + MOVL fpu+0(FP), AX + FSTENV 0(AX) + RET + +TEXT FPrestore(SB), 1, $0 /* restore FPU environment without waiting */ + MOVL fpu+0(FP), AX + FLDENV 0(AX) + RET diff --git a/os/pc/i8250.c b/os/pc/i8250.c new file mode 100644 index 00000000..c625013e --- /dev/null +++ b/os/pc/i8250.c @@ -0,0 +1,328 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +/* + * INS8250 uart + */ +enum +{ + /* + * register numbers + */ + Data= 0, /* xmit/rcv buffer */ + Iena= 1, /* interrupt enable */ + Ircv= (1<<0), /* for char rcv'd */ + Ixmt= (1<<1), /* for xmit buffer empty */ + Irstat=(1<<2), /* for change in rcv'er status */ + Imstat=(1<<3), /* for change in modem status */ + Istat= 2, /* interrupt flag (read) */ + Fenabd=(3<<6), /* on if fifo's enabled */ + Fifoctl=2, /* fifo control (write) */ + Fena= (1<<0), /* enable xmit/rcv fifos */ + Ftrig= (1<<6), /* trigger after 4 input characters */ + Fclear=(3<<1), /* clear xmit & rcv fifos */ + Format= 3, /* byte format */ + Bits8= (3<<0), /* 8 bits/byte */ + Stop2= (1<<2), /* 2 stop bits */ + Pena= (1<<3), /* generate parity */ + Peven= (1<<4), /* even parity */ + Pforce=(1<<5), /* force parity */ + Break= (1<<6), /* generate a break */ + Dra= (1<<7), /* address the divisor */ + Mctl= 4, /* modem control */ + Dtr= (1<<0), /* data terminal ready */ + Rts= (1<<1), /* request to send */ + Ri= (1<<2), /* ring */ + Inton= (1<<3), /* turn on interrupts */ + Loop= (1<<4), /* loop back */ + Lstat= 5, /* line status */ + Inready=(1<<0), /* receive buffer full */ + Oerror=(1<<1), /* receiver overrun */ + Perror=(1<<2), /* receiver parity error */ + Ferror=(1<<3), /* rcv framing error */ + Outready=(1<<5), /* output buffer empty */ + Mstat= 6, /* modem status */ + Ctsc= (1<<0), /* clear to send changed */ + Dsrc= (1<<1), /* data set ready changed */ + Rire= (1<<2), /* rising edge of ring indicator */ + Dcdc= (1<<3), /* data carrier detect changed */ + Cts= (1<<4), /* complement of clear to send line */ + Dsr= (1<<5), /* complement of data set ready line */ + Ring= (1<<6), /* complement of ring indicator line */ + Dcd= (1<<7), /* complement of data carrier detect line */ + Scratch=7, /* scratchpad */ + Dlsb= 0, /* divisor lsb */ + Dmsb= 1, /* divisor msb */ + + Serial= 0, + Modem= 1, +}; + +typedef struct Uart Uart; +struct Uart +{ + int port; + uchar sticky[8]; /* sticky write register values */ + int nofifo; + + void (*rx)(int); /* routine to take a received character */ + int (*tx)(void); /* routine to get a character to transmit */ + + ulong frame; + ulong overrun; +}; + +static Uart i8250uart[1]; + +#define UartFREQ 1843200 + +#define i8250regw(u, r, v) outb((u)->port+(r), (u)->sticky[(r)]|(v)) +#define i8250regr(u, r) inb((u)->port+(r)) + +/* + * set the baud rate by calculating and setting the baudrate + * generator constant. This will work with fairly non-standard + * baud rates. + */ +static void +i8250setbaud(Uart* uart, int rate) +{ + ulong brconst; + + brconst = (UartFREQ+8*rate-1)/(16*rate); + + i8250regw(uart, Format, Dra); + outb(uart->port+Dmsb, (brconst>>8) & 0xff); + outb(uart->port+Dlsb, brconst & 0xff); + i8250regw(uart, Format, 0); +} + +/* + * toggle DTR + */ +static void +i8250dtr(Uart* uart, int n) +{ + if(n) + uart->sticky[Mctl] |= Dtr; + else + uart->sticky[Mctl] &= ~Dtr; + i8250regw(uart, Mctl, 0); +} + +/* + * toggle RTS + */ +static void +i8250rts(Uart* uart, int n) +{ + if(n) + uart->sticky[Mctl] |= Rts; + else + uart->sticky[Mctl] &= ~Rts; + i8250regw(uart, Mctl, 0); +} + +/* + * Enable/disable FIFOs (if possible). + */ +static void +i8250fifo(Uart* uart, int n) +{ + int i, s; + + if(uart->nofifo) + return; + + s = splhi(); + + /* reset fifos */ + i8250regw(uart, Fifoctl, Fclear); + + /* empty buffer and interrupt conditions */ + for(i = 0; i < 16; i++){ + if(i8250regr(uart, Istat)) + {} + if(i8250regr(uart, Data)) + {} + } + + /* turn on fifo */ + if(n){ + i8250regw(uart, Fifoctl, Fena|Ftrig); + + if((i8250regr(uart, Istat) & Fenabd) == 0){ + /* didn't work, must be an earlier chip type */ + uart->nofifo = 1; + } + } + + splx(s); +} + +#ifdef notdef +static void +i8250intr(Ureg*, void* arg) +{ + Uart *uart; + int ch; + int s, l, loops; + + uart = arg; + for(loops = 0; loops < 1024; loops++){ + s = i8250regr(uart, Istat); + switch(s & 0x3F){ + case 6: /* receiver line status */ + l = i8250regr(uart, Lstat); + if(l & Ferror) + uart->frame++; + if(l & Oerror) + uart->overrun++; + break; + + case 4: /* received data available */ + case 12: + ch = inb(uart->port+Data); + if(uart->rx) + (*uart->rx)(ch & 0x7F); + break; + + case 2: /* transmitter empty */ + ch = -1; + if(uart->tx) + ch = (*uart->tx)(); + if(ch != -1) + outb(uart->port+Data, ch); + break; + + case 0: /* modem status */ + i8250regr(uart, Mstat); + break; + + default: + if(s&1) + return; + print("weird modem interrupt #%2.2ux\n", s); + break; + } + } + panic("i8250intr: 0x%2.2ux\n", i8250regr(uart, Istat)); +} +#endif /* notdef */ + +/* + * turn on a port's interrupts. set DTR and RTS + */ +static void +i8250enable(Uart* uart) +{ + /* + * turn on interrupts + */ + uart->sticky[Iena] = 0; +#ifdef notdef + if(uart->tx) + uart->sticky[Iena] |= Ixmt; + if(uart->rx) + uart->sticky[Iena] |= Ircv|Irstat; +#endif /* notdef */ + + /* + * turn on DTR and RTS + */ + i8250dtr(uart, 1); + i8250rts(uart, 1); + i8250fifo(uart, 1); + + i8250regw(uart, Iena, 0); +} + +void +i8250special(int port, void (*rx)(int), int (*tx)(void), int baud) +{ + Uart *uart = &i8250uart[0]; + + if(uart->port) + return; + + switch(port){ + + case 0: + uart->port = 0x3F8; +#ifdef notdef + intrenable(VectorUART0, i8250intr, uart, BUSUNKNOWN); +#endif /* notdef */ + break; + + case 1: + uart->port = 0x2F8; +#ifdef notdef + intrenable(VectorUART1, i8250intr, uart, BUSUNKNOWN); +#endif /* notdef */ + break; + + default: + return; + } + + /* + * set rate to 9600 baud. + * 8 bits/character. + * 1 stop bit. + * interrupts enabled. + */ + i8250setbaud(uart, 9600); + uart->sticky[Format] = Bits8; + i8250regw(uart, Format, 0); + uart->sticky[Mctl] |= Inton; + i8250regw(uart, Mctl, 0x0); + + uart->rx = rx; + uart->tx = tx; + i8250enable(uart); + if(baud) + i8250setbaud(uart, baud); +} + +int +i8250getc(void) +{ + Uart *uart = &i8250uart[0]; + + if(i8250regr(uart, Lstat) & Inready) + return inb(uart->port+Data); + return 0; +} + +void +i8250putc(int c) +{ + Uart *uart = &i8250uart[0]; + int i; + + for(i = 0; i < 100; i++){ + if(i8250regr(uart, Lstat) & Outready) + break; + delay(1); + } + outb(uart->port+Data, c); +} + +void +i8250puts(char* s, int n) +{ + int x; + + x = splhi(); + while(n--){ + if(*s == '\n') + i8250putc('\r'); + i8250putc(*s++); + } + splx(x); +} diff --git a/os/pc/i8253.c b/os/pc/i8253.c new file mode 100644 index 00000000..d7cff39e --- /dev/null +++ b/os/pc/i8253.c @@ -0,0 +1,314 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +/* + * 8253 timer + */ +enum +{ + T0cntr= 0x40, /* counter ports */ + T1cntr= 0x41, /* ... */ + T2cntr= 0x42, /* ... */ + Tmode= 0x43, /* mode port (control word register) */ + T2ctl= 0x61, /* counter 2 control port */ + + /* commands */ + Latch0= 0x00, /* latch counter 0's value */ + Load0l= 0x10, /* load counter 0's lsb */ + Load0m= 0x20, /* load counter 0's msb */ + Load0= 0x30, /* load counter 0 with 2 bytes */ + + Latch1= 0x40, /* latch counter 1's value */ + Load1l= 0x50, /* load counter 1's lsb */ + Load1m= 0x60, /* load counter 1's msb */ + Load1= 0x70, /* load counter 1 with 2 bytes */ + + Latch2= 0x80, /* latch counter 2's value */ + Load2l= 0x90, /* load counter 2's lsb */ + Load2m= 0xa0, /* load counter 2's msb */ + Load2= 0xb0, /* load counter 2 with 2 bytes */ + + /* 8254 read-back command: everything > pc-at has an 8254 */ + Rdback= 0xc0, /* readback counters & status */ + Rdnstat=0x10, /* don't read status */ + Rdncnt= 0x20, /* don't read counter value */ + Rd0cntr=0x02, /* read back for which counter */ + Rd1cntr=0x04, + Rd2cntr=0x08, + + /* modes */ + ModeMsk=0xe, + Square= 0x6, /* periodic square wave */ + Trigger=0x0, /* interrupt on terminal count */ + Sstrobe=0x8, /* software triggered strobe */ + + /* T2ctl bits */ + T2gate= (1<<0), /* enable T2 counting */ + T2spkr= (1<<1), /* connect T2 out to speaker */ + T2out= (1<<5), /* output of T2 */ + + Freq= 1193182, /* Real clock frequency */ + Tickshift=8, /* extra accuracy */ + MaxPeriod=Freq/HZ, + MinPeriod=Freq/(100*HZ), +}; + +typedef struct I8253 I8253; +struct I8253 +{ + Lock; + ulong period; /* current clock period */ + int enabled; + uvlong hz; + + ushort last; /* last value of clock 1 */ + uvlong ticks; /* cumulative ticks of counter 1 */ + + ulong periodset; +}; +I8253 i8253; + +void +i8253init(void) +{ + int loops, x; + + ioalloc(T0cntr, 4, 0, "i8253"); + ioalloc(T2ctl, 1, 0, "i8253.cntr2ctl"); + + i8253.period = Freq/HZ; + + /* + * enable a 1/HZ interrupt for providing scheduling interrupts + */ + outb(Tmode, Load0|Square); + outb(T0cntr, (Freq/HZ)); /* low byte */ + outb(T0cntr, (Freq/HZ)>>8); /* high byte */ + + /* + * enable a longer period counter to use as a clock + */ + outb(Tmode, Load2|Square); + outb(T2cntr, 0); /* low byte */ + outb(T2cntr, 0); /* high byte */ + x = inb(T2ctl); + x |= T2gate; + outb(T2ctl, x); + + /* + * Introduce a little delay to make sure the count is + * latched and the timer is counting down; with a fast + * enough processor this may not be the case. + * The i8254 (which this probably is) has a read-back + * command which can be used to make sure the counting + * register has been written into the counting element. + */ + x = (Freq/HZ); + for(loops = 0; loops < 100000 && x >= (Freq/HZ); loops++){ + outb(Tmode, Latch0); + x = inb(T0cntr); + x |= inb(T0cntr)<<8; + } +} + +void +guesscpuhz(int aalcycles) +{ + int loops, incr, x, y; + uvlong a, b, cpufreq; + + /* find biggest loop that doesn't wrap */ + incr = 16000000/(aalcycles*HZ*2); + x = 2000; + for(loops = incr; loops < 64*1024; loops += incr) { + + /* + * measure time for the loop + * + * MOVL loops,CX + * aaml1: AAM + * LOOP aaml1 + * + * the time for the loop should be independent of external + * cache and memory system since it fits in the execution + * prefetch buffer. + * + */ + outb(Tmode, Latch0); + cycles(&a); + x = inb(T0cntr); + x |= inb(T0cntr)<<8; + aamloop(loops); + outb(Tmode, Latch0); + cycles(&b); + y = inb(T0cntr); + y |= inb(T0cntr)<<8; + x -= y; + + if(x < 0) + x += Freq/HZ; + + if(x > Freq/(3*HZ)) + break; + } + + /* + * figure out clock frequency and a loop multiplier for delay(). + * n.b. counter goes up by 2*Freq + */ + cpufreq = (vlong)loops*((aalcycles*2*Freq)/x); + m->loopconst = (cpufreq/1000)/aalcycles; /* AAM+LOOP's for 1 ms */ + + if(m->havetsc){ + /* counter goes up by 2*Freq */ + b = (b-a)<<1; + b *= Freq; + b /= x; + + /* + * round to the nearest megahz + */ + m->cpumhz = (b+500000)/1000000L; + m->cpuhz = b; + m->cyclefreq = b; + } else { + /* + * add in possible 0.5% error and convert to MHz + */ + m->cpumhz = (cpufreq + cpufreq/200)/1000000; + m->cpuhz = cpufreq; + } + + i8253.hz = Freq<<Tickshift; +} + +void +i8253timerset(uvlong next) +{ + long period; + ulong want; + ulong now; + + period = MaxPeriod; + if(next != 0){ + want = next>>Tickshift; + now = i8253.ticks; /* assuming whomever called us just did fastticks() */ + + period = want - now; + if(period < MinPeriod) + period = MinPeriod; + else if(period > (4*MaxPeriod)/5) /* strong attraction to MaxPeriod */ + period = MaxPeriod; + } + + /* hysteresis */ + if(i8253.period != period){ + ilock(&i8253); + /* load new value */ + outb(Tmode, Load0|Square); + outb(T0cntr, period); /* low byte */ + outb(T0cntr, period >> 8); /* high byte */ + + /* remember period */ + i8253.period = period; + i8253.periodset++; + iunlock(&i8253); + } +} + +static void +i8253clock(Ureg* ureg, void*) +{ + timerintr(ureg, 0); +} + +void +i8253enable(void) +{ + i8253.enabled = 1; + i8253.period = Freq/HZ; + intrenable(IrqCLOCK, i8253clock, 0, BUSUNKNOWN, "clock"); +} + +void +i8253link(void) +{ +} + +/* + * return the total ticks of counter 2. We shift by + * 8 to give timesync more wriggle room for interpretation + * of the frequency + */ +uvlong +i8253read(uvlong *hz) +{ + ushort y, x; + uvlong ticks; + + if(hz) + *hz = i8253.hz; + + ilock(&i8253); + outb(Tmode, Latch2); + y = inb(T2cntr); + y |= inb(T2cntr)<<8; + + if(y < i8253.last) + x = i8253.last - y; + else { + x = i8253.last + (0x10000 - y); + if (x > 3*MaxPeriod) { + outb(Tmode, Load2|Square); + outb(T2cntr, 0); /* low byte */ + outb(T2cntr, 0); /* high byte */ + y = 0xFFFF; + x = i8253.period; + } + } + i8253.last = y; + i8253.ticks += x>>1; + ticks = i8253.ticks; + iunlock(&i8253); + + return ticks<<Tickshift; +} + +void +delay(int millisecs) +{ + millisecs *= m->loopconst; + if(millisecs <= 0) + millisecs = 1; + aamloop(millisecs); +} + +void +microdelay(int microsecs) +{ + microsecs *= m->loopconst; + microsecs /= 1000; + if(microsecs <= 0) + microsecs = 1; + aamloop(microsecs); +} + +/* + * performance measurement ticks. must be low overhead. + * doesn't have to count over a second. + */ +ulong +perfticks(void) +{ + uvlong x; + + if(m->havetsc) + cycles(&x); + else + x = 0; + return x; +} diff --git a/os/pc/i8259.c b/os/pc/i8259.c new file mode 100644 index 00000000..562196b9 --- /dev/null +++ b/os/pc/i8259.c @@ -0,0 +1,199 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +/* + * 8259 interrupt controllers + */ +enum +{ + Int0ctl= 0x20, /* control port (ICW1, OCW2, OCW3) */ + Int0aux= 0x21, /* everything else (ICW2, ICW3, ICW4, OCW1) */ + Int1ctl= 0xA0, /* control port */ + Int1aux= 0xA1, /* everything else (ICW2, ICW3, ICW4, OCW1) */ + + Icw1= 0x10, /* select bit in ctl register */ + Ocw2= 0x00, + Ocw3= 0x08, + + EOI= 0x20, /* non-specific end of interrupt */ + + Elcr1= 0x4D0, /* Edge/Level Triggered Register */ + Elcr2= 0x4D1, +}; + +static Lock i8259lock; +static int i8259mask = 0xFFFF; /* disabled interrupts */ +int i8259elcr; /* mask of level-triggered interrupts */ + +void +i8259init(void) +{ + int x; + + ioalloc(Int0ctl, 2, 0, "i8259.0"); + ioalloc(Int1ctl, 2, 0, "i8259.1"); + ilock(&i8259lock); + + /* + * Set up the first 8259 interrupt processor. + * Make 8259 interrupts start at CPU vector VectorPIC. + * Set the 8259 as master with edge triggered + * input with fully nested interrupts. + */ + outb(Int0ctl, (1<<4)|(0<<3)|(1<<0)); /* ICW1 - master, edge triggered, + ICW4 will be sent */ + outb(Int0aux, VectorPIC); /* ICW2 - interrupt vector offset */ + outb(Int0aux, 0x04); /* ICW3 - have slave on level 2 */ + outb(Int0aux, 0x01); /* ICW4 - 8086 mode, not buffered */ + + /* + * Set up the second 8259 interrupt processor. + * Make 8259 interrupts start at CPU vector VectorPIC+8. + * Set the 8259 as slave with edge triggered + * input with fully nested interrupts. + */ + outb(Int1ctl, (1<<4)|(0<<3)|(1<<0)); /* ICW1 - master, edge triggered, + ICW4 will be sent */ + outb(Int1aux, VectorPIC+8); /* ICW2 - interrupt vector offset */ + outb(Int1aux, 0x02); /* ICW3 - I am a slave on level 2 */ + outb(Int1aux, 0x01); /* ICW4 - 8086 mode, not buffered */ + outb(Int1aux, (i8259mask>>8) & 0xFF); + + /* + * pass #2 8259 interrupts to #1 + */ + i8259mask &= ~0x04; + outb(Int0aux, i8259mask & 0xFF); + + /* + * Set Ocw3 to return the ISR when ctl read. + * After initialisation status read is set to IRR. + * Read IRR first to possibly deassert an outstanding + * interrupt. + */ + inb(Int0ctl); + outb(Int0ctl, Ocw3|0x03); + inb(Int1ctl); + outb(Int1ctl, Ocw3|0x03); + + /* + * Check for Edge/Level register. + * This check may not work for all chipsets. + * First try a non-intrusive test - the bits for + * IRQs 13, 8, 2, 1 and 0 must be edge (0). If + * that's OK try a R/W test. + */ + x = (inb(Elcr2)<<8)|inb(Elcr1); + if(!(x & 0x2107)){ + outb(Elcr1, 0); + if(inb(Elcr1) == 0){ + outb(Elcr1, 0x20); + if(inb(Elcr1) == 0x20) + i8259elcr = x; + outb(Elcr1, x & 0xFF); + print("ELCR: %4.4uX\n", i8259elcr); + } + } + iunlock(&i8259lock); +} + +int +i8259isr(int vno) +{ + int irq, isr; + + if(vno < VectorPIC || vno > VectorPIC+MaxIrqPIC) + return 0; + irq = vno-VectorPIC; + + /* + * tell the 8259 that we're done with the + * highest level interrupt (interrupts are still + * off at this point) + */ + ilock(&i8259lock); + isr = inb(Int0ctl); + outb(Int0ctl, EOI); + if(irq >= 8){ + isr |= inb(Int1ctl)<<8; + outb(Int1ctl, EOI); + } + iunlock(&i8259lock); + + return isr & (1<<irq); +} + +int +i8259enable(Vctl* v) +{ + int irq, irqbit; + + /* + * Given an IRQ, enable the corresponding interrupt in the i8259 + * and return the vector to be used. The i8259 is set to use a fixed + * range of vectors starting at VectorPIC. + */ + irq = v->irq; + if(irq < 0 || irq > MaxIrqPIC){ + print("i8259enable: irq %d out of range\n", irq); + return -1; + } + irqbit = 1<<irq; + + ilock(&i8259lock); + if(!(i8259mask & irqbit) && !(i8259elcr & irqbit)){ + print("i8259enable: irq %d shared but not level\n", irq); + iunlock(&i8259lock); + return -1; + } + i8259mask &= ~irqbit; + if(irq < 8) + outb(Int0aux, i8259mask & 0xFF); + else + outb(Int1aux, (i8259mask>>8) & 0xFF); + + if(i8259elcr & irqbit) + v->eoi = i8259isr; + else + v->isr = i8259isr; + iunlock(&i8259lock); + + return VectorPIC+irq; +} + +int +i8259vecno(int irq) +{ + return VectorPIC+irq; +} + +int +i8259disable(int irq) +{ + int irqbit; + + /* + * Given an IRQ, disable the corresponding interrupt + * in the 8259. + */ + if(irq < 0 || irq > MaxIrqPIC){ + print("i8259disable: irq %d out of range\n", irq); + return -1; + } + irqbit = 1<<irq; + + ilock(&i8259lock); + if(!(i8259mask & irqbit)){ + i8259mask |= irqbit; + if(irq < 8) + outb(Int0aux, i8259mask & 0xFF); + else + outb(Int1aux, (i8259mask>>8) & 0xFF); + } + iunlock(&i8259lock); + return 0; +} diff --git a/os/pc/io.h b/os/pc/io.h new file mode 100644 index 00000000..cd35265e --- /dev/null +++ b/os/pc/io.h @@ -0,0 +1,323 @@ +#define X86STEPPING(x) ((x) & 0x0F) +#define X86MODEL(x) (((x)>>4) & 0x0F) +#define X86FAMILY(x) (((x)>>8) & 0x0F) + +enum { + VectorDBG = 1, /* debug exception */ + VectorNMI = 2, /* non-maskable interrupt */ + VectorBPT = 3, /* breakpoint */ + VectorUD = 6, /* invalid opcode exception */ + VectorCNA = 7, /* coprocessor not available */ + Vector2F = 8, /* double fault */ + VectorCSO = 9, /* coprocessor segment overrun */ + VectorPF = 14, /* page fault */ + Vector15 = 15, /* reserved */ + VectorCERR = 16, /* coprocessor error */ + + VectorPIC = 32, /* external i8259 interrupts */ + IrqCLOCK = 0, + IrqKBD = 1, + IrqUART1 = 3, + IrqUART0 = 4, + IrqPCMCIA = 5, + IrqFLOPPY = 6, + IrqLPT = 7, + IrqIRQ7 = 7, + IrqAUX = 12, /* PS/2 port */ + IrqIRQ13 = 13, /* coprocessor on 386 */ + IrqATA0 = 14, + IrqATA1 = 15, + MaxIrqPIC = 15, + + VectorLAPIC = VectorPIC+16, /* local APIC interrupts */ + IrqLINT0 = 16, /* LINT[01] must be offsets 0 and 1 */ + IrqLINT1 = 17, + IrqTIMER = 18, + IrqERROR = 19, + IrqPCINT = 20, + IrqSPURIOUS = 31, /* must have bits [3-0] == 0x0F */ + MaxIrqLAPIC = 31, + + VectorSYSCALL = 64, + + VectorAPIC = 65, /* external APIC interrupts */ + MaxVectorAPIC = 255, +}; + +typedef struct Vctl { + Vctl* next; /* handlers on this vector */ + + char name[KNAMELEN]; /* of driver */ + int isintr; /* interrupt or fault/trap */ + int irq; + int tbdf; + int (*isr)(int); /* get isr bit for this irq */ + int (*eoi)(int); /* eoi */ + + void (*f)(Ureg*, void*); /* handler to call */ + void* a; /* argument to call it with */ +} Vctl; + +enum { + BusCBUS = 0, /* Corollary CBUS */ + BusCBUSII, /* Corollary CBUS II */ + BusEISA, /* Extended ISA */ + BusFUTURE, /* IEEE Futurebus */ + BusINTERN, /* Internal bus */ + BusISA, /* Industry Standard Architecture */ + BusMBI, /* Multibus I */ + BusMBII, /* Multibus II */ + BusMCA, /* Micro Channel Architecture */ + BusMPI, /* MPI */ + BusMPSA, /* MPSA */ + BusNUBUS, /* Apple Macintosh NuBus */ + BusPCI, /* Peripheral Component Interconnect */ + BusPCMCIA, /* PC Memory Card International Association */ + BusTC, /* DEC TurboChannel */ + BusVL, /* VESA Local bus */ + BusVME, /* VMEbus */ + BusXPRESS, /* Express System Bus */ +}; + +#define MKBUS(t,b,d,f) (((t)<<24)|(((b)&0xFF)<<16)|(((d)&0x1F)<<11)|(((f)&0x07)<<8)) +#define BUSFNO(tbdf) (((tbdf)>>8)&0x07) +#define BUSDNO(tbdf) (((tbdf)>>11)&0x1F) +#define BUSBNO(tbdf) (((tbdf)>>16)&0xFF) +#define BUSTYPE(tbdf) ((tbdf)>>24) +#define BUSBDF(tbdf) ((tbdf)&0x00FFFF00) +#define BUSUNKNOWN (-1) + +enum { + MaxEISA = 16, + CfgEISA = 0xC80, +}; + +/* + * PCI support code. + */ +enum { /* type 0 and type 1 pre-defined header */ + PciVID = 0x00, /* vendor ID */ + PciDID = 0x02, /* device ID */ + PciPCR = 0x04, /* command */ + PciPSR = 0x06, /* status */ + PciRID = 0x08, /* revision ID */ + PciCCRp = 0x09, /* programming interface class code */ + PciCCRu = 0x0A, /* sub-class code */ + PciCCRb = 0x0B, /* base class code */ + PciCLS = 0x0C, /* cache line size */ + PciLTR = 0x0D, /* latency timer */ + PciHDT = 0x0E, /* header type */ + PciBST = 0x0F, /* BIST */ + + PciBAR0 = 0x10, /* base address */ + PciBAR1 = 0x14, + + PciINTL = 0x3C, /* interrupt line */ + PciINTP = 0x3D, /* interrupt pin */ +}; + +enum { /* type 0 pre-defined header */ + PciCIS = 0x28, /* cardbus CIS pointer */ + PciSVID = 0x2C, /* subsystem vendor ID */ + PciSID = 0x2E, /* cardbus CIS pointer */ + PciEBAR0 = 0x30, /* expansion ROM base address */ + PciMGNT = 0x3E, /* burst period length */ + PciMLT = 0x3F, /* maximum latency between bursts */ +}; + +enum { /* type 1 pre-defined header */ + PciPBN = 0x18, /* primary bus number */ + PciSBN = 0x19, /* secondary bus number */ + PciUBN = 0x1A, /* subordinate bus number */ + PciSLTR = 0x1B, /* secondary latency timer */ + PciIBR = 0x1C, /* I/O base */ + PciILR = 0x1D, /* I/O limit */ + PciSPSR = 0x1E, /* secondary status */ + PciMBR = 0x20, /* memory base */ + PciMLR = 0x22, /* memory limit */ + PciPMBR = 0x24, /* prefetchable memory base */ + PciPMLR = 0x26, /* prefetchable memory limit */ + PciPUBR = 0x28, /* prefetchable base upper 32 bits */ + PciPULR = 0x2C, /* prefetchable limit upper 32 bits */ + PciIUBR = 0x30, /* I/O base upper 16 bits */ + PciIULR = 0x32, /* I/O limit upper 16 bits */ + PciEBAR1 = 0x28, /* expansion ROM base address */ + PciBCR = 0x3E, /* bridge control register */ +}; + +enum { /* type 2 pre-defined header */ + PciCBExCA = 0x10, + PciCBSPSR = 0x16, + PciCBPBN = 0x18, /* primary bus number */ + PciCBSBN = 0x19, /* secondary bus number */ + PciCBUBN = 0x1A, /* subordinate bus number */ + PciCBSLTR = 0x1B, /* secondary latency timer */ + PciCBMBR0 = 0x1C, + PciCBMLR0 = 0x20, + PciCBMBR1 = 0x24, + PciCBMLR1 = 0x28, + PciCBIBR0 = 0x2C, /* I/O base */ + PciCBILR0 = 0x30, /* I/O limit */ + PciCBIBR1 = 0x34, /* I/O base */ + PciCBILR1 = 0x38, /* I/O limit */ + PciCBSVID = 0x40, /* subsystem vendor ID */ + PciCBSID = 0x42, /* subsystem ID */ + PciCBLMBAR = 0x44, /* legacy mode base address */ +}; + +typedef struct Pcisiz Pcisiz; +struct Pcisiz +{ + Pcidev* dev; + int siz; + int bar; +}; + +typedef struct Pcidev Pcidev; +struct Pcidev +{ + int tbdf; /* type+bus+device+function */ + ushort vid; /* vendor ID */ + ushort did; /* device ID */ + + ushort pcr; + + uchar rid; + uchar ccrp; + uchar ccru; + uchar ccrb; + uchar cls; + uchar ltr; + + struct { + ulong bar; /* base address */ + int size; + } mem[6]; + + struct { + ulong bar; + int size; + } rom; + uchar intl; /* interrupt line */ + + Pcidev* list; + Pcidev* link; /* next device on this bno */ + + Pcidev* bridge; /* down a bus */ + struct { + ulong bar; + int size; + } ioa, mema; + + int pmrb; /* power management register block */ +}; + +#define PCIWINDOW 0 +#define PCIWADDR(va) (PADDR(va)+PCIWINDOW) +#define ISAWINDOW 0 +#define ISAWADDR(va) (PADDR(va)+ISAWINDOW) + +/* SMBus transactions */ +enum +{ + SMBquick, /* sends address only */ + + /* write */ + SMBsend, /* sends address and cmd */ + SMBbytewrite, /* sends address and cmd and 1 byte */ + SMBwordwrite, /* sends address and cmd and 2 bytes */ + + /* read */ + SMBrecv, /* sends address, recvs 1 byte */ + SMBbyteread, /* sends address and cmd, recv's byte */ + SMBwordread, /* sends address and cmd, recv's 2 bytes */ +}; + +typedef struct SMBus SMBus; +struct SMBus { + QLock; /* mutex */ + Rendez r; /* rendezvous point for completion interrupts */ + void *arg; /* implementation dependent */ + ulong base; /* port or memory base of smbus */ + int busy; + void (*transact)(SMBus*, int, int, int, uchar*); +}; + +/* + * PCMCIA support code. + */ + +typedef struct PCMslot PCMslot; +typedef struct PCMconftab PCMconftab; + +/* + * Map between ISA memory space and PCMCIA card memory space. + */ +struct PCMmap { + ulong ca; /* card address */ + ulong cea; /* card end address */ + ulong isa; /* ISA address */ + int len; /* length of the ISA area */ + int attr; /* attribute memory */ + int ref; +}; + +/* configuration table entry */ +struct PCMconftab +{ + int index; + ushort irqs; /* legal irqs */ + uchar irqtype; + uchar bit16; /* true for 16 bit access */ + struct { + ulong start; + ulong len; + } io[16]; + int nio; + uchar vpp1; + uchar vpp2; + uchar memwait; + ulong maxwait; + ulong readywait; + ulong otherwait; +}; + +/* a card slot */ +struct PCMslot +{ + Lock; + int ref; + + void *cp; /* controller for this slot */ + long memlen; /* memory length */ + uchar base; /* index register base */ + uchar slotno; /* slot number */ + + /* status */ + uchar special; /* in use for a special device */ + uchar already; /* already inited */ + uchar occupied; + uchar battery; + uchar wrprot; + uchar powered; + uchar configed; + uchar enabled; + uchar busy; + + /* cis info */ + ulong msec; /* time of last slotinfo call */ + char verstr[512]; /* version string */ + int ncfg; /* number of configurations */ + struct { + ushort cpresent; /* config registers present */ + ulong caddr; /* relative address of config registers */ + } cfg[8]; + int nctab; /* number of config table entries */ + PCMconftab ctab[8]; + PCMconftab *def; /* default conftab */ + + /* memory maps */ + Lock mlock; /* lock down the maps */ + int time; + PCMmap mmap[4]; /* maps, last is always for the kernel */ +}; diff --git a/os/pc/kbd.c b/os/pc/kbd.c new file mode 100644 index 00000000..8db7009c --- /dev/null +++ b/os/pc/kbd.c @@ -0,0 +1,477 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +enum { + Data= 0x60, /* data port */ + + Status= 0x64, /* status port */ + Inready= 0x01, /* input character ready */ + Outbusy= 0x02, /* output busy */ + Sysflag= 0x04, /* system flag */ + Cmddata= 0x08, /* cmd==0, data==1 */ + Inhibit= 0x10, /* keyboard/mouse inhibited */ + Minready= 0x20, /* mouse character ready */ + Rtimeout= 0x40, /* general timeout */ + Parity= 0x80, + + Cmd= 0x64, /* command port (write only) */ + + Spec= 0x80, + + PF= Spec|0x20, /* num pad function key */ + View= Spec|0x00, /* view (shift window up) */ + KF= 0xF000, /* function key (begin Unicode private space) */ + Shift= Spec|0x60, + Break= Spec|0x61, + Ctrl= Spec|0x62, + Latin= Spec|0x63, + Caps= Spec|0x64, + Num= Spec|0x65, + Middle= Spec|0x66, + No= 0x00, /* peter */ + + Home= KF|13, + Up= KF|14, + Pgup= KF|15, + Print= KF|16, + Left= KF|17, + Right= KF|18, + End= '\r', + Down= View, + Pgdown= KF|19, + Ins= KF|20, + Del= 0x7F, + Scroll= KF|21, +}; + +/* + * The codes at 0x79 and 0x81 are produed by the PFU Happy Hacking keyboard. + * A 'standard' keyboard doesn't produce anything above 0x58. + */ +Rune kbtab[] = +{ +[0x00] No, 0x1b, '1', '2', '3', '4', '5', '6', +[0x08] '7', '8', '9', '0', '-', '=', '\b', '\t', +[0x10] 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', +[0x18] 'o', 'p', '[', ']', '\n', Ctrl, 'a', 's', +[0x20] 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', +[0x28] '\'', '`', Shift, '\\', 'z', 'x', 'c', 'v', +[0x30] 'b', 'n', 'm', ',', '.', '/', Shift, '*', +[0x38] Latin, ' ', Ctrl, KF|1, KF|2, KF|3, KF|4, KF|5, +[0x40] KF|6, KF|7, KF|8, KF|9, KF|10, Num, Scroll, '7', +[0x48] '8', '9', '-', '4', '5', '6', '+', '1', +[0x50] '2', '3', '0', '.', No, No, No, KF|11, +[0x58] KF|12, No, No, No, No, No, No, No, +[0x60] No, No, No, No, No, No, No, No, +[0x68] No, No, No, No, No, No, No, No, +[0x70] No, No, No, No, No, No, No, No, +[0x78] No, View, No, Up, No, No, No, No, +}; + +Rune kbtabshift[] = +{ +[0x00] No, 0x1b, '!', '@', '#', '$', '%', '^', +[0x08] '&', '*', '(', ')', '_', '+', '\b', '\t', +[0x10] 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', +[0x18] 'O', 'P', '{', '}', '\n', Ctrl, 'A', 'S', +[0x20] 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', +[0x28] '"', '~', Shift, '|', 'Z', 'X', 'C', 'V', +[0x30] 'B', 'N', 'M', '<', '>', '?', Shift, '*', +[0x38] Latin, ' ', Ctrl, KF|1, KF|2, KF|3, KF|4, KF|5, +[0x40] KF|6, KF|7, KF|8, KF|9, KF|10, Num, Scroll, '7', +[0x48] '8', '9', '-', '4', '5', '6', '+', '1', +[0x50] '2', '3', '0', '.', No, No, No, KF|11, +[0x58] KF|12, No, No, No, No, No, No, No, +[0x60] No, No, No, No, No, No, No, No, +[0x68] No, No, No, No, No, No, No, No, +[0x70] No, No, No, No, No, No, No, No, +[0x78] No, Up, No, Up, No, No, No, No, +}; + +Rune kbtabesc1[] = +{ +[0x00] No, No, No, No, No, No, No, No, +[0x08] No, No, No, No, No, No, No, No, +[0x10] No, No, No, No, No, No, No, No, +[0x18] No, No, No, No, '\n', Ctrl, No, No, +[0x20] No, No, No, No, No, No, No, No, +[0x28] No, No, Shift, No, No, No, No, No, +[0x30] No, No, No, No, No, '/', No, Print, +[0x38] Latin, No, No, No, No, No, No, No, +[0x40] No, No, No, No, No, No, Break, Home, +[0x48] Up, Pgup, No, Left, No, Right, No, End, +[0x50] Down, Pgdown, Ins, Del, No, No, No, No, +[0x58] No, No, No, No, No, No, No, No, +[0x60] No, No, No, No, No, No, No, No, +[0x68] No, No, No, No, No, No, No, No, +[0x70] No, No, No, No, No, No, No, No, +[0x78] No, Up, No, No, No, No, No, No, +}; + +enum +{ + /* controller command byte */ + Cscs1= (1<<6), /* scan code set 1 */ + Cauxdis= (1<<5), /* mouse disable */ + Ckbddis= (1<<4), /* kbd disable */ + Csf= (1<<2), /* system flag */ + Cauxint= (1<<1), /* mouse interrupt enable */ + Ckbdint= (1<<0), /* kbd interrupt enable */ +}; + +int mouseshifted; + +static Lock i8042lock; +static uchar ccc; +static void (*auxputc)(int, int); + +/* + * wait for output no longer busy + */ +static int +outready(void) +{ + int tries; + + for(tries = 0; (inb(Status) & Outbusy); tries++){ + if(tries > 500) + return -1; + delay(2); + } + return 0; +} + +/* + * wait for input + */ +static int +inready(void) +{ + int tries; + + for(tries = 0; !(inb(Status) & Inready); tries++){ + if(tries > 500) + return -1; + delay(2); + } + return 0; +} + +/* + * ask 8042 to reset the machine + */ +void +i8042reset(void) +{ + ushort *s = KADDR(0x472); + int i, x; + + *s = 0x1234; /* BIOS warm-boot flag */ + + /* + * newer reset the machine command + */ + outready(); + outb(Cmd, 0xFE); + outready(); + + /* + * Pulse it by hand (old somewhat reliable) + */ + x = 0xDF; + for(i = 0; i < 5; i++){ + x ^= 1; + outready(); + outb(Cmd, 0xD1); + outready(); + outb(Data, x); /* toggle reset */ + delay(100); + } +} + +int +i8042auxcmd(int cmd) +{ + unsigned int c; + int tries; + + c = 0; + tries = 0; + + ilock(&i8042lock); + do{ + if(tries++ > 2) + break; + if(outready() < 0) + break; + outb(Cmd, 0xD4); + if(outready() < 0) + break; + outb(Data, cmd); + if(outready() < 0) + break; + if(inready() < 0) + break; + c = inb(Data); + } while(c == 0xFE || c == 0); + iunlock(&i8042lock); + + if(c != 0xFA){ + print("i8042: %2.2ux returned to the %2.2ux command\n", c, cmd); + return -1; + } + return 0; +} + +int +i8042auxcmds(uchar *cmd, int ncmd) +{ + int i; + + ilock(&i8042lock); + for(i=0; i<ncmd; i++){ + if(outready() < 0) + break; + outb(Cmd, 0xD4); + if(outready() < 0) + break; + outb(Data, cmd[i]); + } + iunlock(&i8042lock); + return i; +} + +/* + * keyboard interrupt + */ +static void +i8042intr(Ureg*, void*) +{ + int s, c, i; + static int esc1, esc2; + static int alt, caps, ctl, num, shift; + static int collecting, nk; + static Rune kc[5]; + int keyup; + + /* + * get status + */ + lock(&i8042lock); + s = inb(Status); + if(!(s&Inready)){ + unlock(&i8042lock); + return; + } + + /* + * get the character + */ + c = inb(Data); + unlock(&i8042lock); + + /* + * if it's the aux port... + */ + if(s & Minready){ + if(auxputc != nil) + auxputc(c, shift); + return; + } + + /* + * e0's is the first of a 2 character sequence + */ + if(c == 0xe0){ + esc1 = 1; + return; + } else if(c == 0xe1){ + esc2 = 2; + return; + } + + keyup = c&0x80; + c &= 0x7f; + if(c > sizeof kbtab){ + c |= keyup; + if(c != 0xFF) /* these come fairly often: CAPSLOCK U Y */ + print("unknown key %ux\n", c); + return; + } + + if(esc1){ + c = kbtabesc1[c]; + esc1 = 0; + } else if(esc2){ + esc2--; + return; + } else if(shift) + c = kbtabshift[c]; + else + c = kbtab[c]; + + if(caps && c<='z' && c>='a') + c += 'A' - 'a'; + + /* + * keyup only important for shifts + */ + if(keyup){ + switch(c){ + case Latin: + alt = 0; + break; + case Shift: + shift = 0; + mouseshifted = 0; + break; + case Ctrl: + ctl = 0; + break; + } + return; + } + + /* + * normal character + */ + if(!(c & (Spec|KF))){ + if(ctl){ + if(alt && c == Del) + exit(0); + c &= 0x1f; + } + if(!collecting){ + kbdputc(kbdq, c); + return; + } + kc[nk++] = c; + c = latin1(kc, nk); + if(c < -1) /* need more keystrokes */ + return; + if(c != -1) /* valid sequence */ + kbdputc(kbdq, c); + else /* dump characters */ + for(i=0; i<nk; i++) + kbdputc(kbdq, kc[i]); + nk = 0; + collecting = 0; + return; + } else { + switch(c){ + case Caps: + caps ^= 1; + return; + case Num: + num ^= 1; + return; + case Shift: + shift = 1; + mouseshifted = 1; + return; + case Latin: + alt = 1; + /* + * VMware uses Ctl-Alt as the key combination + * to make the VM give up keyboard and mouse focus. + * This has the unfortunate side effect that when you + * come back into focus, Plan 9 thinks you want to type + * a compose sequence (you just typed alt). + * + * As a clumsy hack around this, we look for ctl-alt + * and don't treat it as the start of a compose sequence. + */ + if(!ctl){ + collecting = 1; + nk = 0; + } + return; + case Ctrl: + collecting = 0; + nk = 0; + ctl = 1; + return; + } + } + kbdputc(kbdq, c); +} + +void +i8042auxenable(void (*putc)(int, int)) +{ + char *err = "i8042: aux init failed\n"; + + /* enable kbd/aux xfers and interrupts */ + ccc &= ~Cauxdis; + ccc |= Cauxint; + + ilock(&i8042lock); + if(outready() < 0) + print(err); + outb(Cmd, 0x60); /* write control register */ + if(outready() < 0) + print(err); + outb(Data, ccc); + if(outready() < 0) + print(err); + outb(Cmd, 0xA8); /* auxilliary device enable */ + if(outready() < 0){ + iunlock(&i8042lock); + return; + } + auxputc = putc; + intrenable(IrqAUX, i8042intr, 0, BUSUNKNOWN, "kbdaux"); + iunlock(&i8042lock); +} + +void +kbdinit(void) +{ + int c; + + /* wait for a quiescent controller */ + while((c = inb(Status)) & (Outbusy | Inready)) + if(c & Inready) + inb(Data); + + /* get current controller command byte */ + outb(Cmd, 0x20); + if(inready() < 0){ + print("kbdinit: can't read ccc\n"); + ccc = 0; + } else + ccc = inb(Data); + + /* enable kbd xfers and interrupts */ + /* disable mouse */ + ccc &= ~Ckbddis; + ccc |= Csf | Ckbdint | Cscs1; + if(outready() < 0) + print("kbd init failed\n"); + outb(Cmd, 0x60); + if(outready() < 0) + print("kbd init failed\n"); + outb(Data, ccc); + outready(); +} + +void +kbdenable(void) +{ + if(kbdq == nil){ + kbdq = qopen(4*1024, 0, 0, 0); + if(kbdq == nil) + panic("kbdinit"); + qnoblock(kbdq, 1); + } + + ioalloc(Data, 1, 0, "kbd"); + ioalloc(Cmd, 1, 0, "kbd"); + + intrenable(IrqKBD, i8042intr, 0, BUSUNKNOWN, "kbd"); +} diff --git a/os/pc/l.s b/os/pc/l.s new file mode 100644 index 00000000..71c97d26 --- /dev/null +++ b/os/pc/l.s @@ -0,0 +1,953 @@ +#include "mem.h" + +#define PADDR(a) ((a) & ~KZERO) +#define KADDR(a) (KZERO|(a)) + +/* + * Some machine instructions not handled by 8[al]. + */ +#define OP16 BYTE $0x66 +#define DELAY BYTE $0xEB; BYTE $0x00 /* JMP .+2 */ +#define CPUID BYTE $0x0F; BYTE $0xA2 /* CPUID, argument in AX */ +#define WRMSR BYTE $0x0F; BYTE $0x30 /* WRMSR, argument in AX/DX (lo/hi) */ +#define RDTSC BYTE $0x0F; BYTE $0x31 /* RDTSC, result in AX/DX (lo/hi) */ +#define RDMSR BYTE $0x0F; BYTE $0x32 /* RDMSR, result in AX/DX (lo/hi) */ +#define WBINVD BYTE $0x0F; BYTE $0x09 +#define HLT BYTE $0xF4 + +/* + * Macros for calculating offsets within the page directory base + * and page tables. Note that these are assembler-specific hence + * the '<<2'. + */ +#define PDO(a) (((((a))>>22) & 0x03FF)<<2) +#define PTO(a) (((((a))>>12) & 0x03FF)<<2) + +/* + * For backwards compatiblity with 9load - should go away when 9load is changed + * 9load currently sets up the mmu, however the first 16MB of memory is identity + * mapped, so behave as if the mmu was not setup + */ +TEXT _start0x80100020(SB), $0 + MOVL $_start0x00100020(SB), AX + ANDL $~KZERO, AX + JMP* AX + +/* + * Must be 4-byte aligned. + */ +TEXT _multibootheader(SB), $0 + LONG $0x1BADB002 /* magic */ + LONG $0x00010003 /* flags */ + LONG $-(0x1BADB002 + 0x00010003) /* checksum */ + LONG $_multibootheader-KZERO(SB) /* header_addr */ + LONG $_start0x80100020-KZERO(SB) /* load_addr */ + LONG $edata-KZERO(SB) /* load_end_addr */ + LONG $end-KZERO(SB) /* bss_end_addr */ + LONG $_start0x80100020-KZERO(SB) /* entry_addr */ + LONG $0 /* mode_type */ + LONG $0 /* width */ + LONG $0 /* height */ + LONG $0 /* depth */ + +/* + * In protected mode with paging turned off and segment registers setup to linear map all memory. + * Entered via a jump to 0x00100020, the physical address of the virtual kernel entry point of 0x80100020 + * Make the basic page tables for processor 0. Four pages are needed for the basic set: + * a page directory, a page table for mapping the first 4MB of physical memory to KZERO, + * and virtual and physical pages for mapping the Mach structure. + * The remaining PTEs will be allocated later when memory is sized. + * An identity mmu map is also needed for the switch to virtual mode. This + * identity mapping is removed once the MMU is going and the JMP has been made + * to virtual memory. + */ +TEXT _start0x00100020(SB), $0 + CLI /* make sure interrupts are off */ + + /* set up the gdt so we have sane plan 9 style gdts. */ + MOVL $tgdtptr(SB), AX + ANDL $~KZERO, AX + MOVL (AX), GDTR + MOVW $1, AX + MOVW AX, MSW + + /* clear prefetch queue (weird code to avoid optimizations) */ + DELAY + + /* set segs to something sane (avoid traps later) */ + MOVW $(1<<3), AX + MOVW AX, DS + MOVW AX, SS + MOVW AX, ES + MOVW AX, FS + MOVW AX, GS + +/* JMP $(2<<3):$mode32bit(SB) /**/ + BYTE $0xEA + LONG $mode32bit-KZERO(SB) + WORD $(2<<3) + +/* + * gdt to get us to 32-bit/segmented/unpaged mode + */ +TEXT tgdt(SB), $0 + + /* null descriptor */ + LONG $0 + LONG $0 + + /* data segment descriptor for 4 gigabytes (PL 0) */ + LONG $(0xFFFF) + LONG $(SEGG|SEGB|(0xF<<16)|SEGP|SEGPL(0)|SEGDATA|SEGW) + + /* exec segment descriptor for 4 gigabytes (PL 0) */ + LONG $(0xFFFF) + LONG $(SEGG|SEGD|(0xF<<16)|SEGP|SEGPL(0)|SEGEXEC|SEGR) + +/* + * pointer to initial gdt + * Note the -KZERO which puts the physical address in the gdtptr. + * that's needed as we start executing in physical addresses. + */ +TEXT tgdtptr(SB), $0 + + WORD $(3*8) + LONG $tgdt-KZERO(SB) + +TEXT mode32bit(SB), $0 + /* At this point, the GDT setup is done. */ + + MOVL $PADDR(CPU0PDB), DI /* clear 4 pages for the tables etc. */ + XORL AX, AX + MOVL $(4*BY2PG), CX + SHRL $2, CX + + CLD + REP; STOSL + + MOVL $PADDR(CPU0PDB), AX + ADDL $PDO(KZERO), AX /* page directory offset for KZERO */ + MOVL $PADDR(CPU0PTE), (AX) /* PTE's for 0x80000000 */ + MOVL $(PTEWRITE|PTEVALID), BX /* page permissions */ + ORL BX, (AX) + + MOVL $PADDR(CPU0PTE), AX /* first page of page table */ + MOVL $1024, CX /* 1024 pages in 4MB */ +_setpte: + MOVL BX, (AX) + ADDL $(1<<PGSHIFT), BX + ADDL $4, AX + LOOP _setpte + + MOVL $PADDR(CPU0PTE), AX + ADDL $PTO(MACHADDR), AX /* page table entry offset for MACHADDR */ + MOVL $PADDR(CPU0MACH), (AX) /* PTE for Mach */ + MOVL $(PTEWRITE|PTEVALID), BX /* page permissions */ + ORL BX, (AX) + +/* + * Now ready to use the new map. Make sure the processor options are what is wanted. + * It is necessary on some processors to immediately follow mode switching with a JMP instruction + * to clear the prefetch queues. + */ + MOVL $PADDR(CPU0PDB), CX /* load address of page directory */ + MOVL (PDO(KZERO))(CX), DX /* double-map KZERO at 0 */ + MOVL DX, (PDO(0))(CX) + MOVL CX, CR3 + DELAY /* JMP .+2 */ + + MOVL CR0, DX + ORL $0x80010000, DX /* PG|WP */ + ANDL $~0x6000000A, DX /* ~(CD|NW|TS|MP) */ + + MOVL $_startpg(SB), AX /* this is a virtual address */ + MOVL DX, CR0 /* turn on paging */ + JMP* AX /* jump to the virtual nirvana */ + +/* + * Basic machine environment set, can clear BSS and create a stack. + * The stack starts at the top of the page containing the Mach structure. + * The x86 architecture forces the use of the same virtual address for + * each processor's Mach structure, so the global Mach pointer 'm' can + * be initialised here. + */ +TEXT _startpg(SB), $0 + MOVL $0, (PDO(0))(CX) /* undo double-map of KZERO at 0 */ + MOVL CX, CR3 /* load and flush the mmu */ + +_clearbss: + MOVL $edata(SB), DI + XORL AX, AX + MOVL $end(SB), CX + SUBL DI, CX /* end-edata bytes */ + SHRL $2, CX /* end-edata doublewords */ + + CLD + REP; STOSL /* clear BSS */ + + MOVL $MACHADDR, SP + MOVL SP, m(SB) /* initialise global Mach pointer */ + MOVL $0, 0(SP) /* initialise m->machno */ + + ADDL $(MACHSIZE-4), SP /* initialise stack */ + +/* + * Need to do one final thing to ensure a clean machine environment, + * clear the EFLAGS register, which can only be done once there is a stack. + */ + MOVL $0, AX + PUSHL AX + POPFL + + CALL main(SB) + +/* + * Park a processor. Should never fall through a return from main to here, + * should only be called by application processors when shutting down. + */ +TEXT idle(SB), $0 +_idle: + STI + HLT + JMP _idle + +/* + * Port I/O. + * in[bsl] input a byte|short|long + * ins[bsl] input a string of bytes|shorts|longs + * out[bsl] output a byte|short|long + * outs[bsl] output a string of bytes|shorts|longs + */ +TEXT inb(SB), $0 + MOVL port+0(FP), DX + XORL AX, AX + INB + RET + +TEXT insb(SB), $0 + MOVL port+0(FP), DX + MOVL address+4(FP), DI + MOVL count+8(FP), CX + CLD + REP; INSB + RET + +TEXT ins(SB), $0 + MOVL port+0(FP), DX + XORL AX, AX + OP16; INL + RET + +TEXT inss(SB), $0 + MOVL port+0(FP), DX + MOVL address+4(FP), DI + MOVL count+8(FP), CX + CLD + REP; OP16; INSL + RET + +TEXT inl(SB), $0 + MOVL port+0(FP), DX + INL + RET + +TEXT insl(SB), $0 + MOVL port+0(FP), DX + MOVL address+4(FP), DI + MOVL count+8(FP), CX + CLD + REP; INSL + RET + +TEXT outb(SB), $0 + MOVL port+0(FP), DX + MOVL byte+4(FP), AX + OUTB + RET + +TEXT outsb(SB), $0 + MOVL port+0(FP), DX + MOVL address+4(FP), SI + MOVL count+8(FP), CX + CLD + REP; OUTSB + RET + +TEXT outs(SB), $0 + MOVL port+0(FP), DX + MOVL short+4(FP), AX + OP16; OUTL + RET + +TEXT outss(SB), $0 + MOVL port+0(FP), DX + MOVL address+4(FP), SI + MOVL count+8(FP), CX + CLD + REP; OP16; OUTSL + RET + +TEXT outl(SB), $0 + MOVL port+0(FP), DX + MOVL long+4(FP), AX + OUTL + RET + +TEXT outsl(SB), $0 + MOVL port+0(FP), DX + MOVL address+4(FP), SI + MOVL count+8(FP), CX + CLD + REP; OUTSL + RET + +/* there's a macro in fns.h but libinterp can't see it */ +TEXT getcallerpc(SB), $0 + MOVL a+0(FP), AX + RET + +/* + * Read/write various system registers. + * CR4 and the 'model specific registers' should only be read/written + * after it has been determined the processor supports them + */ +TEXT lgdt(SB), $0 /* GDTR - global descriptor table */ + MOVL gdtptr+0(FP), AX + MOVL (AX), GDTR + RET + +TEXT lidt(SB), $0 /* IDTR - interrupt descriptor table */ + MOVL idtptr+0(FP), AX + MOVL (AX), IDTR + RET + +TEXT ltr(SB), $0 /* TR - task register */ + MOVL tptr+0(FP), AX + MOVW AX, TASK + RET + +TEXT getcr0(SB), $0 /* CR0 - processor control */ + MOVL CR0, AX + RET + +TEXT getcr2(SB), $0 /* CR2 - page fault linear address */ + MOVL CR2, AX + RET + +TEXT getcr3(SB), $0 /* CR3 - page directory base */ + MOVL CR3, AX + RET + +TEXT putcr3(SB), $0 + MOVL cr3+0(FP), AX + MOVL AX, CR3 + RET + +TEXT getcr4(SB), $0 /* CR4 - extensions */ + MOVL CR4, AX + RET + +TEXT putcr4(SB), $0 + MOVL cr4+0(FP), AX + MOVL AX, CR4 + RET + +TEXT _cycles(SB), $0 /* time stamp counter; cycles since power up */ + RDTSC + MOVL vlong+0(FP), CX /* &vlong */ + MOVL AX, 0(CX) /* lo */ + MOVL DX, 4(CX) /* hi */ + RET + +TEXT rdmsr(SB), $0 /* model-specific register */ + MOVL index+0(FP), CX + RDMSR + MOVL vlong+4(FP), CX /* &vlong */ + MOVL AX, 0(CX) /* lo */ + MOVL DX, 4(CX) /* hi */ + RET + +TEXT wrmsr(SB), $0 + MOVL index+0(FP), CX + MOVL lo+4(FP), AX + MOVL hi+8(FP), DX + WRMSR + RET + +TEXT wbinvd(SB), $0 + WBINVD + RET + +TEXT rdtsc32(SB), $0 + CPUID + RDTSC + RET + +/* + * Try to determine the CPU type which requires fiddling with EFLAGS. + * If the Id bit can be toggled then the CPUID instruction can be used + * to determine CPU identity and features. First have to check if it's + * a 386 (Ac bit can't be set). If it's not a 386 and the Id bit can't be + * toggled then it's an older 486 of some kind. + * + * cpuid(id[], &ax, &dx); + */ +TEXT cpuid(SB), $0 + MOVL $0x240000, AX + PUSHL AX + POPFL /* set Id|Ac */ + + PUSHFL + POPL BX /* retrieve value */ + + MOVL $0, AX + PUSHL AX + POPFL /* clear Id|Ac, EFLAGS initialised */ + + PUSHFL + POPL AX /* retrieve value */ + XORL BX, AX + TESTL $0x040000, AX /* Ac */ + JZ _cpu386 /* can't set this bit on 386 */ + TESTL $0x200000, AX /* Id */ + JZ _cpu486 /* can't toggle this bit on some 486 */ + + MOVL $0, AX + CPUID + MOVL id+0(FP), BP + MOVL BX, 0(BP) /* "Genu" "Auth" "Cyri" */ + MOVL DX, 4(BP) /* "ineI" "enti" "xIns" */ + MOVL CX, 8(BP) /* "ntel" "cAMD" "tead" */ + + MOVL $1, AX + CPUID + JMP _cpuid + +_cpu486: + MOVL $0x400, AX + MOVL $0, DX + JMP _cpuid + +_cpu386: + MOVL $0x300, AX + MOVL $0, DX + +_cpuid: + MOVL ax+4(FP), BP + MOVL AX, 0(BP) + MOVL dx+8(FP), BP + MOVL DX, 0(BP) + RET + +/* + * Basic timing loop to determine CPU frequency. + */ +TEXT aamloop(SB), $0 + MOVL count+0(FP), CX +_aamloop: + AAM + LOOP _aamloop + RET + +/* + * Floating point. + * Note: the encodings for the FCLEX, FINIT, FSAVE, FSTCW, FSENV and FSTSW + * instructions do NOT have the WAIT prefix byte (i.e. they act like their + * FNxxx variations) so WAIT instructions must be explicitly placed in the + * code as necessary. + */ +#define FPOFF(l) ;\ + MOVL CR0, AX ;\ + ANDL $0xC, AX /* EM, TS */ ;\ + CMPL AX, $0x8 ;\ + JEQ l ;\ + WAIT ;\ +l: ;\ + MOVL CR0, AX ;\ + ANDL $~0x4, AX /* EM=0 */ ;\ + ORL $0x28, AX /* NE=1, TS=1 */ ;\ + MOVL AX, CR0 + +#define FPON ;\ + MOVL CR0, AX ;\ + ANDL $~0xC, AX /* EM=0, TS=0 */ ;\ + MOVL AX, CR0 + +TEXT fpoff(SB), $0 /* disable */ + FPOFF(l1) + RET + +TEXT fpinit(SB), $0 /* enable and init */ + FPON + FINIT + WAIT + /* setfcr(FPPDBL|FPRNR|FPINVAL|FPZDIV|FPOVFL) */ + /* note that low 6 bits are masks, not enables, on this chip */ + PUSHW $0x0232 + FLDCW 0(SP) + POPW AX + WAIT + RET + +TEXT fpsave(SB), $0 /* save state and disable */ + MOVL p+0(FP), AX + FSAVE 0(AX) /* no WAIT */ + FPOFF(l2) + RET + +TEXT fprestore(SB), $0 /* enable and restore state */ + FPON + MOVL p+0(FP), AX + FRSTOR 0(AX) + WAIT + RET + +TEXT fpstatus(SB), $0 /* get floating point status */ + FSTSW AX + RET + +TEXT fpenv(SB), $0 /* save state without waiting */ + MOVL p+0(FP), AX + FSTENV 0(AX) + RET + +TEXT fpclear(SB), $0 /* clear pending exceptions */ + FPON + FCLEX /* no WAIT */ + FPOFF(l3) + RET + +/* + */ +TEXT splhi(SB), $0 + MOVL $(MACHADDR+0x04), AX /* save PC in m->splpc */ + MOVL (SP), BX + MOVL BX, (AX) + + PUSHFL + POPL AX + CLI + RET + +TEXT spllo(SB), $0 + PUSHFL + POPL AX + STI + RET + +TEXT splx(SB), $0 + MOVL $(MACHADDR+0x04), AX /* save PC in m->splpc */ + MOVL (SP), BX + MOVL BX, (AX) + /*FALLTHROUGH*/ + +TEXT splxpc(SB), $0 /* for iunlock */ + MOVL s+0(FP), AX + PUSHL AX + POPFL + RET + +TEXT spldone(SB), $0 + RET + +TEXT islo(SB), $0 + PUSHFL + POPL AX + ANDL $0x200, AX /* interrupt enable flag */ + RET + +/* + * Test-And-Set + */ +TEXT _tas(SB), $0 + MOVL $0xDEADDEAD, AX + MOVL lock+0(FP), BX + XCHGL AX, (BX) /* lock->key */ + RET + +TEXT _xinc(SB), $0 /* void _xinc(long*); */ + MOVL l+0(FP), AX + LOCK; INCL 0(AX) + RET + +TEXT _xdec(SB), $0 /* long _xdec(long*); */ + MOVL l+0(FP), BX + XORL AX, AX + LOCK; DECL 0(BX) + JLT _xdeclt + JGT _xdecgt + RET +_xdecgt: + INCL AX + RET +_xdeclt: + DECL AX + RET + +TEXT mb386(SB), $0 + POPL AX /* return PC */ + PUSHFL + PUSHL CS + PUSHL AX + IRETL + +TEXT mb586(SB), $0 + XORL AX, AX + CPUID + RET + +TEXT xchgw(SB), $0 + MOVL v+4(FP), AX + MOVL p+0(FP), BX + XCHGW AX, (BX) + RET + +TEXT mul64fract(SB), $0 +/* + * Multiply two 64-bit number s and keep the middle 64 bits from the 128-bit result + * See ../port/tod.c for motivation. + */ + MOVL r+0(FP), CX + XORL BX, BX /* BX = 0 */ + + MOVL a+8(FP), AX + MULL b+16(FP) /* a1*b1 */ + MOVL AX, 4(CX) /* r2 = lo(a1*b1) */ + + MOVL a+8(FP), AX + MULL b+12(FP) /* a1*b0 */ + MOVL AX, 0(CX) /* r1 = lo(a1*b0) */ + ADDL DX, 4(CX) /* r2 += hi(a1*b0) */ + + MOVL a+4(FP), AX + MULL b+16(FP) /* a0*b1 */ + ADDL AX, 0(CX) /* r1 += lo(a0*b1) */ + ADCL DX, 4(CX) /* r2 += hi(a0*b1) + carry */ + + MOVL a+4(FP), AX + MULL b+12(FP) /* a0*b0 */ + ADDL DX, 0(CX) /* r1 += hi(a0*b0) */ + ADCL BX, 4(CX) /* r2 += carry */ + RET + +/* + * label consists of a stack pointer and a PC + */ +TEXT gotolabel(SB), $0 + MOVL label+0(FP), AX + MOVL 0(AX), SP /* restore sp */ + MOVL 4(AX), AX /* put return pc on the stack */ + MOVL AX, 0(SP) + MOVL $1, AX /* return 1 */ + RET + +TEXT setlabel(SB), $0 + MOVL label+0(FP), AX + MOVL SP, 0(AX) /* store sp */ + MOVL 0(SP), BX /* store return pc */ + MOVL BX, 4(AX) + MOVL $0, AX /* return 0 */ + RET + +TEXT halt(SB), $0 + STI + HLT + RET + +/* + * Interrupt/exception handling. + * Each entry in the vector table calls either _strayintr or _strayintrx depending + * on whether an error code has been automatically pushed onto the stack + * (_strayintrx) or not, in which case a dummy entry must be pushed before retrieving + * the trap type from the vector table entry and placing it on the stack as part + * of the Ureg structure. + * The size of each entry in the vector table (6 bytes) is known in trapinit(). + */ +TEXT _strayintr(SB), $0 + PUSHL AX /* save AX */ + MOVL 4(SP), AX /* return PC from vectortable(SB) */ + JMP intrcommon + +TEXT _strayintrx(SB), $0 + XCHGL AX, (SP) /* exchange AX with pointer to trap type */ +intrcommon: + PUSHL DS + MOVBLZX (AX), AX /* trap type -> AX */ + XCHGL AX, 4(SP) /* exchange trap type with AX */ + PUSHL ES + PUSHL FS + PUSHL GS + PUSHAL + MOVL $(KDSEL), AX + MOVW AX, DS + MOVW AX, ES + PUSHL SP /* Ureg* argument to trap */ + CALL trap(SB) + +TEXT forkret(SB), $0 + POPL AX + POPAL + POPL GS + POPL FS + POPL ES + POPL DS + ADDL $8, SP /* pop error code and trap type */ + IRETL + +TEXT vectortable(SB), $0 + CALL _strayintr(SB); BYTE $0x00 /* divide error */ + CALL _strayintr(SB); BYTE $0x01 /* debug exception */ + CALL _strayintr(SB); BYTE $0x02 /* NMI interrupt */ + CALL _strayintr(SB); BYTE $0x03 /* breakpoint */ + CALL _strayintr(SB); BYTE $0x04 /* overflow */ + CALL _strayintr(SB); BYTE $0x05 /* bound */ + CALL _strayintr(SB); BYTE $0x06 /* invalid opcode */ + CALL _strayintr(SB); BYTE $0x07 /* no coprocessor available */ + CALL _strayintrx(SB); BYTE $0x08 /* double fault */ + CALL _strayintr(SB); BYTE $0x09 /* coprocessor segment overflow */ + CALL _strayintrx(SB); BYTE $0x0A /* invalid TSS */ + CALL _strayintrx(SB); BYTE $0x0B /* segment not available */ + CALL _strayintrx(SB); BYTE $0x0C /* stack exception */ + CALL _strayintrx(SB); BYTE $0x0D /* general protection error */ + CALL _strayintrx(SB); BYTE $0x0E /* page fault */ + CALL _strayintr(SB); BYTE $0x0F /* */ + CALL _strayintr(SB); BYTE $0x10 /* coprocessor error */ + CALL _strayintrx(SB); BYTE $0x11 /* alignment check */ + CALL _strayintr(SB); BYTE $0x12 /* machine check */ + CALL _strayintr(SB); BYTE $0x13 + CALL _strayintr(SB); BYTE $0x14 + CALL _strayintr(SB); BYTE $0x15 + CALL _strayintr(SB); BYTE $0x16 + CALL _strayintr(SB); BYTE $0x17 + CALL _strayintr(SB); BYTE $0x18 + CALL _strayintr(SB); BYTE $0x19 + CALL _strayintr(SB); BYTE $0x1A + CALL _strayintr(SB); BYTE $0x1B + CALL _strayintr(SB); BYTE $0x1C + CALL _strayintr(SB); BYTE $0x1D + CALL _strayintr(SB); BYTE $0x1E + CALL _strayintr(SB); BYTE $0x1F + CALL _strayintr(SB); BYTE $0x20 /* VectorLAPIC */ + CALL _strayintr(SB); BYTE $0x21 + CALL _strayintr(SB); BYTE $0x22 + CALL _strayintr(SB); BYTE $0x23 + CALL _strayintr(SB); BYTE $0x24 + CALL _strayintr(SB); BYTE $0x25 + CALL _strayintr(SB); BYTE $0x26 + CALL _strayintr(SB); BYTE $0x27 + CALL _strayintr(SB); BYTE $0x28 + CALL _strayintr(SB); BYTE $0x29 + CALL _strayintr(SB); BYTE $0x2A + CALL _strayintr(SB); BYTE $0x2B + CALL _strayintr(SB); BYTE $0x2C + CALL _strayintr(SB); BYTE $0x2D + CALL _strayintr(SB); BYTE $0x2E + CALL _strayintr(SB); BYTE $0x2F + CALL _strayintr(SB); BYTE $0x30 + CALL _strayintr(SB); BYTE $0x31 + CALL _strayintr(SB); BYTE $0x32 + CALL _strayintr(SB); BYTE $0x33 + CALL _strayintr(SB); BYTE $0x34 + CALL _strayintr(SB); BYTE $0x35 + CALL _strayintr(SB); BYTE $0x36 + CALL _strayintr(SB); BYTE $0x37 + CALL _strayintr(SB); BYTE $0x38 + CALL _strayintr(SB); BYTE $0x39 + CALL _strayintr(SB); BYTE $0x3A + CALL _strayintr(SB); BYTE $0x3B + CALL _strayintr(SB); BYTE $0x3C + CALL _strayintr(SB); BYTE $0x3D + CALL _strayintr(SB); BYTE $0x3E + CALL _strayintr(SB); BYTE $0x3F + CALL _strayintr(SB); BYTE $0x40 /* VectorSYSCALL */ + CALL _strayintr(SB); BYTE $0x41 + CALL _strayintr(SB); BYTE $0x42 + CALL _strayintr(SB); BYTE $0x43 + CALL _strayintr(SB); BYTE $0x44 + CALL _strayintr(SB); BYTE $0x45 + CALL _strayintr(SB); BYTE $0x46 + CALL _strayintr(SB); BYTE $0x47 + CALL _strayintr(SB); BYTE $0x48 + CALL _strayintr(SB); BYTE $0x49 + CALL _strayintr(SB); BYTE $0x4A + CALL _strayintr(SB); BYTE $0x4B + CALL _strayintr(SB); BYTE $0x4C + CALL _strayintr(SB); BYTE $0x4D + CALL _strayintr(SB); BYTE $0x4E + CALL _strayintr(SB); BYTE $0x4F + CALL _strayintr(SB); BYTE $0x50 + CALL _strayintr(SB); BYTE $0x51 + CALL _strayintr(SB); BYTE $0x52 + CALL _strayintr(SB); BYTE $0x53 + CALL _strayintr(SB); BYTE $0x54 + CALL _strayintr(SB); BYTE $0x55 + CALL _strayintr(SB); BYTE $0x56 + CALL _strayintr(SB); BYTE $0x57 + CALL _strayintr(SB); BYTE $0x58 + CALL _strayintr(SB); BYTE $0x59 + CALL _strayintr(SB); BYTE $0x5A + CALL _strayintr(SB); BYTE $0x5B + CALL _strayintr(SB); BYTE $0x5C + CALL _strayintr(SB); BYTE $0x5D + CALL _strayintr(SB); BYTE $0x5E + CALL _strayintr(SB); BYTE $0x5F + CALL _strayintr(SB); BYTE $0x60 + CALL _strayintr(SB); BYTE $0x61 + CALL _strayintr(SB); BYTE $0x62 + CALL _strayintr(SB); BYTE $0x63 + CALL _strayintr(SB); BYTE $0x64 + CALL _strayintr(SB); BYTE $0x65 + CALL _strayintr(SB); BYTE $0x66 + CALL _strayintr(SB); BYTE $0x67 + CALL _strayintr(SB); BYTE $0x68 + CALL _strayintr(SB); BYTE $0x69 + CALL _strayintr(SB); BYTE $0x6A + CALL _strayintr(SB); BYTE $0x6B + CALL _strayintr(SB); BYTE $0x6C + CALL _strayintr(SB); BYTE $0x6D + CALL _strayintr(SB); BYTE $0x6E + CALL _strayintr(SB); BYTE $0x6F + CALL _strayintr(SB); BYTE $0x70 + CALL _strayintr(SB); BYTE $0x71 + CALL _strayintr(SB); BYTE $0x72 + CALL _strayintr(SB); BYTE $0x73 + CALL _strayintr(SB); BYTE $0x74 + CALL _strayintr(SB); BYTE $0x75 + CALL _strayintr(SB); BYTE $0x76 + CALL _strayintr(SB); BYTE $0x77 + CALL _strayintr(SB); BYTE $0x78 + CALL _strayintr(SB); BYTE $0x79 + CALL _strayintr(SB); BYTE $0x7A + CALL _strayintr(SB); BYTE $0x7B + CALL _strayintr(SB); BYTE $0x7C + CALL _strayintr(SB); BYTE $0x7D + CALL _strayintr(SB); BYTE $0x7E + CALL _strayintr(SB); BYTE $0x7F + CALL _strayintr(SB); BYTE $0x80 /* Vector[A]PIC */ + CALL _strayintr(SB); BYTE $0x81 + CALL _strayintr(SB); BYTE $0x82 + CALL _strayintr(SB); BYTE $0x83 + CALL _strayintr(SB); BYTE $0x84 + CALL _strayintr(SB); BYTE $0x85 + CALL _strayintr(SB); BYTE $0x86 + CALL _strayintr(SB); BYTE $0x87 + CALL _strayintr(SB); BYTE $0x88 + CALL _strayintr(SB); BYTE $0x89 + CALL _strayintr(SB); BYTE $0x8A + CALL _strayintr(SB); BYTE $0x8B + CALL _strayintr(SB); BYTE $0x8C + CALL _strayintr(SB); BYTE $0x8D + CALL _strayintr(SB); BYTE $0x8E + CALL _strayintr(SB); BYTE $0x8F + CALL _strayintr(SB); BYTE $0x90 + CALL _strayintr(SB); BYTE $0x91 + CALL _strayintr(SB); BYTE $0x92 + CALL _strayintr(SB); BYTE $0x93 + CALL _strayintr(SB); BYTE $0x94 + CALL _strayintr(SB); BYTE $0x95 + CALL _strayintr(SB); BYTE $0x96 + CALL _strayintr(SB); BYTE $0x97 + CALL _strayintr(SB); BYTE $0x98 + CALL _strayintr(SB); BYTE $0x99 + CALL _strayintr(SB); BYTE $0x9A + CALL _strayintr(SB); BYTE $0x9B + CALL _strayintr(SB); BYTE $0x9C + CALL _strayintr(SB); BYTE $0x9D + CALL _strayintr(SB); BYTE $0x9E + CALL _strayintr(SB); BYTE $0x9F + CALL _strayintr(SB); BYTE $0xA0 + CALL _strayintr(SB); BYTE $0xA1 + CALL _strayintr(SB); BYTE $0xA2 + CALL _strayintr(SB); BYTE $0xA3 + CALL _strayintr(SB); BYTE $0xA4 + CALL _strayintr(SB); BYTE $0xA5 + CALL _strayintr(SB); BYTE $0xA6 + CALL _strayintr(SB); BYTE $0xA7 + CALL _strayintr(SB); BYTE $0xA8 + CALL _strayintr(SB); BYTE $0xA9 + CALL _strayintr(SB); BYTE $0xAA + CALL _strayintr(SB); BYTE $0xAB + CALL _strayintr(SB); BYTE $0xAC + CALL _strayintr(SB); BYTE $0xAD + CALL _strayintr(SB); BYTE $0xAE + CALL _strayintr(SB); BYTE $0xAF + CALL _strayintr(SB); BYTE $0xB0 + CALL _strayintr(SB); BYTE $0xB1 + CALL _strayintr(SB); BYTE $0xB2 + CALL _strayintr(SB); BYTE $0xB3 + CALL _strayintr(SB); BYTE $0xB4 + CALL _strayintr(SB); BYTE $0xB5 + CALL _strayintr(SB); BYTE $0xB6 + CALL _strayintr(SB); BYTE $0xB7 + CALL _strayintr(SB); BYTE $0xB8 + CALL _strayintr(SB); BYTE $0xB9 + CALL _strayintr(SB); BYTE $0xBA + CALL _strayintr(SB); BYTE $0xBB + CALL _strayintr(SB); BYTE $0xBC + CALL _strayintr(SB); BYTE $0xBD + CALL _strayintr(SB); BYTE $0xBE + CALL _strayintr(SB); BYTE $0xBF + CALL _strayintr(SB); BYTE $0xC0 + CALL _strayintr(SB); BYTE $0xC1 + CALL _strayintr(SB); BYTE $0xC2 + CALL _strayintr(SB); BYTE $0xC3 + CALL _strayintr(SB); BYTE $0xC4 + CALL _strayintr(SB); BYTE $0xC5 + CALL _strayintr(SB); BYTE $0xC6 + CALL _strayintr(SB); BYTE $0xC7 + CALL _strayintr(SB); BYTE $0xC8 + CALL _strayintr(SB); BYTE $0xC9 + CALL _strayintr(SB); BYTE $0xCA + CALL _strayintr(SB); BYTE $0xCB + CALL _strayintr(SB); BYTE $0xCC + CALL _strayintr(SB); BYTE $0xCD + CALL _strayintr(SB); BYTE $0xCE + CALL _strayintr(SB); BYTE $0xCF + CALL _strayintr(SB); BYTE $0xD0 + CALL _strayintr(SB); BYTE $0xD1 + CALL _strayintr(SB); BYTE $0xD2 + CALL _strayintr(SB); BYTE $0xD3 + CALL _strayintr(SB); BYTE $0xD4 + CALL _strayintr(SB); BYTE $0xD5 + CALL _strayintr(SB); BYTE $0xD6 + CALL _strayintr(SB); BYTE $0xD7 + CALL _strayintr(SB); BYTE $0xD8 + CALL _strayintr(SB); BYTE $0xD9 + CALL _strayintr(SB); BYTE $0xDA + CALL _strayintr(SB); BYTE $0xDB + CALL _strayintr(SB); BYTE $0xDC + CALL _strayintr(SB); BYTE $0xDD + CALL _strayintr(SB); BYTE $0xDE + CALL _strayintr(SB); BYTE $0xDF + CALL _strayintr(SB); BYTE $0xE0 + CALL _strayintr(SB); BYTE $0xE1 + CALL _strayintr(SB); BYTE $0xE2 + CALL _strayintr(SB); BYTE $0xE3 + CALL _strayintr(SB); BYTE $0xE4 + CALL _strayintr(SB); BYTE $0xE5 + CALL _strayintr(SB); BYTE $0xE6 + CALL _strayintr(SB); BYTE $0xE7 + CALL _strayintr(SB); BYTE $0xE8 + CALL _strayintr(SB); BYTE $0xE9 + CALL _strayintr(SB); BYTE $0xEA + CALL _strayintr(SB); BYTE $0xEB + CALL _strayintr(SB); BYTE $0xEC + CALL _strayintr(SB); BYTE $0xED + CALL _strayintr(SB); BYTE $0xEE + CALL _strayintr(SB); BYTE $0xEF + CALL _strayintr(SB); BYTE $0xF0 + CALL _strayintr(SB); BYTE $0xF1 + CALL _strayintr(SB); BYTE $0xF2 + CALL _strayintr(SB); BYTE $0xF3 + CALL _strayintr(SB); BYTE $0xF4 + CALL _strayintr(SB); BYTE $0xF5 + CALL _strayintr(SB); BYTE $0xF6 + CALL _strayintr(SB); BYTE $0xF7 + CALL _strayintr(SB); BYTE $0xF8 + CALL _strayintr(SB); BYTE $0xF9 + CALL _strayintr(SB); BYTE $0xFA + CALL _strayintr(SB); BYTE $0xFB + CALL _strayintr(SB); BYTE $0xFC + CALL _strayintr(SB); BYTE $0xFD + CALL _strayintr(SB); BYTE $0xFE + CALL _strayintr(SB); BYTE $0xFF diff --git a/os/pc/main.c b/os/pc/main.c new file mode 100644 index 00000000..df4f240e --- /dev/null +++ b/os/pc/main.c @@ -0,0 +1,457 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" + +extern int main_pool_pcnt; +extern int heap_pool_pcnt; +extern int image_pool_pcnt; +int pckdebug; + +Mach *m; + +static uchar *sp; /* stack pointer for /boot */ + +/* + * Where configuration info is left for the loaded programme. + * This will turn into a structure as more is done by the boot loader + * (e.g. why parse the .ini file twice?). + * There are 3584 bytes available at CONFADDR. + */ +#define BOOTLINE ((char*)CONFADDR) +#define BOOTLINELEN 64 +#define BOOTARGS ((char*)(CONFADDR+BOOTLINELEN)) +#define BOOTARGSLEN (4096-0x200-BOOTLINELEN) +#define MAXCONF 64 + +char bootdisk[KNAMELEN]; +char *confname[MAXCONF]; +char *confval[MAXCONF]; +int nconf; + +static void +options(void) +{ + long i, n; + char *cp, *line[MAXCONF], *p, *q; + + /* + * parse configuration args from dos file plan9.ini + */ + cp = BOOTARGS; /* where b.com leaves its config */ + cp[BOOTARGSLEN-1] = 0; + + /* + * Strip out '\r', change '\t' -> ' '. + */ + p = cp; + for(q = cp; *q; q++){ + if(*q == '\r') + continue; + if(*q == '\t') + *q = ' '; + *p++ = *q; + } + *p = 0; + + n = getfields(cp, line, MAXCONF, 1, "\n"); + for(i = 0; i < n; i++){ + if(*line[i] == '#') + continue; + cp = strchr(line[i], '='); + if(cp == nil) + continue; + *cp++ = '\0'; + confname[nconf] = line[i]; + confval[nconf] = cp; + nconf++; + } +} + +static void +doc(char *m) +{ + int i; + print("%s...\n", m); + for(i = 0; i < 100*1024*1024; i++) + i++; +} + +void +main(void) +{ + outb(0x3F2, 0x00); /* botch: turn off the floppy motor */ + + mach0init(); + options(); + ioinit(); + i8250console(); + quotefmtinstall(); + kbdinit(); + i8253init(); + cpuidentify(); + confinit(); + archinit(); + xinit(); + poolsizeinit(); + trapinit(); + printinit(); + screeninit(); + cpuidprint(); + mmuinit(); + eve = strdup("inferno"); + if(arch->intrinit){ /* launches other processors on an mp */ + doc("intrinit"); + arch->intrinit(); + } + doc("timersinit"); + timersinit(); + doc("mathinit"); + mathinit(); + doc("kbdenable"); + kbdenable(); + if(arch->clockenable){ + doc("clockinit"); + arch->clockenable(); + } + doc("procinit"); + procinit(); + doc("links"); + links(); + doc("chandevreset"); + chandevreset(); + doc("userinit"); + userinit(); + doc("schedinit"); + active.thunderbirdsarego = 1; + schedinit(); + +} + +void +mach0init(void) +{ + conf.nmach = 1; + MACHP(0) = (Mach*)CPU0MACH; + m->pdb = (ulong*)CPU0PDB; + m->gdt = (Segdesc*)CPU0GDT; + + machinit(); + + active.machs = 1; + active.exiting = 0; +} + +void +machinit(void) +{ + int machno; + ulong *pdb; + Segdesc *gdt; + + machno = m->machno; + pdb = m->pdb; + gdt = m->gdt; + memset(m, 0, sizeof(Mach)); + m->machno = machno; + m->pdb = pdb; + m->gdt = gdt; + + /* + * For polled uart output at boot, need + * a default delay constant. 100000 should + * be enough for a while. Cpuidentify will + * calculate the real value later. + */ + m->loopconst = 100000; +} + +void +init0(void) +{ + Osenv *o; + int i; + char buf[2*KNAMELEN]; + + up->nerrlab = 0; + + spllo(); + if(waserror()) + panic("init0: %r"); + /* + * These are o.k. because rootinit is null. + * Then early kproc's will have a root and dot. + */ + o = up->env; + o->pgrp->slash = namec("#/", Atodir, 0, 0); + cnameclose(o->pgrp->slash->name); + o->pgrp->slash->name = newcname("/"); + o->pgrp->dot = cclone(o->pgrp->slash); + + chandevinit(); + + if(!waserror()){ + ksetenv("cputype", "386", 0); + snprint(buf, sizeof(buf), "386 %s", conffile); + ksetenv("terminal", buf, 0); + for(i = 0; i < nconf; i++){ + if(confname[i][0] != '*') + ksetenv(confname[i], confval[i], 0); + ksetenv(confname[i], confval[i], 1); + } + poperror(); + } + + poperror(); + + disinit("/osinit.dis"); +} + +void +userinit(void) +{ + Proc *p; + Osenv *o; + + p = newproc(); + o = p->env; + + o->fgrp = newfgrp(nil); + + o->pgrp = newpgrp(); + kstrdup(&o->user, eve); + + strcpy(p->text, "interp"); + + p->fpstate = FPINIT; + fpoff(); + + /* + * Kernel Stack + * + * N.B. make sure there's + * 4 bytes for gotolabel's return PC + */ + p->sched.pc = (ulong)init0; + p->sched.sp = (ulong)p->kstack+KSTACK-BY2WD; + + ready(p); +} + +Conf conf; + +char* +getconf(char *name) +{ + int i; + + for(i = 0; i < nconf; i++) + if(cistrcmp(confname[i], name) == 0) + return confval[i]; + return 0; +} + +void +confinit(void) +{ + char *p; + int pcnt; + ulong maxmem; + + if(p = getconf("*maxmem")) + maxmem = strtoul(p, 0, 0); + else + maxmem = 0; + if(p = getconf("*kernelpercent")) + pcnt = 100 - strtol(p, 0, 0); + else + pcnt = 0; + + meminit(maxmem); + + conf.npage = conf.npage0 + conf.npage1; + if(pcnt < 10) + pcnt = 70; + conf.ialloc = (((conf.npage*(100-pcnt))/100)/2)*BY2PG; + + conf.nproc = 100 + ((conf.npage*BY2PG)/MB)*5; +} + +void +poolsizeinit(void) +{ + ulong nb; + + nb = conf.npage*BY2PG; + poolsize(mainmem, (nb*main_pool_pcnt)/100, 0); + poolsize(heapmem, (nb*heap_pool_pcnt)/100, 0); + poolsize(imagmem, (nb*image_pool_pcnt)/100, 1); +} + +static char *mathmsg[] = +{ + "invalid operation", + "denormalized operand", + "division by zero", + "numeric overflow", + "numeric underflow", + "precision loss", + "stack", + "error", +}; + +/* + * math coprocessor error + */ +void +matherror(Ureg* ureg, void* arg) +{ + ulong status; + int i; + char *msg; + char note[ERRMAX]; + + USED(arg); + + /* + * a write cycle to port 0xF0 clears the interrupt latch attached + * to the error# line from the 387 + */ + if(!(m->cpuiddx & 0x01)) + outb(0xF0, 0xFF); + + /* + * save floating point state to check out error + */ + FPsave(&up->fpsave.env); + status = up->fpsave.env.status; + + msg = 0; + for(i = 0; i < 8; i++) + if((1<<i) & status){ + msg = mathmsg[i]; + sprint(note, "sys: fp: %s fppc=0x%lux", msg, up->fpsave.env.pc); + error(note); + break; + } + if(msg == 0){ + sprint(note, "sys: fp: unknown fppc=0x%lux", up->fpsave.env.pc); + error(note); + } + if(ureg->pc & KZERO) + panic("fp: status %lux fppc=0x%lux pc=0x%lux", status, + up->fpsave.env.pc, ureg->pc); +} + +/* + * math coprocessor emulation fault + */ +void +mathemu(Ureg* ureg, void* arg) +{ + USED(ureg, arg); + switch(up->fpstate){ + case FPINIT: + fpinit(); + up->fpstate = FPACTIVE; + break; + case FPINACTIVE: + fprestore(&up->fpsave); + up->fpstate = FPACTIVE; + break; + case FPACTIVE: + panic("math emu"); + break; + } +} + +/* + * math coprocessor segment overrun + */ +void +mathover(Ureg* ureg, void* arg) +{ + USED(arg); + print("sys: fp: math overrun pc 0x%lux pid %ld\n", ureg->pc, up->pid); + pexit("math overrun", 0); +} + +void +mathinit(void) +{ + trapenable(VectorCERR, matherror, 0, "matherror"); + if(X86FAMILY(m->cpuidax) == 3) + intrenable(IrqIRQ13, matherror, 0, BUSUNKNOWN, "matherror"); + trapenable(VectorCNA, mathemu, 0, "mathemu"); + trapenable(VectorCSO, mathover, 0, "mathover"); +} + +/* + * Save the mach dependent part of the process state. + */ +void +procsave(Proc *p) +{ + if(p->fpstate == FPACTIVE){ + if(p->state == Moribund) + fpoff(); + else + fpsave(&up->fpsave); + p->fpstate = FPINACTIVE; + } +} + +void +exit(int ispanic) +{ + USED(ispanic); + + up = 0; + print("exiting\n"); + + /* Shutdown running devices */ + chandevshutdown(); + + arch->reset(); +} + +void +reboot(void) +{ + exit(0); +} + +int +isaconfig(char *class, int ctlrno, ISAConf *isa) +{ + char cc[32], *p; + int i; + + snprint(cc, sizeof cc, "%s%d", class, ctlrno); + p = getconf(cc); + if(p == nil) + return 0; + + isa->nopt = tokenize(p, isa->opt, NISAOPT); + for(i = 0; i < isa->nopt; i++){ + p = isa->opt[i]; + if(cistrncmp(p, "type=", 5) == 0) + isa->type = p + 5; + else if(cistrncmp(p, "port=", 5) == 0) + isa->port = strtoul(p+5, &p, 0); + else if(cistrncmp(p, "irq=", 4) == 0) + isa->irq = strtoul(p+4, &p, 0); + else if(cistrncmp(p, "dma=", 4) == 0) + isa->dma = strtoul(p+4, &p, 0); + else if(cistrncmp(p, "mem=", 4) == 0) + isa->mem = strtoul(p+4, &p, 0); + else if(cistrncmp(p, "size=", 5) == 0) + isa->size = strtoul(p+5, &p, 0); + else if(cistrncmp(p, "freq=", 5) == 0) + isa->freq = strtoul(p+5, &p, 0); + } + return 1; +} diff --git a/os/pc/mem.h b/os/pc/mem.h new file mode 100644 index 00000000..b67252ee --- /dev/null +++ b/os/pc/mem.h @@ -0,0 +1,144 @@ +/* + * Memory and machine-specific definitions. Used in C and assembler. + */ + +/* + * Sizes + */ +#define BI2BY 8 /* bits per byte */ +#define BI2WD 32 /* bits per word */ +#define BY2WD 4 /* bytes per word */ +#define BY2V 8 /* bytes per double word */ +#define BY2PG 4096 /* bytes per page */ +#define WD2PG (BY2PG/BY2WD) /* words per page */ +#define PGSHIFT 12 /* log(BY2PG) */ +#define ROUND(s, sz) (((s)+((sz)-1))&~((sz)-1)) +#define PGROUND(s) ROUND(s, BY2PG) +#define BLOCKALIGN 8 + +#define MAXMACH 8 /* max # cpus system can run */ +#define KSTACK 8192 /* Size of kernel stack */ + +/* + * Time + */ +#define HZ (100) /* clock frequency */ +#define MS2HZ (1000/HZ) /* millisec per clock tick */ +#define TK2SEC(t) ((t)/HZ) /* ticks to seconds */ +#define MS2TK(t) ((((ulong)(t))*HZ)/1000) /* milliseconds to ticks */ + +/* + * Fundamental addresses + */ +#define IDTADDR 0x80000800 /* idt */ +#define REBOOTADDR 0x00001000 /* reboot code - physical address */ +#define APBOOTSTRAP 0x80001000 /* AP bootstrap code */ +#define CONFADDR 0x80001200 /* info passed from boot loader */ +#define CPU0PDB 0x80002000 /* bootstrap processor PDB */ +#define CPU0PTE 0x80003000 /* bootstrap processor PTE's for 0-4MB */ +#define CPU0GDT 0x80004000 /* bootstrap processor GDT */ +#define MACHADDR 0x80005000 /* as seen by current processor */ +#define CPU0MACH 0x80006000 /* Mach for bootstrap processor */ +#define MACHSIZE BY2PG +/* + * N.B. ramscan knows that CPU0MACH+BY2PG is the end of reserved data + * N.B. _start0x00100020 knows that CPU0PDB is the first reserved page + * and that there are 5 of them. + */ + +/* + * Address spaces + * + * User is at 0-2GB + * Kernel is at 2GB-4GB + */ +#define UZERO 0 /* base of user address space */ +#define UTZERO (UZERO+BY2PG) /* first address in user text */ +#define KZERO 0x80000000 /* base of kernel address space */ +#define KTZERO 0x80100000 /* first address in kernel text */ +#define USTKTOP (KZERO-BY2PG) /* byte just beyond user stack */ +#define USTKSIZE (16*1024*1024) /* size of user stack */ +#define TSTKTOP (USTKTOP-USTKSIZE) /* end of new stack in sysexec */ +#define TSTKSIZ 100 + +/* + * known x86 segments (in GDT) and their selectors + */ +#define NULLSEG 0 /* null segment */ +#define KDSEG 1 /* kernel data/stack */ +#define KESEG 2 /* kernel executable */ +#define UDSEG 3 /* user data/stack */ +#define UESEG 4 /* user executable */ +#define TSSSEG 5 /* task segment */ +#define APMCSEG 6 /* APM code segment */ +#define APMCSEG16 7 /* APM 16-bit code segment */ +#define APMDSEG 8 /* APM data segment */ +#define NGDT 10 /* number of GDT entries required */ +/* #define APM40SEG 8 /* APM segment 0x40 */ + +#define SELGDT (0<<2) /* selector is in gdt */ +#define SELLDT (1<<2) /* selector is in ldt */ + +#define SELECTOR(i, t, p) (((i)<<3) | (t) | (p)) + +#define NULLSEL SELECTOR(NULLSEG, SELGDT, 0) +#define KDSEL SELECTOR(KDSEG, SELGDT, 0) +#define KESEL SELECTOR(KESEG, SELGDT, 0) +#define UESEL SELECTOR(UESEG, SELGDT, 3) +#define UDSEL SELECTOR(UDSEG, SELGDT, 3) +#define TSSSEL SELECTOR(TSSSEG, SELGDT, 0) +#define APMCSEL SELECTOR(APMCSEG, SELGDT, 0) +#define APMCSEL16 SELECTOR(APMCSEG16, SELGDT, 0) +#define APMDSEL SELECTOR(APMDSEG, SELGDT, 0) +/* #define APM40SEL SELECTOR(APM40SEG, SELGDT, 0) */ + +/* + * fields in segment descriptors + */ +#define SEGDATA (0x10<<8) /* data/stack segment */ +#define SEGEXEC (0x18<<8) /* executable segment */ +#define SEGTSS (0x9<<8) /* TSS segment */ +#define SEGCG (0x0C<<8) /* call gate */ +#define SEGIG (0x0E<<8) /* interrupt gate */ +#define SEGTG (0x0F<<8) /* trap gate */ +#define SEGTYPE (0x1F<<8) + +#define SEGP (1<<15) /* segment present */ +#define SEGPL(x) ((x)<<13) /* priority level */ +#define SEGB (1<<22) /* granularity 1==4k (for expand-down) */ +#define SEGG (1<<23) /* granularity 1==4k (for other) */ +#define SEGE (1<<10) /* expand down */ +#define SEGW (1<<9) /* writable (for data/stack) */ +#define SEGR (1<<9) /* readable (for code) */ +#define SEGD (1<<22) /* default 1==32bit (for code) */ + +/* + * virtual MMU + */ +#define PTEMAPMEM (1024*1024) +#define PTEPERTAB (PTEMAPMEM/BY2PG) +#define SEGMAPSIZE 1984 +#define SSEGMAPSIZE 16 +#define PPN(x) ((x)&~(BY2PG-1)) + +/* + * physical MMU + */ +#define PTEVALID (1<<0) +#define PTEWT (1<<3) +#define PTEUNCACHED (1<<4) +#define PTEWRITE (1<<1) +#define PTERONLY (0<<1) +#define PTEKERNEL (0<<2) +#define PTEUSER (1<<2) +#define PTESIZE (1<<7) +#define PTEGLOBAL (1<<8) + +/* + * Macros for calculating offsets within the page directory base + * and page tables. + */ +#define PDX(va) ((((ulong)(va))>>22) & 0x03FF) +#define PTX(va) ((((ulong)(va))>>12) & 0x03FF) + +#define getpgcolor(a) 0 diff --git a/os/pc/memory.c b/os/pc/memory.c new file mode 100644 index 00000000..a58119c6 --- /dev/null +++ b/os/pc/memory.c @@ -0,0 +1,570 @@ +/* + * Size memory and create the kernel page-tables on the fly while doing so. + * Called from main(), this code should only be run by the bootstrap processor. + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#define MEMDEBUG 0 + +enum { + MemUPA = 0, /* unbacked physical address */ + MemRAM = 1, /* physical memory */ + MemUMB = 2, /* upper memory block (<16MB) */ + NMemType = 3, + + KB = 1024, + + MemMinMB = 4, /* minimum physical memory (<=4MB) */ + MemMaxMB = 768, /* maximum physical memory to check */ + + NMemBase = 10, +}; + +typedef struct { + int size; + ulong addr; +} Map; + +typedef struct { + char* name; + Map* map; + Map* mapend; + + Lock; +} RMap; + +static Map mapupa[16]; +static RMap rmapupa = { + "unallocated unbacked physical memory", + mapupa, + &mapupa[nelem(mapupa)-1], +}; + +static Map xmapupa[16]; +static RMap xrmapupa = { + "unbacked physical memory", + xmapupa, + &xmapupa[nelem(xmapupa)-1], +}; + +static Map mapram[16]; +static RMap rmapram = { + "physical memory", + mapram, + &mapram[nelem(mapram)-1], +}; + +static Map mapumb[64]; +static RMap rmapumb = { + "upper memory block", + mapumb, + &mapumb[nelem(mapumb)-1], +}; + +static Map mapumbrw[16]; +static RMap rmapumbrw = { + "UMB device memory", + mapumbrw, + &mapumbrw[nelem(mapumbrw)-1], +}; + +void +mapprint(RMap *rmap) +{ + Map *mp; + + print("%s\n", rmap->name); + for(mp = rmap->map; mp->size; mp++) + print("\t%8.8luX %8.8uX %8.8luX\n", mp->addr, mp->size, mp->addr+mp->size); +} + +void +memdebug(void) +{ + ulong maxpa, maxpa1, maxpa2; + + if(MEMDEBUG == 0) + return; + + maxpa = (nvramread(0x18)<<8)|nvramread(0x17); + maxpa1 = (nvramread(0x31)<<8)|nvramread(0x30); + maxpa2 = (nvramread(0x16)<<8)|nvramread(0x15); + print("maxpa = %luX -> %luX, maxpa1 = %luX maxpa2 = %luX\n", + maxpa, MB+maxpa*KB, maxpa1, maxpa2); + + mapprint(&rmapram); + mapprint(&rmapumb); + mapprint(&rmapumbrw); + mapprint(&rmapupa); +} + +void +mapfree(RMap* rmap, ulong addr, ulong size) +{ + Map *mp; + ulong t; + + if(size <= 0) + return; + + lock(rmap); + for(mp = rmap->map; mp->addr <= addr && mp->size; mp++) + ; + + if(mp > rmap->map && (mp-1)->addr+(mp-1)->size == addr){ + (mp-1)->size += size; + if(addr+size == mp->addr){ + (mp-1)->size += mp->size; + while(mp->size){ + mp++; + (mp-1)->addr = mp->addr; + (mp-1)->size = mp->size; + } + } + } + else{ + if(addr+size == mp->addr && mp->size){ + mp->addr -= size; + mp->size += size; + } + else do{ + if(mp >= rmap->mapend){ + print("mapfree: %s: losing 0x%luX, %ld\n", + rmap->name, addr, size); + break; + } + t = mp->addr; + mp->addr = addr; + addr = t; + t = mp->size; + mp->size = size; + mp++; + }while(size = t); + } + unlock(rmap); +} + +ulong +mapalloc(RMap* rmap, ulong addr, int size, int align) +{ + Map *mp; + ulong maddr, oaddr; + + lock(rmap); + for(mp = rmap->map; mp->size; mp++){ + maddr = mp->addr; + + if(addr){ + /* + * A specific address range has been given: + * if the current map entry is greater then + * the address is not in the map; + * if the current map entry does not overlap + * the beginning of the requested range then + * continue on to the next map entry; + * if the current map entry does not entirely + * contain the requested range then the range + * is not in the map. + */ + if(maddr > addr) + break; + if(mp->size < addr - maddr) /* maddr+mp->size < addr, but no overflow */ + continue; + if(addr - maddr > mp->size - size) /* addr+size > maddr+mp->size, but no overflow */ + break; + maddr = addr; + } + + if(align > 0) + maddr = ((maddr+align-1)/align)*align; + if(mp->addr+mp->size-maddr < size) + continue; + + oaddr = mp->addr; + mp->addr = maddr+size; + mp->size -= maddr-oaddr+size; + if(mp->size == 0){ + do{ + mp++; + (mp-1)->addr = mp->addr; + }while((mp-1)->size = mp->size); + } + + unlock(rmap); + if(oaddr != maddr) + mapfree(rmap, oaddr, maddr-oaddr); + + return maddr; + } + unlock(rmap); + + return 0; +} + +static void +umbscan(void) +{ + uchar *p; + + /* + * Scan the Upper Memory Blocks (0xA0000->0xF0000) for pieces + * which aren't used; they can be used later for devices which + * want to allocate some virtual address space. + * Check for two things: + * 1) device BIOS ROM. This should start with a two-byte header + * of 0x55 0xAA, followed by a byte giving the size of the ROM + * in 512-byte chunks. These ROM's must start on a 2KB boundary. + * 2) device memory. This is read-write. + * There are some assumptions: there's VGA memory at 0xA0000 and + * the VGA BIOS ROM is at 0xC0000. Also, if there's no ROM signature + * at 0xE0000 then the whole 64KB up to 0xF0000 is theoretically up + * for grabs; check anyway. + */ + p = KADDR(0xD0000); + while(p < (uchar*)KADDR(0xE0000)){ + /* + * Test for 0x55 0xAA before poking obtrusively, + * some machines (e.g. Thinkpad X20) seem to map + * something dynamic here (cardbus?) causing weird + * problems if it is changed. + */ + if(p[0] == 0x55 && p[1] == 0xAA){ + p += p[2]*512; + continue; + } + + p[0] = 0xCC; + p[2*KB-1] = 0xCC; + if(p[0] != 0xCC || p[2*KB-1] != 0xCC){ + p[0] = 0x55; + p[1] = 0xAA; + p[2] = 4; + if(p[0] == 0x55 && p[1] == 0xAA){ + p += p[2]*512; + continue; + } + if(p[0] == 0xFF && p[1] == 0xFF) + mapfree(&rmapumb, PADDR(p), 2*KB); + } + else + mapfree(&rmapumbrw, PADDR(p), 2*KB); + p += 2*KB; + } + + p = KADDR(0xE0000); + if(p[0] != 0x55 || p[1] != 0xAA){ + p[0] = 0xCC; + p[64*KB-1] = 0xCC; + if(p[0] != 0xCC && p[64*KB-1] != 0xCC) + mapfree(&rmapumb, PADDR(p), 64*KB); + } +} + + +static void +ramscan(ulong maxmem) +{ + ulong *k0, kzero, map, maxpa, pa, *pte, *table, *va, x, n; + int nvalid[NMemType]; + uchar *bda; + + /* + * The bootstrap code has has created a prototype page + * table which maps the first MemMinMB of physical memory to KZERO. + * The page directory is at m->pdb and the first page of + * free memory is after the per-processor MMU information. + */ + /* + * Initialise the memory bank information for conventional memory + * (i.e. less than 640KB). The base is the first location after the + * bootstrap processor MMU information and the limit is obtained from + * the BIOS data area. + */ + x = PADDR(CPU0MACH+BY2PG); + bda = (uchar*)KADDR(0x400); + n = ((bda[0x14]<<8)|bda[0x13])*KB-x; + mapfree(&rmapram, x, n); +// memset(KADDR(x), 0, n); /* keep us honest */ + + x = PADDR(PGROUND((ulong)end)); + pa = MemMinMB*MB; + mapfree(&rmapram, x, pa-x); +// memset(KADDR(x), 0, pa-x); /* keep us honest */ + + /* + * Check if the extended memory size can be obtained from the CMOS. + * If it's 0 then it's either not known or >= 64MB. Always check + * at least 24MB in case there's a memory gap (up to 8MB) below 16MB; + * in this case the memory from the gap is remapped to the top of + * memory. + * The value in CMOS is supposed to be the number of KB above 1MB. + */ + if(maxmem == 0){ + x = (nvramread(0x18)<<8)|nvramread(0x17); + if(x == 0 || x >= (63*KB)) + maxpa = MemMaxMB*MB; + else + maxpa = MB+x*KB; + if(maxpa < 24*MB) + maxpa = 24*MB; + maxmem = MemMaxMB*MB; + } + else + maxpa = maxmem; + + /* + * March up memory from MemMinMB to maxpa 1MB at a time, + * mapping the first page and checking the page can + * be written and read correctly. The page tables are created here + * on the fly, allocating from low memory as necessary. + */ + k0 = (ulong*)KADDR(0); + kzero = *k0; + map = 0; + x = 0x12345678; + memset(nvalid, 0, sizeof(nvalid)); + while(pa < maxpa){ + /* + * Map the page. Use mapalloc(&rmapram, ...) to make + * the page table if necessary, it will be returned to the + * pool later if it isn't needed. + */ + va = KADDR(pa); + table = &m->pdb[PDX(va)]; + if(*table == 0){ + if(map == 0 && (map = mapalloc(&rmapram, 0, BY2PG, BY2PG)) == 0) + break; + memset(KADDR(map), 0, BY2PG); + *table = map|PTEWRITE|PTEVALID; + memset(nvalid, 0, sizeof(nvalid)); + } + table = KADDR(PPN(*table)); + pte = &table[PTX(va)]; + + *pte = pa|PTEWRITE|PTEUNCACHED|PTEVALID; + mmuflushtlb(PADDR(m->pdb)); + + /* + * Write a pattern to the page and write a different + * pattern to a possible mirror at KZER0. If the data + * reads back correctly the chunk is some type of RAM (possibly + * a linearly-mapped VGA framebuffer, for instance...) and + * can be cleared and added to the memory pool. If not, the + * chunk is marked uncached and added to the UMB pool if <16MB + * or is marked invalid and added to the UPA pool. + */ + *va = x; + *k0 = ~x; + if(*va == x){ + nvalid[MemRAM] += MB/BY2PG; + mapfree(&rmapram, pa, MB); + + do{ + *pte++ = pa|PTEWRITE|PTEVALID; + pa += BY2PG; + }while(pa % MB); + mmuflushtlb(PADDR(m->pdb)); + /* memset(va, 0, MB); so damn slow to memset all of memory */ + } + else if(pa < 16*MB){ + nvalid[MemUMB] += MB/BY2PG; + mapfree(&rmapumb, pa, MB); + + do{ + *pte++ = pa|PTEWRITE|PTEUNCACHED|PTEVALID; + pa += BY2PG; + }while(pa % MB); + } + else{ + nvalid[MemUPA] += MB/BY2PG; + mapfree(&rmapupa, pa, MB); + + *pte = 0; + pa += MB; + } + + /* + * Done with this 4MB chunk, review the options: + * 1) not physical memory and >=16MB - invalidate the PDB entry; + * 2) physical memory - use the 4MB page extension if possible; + * 3) not physical memory and <16MB - use the 4MB page extension + * if possible; + * 4) mixed or no 4MB page extension - commit the already + * initialised space for the page table. + */ + if((pa % (4*MB)) == 0){ + table = &m->pdb[PDX(va)]; + if(nvalid[MemUPA] == (4*MB)/BY2PG) + *table = 0; + else if(nvalid[MemRAM] == (4*MB)/BY2PG && (m->cpuiddx & 0x08)) + *table = (pa - 4*MB)|PTESIZE|PTEWRITE|PTEVALID; + else if(nvalid[MemUMB] == (4*MB)/BY2PG && (m->cpuiddx & 0x08)) + *table = (pa - 4*MB)|PTESIZE|PTEWRITE|PTEUNCACHED|PTEVALID; + else + map = 0; + } + + mmuflushtlb(PADDR(m->pdb)); + x += 0x3141526; + } + + /* + * If we didn't reach the end of the 4MB chunk, that part won't + * be mapped. Commit the already initialised space for the page table. + */ + if(pa % (4*MB)) + map = 0; + + if(map) + mapfree(&rmapram, map, BY2PG); + if(pa < maxmem) + mapfree(&rmapupa, pa, maxmem-pa); + if(maxmem < 0xFFE00000) + mapfree(&rmapupa, maxmem, 0x00000000-maxmem); + if(MEMDEBUG) + print("maxmem %luX %luX\n", maxmem, 0x00000000-maxmem); + *k0 = kzero; +} + +void +meminit(ulong maxmem) +{ + Map *mp, *xmp; + ulong pa, *pte; + + /* + * Set special attributes for memory between 640KB and 1MB: + * VGA memory is writethrough; + * BIOS ROM's/UMB's are uncached; + * then scan for useful memory. + */ + for(pa = 0xA0000; pa < 0xC0000; pa += BY2PG){ + pte = mmuwalk(m->pdb, (ulong)KADDR(pa), 2, 0); + *pte |= PTEWT; + } + for(pa = 0xC0000; pa < 0x100000; pa += BY2PG){ + pte = mmuwalk(m->pdb, (ulong)KADDR(pa), 2, 0); + *pte |= PTEUNCACHED; + } + mmuflushtlb(PADDR(m->pdb)); + + umbscan(); + ramscan(maxmem); + + /* + * Set the conf entries describing two banks of allocatable memory. + * Grab the first and largest entries in rmapram as left by ramscan(). + * + * It would be nice to have more than 2 memory banks describable in conf. + */ + mp = rmapram.map; + conf.base0 = mp->addr; + conf.npage0 = mp->size/BY2PG; + mp++; + for(xmp = 0; mp->size; mp++){ + if(xmp == 0 || mp->size > xmp->size) + xmp = mp; + } + + if(xmp){ + conf.base1 = xmp->addr; + conf.npage1 = xmp->size/BY2PG; + } + if(MEMDEBUG) + memdebug(); +} + +ulong +umbmalloc(ulong addr, int size, int align) +{ + ulong a; + + if(a = mapalloc(&rmapumb, addr, size, align)) + return (ulong)KADDR(a); + + return 0; +} + +void +umbfree(ulong addr, int size) +{ + mapfree(&rmapumb, PADDR(addr), size); +} + +ulong +umbrwmalloc(ulong addr, int size, int align) +{ + ulong a; + uchar *p; + + if(a = mapalloc(&rmapumbrw, addr, size, align)) + return(ulong)KADDR(a); + + /* + * Perhaps the memory wasn't visible before + * the interface is initialised, so try again. + */ + if((a = umbmalloc(addr, size, align)) == 0) + return 0; + p = (uchar*)a; + p[0] = 0xCC; + p[size-1] = 0xCC; + if(p[0] == 0xCC && p[size-1] == 0xCC) + return a; + umbfree(a, size); + + return 0; +} + +void +umbrwfree(ulong addr, int size) +{ + mapfree(&rmapumbrw, PADDR(addr), size); +} + +ulong +upamalloc(ulong pa, int size, int align) +{ + ulong a, ae; + + if(a = mapalloc(&xrmapupa, pa, size, align)) + return a; + + if((a = mapalloc(&rmapupa, pa, size, align)) == 0){ + memdebug(); + return 0; + } + + /* + * Upamalloc is a request to map a range of physical addresses. + * Therefore, if pa is 0 mapalloc will choose the base address. + * Note, however, mmukmap is always asked to give a 1-to-1 mapping + * of va to pa. + */ + ae = mmukmap(a, a, size); + + /* + * Should check here that it was all delivered + * and put it back and barf if not. + */ + USED(ae); + + /* + * Be very careful this returns a PHYSICAL address + * mapped 1-to-1 with the virtual address. + * If a < KZERO it's probably not a good idea to + * try KADDR(a)... + */ + return a; +} + +void +upafree(ulong pa, int size) +{ + mapfree(&xrmapupa, pa, size); +} diff --git a/os/pc/mkfile b/os/pc/mkfile new file mode 100644 index 00000000..c7366fd2 --- /dev/null +++ b/os/pc/mkfile @@ -0,0 +1,83 @@ +<../../mkconfig + +#Configurable parameters + +CONF=pc #default configuration +CONFLIST=pc pcdisk +CLEANCONFLIST=pc pcdisk zpc zpcrem pix pcsoe + +SYSTARG=$OSTARG +OBJTYPE=386 +INSTALLDIR=$ROOT/Inferno/$OBJTYPE/bin #path of directory where kernel is installed +#INSTALLDIR=/$OBJTYPE + +#end configurable parameters + +<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE #set vars based on target system + +<| $SHELLNAME ../port/mkdevlist $CONF #sets $IP, $DEVS, $ETHERS, $VGAS, $PORT, $MISC, $LIBS, $OTHERS + +OBJ=\ + l.$O\ + fpsave.$O\ + portclock.$O\ + tod.$O\ + i8250.$O\ + i8253.$O\ + i8259.$O\ + kbd.$O\ + main.$O\ + memory.$O\ + mmu.$O\ + trap.$O\ + $CONF.root.$O\ + $IP\ + $DEVS\ + $ETHERS\ + $LINKS\ + $PORT\ + $MISC\ + $OTHERS\ + +LIBNAMES=${LIBS:%=lib%.a} + +HFILES=\ + mem.h\ + dat.h\ + fns.h\ + io.h\ + +CFLAGS=-wFVT -I$ROOT/Inferno/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp -I../port +KERNDATE=`{$NDATE} + +default:V: i$CONF + +i$CONF: $OBJ $CONF.c $CONF.root.h $LIBNAMES + $CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c + $LD -o $target -T0x80100020 -l $OBJ $CONF.$O $LIBFILES + $KSIZE $target + +install:V: i$CONF + cp i$CONF $INSTALLDIR/i$CONF + +<../port/portmkfile + +clock.$O: $ROOT/Inferno/$OBJTYPE/include/ureg.h +devether.$O: $ROOT/Inferno/$OBJTYPE/include/ureg.h +fault386.$O: $ROOT/Inferno/$OBJTYPE/include/ureg.h +main.$O: $ROOT/Inferno/$OBJTYPE/include/ureg.h +trap.$O: $ROOT/Inferno/$OBJTYPE/include/ureg.h + +devether.$O $ETHERS: etherif.h ../port/netif.h +$IP devip.$O: ../ip/ip.h + +# to be moved to port/interp +bench.h:D: ../../module/bench.m + rm -f $target && limbo -a -I../../module ../../module/bench.m > $target +benchmod.h:D: ../../module/bench.m + rm -f $target && limbo -t Bench -I../../module ../../module/bench.m > $target +devbench.$O: bench.h benchmod.h +$VGA screen.$O: screen.h vga.h + +devuart.$O: ../port/devuart.c ../port/uart.h + $CC $CFLAGS ../port/devuart.c diff --git a/os/pc/mmu.c b/os/pc/mmu.c new file mode 100644 index 00000000..6bd4ddfb --- /dev/null +++ b/os/pc/mmu.c @@ -0,0 +1,321 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#define DATASEGM(p) { 0xFFFF, SEGG|SEGB|(0xF<<16)|SEGP|SEGPL(p)|SEGDATA|SEGW } +#define EXECSEGM(p) { 0xFFFF, SEGG|SEGD|(0xF<<16)|SEGP|SEGPL(p)|SEGEXEC|SEGR } +#define TSSSEGM(b,p) { ((b)<<16)|sizeof(Tss),\ + ((b)&0xFF000000)|(((b)>>16)&0xFF)|SEGTSS|SEGPL(p)|SEGP } + +Segdesc gdt[NGDT] = +{ +[NULLSEG] { 0, 0}, /* null descriptor */ +[KDSEG] DATASEGM(0), /* kernel data/stack */ +[KESEG] EXECSEGM(0), /* kernel code */ +[UDSEG] DATASEGM(3), /* user data/stack */ +[UESEG] EXECSEGM(3), /* user code */ +[TSSSEG] TSSSEGM(0,0), /* tss segment */ +}; + +static void +taskswitch(ulong pdb, ulong stack) +{ + Tss *tss; + + tss = m->tss; + tss->ss0 = KDSEL; + tss->esp0 = stack; + tss->ss1 = KDSEL; + tss->esp1 = stack; + tss->ss2 = KDSEL; + tss->esp2 = stack; + tss->cr3 = pdb; + putcr3(pdb); +} + +/* + * On processors that support it, we set the PTEGLOBAL bit in + * page table and page directory entries that map kernel memory. + * Doing this tells the processor not to bother flushing them + * from the TLB when doing the TLB flush associated with a + * context switch (write to CR3). Since kernel memory mappings + * are never removed, this is safe. (If we ever remove kernel memory + * mappings, we can do a full flush by turning off the PGE bit in CR4, + * writing to CR3, and then turning the PGE bit back on.) + * + * See also mmukmap below. + * + * Processor support for the PTEGLOBAL bit is enabled in devarch.c. + */ +static void +memglobal(void) +{ + int i, j; + ulong *pde, *pte; + + /* only need to do this once, on bootstrap processor */ + if(m->machno != 0) + return; + + if(!m->havepge) + return; + + pde = m->pdb; + for(i=512; i<1024; i++){ /* 512: start at entry for virtual 0x80000000 */ + if(pde[i] & PTEVALID){ + pde[i] |= PTEGLOBAL; + if(!(pde[i] & PTESIZE)){ + pte = KADDR(pde[i]&~(BY2PG-1)); + for(j=0; j<1024; j++) + if(pte[j] & PTEVALID) + pte[j] |= PTEGLOBAL; + } + } + } +} + +void +mmuinit(void) +{ + ulong x, *p; + ushort ptr[3]; + + memglobal(); + + m->tss = malloc(sizeof(Tss)); + memset(m->tss, 0, sizeof(Tss)); + m->tss->iomap = 0xDFFF<<16; + + /* + * We used to keep the GDT in the Mach structure, but it + * turns out that that slows down access to the rest of the + * page. Since the Mach structure is accessed quite often, + * it pays off anywhere from a factor of 1.25 to 2 on real + * hardware to separate them (the AMDs are more sensitive + * than Intels in this regard). Under VMware it pays off + * a factor of about 10 to 100. + */ + + memmove(m->gdt, gdt, sizeof gdt); + x = (ulong)m->tss; + m->gdt[TSSSEG].d0 = (x<<16)|sizeof(Tss); + m->gdt[TSSSEG].d1 = (x&0xFF000000)|((x>>16)&0xFF)|SEGTSS|SEGPL(0)|SEGP; + + ptr[0] = sizeof(gdt)-1; + x = (ulong)m->gdt; + ptr[1] = x & 0xFFFF; + ptr[2] = (x>>16) & 0xFFFF; + lgdt(ptr); + + ptr[0] = sizeof(Segdesc)*256-1; + x = IDTADDR; + ptr[1] = x & 0xFFFF; + ptr[2] = (x>>16) & 0xFFFF; + lidt(ptr); + + /* make kernel text unwritable */ + for(x = KTZERO; x < (ulong)etext; x += BY2PG){ + p = mmuwalk(m->pdb, x, 2, 0); + if(p == nil) + panic("mmuinit"); + *p &= ~PTEWRITE; + } + + taskswitch(PADDR(m->pdb), (ulong)m + BY2PG); + ltr(TSSSEL); +} + + + + +ulong* +mmuwalk(ulong* pdb, ulong va, int level, int create) +{ + ulong pa, *table; + + /* + * Walk the page-table pointed to by pdb and return a pointer + * to the entry for virtual address va at the requested level. + * If the entry is invalid and create isn't requested then bail + * out early. Otherwise, for the 2nd level walk, allocate a new + * page-table page and register it in the 1st level. + */ + table = &pdb[PDX(va)]; + if(!(*table & PTEVALID) && create == 0) + return 0; + + switch(level){ + + default: + return 0; + + case 1: + return table; + + case 2: + if(*table & PTESIZE) + panic("mmuwalk2: va %luX entry %luX\n", va, *table); + if(!(*table & PTEVALID)){ + pa = PADDR(xspanalloc(BY2PG, BY2PG, 0)); + *table = pa|PTEWRITE|PTEVALID; + } + table = KADDR(PPN(*table)); + + return &table[PTX(va)]; + } +} + +static Lock mmukmaplock; + +int +mmukmapsync(ulong va) +{ + Mach *mach0; + ulong entry, *pte; + + mach0 = MACHP(0); + + ilock(&mmukmaplock); + + if((pte = mmuwalk(mach0->pdb, va, 1, 0)) == nil){ + iunlock(&mmukmaplock); + return 0; + } + if(!(*pte & PTESIZE) && mmuwalk(mach0->pdb, va, 2, 0) == nil){ + iunlock(&mmukmaplock); + return 0; + } + entry = *pte; + + if(!(m->pdb[PDX(va)] & PTEVALID)) + m->pdb[PDX(va)] = entry; + +// if(up && up->mmupdb){ +// ((ulong*)up->mmupdb->va)[PDX(va)] = entry; +// mmuflushtlb(up->mmupdb->pa); +// } +// else + mmuflushtlb(PADDR(m->pdb)); + + iunlock(&mmukmaplock); + + return 1; +} + +ulong +mmukmap(ulong pa, ulong va, int size) +{ + Mach *mach0; + ulong ova, pae, *table, pgsz, *pte, x; + int pse, sync; + + mach0 = MACHP(0); + if((mach0->cpuiddx & 0x08) && (getcr4() & 0x10)) + pse = 1; + else + pse = 0; + sync = 0; + + pa = PPN(pa); + if(va == 0) + va = (ulong)KADDR(pa); + else + va = PPN(va); + ova = va; + + pae = pa + size; + ilock(&mmukmaplock); + while(pa < pae){ + table = &mach0->pdb[PDX(va)]; + /* + * Possibly already mapped. + */ + if(*table & PTEVALID){ + if(*table & PTESIZE){ + /* + * Big page. Does it fit within? + * If it does, adjust pgsz so the correct end can be + * returned and get out. + * If not, adjust pgsz up to the next 4MB boundary + * and continue. + */ + x = PPN(*table); + if(x != pa) + panic("mmukmap1: pa %luX entry %luX\n", + pa, *table); + x += 4*MB; + if(pae <= x){ + pa = pae; + break; + } + pgsz = x - pa; + pa += pgsz; + va += pgsz; + + continue; + } + else{ + /* + * Little page. Walk to the entry. + * If the entry is valid, set pgsz and continue. + * If not, make it so, set pgsz, sync and continue. + */ + pte = mmuwalk(mach0->pdb, va, 2, 0); + if(pte && *pte & PTEVALID){ + x = PPN(*pte); + if(x != pa) + panic("mmukmap2: pa %luX entry %luX\n", + pa, *pte); + pgsz = BY2PG; + pa += pgsz; + va += pgsz; + sync++; + + continue; + } + } + } + + /* + * Not mapped. Check if it can be mapped using a big page - + * starts on a 4MB boundary, size >= 4MB and processor can do it. + * If not a big page, walk the walk, talk the talk. + * Sync is set. + * + * If we're creating a kernel mapping, we know that it will never + * expire and thus we can set the PTEGLOBAL bit to make the entry + * persist in the TLB across flushes. If we do add support later for + * unmapping kernel addresses, see devarch.c for instructions on + * how to do a full TLB flush. + */ + if(pse && (pa % (4*MB)) == 0 && (pae >= pa+4*MB)){ + *table = pa|PTESIZE|PTEWRITE|PTEUNCACHED|PTEVALID; + if((va&KZERO) && m->havepge) + *table |= PTEGLOBAL; + pgsz = 4*MB; + } + else{ + pte = mmuwalk(mach0->pdb, va, 2, 1); + *pte = pa|PTEWRITE|PTEUNCACHED|PTEVALID; + if((va&KZERO) && m->havepge) + *pte |= PTEGLOBAL; + pgsz = BY2PG; + } + pa += pgsz; + va += pgsz; + sync++; + } + iunlock(&mmukmaplock); + + /* + * If something was added + * then need to sync up. + */ + if(sync) + mmukmapsync(ova); + + return pa; +} diff --git a/os/pc/mouse.c b/os/pc/mouse.c new file mode 100644 index 00000000..49654bad --- /dev/null +++ b/os/pc/mouse.c @@ -0,0 +1,84 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "io.h" + +/* + * mouse types + */ +enum +{ + Mouseother= 0, + Mouseserial= 1, + MousePS2= 2, +}; + +static int mousetype; + +/* + * ps/2 mouse message is three bytes + * + * byte 0 - 0 0 SDY SDX 1 M R L + * byte 1 - DX + * byte 2 - DY + * + * shift & left button is the same as middle button + */ +static void +ps2mouseputc(int c, int shift) +{ + static short msg[3]; + static int nb; + static uchar b[] = {0, 1, 4, 5, 2, 3, 6, 7, 0, 1, 2, 5, 2, 3, 6, 7 }; + int buttons, dx, dy; + + /* + * check byte 0 for consistency + */ + if(nb==0 && (c&0xc8)!=0x08) + return; + + msg[nb] = c; + if(++nb == 3){ + nb = 0; + if(msg[0] & 0x10) + msg[1] |= 0xFF00; + if(msg[0] & 0x20) + msg[2] |= 0xFF00; + + buttons = b[(msg[0]&7) | (shift ? 8 : 0)]; + dx = msg[1]; + dy = -msg[2]; + mousetrack(buttons, dx, dy, 1); + } + return; +} + +/* + * set up a ps2 mouse + */ +static void +ps2mouse(void) +{ + if(mousetype == MousePS2) + return; + + i8042auxenable(ps2mouseputc); + /* make mouse streaming, enabled */ + i8042auxcmd(0xEA); + i8042auxcmd(0xF4); + + mousetype = MousePS2; +} + +void +ps2mouselink(void) +{ + /* + * hack + */ + ps2mouse(); +} diff --git a/os/pc/mp.c b/os/pc/mp.c new file mode 100644 index 00000000..f16fab91 --- /dev/null +++ b/os/pc/mp.c @@ -0,0 +1,815 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" + +#include "mp.h" +#include "apbootstrap.h" + +static Bus* mpbus; +static Bus* mpbuslast; +static int mpisabus = -1; +static int mpeisabus = -1; +extern int i8259elcr; /* mask of level-triggered interrupts */ +static Apic mpapic[MaxAPICNO+1]; +static int machno2apicno[MaxAPICNO+1]; /* inverse map: machno -> APIC ID */ +static Lock mprdthilock; +static int mprdthi; +static Ref mpvnoref; /* unique vector assignment */ +static int mpmachno = 1; + +static char* buses[] = { + "CBUSI ", + "CBUSII", + "EISA ", + "FUTURE", + "INTERN", + "ISA ", + "MBI ", + "MBII ", + "MCA ", + "MPI ", + "MPSA ", + "NUBUS ", + "PCI ", + "PCMCIA", + "TC ", + "VL ", + "VME ", + "XPRESS", + 0, +}; + +static Apic* +mkprocessor(PCMPprocessor* p) +{ + Apic *apic; + + if(!(p->flags & PcmpEN) || p->apicno > MaxAPICNO) + return 0; + + apic = &mpapic[p->apicno]; + apic->type = PcmpPROCESSOR; + apic->apicno = p->apicno; + apic->flags = p->flags; + apic->lintr[0] = ApicIMASK; + apic->lintr[1] = ApicIMASK; + + if(p->flags & PcmpBP){ + machno2apicno[0] = p->apicno; + apic->machno = 0; + } + else{ + machno2apicno[mpmachno] = p->apicno; + apic->machno = mpmachno; + mpmachno++; + } + + return apic; +} + +static Bus* +mkbus(PCMPbus* p) +{ + Bus *bus; + int i; + + for(i = 0; buses[i]; i++){ + if(strncmp(buses[i], p->string, sizeof(p->string)) == 0) + break; + } + if(buses[i] == 0) + return 0; + + bus = xalloc(sizeof(Bus)); + if(mpbus) + mpbuslast->next = bus; + else + mpbus = bus; + mpbuslast = bus; + + bus->type = i; + bus->busno = p->busno; + if(bus->type == BusEISA){ + bus->po = PcmpLOW; + bus->el = PcmpLEVEL; + if(mpeisabus != -1) + print("mkbus: more than one EISA bus\n"); + mpeisabus = bus->busno; + } + else if(bus->type == BusPCI){ + bus->po = PcmpLOW; + bus->el = PcmpLEVEL; + } + else if(bus->type == BusISA){ + bus->po = PcmpHIGH; + bus->el = PcmpEDGE; + if(mpisabus != -1) + print("mkbus: more than one ISA bus\n"); + mpisabus = bus->busno; + } + else{ + bus->po = PcmpHIGH; + bus->el = PcmpEDGE; + } + + return bus; +} + +static Bus* +mpgetbus(int busno) +{ + Bus *bus; + + for(bus = mpbus; bus; bus = bus->next){ + if(bus->busno == busno) + return bus; + } + print("mpgetbus: can't find bus %d\n", busno); + + return 0; +} + +static Apic* +mkioapic(PCMPioapic* p) +{ + Apic *apic; + + if(!(p->flags & PcmpEN) || p->apicno > MaxAPICNO) + return 0; + + /* + * Map the I/O APIC. + */ + if(mmukmap(p->addr, 0, 1024) == 0) + return 0; + + apic = &mpapic[p->apicno]; + apic->type = PcmpIOAPIC; + apic->apicno = p->apicno; + apic->addr = KADDR(p->addr); + apic->flags = p->flags; + + return apic; +} + +static Aintr* +mkiointr(PCMPintr* p) +{ + Bus *bus; + Aintr *aintr; + + /* + * According to the MultiProcessor Specification, a destination + * I/O APIC of 0xFF means the signal is routed to all I/O APICs. + * It's unclear how that can possibly be correct so treat it as + * an error for now. + */ + if(p->apicno == 0xFF) + return 0; + if((bus = mpgetbus(p->busno)) == 0) + return 0; + + aintr = xalloc(sizeof(Aintr)); + aintr->intr = p; + aintr->apic = &mpapic[p->apicno]; + aintr->next = bus->aintr; + bus->aintr = aintr; + + return aintr; +} + +static int +mpintrinit(Bus* bus, PCMPintr* intr, int vno, int /*irq*/) +{ + int el, po, v; + + /* + * Parse an I/O or Local APIC interrupt table entry and + * return the encoded vector. + */ + v = vno; + + po = intr->flags & PcmpPOMASK; + el = intr->flags & PcmpELMASK; + + switch(intr->intr){ + + default: /* PcmpINT */ + v |= ApicLOWEST; + break; + + case PcmpNMI: + v |= ApicNMI; + po = PcmpHIGH; + el = PcmpEDGE; + break; + + case PcmpSMI: + v |= ApicSMI; + break; + + case PcmpExtINT: + v |= ApicExtINT; + /* + * The AMI Goliath doesn't boot successfully with it's LINTR0 + * entry which decodes to low+level. The PPro manual says ExtINT + * should be level, whereas the Pentium is edge. Setting the + * Goliath to edge+high seems to cure the problem. Other PPro + * MP tables (e.g. ASUS P/I-P65UP5 have a entry which decodes + * to edge+high, so who knows. + * Perhaps it would be best just to not set an ExtINT entry at + * all, it shouldn't be needed for SMP mode. + */ + po = PcmpHIGH; + el = PcmpEDGE; + break; + } + + /* + */ + if(bus->type == BusEISA && !po && !el /*&& !(i8259elcr & (1<<irq))*/){ + po = PcmpHIGH; + el = PcmpEDGE; + } + if(!po) + po = bus->po; + if(po == PcmpLOW) + v |= ApicLOW; + else if(po != PcmpHIGH){ + print("mpintrinit: bad polarity 0x%uX\n", po); + return ApicIMASK; + } + + if(!el) + el = bus->el; + if(el == PcmpLEVEL) + v |= ApicLEVEL; + else if(el != PcmpEDGE){ + print("mpintrinit: bad trigger 0x%uX\n", el); + return ApicIMASK; + } + + return v; +} + +static int +mklintr(PCMPintr* p) +{ + Apic *apic; + Bus *bus; + int intin, v; + + /* + * The offsets of vectors for LINT[01] are known to be + * 0 and 1 from the local APIC vector space at VectorLAPIC. + */ + if((bus = mpgetbus(p->busno)) == 0) + return 0; + intin = p->intin; + + /* + * Pentium Pros have problems if LINT[01] are set to ExtINT + * so just bag it, SMP mode shouldn't need ExtINT anyway. + */ + if(p->intr == PcmpExtINT || p->intr == PcmpNMI) + v = ApicIMASK; + else + v = mpintrinit(bus, p, VectorLAPIC+intin, p->irq); + + if(p->apicno == 0xFF){ + for(apic = mpapic; apic <= &mpapic[MaxAPICNO]; apic++){ + if((apic->flags & PcmpEN) + && apic->type == PcmpPROCESSOR) + apic->lintr[intin] = v; + } + } + else{ + apic = &mpapic[p->apicno]; + if((apic->flags & PcmpEN) && apic->type == PcmpPROCESSOR) + apic->lintr[intin] = v; + } + + return v; +} + +static void +checkmtrr(void) +{ + int i, vcnt; + Mach *mach0; + + /* + * If there are MTRR registers, snarf them for validation. + */ + if(!(m->cpuiddx & 0x1000)) + return; + + rdmsr(0x0FE, &m->mtrrcap); + rdmsr(0x2FF, &m->mtrrdef); + if(m->mtrrcap & 0x0100){ + rdmsr(0x250, &m->mtrrfix[0]); + rdmsr(0x258, &m->mtrrfix[1]); + rdmsr(0x259, &m->mtrrfix[2]); + for(i = 0; i < 8; i++) + rdmsr(0x268+i, &m->mtrrfix[(i+3)]); + } + vcnt = m->mtrrcap & 0x00FF; + if(vcnt > nelem(m->mtrrvar)) + vcnt = nelem(m->mtrrvar); + for(i = 0; i < vcnt; i++) + rdmsr(0x200+i, &m->mtrrvar[i]); + + /* + * If not the bootstrap processor, compare. + */ + if(m->machno == 0) + return; + + mach0 = MACHP(0); + if(mach0->mtrrcap != m->mtrrcap) + print("mtrrcap%d: %lluX %lluX\n", + m->machno, mach0->mtrrcap, m->mtrrcap); + if(mach0->mtrrdef != m->mtrrdef) + print("mtrrdef%d: %lluX %lluX\n", + m->machno, mach0->mtrrdef, m->mtrrdef); + for(i = 0; i < 11; i++){ + if(mach0->mtrrfix[i] != m->mtrrfix[i]) + print("mtrrfix%d: i%d: %lluX %lluX\n", + m->machno, i, mach0->mtrrfix[i], m->mtrrfix[i]); + } + for(i = 0; i < vcnt; i++){ + if(mach0->mtrrvar[i] != m->mtrrvar[i]) + print("mtrrvar%d: i%d: %lluX %lluX\n", + m->machno, i, mach0->mtrrvar[i], m->mtrrvar[i]); + } +} + +static void +squidboy(Apic* apic) +{ +// iprint("Hello Squidboy\n"); + + machinit(); + mmuinit(); + + cpuidentify(); + cpuidprint(); + checkmtrr(); + + lock(&mprdthilock); + mprdthi |= (1<<apic->apicno)<<24; + unlock(&mprdthilock); + + lapicinit(apic); + lapiconline(); + syncclock(); + timersinit(); + + fpoff(); + + lock(&active); + active.machs |= 1<<m->machno; + unlock(&active); + + while(!active.thunderbirdsarego) + microdelay(100); + + schedinit(); +} + +static void +mpstartap(Apic* apic) +{ + ulong *apbootp, *pdb, *pte; + Mach *mach, *mach0; + int i, machno; + uchar *p; + + mach0 = MACHP(0); + + /* + * Initialise the AP page-tables and Mach structure. The page-tables + * are the same as for the bootstrap processor with the exception of + * the PTE for the Mach structure. + * Xspanalloc will panic if an allocation can't be made. + */ + p = xspanalloc(4*BY2PG, BY2PG, 0); + pdb = (ulong*)p; + memmove(pdb, mach0->pdb, BY2PG); + p += BY2PG; + + if((pte = mmuwalk(pdb, MACHADDR, 1, 0)) == nil) + return; + memmove(p, KADDR(PPN(*pte)), BY2PG); + *pte = PADDR(p)|PTEWRITE|PTEVALID; + if(mach0->havepge) + *pte |= PTEGLOBAL; + p += BY2PG; + + mach = (Mach*)p; + if((pte = mmuwalk(pdb, MACHADDR, 2, 0)) == nil) + return; + *pte = PADDR(mach)|PTEWRITE|PTEVALID; + if(mach0->havepge) + *pte |= PTEGLOBAL; + p += BY2PG; + + machno = apic->machno; + MACHP(machno) = mach; + mach->machno = machno; + mach->pdb = pdb; + mach->gdt = (Segdesc*)p; /* filled by mmuinit */ + + /* + * Tell the AP where its kernel vector and pdb are. + * The offsets are known in the AP bootstrap code. + */ + apbootp = (ulong*)(APBOOTSTRAP+0x08); + *apbootp++ = (ulong)squidboy; + *apbootp++ = PADDR(pdb); + *apbootp = (ulong)apic; + + /* + * Universal Startup Algorithm. + */ + p = KADDR(0x467); + *p++ = PADDR(APBOOTSTRAP); + *p++ = PADDR(APBOOTSTRAP)>>8; + i = (PADDR(APBOOTSTRAP) & ~0xFFFF)/16; + *p++ = i; + *p = i>>8; + + nvramwrite(0x0F, 0x0A); + lapicstartap(apic, PADDR(APBOOTSTRAP)); + for(i = 0; i < 1000; i++){ + lock(&mprdthilock); + if(mprdthi & ((1<<apic->apicno)<<24)){ + unlock(&mprdthilock); + break; + } + unlock(&mprdthilock); + delay(10); + } + nvramwrite(0x0F, 0x00); +} + +void +mpinit(void) +{ + int ncpu; + char *cp; + PCMP *pcmp; + uchar *e, *p; + Apic *apic, *bpapic; + + i8259init(); + syncclock(); + + if(_mp_ == 0) + return; + pcmp = KADDR(_mp_->physaddr); + + /* + * Map the local APIC. + */ + if(mmukmap(pcmp->lapicbase, 0, 1024) == 0) + return; + + bpapic = nil; + + /* + * Run through the table saving information needed for starting + * application processors and initialising any I/O APICs. The table + * is guaranteed to be in order such that only one pass is necessary. + */ + p = ((uchar*)pcmp)+sizeof(PCMP); + e = ((uchar*)pcmp)+pcmp->length; + while(p < e) switch(*p){ + + default: + print("mpinit: unknown PCMP type 0x%uX (e-p 0x%luX)\n", + *p, e-p); + while(p < e){ + print("%uX ", *p); + p++; + } + break; + + case PcmpPROCESSOR: + if(apic = mkprocessor((PCMPprocessor*)p)){ + /* + * Must take a note of bootstrap processor APIC + * now as it will be needed in order to start the + * application processors later and there's no + * guarantee that the bootstrap processor appears + * first in the table before the others. + */ + apic->addr = KADDR(pcmp->lapicbase); + if(apic->flags & PcmpBP) + bpapic = apic; + } + p += sizeof(PCMPprocessor); + continue; + + case PcmpBUS: + mkbus((PCMPbus*)p); + p += sizeof(PCMPbus); + continue; + + case PcmpIOAPIC: + if(apic = mkioapic((PCMPioapic*)p)) + ioapicinit(apic, ((PCMPioapic*)p)->apicno); + p += sizeof(PCMPioapic); + continue; + + case PcmpIOINTR: + mkiointr((PCMPintr*)p); + p += sizeof(PCMPintr); + continue; + + case PcmpLINTR: + mklintr((PCMPintr*)p); + p += sizeof(PCMPintr); + continue; + } + + /* + * No bootstrap processor, no need to go further. + */ + if(bpapic == 0) + return; + + lapicinit(bpapic); + lock(&mprdthilock); + mprdthi |= (1<<bpapic->apicno)<<24; + unlock(&mprdthilock); + + /* + * These interrupts are local to the processor + * and do not appear in the I/O APIC so it is OK + * to set them now. + */ + intrenable(IrqTIMER, lapicclock, 0, BUSUNKNOWN, "clock"); + intrenable(IrqERROR, lapicerror, 0, BUSUNKNOWN, "lapicerror"); + intrenable(IrqSPURIOUS, lapicspurious, 0, BUSUNKNOWN, "lapicspurious"); + lapiconline(); + + checkmtrr(); + + /* + * Initialise the application processors. + */ + if(cp = getconf("*ncpu")){ + ncpu = strtol(cp, 0, 0); + if(ncpu < 1) + ncpu = 1; + } + else + ncpu = MaxAPICNO; + memmove((void*)APBOOTSTRAP, apbootstrap, sizeof(apbootstrap)); + for(apic = mpapic; apic <= &mpapic[MaxAPICNO]; apic++){ + if(ncpu <= 1) + break; + if((apic->flags & (PcmpBP|PcmpEN)) == PcmpEN + && apic->type == PcmpPROCESSOR){ + mpstartap(apic); + conf.nmach++; + ncpu--; + } + } + + /* + * we don't really know the number of processors till + * here. + * + * set conf.copymode here if nmach > 1. + * Should look for an ExtINT line and enable it. + */ + if(X86FAMILY(m->cpuidax) == 3 || conf.nmach > 1) + conf.copymode = 1; +} + +static int +mpintrenablex(Vctl* v, int tbdf) +{ + Bus *bus; + Aintr *aintr; + Apic *apic; + Pcidev *pcidev; + int bno, dno, irq, lo, n, type, vno; + + /* + * Find the bus. + */ + type = BUSTYPE(tbdf); + bno = BUSBNO(tbdf); + dno = BUSDNO(tbdf); + n = 0; + for(bus = mpbus; bus != nil; bus = bus->next){ + if(bus->type != type) + continue; + if(n == bno) + break; + n++; + } + if(bus == nil){ + print("ioapicirq: can't find bus type %d\n", type); + return -1; + } + + /* + * For PCI devices the interrupt pin (INT[ABCD]) and device + * number are encoded into the entry irq field, so create something + * to match on. The interrupt pin used by the device has to be + * obtained from the PCI config space. + */ + if(bus->type == BusPCI){ + pcidev = pcimatchtbdf(tbdf); + if(pcidev != nil && (n = pcicfgr8(pcidev, PciINTP)) != 0) + irq = (dno<<2)|(n-1); + else + irq = -1; + //print("pcidev %uX: irq %uX v->irq %uX\n", tbdf, irq, v->irq); + } + else + irq = v->irq; + + /* + * Find a matching interrupt entry from the list of interrupts + * attached to this bus. + */ + for(aintr = bus->aintr; aintr; aintr = aintr->next){ + if(aintr->intr->irq != irq) + continue; + + /* + * Check if already enabled. Multifunction devices may share + * INT[A-D]# so, if already enabled, check the polarity matches + * and the trigger is level. + * + * Should check the devices differ only in the function number, + * but that can wait for the planned enable/disable rewrite. + * The RDT read here is safe for now as currently interrupts + * are never disabled once enabled. + */ + apic = aintr->apic; + ioapicrdtr(apic, aintr->intr->intin, 0, &lo); + if(!(lo & ApicIMASK)){ + vno = lo & 0xFF; + n = mpintrinit(bus, aintr->intr, vno, v->irq); + n |= ApicLOGICAL; + if(n != lo || !(n & ApicLEVEL)){ + print("mpintrenable: multiple botch irq%d, tbdf %uX, lo %8.8uX, n %8.8uX\n", + v->irq, tbdf, lo, n); + return -1; + } + + v->isr = lapicisr; + v->eoi = lapiceoi; + + return vno; + } + + /* + * With the APIC a unique vector can be assigned to each + * request to enable an interrupt. There are two reasons this + * is a good idea: + * 1) to prevent lost interrupts, no more than 2 interrupts + * should be assigned per block of 16 vectors (there is an + * in-service entry and a holding entry for each priority + * level and there is one priority level per block of 16 + * interrupts). + * 2) each input pin on the IOAPIC will receive a different + * vector regardless of whether the devices on that pin use + * the same IRQ as devices on another pin. + */ + vno = VectorAPIC + (incref(&mpvnoref)-1)*8; + if(vno > MaxVectorAPIC){ + print("mpintrenable: vno %d, irq %d, tbdf %uX\n", + vno, v->irq, tbdf); + return -1; + } + lo = mpintrinit(bus, aintr->intr, vno, v->irq); + //print("lo 0x%uX: busno %d intr %d vno %d irq %d elcr 0x%uX\n", + // lo, bus->busno, aintr->intr->irq, vno, + // v->irq, i8259elcr); + if(lo & ApicIMASK) + return -1; + lo |= ApicLOGICAL; + + if((apic->flags & PcmpEN) && apic->type == PcmpIOAPIC){ + lock(&mprdthilock); + ioapicrdtw(apic, aintr->intr->intin, mprdthi, lo); + unlock(&mprdthilock); + } + //else + // print("lo not enabled 0x%uX %d\n", + // apic->flags, apic->type); + + v->isr = lapicisr; + v->eoi = lapiceoi; + + return vno; + } + + return -1; +} + +int +mpintrenable(Vctl* v) +{ + int irq, tbdf, vno; + + /* + * If the bus is known, try it. + * BUSUNKNOWN is given both by [E]ISA devices and by + * interrupts local to the processor (local APIC, coprocessor + * breakpoint and page-fault). + */ + tbdf = v->tbdf; + if(tbdf != BUSUNKNOWN && (vno = mpintrenablex(v, tbdf)) != -1) + return vno; + + irq = v->irq; + if(irq >= IrqLINT0 && irq <= MaxIrqLAPIC){ + if(irq != IrqSPURIOUS) + v->isr = lapiceoi; + return VectorPIC+irq; + } + if(irq < 0 || irq > MaxIrqPIC){ + print("mpintrenable: irq %d out of range\n", irq); + return -1; + } + + /* + * Either didn't find it or have to try the default buses + * (ISA and EISA). This hack is due to either over-zealousness + * or laziness on the part of some manufacturers. + * + * The MP configuration table on some older systems + * (e.g. ASUS PCI/E-P54NP4) has an entry for the EISA bus + * but none for ISA. It also has the interrupt type and + * polarity set to 'default for this bus' which wouldn't + * be compatible with ISA. + */ + if(mpeisabus != -1){ + vno = mpintrenablex(v, MKBUS(BusEISA, 0, 0, 0)); + if(vno != -1) + return vno; + } + if(mpisabus != -1){ + vno = mpintrenablex(v, MKBUS(BusISA, 0, 0, 0)); + if(vno != -1) + return vno; + } + + return -1; +} + +static Lock mpshutdownlock; + +void +mpshutdown(void) +{ + /* + * To be done... + */ + if(!canlock(&mpshutdownlock)){ + /* + * If this processor received the CTRL-ALT-DEL from + * the keyboard, acknowledge it. Send an INIT to self. + */ +#ifdef FIXTHIS + if(lapicisr(VectorKBD)) + lapiceoi(VectorKBD); +#endif /* FIX THIS */ + idle(); + } + + print("apshutdown: active = 0x%2.2uX\n", active.machs); + delay(1000); + splhi(); + + /* + * INIT all excluding self. + */ + lapicicrw(0, 0x000C0000|ApicINIT); + +#ifdef notdef + /* + * Often the BIOS hangs during restart if a conventional 8042 + * warm-boot sequence is tried. The following is Intel specific and + * seems to perform a cold-boot, but at least it comes back. + */ + *(ushort*)KADDR(0x472) = 0x1234; /* BIOS warm-boot flag */ + outb(0xCF9, 0x02); + outb(0xCF9, 0x06); +#else + pcireset(); + i8042reset(); +#endif /* notdef */ +} diff --git a/os/pc/mp.h b/os/pc/mp.h new file mode 100644 index 00000000..9195c9e9 --- /dev/null +++ b/os/pc/mp.h @@ -0,0 +1,225 @@ +/* + * MultiProcessor Specification Version 1.[14]. + */ +typedef struct { /* floating pointer */ + uchar signature[4]; /* "_MP_" */ + long physaddr; /* physical address of MP configuration table */ + uchar length; /* 1 */ + uchar specrev; /* [14] */ + uchar checksum; /* all bytes must add up to 0 */ + uchar type; /* MP system configuration type */ + uchar imcrp; + uchar reserved[3]; +} _MP_; + +typedef struct { /* configuration table header */ + uchar signature[4]; /* "PCMP" */ + ushort length; /* total table length */ + uchar version; /* [14] */ + uchar checksum; /* all bytes must add up to 0 */ + uchar product[20]; /* product id */ + ulong oemtable; /* OEM table pointer */ + ushort oemlength; /* OEM table length */ + ushort entry; /* entry count */ + ulong lapicbase; /* address of local APIC */ + ushort xlength; /* extended table length */ + uchar xchecksum; /* extended table checksum */ + uchar reserved; +} PCMP; + +typedef struct { /* processor table entry */ + uchar type; /* entry type (0) */ + uchar apicno; /* local APIC id */ + uchar version; /* local APIC verison */ + uchar flags; /* CPU flags */ + uchar signature[4]; /* CPU signature */ + ulong feature; /* feature flags from CPUID instruction */ + uchar reserved[8]; +} PCMPprocessor; + +typedef struct { /* bus table entry */ + uchar type; /* entry type (1) */ + uchar busno; /* bus id */ + char string[6]; /* bus type string */ +} PCMPbus; + +typedef struct { /* I/O APIC table entry */ + uchar type; /* entry type (2) */ + uchar apicno; /* I/O APIC id */ + uchar version; /* I/O APIC version */ + uchar flags; /* I/O APIC flags */ + ulong addr; /* I/O APIC address */ +} PCMPioapic; + +typedef struct { /* interrupt table entry */ + uchar type; /* entry type ([34]) */ + uchar intr; /* interrupt type */ + ushort flags; /* interrupt flag */ + uchar busno; /* source bus id */ + uchar irq; /* source bus irq */ + uchar apicno; /* destination APIC id */ + uchar intin; /* destination APIC [L]INTIN# */ +} PCMPintr; + +typedef struct { /* system address space mapping entry */ + uchar type; /* entry type (128) */ + uchar length; /* of this entry (20) */ + uchar busno; /* bus id */ + uchar addrtype; + ulong addrbase[2]; + ulong addrlength[2]; +} PCMPsasm; + +typedef struct { /* bus hierarchy descriptor entry */ + uchar type; /* entry type (129) */ + uchar length; /* of this entry (8) */ + uchar busno; /* bus id */ + uchar info; /* bus info */ + uchar parent; /* parent bus */ + uchar reserved[3]; +} PCMPhierarchy; + +typedef struct { /* compatibility bus address space modifier entry */ + uchar type; /* entry type (130) */ + uchar length; /* of this entry (8) */ + uchar busno; /* bus id */ + uchar modifier; /* address modifier */ + ulong range; /* predefined range list */ +} PCMPcbasm; + +enum { /* table entry types */ + PcmpPROCESSOR = 0x00, /* one entry per processor */ + PcmpBUS = 0x01, /* one entry per bus */ + PcmpIOAPIC = 0x02, /* one entry per I/O APIC */ + PcmpIOINTR = 0x03, /* one entry per bus interrupt source */ + PcmpLINTR = 0x04, /* one entry per system interrupt source */ + + PcmpSASM = 0x80, + PcmpHIERARCHY = 0x81, + PcmpCBASM = 0x82, + + /* PCMPprocessor and PCMPioapic flags */ + PcmpEN = 0x01, /* enabled */ + PcmpBP = 0x02, /* bootstrap processor */ + + /* PCMPiointr and PCMPlintr flags */ + PcmpPOMASK = 0x03, /* polarity conforms to specifications of bus */ + PcmpHIGH = 0x01, /* active high */ + PcmpLOW = 0x03, /* active low */ + PcmpELMASK = 0x0C, /* trigger mode of APIC input signals */ + PcmpEDGE = 0x04, /* edge-triggered */ + PcmpLEVEL = 0x0C, /* level-triggered */ + + /* PCMPiointr and PCMPlintr interrupt type */ + PcmpINT = 0x00, /* vectored interrupt from APIC Rdt */ + PcmpNMI = 0x01, /* non-maskable interrupt */ + PcmpSMI = 0x02, /* system management interrupt */ + PcmpExtINT = 0x03, /* vectored interrupt from external PIC */ + + /* PCMPsasm addrtype */ + PcmpIOADDR = 0x00, /* I/O address */ + PcmpMADDR = 0x01, /* memory address */ + PcmpPADDR = 0x02, /* prefetch address */ + + /* PCMPhierarchy info */ + PcmpSD = 0x01, /* subtractive decode bus */ + + /* PCMPcbasm modifier */ + PcmpPR = 0x01, /* predefined range list */ +}; + +/* + * Condensed form of the MP Configuration Table. + * This is created during a single pass through the MP Configuration + * table. + */ +typedef struct Aintr Aintr; +typedef struct Bus Bus; +typedef struct Apic Apic; + +typedef struct Bus { + uchar type; + uchar busno; + uchar po; + uchar el; + + Aintr* aintr; /* interrupts tied to this bus */ + Bus* next; +} Bus; + +typedef struct Aintr { + PCMPintr* intr; + Apic* apic; + Aintr* next; +}; + +typedef struct Apic { + int type; + int apicno; + ulong* addr; /* register base address */ + int flags; /* PcmpBP|PcmpEN */ + + Lock; /* I/O APIC: register access */ + int mre; /* I/O APIC: maximum redirection entry */ + + int lintr[2]; /* Local APIC */ + int machno; +} Apic; + +enum { + MaxAPICNO = 31, +}; + +enum { /* I/O APIC registers */ + IoapicID = 0x00, /* ID */ + IoapicVER = 0x01, /* version */ + IoapicARB = 0x02, /* arbitration ID */ + IoapicRDT = 0x10, /* redirection table */ +}; + +/* + * Common bits for + * I/O APIC Redirection Table Entry; + * Local APIC Local Interrupt Vector Table; + * Local APIC Inter-Processor Interrupt; + * Local APIC Timer Vector Table. + */ +enum { + ApicFIXED = 0x00000000, /* [10:8] Delivery Mode */ + ApicLOWEST = 0x00000100, /* Lowest priority */ + ApicSMI = 0x00000200, /* System Management Interrupt */ + ApicRR = 0x00000300, /* Remote Read */ + ApicNMI = 0x00000400, + ApicINIT = 0x00000500, /* INIT/RESET */ + ApicSTARTUP = 0x00000600, /* Startup IPI */ + ApicExtINT = 0x00000700, + + ApicPHYSICAL = 0x00000000, /* [11] Destination Mode (RW) */ + ApicLOGICAL = 0x00000800, + + ApicDELIVS = 0x00001000, /* [12] Delivery Status (RO) */ + ApicHIGH = 0x00000000, /* [13] Interrupt Input Pin Polarity (RW) */ + ApicLOW = 0x00002000, + ApicRemoteIRR = 0x00004000, /* [14] Remote IRR (RO) */ + ApicEDGE = 0x00000000, /* [15] Trigger Mode (RW) */ + ApicLEVEL = 0x00008000, + ApicIMASK = 0x00010000, /* [16] Interrupt Mask */ +}; + +extern void ioapicrdtr(Apic*, int, int*, int*); +extern void ioapicrdtw(Apic*, int, int, int); +extern void ioapicinit(Apic*, int); +extern void lapiconline(void); +extern void lapicinit(Apic*); +extern void lapicstartap(Apic*, int); +extern void lapicerror(Ureg*, void*); +extern void lapicspurious(Ureg*, void*); +extern int lapicisr(int); +extern int lapiceoi(int); +extern void lapicicrw(int, int); + +extern void mpinit(void); +extern void mpshutdown(void); +extern int mpintrenable(Vctl*); + +extern _MP_ *_mp_; diff --git a/os/pc/pc b/os/pc/pc new file mode 100644 index 00000000..56d82a89 --- /dev/null +++ b/os/pc/pc @@ -0,0 +1,139 @@ +dev + root + cons + arch + env + mnt + pipe + prog + rtc + srv + dup + ssl + cap + + draw screen vga vgax cga + pointer + vga + + ip bootp ip ipv6 ipaux iproute arp netlog ptclbsum386 iprouter plan9 nullmedium pktmedium + ether netif netaux ethermedium + +# ata + audio dma + uart +# floppy dma + tinyfs +# mouse +# dbg x86break +ip + tcp + udp +# rudp +# igmp + ipifc + icmp + icmp6 + ipmux +lib + interp + keyring + draw + memlayer + memdraw + tk + sec + mp + math + kern + +link + ether2114x pci +# ether82557 pci + ether83815 pci + etherelnk3 pci + ps2mouse + ethermedium +# pppmedium ppp compress + +misc + vgas3 +cur vgasavage + vgamach64xx + cga + uarti8250 + +mod + sys + draw + tk + keyring + math + +init + wminit + +code + int kernel_pool_pcnt = 10; + int main_pool_pcnt = 40; + int heap_pool_pcnt = 20; + int image_pool_pcnt = 40; + int cflag=0; + int swcursor=0; + int consoleprint=0; + +port + alarm + alloc + allocb + chan + dev + dial + dis + discall + exception + exportfs + inferno + latin1 + nocache + nodynld + parse + pgrp + print + proc + qio + qlock + random + sysfile + taslock + xalloc + +root + /chan / + /dev / + /dis + /env / + /fd / + /n + /n/remote + /net / + /nvfs / + /prog / + /dis/lib + /dis/svc + /dis/wm + /osinit.dis + /dis/sh.dis + /dis/ls.dis + /dis/cat.dis + /dis/bind.dis + /dis/mount.dis + /dis/pwd.dis + /dis/echo.dis + /dis/cd.dis + /dis/lib/bufio.dis + /dis/lib/string.dis + /dis/lib/readdir.dis + /dis/lib/workdir.dis + /dis/lib/daytime.dis + /dis/lib/auth.dis + /dis/lib/ssl.dis diff --git a/os/pc/pc4e b/os/pc/pc4e new file mode 100644 index 00000000..0507118c --- /dev/null +++ b/os/pc/pc4e @@ -0,0 +1,145 @@ +dev + root + cons + arch + pnp pci + env + mnt + pipe + prog + rtc + srv + dup + ssl + cap + draw + ip bootp ip ipv6 ipaux iproute arp netlog ptclbsum386 iprouter plan9 nullmedium pktmedium netaux + ether netif netaux ethermedium + +# ata +# audio + uart + floppy dma +# tinyfs +# mouse +# dbg x86break +ip + tcp + udp +# rudp +# igmp + ipifc + icmp + icmp6 + ipmux +lib + interp + keyring + draw + memlayer + memdraw + tk + sec + mp + math + kern + +link +# ether2114x pci +# ether82557 pci + ether83815 pci + etherelnk3 pci +# ps2mouse +# pppmedium ppp compress + ethermedium + +misc + cgamemscr +# vgaclgd542x +# vgas3 +# vgamach64ct + uarti8250 + +mod + sys + draw + tk + keyring + math + +init + i4e + +code + int kernel_pool_pcnt = 10; + int main_pool_pcnt = 40; + int heap_pool_pcnt = 20; + int image_pool_pcnt = 40; + int cflag=0; + int swcursor=0; + int consoleprint=1; + +port + alarm + alloc + allocb + chan + dev + dial + dis + discall + exception + exportfs + inferno + latin1 + nocache + nodynld + parse + pgrp + print + proc + qio + qlock + random + sysfile + taslock + xalloc + +root + /chan / + /dev / + /dis / + /env / + /fd / + /n + /n/remote + /net / + /net.alt / + /nvfs / + /prog / + /dis/lib + /dis/svc + /dis/wm + /osinit.dis + /dis/sh.dis + /dis/ls.dis + /dis/cat.dis + /dis/bind.dis + /dis/mount.dis + /dis/pwd.dis + /dis/echo.dis + /dis/cd.dis + /dis/lib/arg.dis + /dis/lib/bufio.dis + /dis/lib/dhcpclient.dis + /dis/lib/filepat.dis + /dis/lib/ip.dis + /dis/lib/string.dis + /dis/lib/readdir.dis + /dis/lib/workdir.dis + /dis/lib/daytime.dis + /dis/lib/auth.dis + /dis/lib/ssl.dis + /keydb / + /keydb/mutual /usr/i4e/keyring/mutual + /keydb/spree /usr/i4e/keyring/spree diff --git a/os/pc/pcdisk b/os/pc/pcdisk new file mode 100644 index 00000000..4ae6338a --- /dev/null +++ b/os/pc/pcdisk @@ -0,0 +1,154 @@ +dev + root + cons + arch + env + mnt + pipe + prog + rtc + srv + dup + ssl + cap + + ip bootp ip ipv6 ipaux iproute arp netlog ptclbsum386 iprouter plan9 nullmedium pktmedium + ether netif netaux ethermedium + + draw screen vga vgax cga + pointer + vga + + sd + ds + floppy dma + +# audio + uart + tinyfs + +# dbg x86break + +ip + tcp + udp +# rudp +# igmp + ipifc + icmp + icmp6 + ipmux +lib + interp + keyring + sec + mp + draw + memlayer + memdraw + tk + math + kern + +link + ether2114x pci +# ether82557 pci + ether83815 pci + etherelnk3 pci + ps2mouse + ethermedium +# pppmedium ppp compress + +misc + vgas3 +cur vgasavage + vgamach64xx + cga + + sdata pci sdscsi + sd53c8xx pci sdscsi + + uarti8250 + +mod + sys + draw + tk + keyring + math + +init + wminit + +code + int kernel_pool_pcnt = 10; + int main_pool_pcnt = 40; + int heap_pool_pcnt = 20; + int image_pool_pcnt = 40; + int cflag=0; + int swcursor=0; + int consoleprint=0; + int novgascreen=1; + +port + alarm + alloc + allocb + chan + dev + dial + dis + discall + exception + exportfs + inferno + latin1 + nocache + nodynld + parse + pgrp + print + proc + qio + qlock + random + sysfile + taslock + xalloc + +root + /chan + /dev + /dis + /env + /fd / + /n + /n/remote + /net + /nvfs + /prog + /dis/lib + /dis/svc + /dis/wm + /osinit.dis + /dis/sh.dis + /dis/ls.dis + /dis/cat.dis + /dis/bind.dis + /dis/mount.dis + /dis/pwd.dis + /dis/echo.dis + /dis/cd.dis + /dis/lib/bufio.dis + /dis/lib/string.dis + /dis/lib/readdir.dis + /dis/lib/workdir.dis + /dis/lib/daytime.dis + /dis/lib/auth.dis + /dis/lib/ssl.dis + +# kfs + /dis/disk/kfs.dis + /dis/lib/arg.dis + /dis/lib/daytime.dis + /dis/lib/string.dis + /dis/lib/styx.dis diff --git a/os/pc/pci.acid b/os/pc/pci.acid new file mode 100644 index 00000000..46da501e --- /dev/null +++ b/os/pc/pci.acid @@ -0,0 +1,252 @@ +// ACID PCI support + +pciclassdb = { + {0x0000, "Unclassified device", { + {0x0000,"Non-VGA unclassified device"}, + {0x0001,"VGA compatible unclassified device"}, + }}, + {0x0001, "Mass storage controller", { + {0x0000,"SCSI storage controller"}, + {0x0001,"IDE interface"}, + {0x0002,"Floppy disk controller"}, + {0x0003,"IPI bus controller"}, + {0x0004,"RAID bus controller"}, + {0x0080,"Unknown mass storage controller"}, + }}, + {0x0002, "Network controller", { + {0x0000,"Ethernet controller"}, + {0x0001,"Token ring network controller"}, + {0x0002,"FDDI network controller"}, + {0x0003,"ATM network controller"}, + {0x0080,"Network controller"}, + }}, + {0x0003, "Display controller", { + {0x0000,"VGA compatible controller"}, + {0x0001,"XGA compatible controller"}, + {0x0080,"Display controller"}, + }}, + {0x0004, "Multimedia controller", { + {0x0000,"Multimedia video controller"}, + {0x0001,"Multimedia audio controller"}, + {0x0080,"Multimedia controller"}, + }}, + {0x0005, "Memory controller", { + {0x0000,"RAM memory"}, + {0x0001,"FLASH memory"}, + {0x0080,"Memory"}, + }}, + {0x0006, "Bridge", { + {0x0000,"Host bridge"}, + {0x0001,"ISA bridge"}, + {0x0002,"EISA bridge"}, + {0x0003,"MicroChannel bridge"}, + {0x0004,"PCI bridge"}, + {0x0005,"PCMCIA bridge"}, + {0x0006,"NuBus bridge"}, + {0x0007,"CardBus bridge"}, + {0x0080,"Bridge"}, + }}, + {0x0007, "Communication controller", { + {0x0000,"Serial controller"}, + {0x0001,"Parallel controller"}, + {0x0080,"Communication controller"}, + }}, + {0x0008, "Generic system peripheral", { + {0x0000,"PIC"}, + {0x0001,"DMA controller"}, + {0x0002,"Timer"}, + {0x0003,"RTC"}, + {0x0080,"System peripheral"}, + }}, + {0x0009, "Input device controller", { + {0x0000,"Keyboard controller"}, + {0x0001,"Digitizer Pen"}, + {0x0002,"Mouse controller"}, + {0x0080,"Input device controller"}, + }}, + {0x000A, "Docking station", { + {0x0000,"Generic Docking Station"}, + {0x0080,"Docking Station"}, + }}, + {0x000B, "Processor", { + {0x0000,"386"}, + {0x0001,"486"}, + {0x0002,"Pentium"}, + {0x0010,"Alpha"}, + {0x0020,"Power PC"}, + {0x0040,"Co-processor"}, + }}, + {0x000C, "Serial bus controller", { + {0x0000,"FireWire (IEEE 1394)"}, + {0x0001,"ACCESS Bus"}, + {0x0002,"SSA"}, + {0x0003,"USB Controller"}, + {0x0004,"Fiber Channel"}, + }}, +}; + +// +// Include the vendor and device id database +// + +include( "pcidb.acid" ); + +defn test(thingy) +{ + return thingy; +} + +defn BUSFNO(tbdf) +{ + return (((tbdf\X)>>8)&0x07); +} + +defn BUSDNO(tbdf) +{ + return (((tbdf\X)>>11)&0x1F); +} + +defn BUSBNO(tbdf) +{ + return (((tbdf\X)>>16)&0xFF); +} + +defn lookup( key, dictionary ) +{ + local d, e; + + d = dictionary; + while(d != {}) do { + e = head d; + if e[0] == key then + return e; + d = tail d; + } + return {}; +} + +defn pciclass( ccru ) +{ + local c, sc; + local class, subclass; + c = ccru >> 8; + sc = ccru & 0xff; + + class = lookup( c, pciclassdb ); + subclass = lookup( sc, class[2] ); + + if (subclass != {}) then { + print(" ",subclass[1]); + return 1; + } + if (class != {}) then { + print(" ",class[1]); + return 1; + } + + print(" type=",ccru\x); + return 0; +} + +defn pcivendor( vid ) +{ + local v; + v = lookup( vid, pcivendordb ); + if (v != {}) then { + print(" ",v[1]); + } else { + print(" VendorID=",vid\x,":"); + } +} + +defn pcidev( vid, did ) +{ + local v; + local d; + v = lookup( vid, pcivendordb ); + if (v != {}) then { + d = lookup( did, v[2] ); + if (d != {}) then { + print(" ",d[1]); + return 1; + } + } + print(" DeviceID=",did\x); + return 0; +} + +// +// Dump PCI Info (short form) +// + +defn pciinfo( r ) +{ + local t; + local pcicount; + t = r; + pcicount = 0\d; + while t != 0 do { + print(pcicount\d,":","Bus:",BUSBNO(t.tbdf)\u," dev:",BUSDNO(t.tbdf)\u," fn:",BUSFNO(t.tbdf)\u,":"); + pciclass(t.ccru); + print(":"); + pcivendor(t.vid); + pcidev(t.vid, t.did); + print("\n"); + pcicount = pcicount+1 ; + t = t.link; + } + t = r; + while t!= 0 do { + if (t.bridge !=0) then + pciinfo(t.bridge); + t = t.link; + } +} + +defn lspci() +{ + pciinfo( *pciroot ); +} + +// +// Dump PCI Info (long form - includes interrupt lines & memory segments) +// + +defn pcidumper( r ) +{ + local t; + local m; + t = r; + while t != 0 do { + print("Bus\t",BUSBNO(t.tbdf)\u); + print(", device\t",BUSDNO(t.tbdf)\u); + print(", function\t",BUSFNO(t.tbdf)\u,":\n"); + print(" "); + pciclass(t.ccru); + print(":"); + pcivendor(t.vid); + pcidev(t.vid, t.did); + print("\n"); + print(" Interrupt Line: ",t.intl\u,"\n"); + m =0\d; + loop 0,5 do { + if t.mem[m] != 0 then + print("\t",(m/2)\d,":",t.mem[m]\X," ",t.mem[m+1]\X,"\n"); + m = m + 2; + } + t = t.link; + } + t = r; + while t!= 0 do { + if (t.bridge !=0) then + pcidumper(t.bridge); + t = t.link; + } +} + +defn pcidump() +{ + pcidumper( *pciroot ); +} + +print("/os/pc/pci"); diff --git a/os/pc/pci.c b/os/pc/pci.c new file mode 100644 index 00000000..4f7e79b6 --- /dev/null +++ b/os/pc/pci.c @@ -0,0 +1,1341 @@ +/* + * PCI support code. + * Needs a massive rewrite. + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#define DBG if(0) pcilog + +struct +{ + char output[16384]; + int ptr; +}PCICONS; + +int +pcilog(char *fmt, ...) +{ + int n; + va_list arg; + char buf[PRINTSIZE]; + + va_start(arg, fmt); + n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf; + va_end(arg); + + memmove(PCICONS.output+PCICONS.ptr, buf, n); + PCICONS.ptr += n; + return n; +} + +enum +{ /* configuration mechanism #1 */ + PciADDR = 0xCF8, /* CONFIG_ADDRESS */ + PciDATA = 0xCFC, /* CONFIG_DATA */ + + /* configuration mechanism #2 */ + PciCSE = 0xCF8, /* configuration space enable */ + PciFORWARD = 0xCFA, /* which bus */ + + MaxFNO = 7, + MaxUBN = 255, +}; + +enum +{ /* command register */ + IOen = (1<<0), + MEMen = (1<<1), + MASen = (1<<2), + MemWrInv = (1<<4), + PErrEn = (1<<6), + SErrEn = (1<<8), +}; + +static Lock pcicfglock; +static Lock pcicfginitlock; +static int pcicfgmode = -1; +static int pcimaxbno = 7; +static int pcimaxdno; +static Pcidev* pciroot; +static Pcidev* pcilist; +static Pcidev* pcitail; +static int nobios, nopcirouting; + +static int pcicfgrw32(int, int, int, int); +static int pcicfgrw16(int, int, int, int); +static int pcicfgrw8(int, int, int, int); + +static char* bustypes[] = { + "CBUSI", + "CBUSII", + "EISA", + "FUTURE", + "INTERN", + "ISA", + "MBI", + "MBII", + "MCA", + "MPI", + "MPSA", + "NUBUS", + "PCI", + "PCMCIA", + "TC", + "VL", + "VME", + "XPRESS", +}; + +#pragma varargck type "T" int + +static int +tbdffmt(Fmt* fmt) +{ + char *p; + int l, r, type, tbdf; + + if((p = malloc(READSTR)) == nil) + return fmtstrcpy(fmt, "(tbdfconv)"); + + switch(fmt->r){ + case 'T': + tbdf = va_arg(fmt->args, int); + type = BUSTYPE(tbdf); + if(type < nelem(bustypes)) + l = snprint(p, READSTR, bustypes[type]); + else + l = snprint(p, READSTR, "%d", type); + snprint(p+l, READSTR-l, ".%d.%d.%d", + BUSBNO(tbdf), BUSDNO(tbdf), BUSFNO(tbdf)); + break; + + default: + snprint(p, READSTR, "(tbdfconv)"); + break; + } + r = fmtstrcpy(fmt, p); + free(p); + + return r; +} + +ulong +pcibarsize(Pcidev *p, int rno) +{ + ulong v, size; + + v = pcicfgrw32(p->tbdf, rno, 0, 1); + pcicfgrw32(p->tbdf, rno, 0xFFFFFFF0, 0); + size = pcicfgrw32(p->tbdf, rno, 0, 1); + if(v & 1) + size |= 0xFFFF0000; + pcicfgrw32(p->tbdf, rno, v, 0); + + return -(size & ~0x0F); +} + +static int +pcisizcmp(void *a, void *b) +{ + Pcisiz *aa, *bb; + + aa = a; + bb = b; + return aa->siz - bb->siz; +} + +static ulong +pcimask(ulong v) +{ + ulong m; + + m = BI2BY*sizeof(v); + for(m = 1<<(m-1); m != 0; m >>= 1) { + if(m & v) + break; + } + + m--; + if((v & m) == 0) + return v; + + v |= m; + return v+1; +} + +static void +pcibusmap(Pcidev *root, ulong *pmema, ulong *pioa, int wrreg) +{ + Pcidev *p; + int ntb, i, size, rno, hole; + ulong v, mema, ioa, sioa, smema, base, limit; + Pcisiz *table, *tptr, *mtb, *itb; + extern void qsort(void*, long, long, int (*)(void*, void*)); + + if(!nobios) + return; + + ioa = *pioa; + mema = *pmema; + + DBG("pcibusmap wr=%d %T mem=%luX io=%luX\n", + wrreg, root->tbdf, mema, ioa); + + ntb = 0; + for(p = root; p != nil; p = p->link) + ntb++; + + ntb *= (PciCIS-PciBAR0)/4; + table = malloc(2*ntb*sizeof(Pcisiz)); + itb = table; + mtb = table+ntb; + + /* + * Build a table of sizes + */ + for(p = root; p != nil; p = p->link) { + if(p->ccrb == 0x06) { + if(p->ccru != 0x04 || p->bridge == nil) { +// DBG("pci: ignored bridge %T\n", p->tbdf); + continue; + } + + sioa = ioa; + smema = mema; + pcibusmap(p->bridge, &smema, &sioa, 0); + + hole = pcimask(smema-mema); + if(hole < (1<<20)) + hole = 1<<20; + p->mema.size = hole; + + hole = pcimask(sioa-ioa); + if(hole < (1<<12)) + hole = 1<<12; + + p->ioa.size = hole; + + itb->dev = p; + itb->bar = -1; + itb->siz = p->ioa.size; + itb++; + + mtb->dev = p; + mtb->bar = -1; + mtb->siz = p->mema.size; + mtb++; + continue; + } + + for(i = 0; i <= 5; i++) { + rno = PciBAR0 + i*4; + v = pcicfgrw32(p->tbdf, rno, 0, 1); + size = pcibarsize(p, rno); + if(size == 0) + continue; + + if(v & 1) { + itb->dev = p; + itb->bar = i; + itb->siz = size; + itb++; + } + else { + mtb->dev = p; + mtb->bar = i; + mtb->siz = size; + mtb++; + } + + p->mem[i].size = size; + } + } + + /* + * Sort both tables IO smallest first, Memory largest + */ + qsort(table, itb-table, sizeof(Pcisiz), pcisizcmp); + tptr = table+ntb; + qsort(tptr, mtb-tptr, sizeof(Pcisiz), pcisizcmp); + + /* + * Allocate IO address space on this bus + */ + for(tptr = table; tptr < itb; tptr++) { + hole = tptr->siz; + if(tptr->bar == -1) + hole = 1<<12; + ioa = (ioa+hole-1) & ~(hole-1); + + p = tptr->dev; + if(tptr->bar == -1) + p->ioa.bar = ioa; + else { + p->pcr |= IOen; + p->mem[tptr->bar].bar = ioa|1; + if(wrreg) + pcicfgrw32(p->tbdf, PciBAR0+(tptr->bar*4), ioa|1, 0); + } + + ioa += tptr->siz; + } + + /* + * Allocate Memory address space on this bus + */ + for(tptr = table+ntb; tptr < mtb; tptr++) { + hole = tptr->siz; + if(tptr->bar == -1) + hole = 1<<20; + mema = (mema+hole-1) & ~(hole-1); + + p = tptr->dev; + if(tptr->bar == -1) + p->mema.bar = mema; + else { + p->pcr |= MEMen; + p->mem[tptr->bar].bar = mema; + if(wrreg) + pcicfgrw32(p->tbdf, PciBAR0+(tptr->bar*4), mema, 0); + } + mema += tptr->siz; + } + + *pmema = mema; + *pioa = ioa; + free(table); + + if(wrreg == 0) + return; + + /* + * Finally set all the bridge addresses & registers + */ + for(p = root; p != nil; p = p->link) { + if(p->bridge == nil) { + pcicfgrw8(p->tbdf, PciLTR, 64, 0); + + p->pcr |= MASen; + pcicfgrw16(p->tbdf, PciPCR, p->pcr, 0); + continue; + } + + base = p->ioa.bar; + limit = base+p->ioa.size-1; + v = pcicfgrw32(p->tbdf, PciIBR, 0, 1); + v = (v&0xFFFF0000)|(limit & 0xF000)|((base & 0xF000)>>8); + pcicfgrw32(p->tbdf, PciIBR, v, 0); + v = (limit & 0xFFFF0000)|(base>>16); + pcicfgrw32(p->tbdf, PciIUBR, v, 0); + + base = p->mema.bar; + limit = base+p->mema.size-1; + v = (limit & 0xFFF00000)|((base & 0xFFF00000)>>16); + pcicfgrw32(p->tbdf, PciMBR, v, 0); + + /* + * Disable memory prefetch + */ + pcicfgrw32(p->tbdf, PciPMBR, 0x0000FFFF, 0); + pcicfgrw8(p->tbdf, PciLTR, 64, 0); + + /* + * Enable the bridge + */ + p->pcr |= IOen|MEMen|MASen; + pcicfgrw32(p->tbdf, PciPCR, 0xFFFF0000|p->pcr , 0); + + sioa = p->ioa.bar; + smema = p->mema.bar; + pcibusmap(p->bridge, &smema, &sioa, 1); + } +} + +static int +pcilscan(int bno, Pcidev** list) +{ + Pcidev *p, *head, *tail; + int dno, fno, i, hdt, l, maxfno, maxubn, rno, sbn, tbdf, ubn; + + maxubn = bno; + head = nil; + tail = nil; + for(dno = 0; dno <= pcimaxdno; dno++){ + maxfno = 0; + for(fno = 0; fno <= maxfno; fno++){ + /* + * For this possible device, form the + * bus+device+function triplet needed to address it + * and try to read the vendor and device ID. + * If successful, allocate a device struct and + * start to fill it in with some useful information + * from the device's configuration space. + */ + tbdf = MKBUS(BusPCI, bno, dno, fno); + l = pcicfgrw32(tbdf, PciVID, 0, 1); + if(l == 0xFFFFFFFF || l == 0) + continue; + p = malloc(sizeof(*p)); + p->tbdf = tbdf; + p->vid = l; + p->did = l>>16; + + if(pcilist != nil) + pcitail->list = p; + else + pcilist = p; + pcitail = p; + + p->pcr = pcicfgr16(p, PciPCR); + p->rid = pcicfgr8(p, PciRID); + p->ccrp = pcicfgr8(p, PciCCRp); + p->ccru = pcicfgr8(p, PciCCRu); + p->ccrb = pcicfgr8(p, PciCCRb); + p->cls = pcicfgr8(p, PciCLS); + p->ltr = pcicfgr8(p, PciLTR); + + p->intl = pcicfgr8(p, PciINTL); + + /* + * If the device is a multi-function device adjust the + * loop count so all possible functions are checked. + */ + hdt = pcicfgr8(p, PciHDT); + if(hdt & 0x80) + maxfno = MaxFNO; + + /* + * If appropriate, read the base address registers + * and work out the sizes. + */ + switch(p->ccrb) { + case 0x01: /* mass storage controller */ + case 0x02: /* network controller */ + case 0x03: /* display controller */ + case 0x04: /* multimedia device */ + case 0x07: /* simple comm. controllers */ + case 0x08: /* base system peripherals */ + case 0x09: /* input devices */ + case 0x0A: /* docking stations */ + case 0x0B: /* processors */ + case 0x0C: /* serial bus controllers */ + if((hdt & 0x7F) != 0) + break; + rno = PciBAR0 - 4; + for(i = 0; i < nelem(p->mem); i++) { + rno += 4; + p->mem[i].bar = pcicfgr32(p, rno); + p->mem[i].size = pcibarsize(p, rno); + } + break; + + case 0x00: + case 0x05: /* memory controller */ + case 0x06: /* bridge device */ + default: + break; + } + + if(head != nil) + tail->link = p; + else + head = p; + tail = p; + } + } + + *list = head; + for(p = head; p != nil; p = p->link){ + /* + * Find PCI-PCI bridges and recursively descend the tree. + */ + if(p->ccrb != 0x06 || p->ccru != 0x04) + continue; + + /* + * If the secondary or subordinate bus number is not + * initialised try to do what the PCI BIOS should have + * done and fill in the numbers as the tree is descended. + * On the way down the subordinate bus number is set to + * the maximum as it's not known how many buses are behind + * this one; the final value is set on the way back up. + */ + sbn = pcicfgr8(p, PciSBN); + ubn = pcicfgr8(p, PciUBN); + + if(sbn == 0 || ubn == 0 || nobios) { + sbn = maxubn+1; + /* + * Make sure memory, I/O and master enables are + * off, set the primary, secondary and subordinate + * bus numbers and clear the secondary status before + * attempting to scan the secondary bus. + * + * Initialisation of the bridge should be done here. + */ + pcicfgw32(p, PciPCR, 0xFFFF0000); + l = (MaxUBN<<16)|(sbn<<8)|bno; + pcicfgw32(p, PciPBN, l); + pcicfgw16(p, PciSPSR, 0xFFFF); + maxubn = pcilscan(sbn, &p->bridge); + l = (maxubn<<16)|(sbn<<8)|bno; + + pcicfgw32(p, PciPBN, l); + } + else { + if(ubn > maxubn) + maxubn = ubn; + pcilscan(sbn, &p->bridge); + } + } + + return maxubn; +} + +int +pciscan(int bno, Pcidev **list) +{ + int ubn; + + lock(&pcicfginitlock); + ubn = pcilscan(bno, list); + unlock(&pcicfginitlock); + return ubn; +} + +static uchar +pIIxget(Pcidev *router, uchar link) +{ + uchar pirq; + + /* link should be 0x60, 0x61, 0x62, 0x63 */ + pirq = pcicfgr8(router, link); + return (pirq < 16)? pirq: 0; +} + +static void +pIIxset(Pcidev *router, uchar link, uchar irq) +{ + pcicfgw8(router, link, irq); +} + +static uchar +viaget(Pcidev *router, uchar link) +{ + uchar pirq; + + /* link should be 1, 2, 3, 5 */ + pirq = (link < 6)? pcicfgr8(router, 0x55 + (link>>1)): 0; + + return (link & 1)? (pirq >> 4): (pirq & 15); +} + +static void +viaset(Pcidev *router, uchar link, uchar irq) +{ + uchar pirq; + + pirq = pcicfgr8(router, 0x55 + (link >> 1)); + pirq &= (link & 1)? 0x0f: 0xf0; + pirq |= (link & 1)? (irq << 4): (irq & 15); + pcicfgw8(router, 0x55 + (link>>1), pirq); +} + +static uchar +optiget(Pcidev *router, uchar link) +{ + uchar pirq = 0; + + /* link should be 0x02, 0x12, 0x22, 0x32 */ + if ((link & 0xcf) == 0x02) + pirq = pcicfgr8(router, 0xb8 + (link >> 5)); + return (link & 0x10)? (pirq >> 4): (pirq & 15); +} + +static void +optiset(Pcidev *router, uchar link, uchar irq) +{ + uchar pirq; + + pirq = pcicfgr8(router, 0xb8 + (link >> 5)); + pirq &= (link & 0x10)? 0x0f : 0xf0; + pirq |= (link & 0x10)? (irq << 4): (irq & 15); + pcicfgw8(router, 0xb8 + (link >> 5), pirq); +} + +static uchar +aliget(Pcidev *router, uchar link) +{ + /* No, you're not dreaming */ + static const uchar map[] = { 0, 9, 3, 10, 4, 5, 7, 6, 1, 11, 0, 12, 0, 14, 0, 15 }; + uchar pirq; + + /* link should be 0x01..0x08 */ + pirq = pcicfgr8(router, 0x48 + ((link-1)>>1)); + return (link & 1)? map[pirq&15]: map[pirq>>4]; +} + +static void +aliset(Pcidev *router, uchar link, uchar irq) +{ + /* Inverse of map in aliget */ + static const uchar map[] = { 0, 8, 0, 2, 4, 5, 7, 6, 0, 1, 3, 9, 11, 0, 13, 15 }; + uchar pirq; + + pirq = pcicfgr8(router, 0x48 + ((link-1)>>1)); + pirq &= (link & 1)? 0x0f: 0xf0; + pirq |= (link & 1)? (map[irq] << 4): (map[irq] & 15); + pcicfgw8(router, 0x48 + ((link-1)>>1), pirq); +} + +static uchar +cyrixget(Pcidev *router, uchar link) +{ + uchar pirq; + + /* link should be 1, 2, 3, 4 */ + pirq = pcicfgr8(router, 0x5c + ((link-1)>>1)); + return ((link & 1)? pirq >> 4: pirq & 15); +} + +static void +cyrixset(Pcidev *router, uchar link, uchar irq) +{ + uchar pirq; + + pirq = pcicfgr8(router, 0x5c + (link>>1)); + pirq &= (link & 1)? 0x0f: 0xf0; + pirq |= (link & 1)? (irq << 4): (irq & 15); + pcicfgw8(router, 0x5c + (link>>1), pirq); +} + +typedef struct Bridge Bridge; +struct Bridge +{ + ushort vid; + ushort did; + uchar (*get)(Pcidev *, uchar); + void (*set)(Pcidev *, uchar, uchar); +}; + +static Bridge southbridges[] = { + { 0x8086, 0x122e, pIIxget, pIIxset }, // Intel 82371FB + { 0x8086, 0x1234, pIIxget, pIIxset }, // Intel 82371MX + { 0x8086, 0x7000, pIIxget, pIIxset }, // Intel 82371SB + { 0x8086, 0x7110, pIIxget, pIIxset }, // Intel 82371AB + { 0x8086, 0x7198, pIIxget, pIIxset }, // Intel 82443MX (fn 1) + { 0x8086, 0x2410, pIIxget, pIIxset }, // Intel 82801AA + { 0x8086, 0x2420, pIIxget, pIIxset }, // Intel 82801AB + { 0x8086, 0x2440, pIIxget, pIIxset }, // Intel 82801BA + { 0x8086, 0x244c, pIIxget, pIIxset }, // Intel 82801BAM + { 0x8086, 0x248c, pIIxget, pIIxset }, // Intel 82801CAM + { 0x8086, 0x24cc, pIIxget, pIIxset }, // Intel 82801DBM + { 0x8086, 0x24d0, pIIxget, pIIxset }, // Intel 82801EB + { 0x8086, 0x2640, pIIxget, pIIxset }, // Intel 82801FB + { 0x1106, 0x0586, viaget, viaset }, // Viatech 82C586 + { 0x1106, 0x0596, viaget, viaset }, // Viatech 82C596 + { 0x1106, 0x0686, viaget, viaset }, // Viatech 82C686 + { 0x1106, 0x3227, viaget, viaset }, // Viatech VT8237 + { 0x1045, 0xc700, optiget, optiset }, // Opti 82C700 + { 0x10b9, 0x1533, aliget, aliset }, // Al M1533 + { 0x1039, 0x0008, pIIxget, pIIxset }, // SI 503 + { 0x1039, 0x0496, pIIxget, pIIxset }, // SI 496 + { 0x1078, 0x0100, cyrixget, cyrixset }, // Cyrix 5530 Legacy + + { 0x1022, 0x746B, nil, nil }, // AMD 8111 + { 0x10DE, 0x00D1, nil, nil }, // NVIDIA nForce 3 + { 0x1166, 0x0200, nil, nil }, // ServerWorks ServerSet III LE +}; + +typedef struct Slot Slot; +struct Slot { + uchar bus; // Pci bus number + uchar dev; // Pci device number + uchar maps[12]; // Avoid structs! Link and mask. + uchar slot; // Add-in/built-in slot + uchar reserved; +}; + +typedef struct Router Router; +struct Router { + uchar signature[4]; // Routing table signature + uchar version[2]; // Version number + uchar size[2]; // Total table size + uchar bus; // Interrupt router bus number + uchar devfn; // Router's devfunc + uchar pciirqs[2]; // Exclusive PCI irqs + uchar compat[4]; // Compatible PCI interrupt router + uchar miniport[4]; // Miniport data + uchar reserved[11]; + uchar checksum; +}; + +static ushort pciirqs; // Exclusive PCI irqs +static Bridge *southbridge; // Which southbridge to use. + +static void +pcirouting(void) +{ + Slot *e; + Router *r; + int size, i, fn, tbdf; + Pcidev *sbpci, *pci; + uchar *p, pin, irq, link, *map; + + // Search for PCI interrupt routing table in BIOS + for(p = (uchar *)KADDR(0xf0000); p < (uchar *)KADDR(0xfffff); p += 16) + if(p[0] == '$' && p[1] == 'P' && p[2] == 'I' && p[3] == 'R') + break; + + if(p >= (uchar *)KADDR(0xfffff)) + return; + + r = (Router *)p; + + // print("PCI interrupt routing table version %d.%d at %.6uX\n", + // r->version[0], r->version[1], (ulong)r & 0xfffff); + + tbdf = (BusPCI << 24)|(r->bus << 16)|(r->devfn << 8); + sbpci = pcimatchtbdf(tbdf); + if(sbpci == nil) { + print("pcirouting: Cannot find south bridge %T\n", tbdf); + return; + } + + for(i = 0; i != nelem(southbridges); i++) + if(sbpci->vid == southbridges[i].vid && sbpci->did == southbridges[i].did) + break; + + if(i == nelem(southbridges)) { + print("pcirouting: ignoring south bridge %T %.4uX/%.4uX\n", tbdf, sbpci->vid, sbpci->did); + return; + } + southbridge = &southbridges[i]; + if(southbridge->get == nil || southbridge->set == nil) + return; + + pciirqs = (r->pciirqs[1] << 8)|r->pciirqs[0]; + + size = (r->size[1] << 8)|r->size[0]; + for(e = (Slot *)&r[1]; (uchar *)e < p + size; e++) { + // print("%.2uX/%.2uX %.2uX: ", e->bus, e->dev, e->slot); + // for (i = 0; i != 4; i++) { + // uchar *m = &e->maps[i * 3]; + // print("[%d] %.2uX %.4uX ", + // i, m[0], (m[2] << 8)|m[1]); + // } + // print("\n"); + + for(fn = 0; fn != 8; fn++) { + tbdf = (BusPCI << 24)|(e->bus << 16)|((e->dev | fn) << 8); + pci = pcimatchtbdf(tbdf); + if(pci == nil) + continue; + pin = pcicfgr8(pci, PciINTP); + if(pin == 0 || pin == 0xff) + continue; + + map = &e->maps[(pin - 1) * 3]; + link = map[0]; + irq = southbridge->get(sbpci, link); + if(irq == 0 || irq == pci->intl) + continue; + if(pci->intl != 0 && pci->intl != 0xFF) { + print("pcirouting: BIOS workaround: %T at pin %d link %d irq %d -> %d\n", + tbdf, pin, link, irq, pci->intl); + southbridge->set(sbpci, link, pci->intl); + continue; + } + print("pcirouting: %T at pin %d link %d irq %d\n", tbdf, pin, link, irq); + pcicfgw8(pci, PciINTL, irq); + pci->intl = irq; + } + } +} + +static void +pcicfginit(void) +{ + char *p; + Pcidev **list; + ulong mema, ioa; + int bno, n, pcibios; + + lock(&pcicfginitlock); + if(pcicfgmode != -1) + goto out; + + pcibios = 0; + if(getconf("*nobios")) + nobios = 1; + else if(getconf("*pcibios")) + pcibios = 1; + if(getconf("*nopcirouting")) + nopcirouting = 1; + + /* + * Try to determine which PCI configuration mode is implemented. + * Mode2 uses a byte at 0xCF8 and another at 0xCFA; Mode1 uses + * a DWORD at 0xCF8 and another at 0xCFC and will pass through + * any non-DWORD accesses as normal I/O cycles. There shouldn't be + * a device behind these addresses so if Mode1 accesses fail try + * for Mode2 (Mode2 is deprecated). + */ + if(!pcibios){ + /* + * Bits [30:24] of PciADDR must be 0, + * according to the spec. + */ + n = inl(PciADDR); + if(!(n & 0x7FF00000)){ + outl(PciADDR, 0x80000000); + outb(PciADDR+3, 0); + if(inl(PciADDR) & 0x80000000){ + pcicfgmode = 1; + pcimaxdno = 31; + } + } + outl(PciADDR, n); + + if(pcicfgmode < 0){ + /* + * The 'key' part of PciCSE should be 0. + */ + n = inb(PciCSE); + if(!(n & 0xF0)){ + outb(PciCSE, 0x0E); + if(inb(PciCSE) == 0x0E){ + pcicfgmode = 2; + pcimaxdno = 15; + } + } + outb(PciCSE, n); + } + } + + if(pcicfgmode < 0) + goto out; + + fmtinstall('T', tbdffmt); + + if(p = getconf("*pcimaxbno")){ + n = strtoul(p, 0, 0); + if(n < pcimaxbno) + pcimaxbno = n; + } + if(p = getconf("*pcimaxdno")){ + n = strtoul(p, 0, 0); + if(n < pcimaxdno) + pcimaxdno = n; + } + + list = &pciroot; + for(bno = 0; bno <= pcimaxbno; bno++) { + int sbno = bno; + bno = pcilscan(bno, list); + + while(*list) + list = &(*list)->link; + + if (sbno == 0) { + Pcidev *pci; + + /* + * If we have found a PCI-to-Cardbus bridge, make sure + * it has no valid mappings anymore. + */ + pci = pciroot; + while (pci) { + if (pci->ccrb == 6 && pci->ccru == 7) { + ushort bcr; + + /* reset the cardbus */ + bcr = pcicfgr16(pci, PciBCR); + pcicfgw16(pci, PciBCR, 0x40 | bcr); + delay(50); + } + pci = pci->link; + } + } + } + + if(pciroot == nil) + goto out; + + if(nobios) { + /* + * Work out how big the top bus is + */ + mema = 0; + ioa = 0; + pcibusmap(pciroot, &mema, &ioa, 0); + + DBG("Sizes: mem=%8.8lux size=%8.8lux io=%8.8lux\n", + mema, pcimask(mema), ioa); + + /* + * Align the windows and map it + */ + ioa = 0x1000; + mema = 0x90000000; + + pcilog("Mask sizes: mem=%lux io=%lux\n", mema, ioa); + + pcibusmap(pciroot, &mema, &ioa, 1); + DBG("Sizes2: mem=%lux io=%lux\n", mema, ioa); + + unlock(&pcicfginitlock); + return; + } + + if (!nopcirouting) + pcirouting(); + +out: + unlock(&pcicfginitlock); + + if(getconf("*pcihinv")) + pcihinv(nil); +} + +static int +pcicfgrw8(int tbdf, int rno, int data, int read) +{ + int o, type, x; + + if(pcicfgmode == -1) + pcicfginit(); + + if(BUSBNO(tbdf)) + type = 0x01; + else + type = 0x00; + x = -1; + if(BUSDNO(tbdf) > pcimaxdno) + return x; + + lock(&pcicfglock); + switch(pcicfgmode){ + + case 1: + o = rno & 0x03; + rno &= ~0x03; + outl(PciADDR, 0x80000000|BUSBDF(tbdf)|rno|type); + if(read) + x = inb(PciDATA+o); + else + outb(PciDATA+o, data); + outl(PciADDR, 0); + break; + + case 2: + outb(PciCSE, 0x80|(BUSFNO(tbdf)<<1)); + outb(PciFORWARD, BUSBNO(tbdf)); + if(read) + x = inb((0xC000|(BUSDNO(tbdf)<<8)) + rno); + else + outb((0xC000|(BUSDNO(tbdf)<<8)) + rno, data); + outb(PciCSE, 0); + break; + } + unlock(&pcicfglock); + + return x; +} + +int +pcicfgr8(Pcidev* pcidev, int rno) +{ + return pcicfgrw8(pcidev->tbdf, rno, 0, 1); +} + +void +pcicfgw8(Pcidev* pcidev, int rno, int data) +{ + pcicfgrw8(pcidev->tbdf, rno, data, 0); +} + +static int +pcicfgrw16(int tbdf, int rno, int data, int read) +{ + int o, type, x; + + if(pcicfgmode == -1) + pcicfginit(); + + if(BUSBNO(tbdf)) + type = 0x01; + else + type = 0x00; + x = -1; + if(BUSDNO(tbdf) > pcimaxdno) + return x; + + lock(&pcicfglock); + switch(pcicfgmode){ + + case 1: + o = rno & 0x02; + rno &= ~0x03; + outl(PciADDR, 0x80000000|BUSBDF(tbdf)|rno|type); + if(read) + x = ins(PciDATA+o); + else + outs(PciDATA+o, data); + outl(PciADDR, 0); + break; + + case 2: + outb(PciCSE, 0x80|(BUSFNO(tbdf)<<1)); + outb(PciFORWARD, BUSBNO(tbdf)); + if(read) + x = ins((0xC000|(BUSDNO(tbdf)<<8)) + rno); + else + outs((0xC000|(BUSDNO(tbdf)<<8)) + rno, data); + outb(PciCSE, 0); + break; + } + unlock(&pcicfglock); + + return x; +} + +int +pcicfgr16(Pcidev* pcidev, int rno) +{ + return pcicfgrw16(pcidev->tbdf, rno, 0, 1); +} + +void +pcicfgw16(Pcidev* pcidev, int rno, int data) +{ + pcicfgrw16(pcidev->tbdf, rno, data, 0); +} + +static int +pcicfgrw32(int tbdf, int rno, int data, int read) +{ + int type, x; + + if(pcicfgmode == -1) + pcicfginit(); + + if(BUSBNO(tbdf)) + type = 0x01; + else + type = 0x00; + x = -1; + if(BUSDNO(tbdf) > pcimaxdno) + return x; + + lock(&pcicfglock); + switch(pcicfgmode){ + + case 1: + rno &= ~0x03; + outl(PciADDR, 0x80000000|BUSBDF(tbdf)|rno|type); + if(read) + x = inl(PciDATA); + else + outl(PciDATA, data); + outl(PciADDR, 0); + break; + + case 2: + outb(PciCSE, 0x80|(BUSFNO(tbdf)<<1)); + outb(PciFORWARD, BUSBNO(tbdf)); + if(read) + x = inl((0xC000|(BUSDNO(tbdf)<<8)) + rno); + else + outl((0xC000|(BUSDNO(tbdf)<<8)) + rno, data); + outb(PciCSE, 0); + break; + } + unlock(&pcicfglock); + + return x; +} + +int +pcicfgr32(Pcidev* pcidev, int rno) +{ + return pcicfgrw32(pcidev->tbdf, rno, 0, 1); +} + +void +pcicfgw32(Pcidev* pcidev, int rno, int data) +{ + pcicfgrw32(pcidev->tbdf, rno, data, 0); +} + +Pcidev* +pcimatch(Pcidev* prev, int vid, int did) +{ + if(pcicfgmode == -1) + pcicfginit(); + + if(prev == nil) + prev = pcilist; + else + prev = prev->list; + + while(prev != nil){ + if((vid == 0 || prev->vid == vid) + && (did == 0 || prev->did == did)) + break; + prev = prev->list; + } + return prev; +} + +Pcidev* +pcimatchtbdf(int tbdf) +{ + Pcidev *pcidev; + + if(pcicfgmode == -1) + pcicfginit(); + + for(pcidev = pcilist; pcidev != nil; pcidev = pcidev->list) { + if(pcidev->tbdf == tbdf) + break; + } + return pcidev; +} + +uchar +pciipin(Pcidev *pci, uchar pin) +{ + if (pci == nil) + pci = pcilist; + + while (pci) { + uchar intl; + + if (pcicfgr8(pci, PciINTP) == pin && pci->intl != 0 && pci->intl != 0xff) + return pci->intl; + + if (pci->bridge && (intl = pciipin(pci->bridge, pin)) != 0) + return intl; + + pci = pci->list; + } + return 0; +} + +static void +pcilhinv(Pcidev* p) +{ + int i; + Pcidev *t; + + if(p == nil) { + putstrn(PCICONS.output, PCICONS.ptr); + p = pciroot; + print("bus dev type vid did intl memory\n"); + } + for(t = p; t != nil; t = t->link) { + print("%d %2d/%d %.2ux %.2ux %.2ux %.4ux %.4ux %3d ", + BUSBNO(t->tbdf), BUSDNO(t->tbdf), BUSFNO(t->tbdf), + t->ccrb, t->ccru, t->ccrp, t->vid, t->did, t->intl); + + for(i = 0; i < nelem(p->mem); i++) { + if(t->mem[i].size == 0) + continue; + print("%d:%.8lux %d ", i, + t->mem[i].bar, t->mem[i].size); + } + if(t->ioa.bar || t->ioa.size) + print("ioa:%.8lux %d ", t->ioa.bar, t->ioa.size); + if(t->mema.bar || t->mema.size) + print("mema:%.8lux %d ", t->mema.bar, t->mema.size); + if(t->bridge) + print("->%d", BUSBNO(t->bridge->tbdf)); + print("\n"); + } + while(p != nil) { + if(p->bridge != nil) + pcilhinv(p->bridge); + p = p->link; + } +} + +void +pcihinv(Pcidev* p) +{ + if(pcicfgmode == -1) + pcicfginit(); + lock(&pcicfginitlock); + pcilhinv(p); + unlock(&pcicfginitlock); +} + +void +pcireset(void) +{ + Pcidev *p; + + if(pcicfgmode == -1) + pcicfginit(); + + for(p = pcilist; p != nil; p = p->list) { + /* don't mess with the bridges */ + if(p->ccrb == 0x06) + continue; + pciclrbme(p); + } +} + +void +pcisetioe(Pcidev* p) +{ + p->pcr |= IOen; + pcicfgw16(p, PciPCR, p->pcr); +} + +void +pciclrioe(Pcidev* p) +{ + p->pcr &= ~IOen; + pcicfgw16(p, PciPCR, p->pcr); +} + +void +pcisetbme(Pcidev* p) +{ + p->pcr |= MASen; + pcicfgw16(p, PciPCR, p->pcr); +} + +void +pciclrbme(Pcidev* p) +{ + p->pcr &= ~MASen; + pcicfgw16(p, PciPCR, p->pcr); +} + +void +pcisetmwi(Pcidev* p) +{ + p->pcr |= MemWrInv; + pcicfgw16(p, PciPCR, p->pcr); +} + +void +pciclrmwi(Pcidev* p) +{ + p->pcr &= ~MemWrInv; + pcicfgw16(p, PciPCR, p->pcr); +} + +static int +pcigetpmrb(Pcidev* p) +{ + int ptr; + + if(p->pmrb != 0) + return p->pmrb; + p->pmrb = -1; + + /* + * If there are no extended capabilities implemented, + * (bit 4 in the status register) assume there's no standard + * power management method. + * Find the capabilities pointer based on PCI header type. + */ + if(!(p->pcr & 0x0010)) + return -1; + switch(pcicfgr8(p, PciHDT)){ + default: + return -1; + case 0: /* all other */ + case 1: /* PCI to PCI bridge */ + ptr = 0x34; + break; + case 2: /* CardBus bridge */ + ptr = 0x14; + break; + } + ptr = pcicfgr32(p, ptr); + + while(ptr != 0){ + /* + * Check for validity. + * Can't be in standard header and must be double + * word aligned. + */ + if(ptr < 0x40 || (ptr & ~0xFC)) + return -1; + if(pcicfgr8(p, ptr) == 0x01){ + p->pmrb = ptr; + return ptr; + } + + ptr = pcicfgr8(p, ptr+1); + } + + return -1; +} + +int +pcigetpms(Pcidev* p) +{ + int pmcsr, ptr; + + if((ptr = pcigetpmrb(p)) == -1) + return -1; + + /* + * Power Management Register Block: + * offset 0: Capability ID + * 1: next item pointer + * 2: capabilities + * 4: control/status + * 6: bridge support extensions + * 7: data + */ + pmcsr = pcicfgr16(p, ptr+4); + + return pmcsr & 0x0003; +} + +int +pcisetpms(Pcidev* p, int state) +{ + int ostate, pmc, pmcsr, ptr; + + if((ptr = pcigetpmrb(p)) == -1) + return -1; + + pmc = pcicfgr16(p, ptr+2); + pmcsr = pcicfgr16(p, ptr+4); + ostate = pmcsr & 0x0003; + pmcsr &= ~0x0003; + + switch(state){ + default: + return -1; + case 0: + break; + case 1: + if(!(pmc & 0x0200)) + return -1; + break; + case 2: + if(!(pmc & 0x0400)) + return -1; + break; + case 3: + break; + } + pmcsr |= state; + pcicfgw16(p, ptr+4, pmcsr); + + return ostate; +} diff --git a/os/pc/pcidb.acid b/os/pc/pcidb.acid new file mode 100644 index 00000000..0ab443d5 --- /dev/null +++ b/os/pc/pcidb.acid @@ -0,0 +1,2848 @@ +pcivendordb = { + {0x0000, "Gammagraphx, Inc.", { + }}, + {0x001a, "Ascend Communications, Inc.", { + }}, + {0x003d, "Lockheed Martin-Marietta Corp", { + }}, + {0x0e11, "Compaq Computer Corporation", { + {0x3032,"QVision 1280/p Rev 0"}, + {0x3033,"QVision 1280/p Rev 1"}, + {0x3034,"QVision 1280/p Rev 2"}, + {0x4000,"4000 [Triflex]"}, + {0xae10,"Smart-2/P RAID Controller"}, + {0xae29,"MIS-L"}, + {0xae2a,"MPC"}, + {0xae2b,"MIS-E"}, + {0xae32,"Netelligent 10/100"}, + {0xae34,"Netelligent 10"}, + {0xae35,"Integrated NetFlex-3/P"}, + {0xae40,"Netelligent 10/100 Dual"}, + {0xae43,"ProLiant Integrated Netelligent 10/100"}, + {0xae69,"CETUS-L"}, + {0xae6c,"Northstar"}, + {0xb011,"Integrated Netelligent 10/100"}, + {0xb012,"Netelligent 10 T/2"}, + {0xf130,"NetFlex-3/P ThunderLAN 1.0"}, + {0xf150,"NetFlex-3/P ThunderLAN 2.3"}, + }}, + {0x1000, "Symbios Logic Inc. (formerly NCR)", { + {0x0001,"53c810"}, + {0x0002,"53c820"}, + {0x0003,"53c825"}, + {0x0004,"53c815"}, + {0x0005,"53c810AP"}, + {0x0006,"53c860"}, + {0x000b,"53c896"}, + {0x000c,"53c895"}, + {0x000d,"53c885"}, + {0x000f,"53c875"}, + {0x008f,"53c875J"}, + {0x0901,"61C102"}, + {0x1000,"63C815"}, + }}, + {0x1002, "ATI Technologies Inc", { + {0x4158,"68800AX [Mach32]"}, + {0x4354,"215CT [Mach64 CT]"}, + {0x4358,"210888CX [Mach64 CX]"}, + {0x4554,"210888ET [Mach64 ET]"}, + {0x4742,"215GB [Mach64 GB]"}, + {0x4744,"215GD [Mach64 GD]"}, + {0x4749,"215GI [Mach64 GI]"}, + {0x4750,"215GP [Mach64 GP]"}, + {0x4751,"215GQ [Mach64 GQ]"}, + {0x4754,"215GT [Mach64 GT]"}, + {0x4755,"215GTB [Mach64 GTB]"}, + {0x4756,"215IIC [Mach64 GT IIC]"}, + {0x4758,"210888GX [Mach64 GX]"}, + {0x4c47,"215LG [Mach64 LG]"}, + {0x4c54,"264LT [Mach64 LT]"}, + {0x5654,"264VT [Mach64 VT]"}, + {0x5655,"264VTB [Mach64 VTB]"}, + {0x5656,"264VT4 [Mach64 VT4]"}, + }}, + {0x1003, "ULSI Systems", { + {0x0201,"US201"}, + }}, + {0x1004, "VLSI Technology Inc", { + {0x0005,"82C592-FC1"}, + {0x0006,"82C593-FC1"}, + {0x0007,"82C594-AFC2"}, + {0x0008,"82C596/7 [Wildcat]"}, + {0x0009,"82C597-AFC2"}, + {0x000c,"82C541 [Lynx]"}, + {0x000d,"82C543 [Lynx]"}, + {0x0101,"82C532"}, + {0x0102,"82C534"}, + {0x0103,"82C538"}, + {0x0104,"82C535"}, + {0x0105,"82C147"}, + {0x0200,"82C975"}, + {0x0280,"82C925"}, + {0x0702,"VAS96011 [Golden Gate II]"}, + }}, + {0x1005, "Avance Logic, Inc.", { + {0x2301,"ALG2301"}, + {0x2302,"ALG2302"}, + {0x2364,"ALG2364"}, + }}, + {0x1006, "Reply Group", { + }}, + {0x1007, "NetFrame Systems Inc", { + }}, + {0x1008, "Epson", { + }}, + {0x100a, "Phoenix Technologies", { + }}, + {0x100b, "National Semiconductor Corporation", { + {0x0001,"DP83810"}, + {0x0002,"87415"}, + {0xd001,"87410"}, + }}, + {0x100c, "Tseng Labs Inc", { + {0x3202,"ET4000/W32p rev A"}, + {0x3205,"ET4000/W32p rev B"}, + {0x3206,"ET4000/W32p rev C"}, + {0x3207,"ET4000/W32p rev D"}, + {0x3208,"ET6000"}, + }}, + {0x100d, "AST Research Inc", { + }}, + {0x100e, "Weitek", { + {0x9001,"P9000"}, + {0x9100,"P9100"}, + }}, + {0x1010, "Video Logic, Ltd.", { + }}, + {0x1011, "Digital Equipment Corporation", { + {0x0001,"DECchip 21050"}, + {0x0002,"DECchip 21040 [Tulip]"}, + {0x0004,"DECchip 21030 [TGA]"}, + {0x0007,"NVRAM [Zephyr NVRAM]"}, + {0x0008,"KZPSA [KZPSA]"}, + {0x0009,"DECchip 21140 [FasterNet]"}, + {0x000d,"PBXGB [TGA2]"}, + {0x000f,"DEFPA"}, + {0x0014,"DECchip 21041 [Tulip Pass 3]"}, + {0x0016,"DGLPB [OPPO]"}, + {0x0019,"DECchip 21142/43"}, + {0x0021,"DECchip 21052"}, + {0x0022,"DECchip 21150"}, + {0x0024,"DECchip 21152"}, + }}, + {0x1012, "Micronics Computers Inc", { + }}, + {0x1013, "Cirrus Logic", { + {0x0038,"GD 7548"}, + {0x00a0,"GD 5430/40 [Alpine]"}, + {0x00a4,"GD 5434-4 [Alpine]"}, + {0x00a8,"GD 5434-8 [Alpine]"}, + {0x00ac,"GD 5436 [Alpine]"}, + {0x00b8,"GD 5446"}, + {0x00bc,"GD 5480"}, + {0x00d0,"GD 5462"}, + {0x00d4,"GD 5464 [Laguna]"}, + {0x00d6,"GD 5465 [Laguna]"}, + {0x1100,"CL 6729"}, + {0x1110,"PD 6832"}, + {0x1200,"GD 7542 [Nordic]"}, + {0x1202,"GD 7543 [Viking]"}, + {0x1204,"GD 7541 [Nordic Light]"}, + }}, + {0x1014, "IBM", { + {0x000a,"Fire Coral"}, + {0x0018,"TR"}, + {0x001b,"GXT-150P"}, + {0x001d,"82G2675"}, + {0x0020,"MCA"}, + {0x0022,"IBM27-82351"}, + {0x002e,"ServeRAID controller"}, + {0x0036,"Miami"}, + {0x003e,"TR_Wake"}, + {0x0046,"MPIC interrupt controller"}, + {0x007d,"3780IDSP [MWave]"}, + {0xffff,"MPIC-2 interrupt controller"}, + }}, + {0x1015, "LSI Logic Corp of Canada", { + }}, + {0x1016, "ICL Personal Systems", { + }}, + {0x1017, "SPEA Software AG", { + }}, + {0x1018, "Unisys Systems", { + }}, + {0x1019, "Elitegroup Computer Systems", { + }}, + {0x101a, "AT&T GIS (NCR)", { + }}, + {0x101b, "Vitesse Semiconductor", { + }}, + {0x101c, "Western Digital", { + {0x0193,"33C193A"}, + {0x0197,"33C197A"}, + {0x0296,"33C296A"}, + {0x3193,"7193"}, + {0x3197,"WD 7197"}, + {0x3296,"33C296A"}, + {0x4296,"34C296"}, + {0xc24a,"90C"}, + }}, + {0x101e, "American Megatrends Inc.", { + {0x9010,"MegaRAID"}, + }}, + {0x101f, "PictureTel", { + }}, + {0x1020, "Hitachi Computer Products", { + }}, + {0x1021, "OKI Electric Industry Co. Ltd.", { + }}, + {0x1022, "Advanced Micro Devices", { + {0x2000,"79c970 [PCnet LANCE]"}, + {0x2020,"53c974 [PCscsi]"}, + {0x2040,"79c974"}, + }}, + {0x1023, "Trident Microsystems", { + {0x9320,"TGUI 9320"}, + {0x9397,"Cyber9397"}, + {0x9420,"TGUI 9420"}, + {0x9430,"TGUI 9430"}, + {0x9440,"TGUI 9440"}, + {0x9660,"TGUI 9660/9680/9682"}, + {0x9750,"3DIm`age 975"}, + }}, + {0x1024, "Zenith Data Systems", { + }}, + {0x1025, "Acer Incorporated", { + {0x1435,"M1435"}, + {0x1445,"M1445"}, + {0x1449,"M1449"}, + {0x1451,"M1451"}, + {0x1461,"M1461"}, + {0x3141,"M3141"}, + {0x3143,"M3143"}, + {0x3145,"M3145"}, + {0x3147,"M3147"}, + {0x3149,"M3149"}, + {0x3151,"M3151"}, + }}, + {0x1028, "Dell Computer Corporation", { + }}, + {0x1029, "Siemens Nixdorf IS", { + }}, + {0x102a, "LSI Logic", { + {0x0000,"HYDRA"}, + {0x0010,"ASPEN"}, + }}, + {0x102b, "Matrox Graphics, Inc.", { + {0x0518,"2085PX [Atlas MGA-2]"}, + {0x0519,"MGA 2064W [Millennium]"}, + {0x051a,"MGA 1064SG [Mystique]"}, + {0x051b,"MGA 2164W [Millennium II]"}, + {0x051f,"MGA 2164W AGP [Millennium II AGP]"}, + {0x0d10,"MGA Ultima/Impression"}, + }}, + {0x102c, "Chips and Technologies", { + {0x00b8,"64310"}, + {0x00d8,"65545"}, + {0x00dc,"65548"}, + {0x00e0,"65550"}, + {0x00e4,"65554"}, + {0x00e5,"65555"}, + }}, + {0x102d, "Wyse Technology Inc.", { + }}, + {0x102e, "Olivetti Advanced Technology", { + }}, + {0x102f, "Toshiba America", { + }}, + {0x1030, "TMC Research", { + }}, + {0x1031, "Miro Computer Products AG", { + {0x5601,"DC20 ASIC"}, + }}, + {0x1033, "NEC Corporation", { + {0x0035,"USB"}, + {0x0046,"PowerVR PCX2 [midas]"}, + }}, + {0x1034, "Framatome Connectors USA Inc.", { + }}, + {0x1035, "Comp. & Comm. Research Lab", { + }}, + {0x1036, "Future Domain Corp.", { + {0x0000,"TMC-18C30 [36C70]"}, + }}, + {0x1037, "Hitachi Micro Systems", { + }}, + {0x1038, "AMP, Inc", { + }}, + {0x1039, "Silicon Integrated Systems", { + {0x0001,"5591/5592 AGP"}, + {0x0002,"SG86C202"}, + {0x0008,"85C503"}, + {0x0009,"ACPI"}, + {0x0200,"5597/5598 VGA"}, + {0x0204,"82C204"}, + {0x0205,"SG86C205"}, + {0x0406,"85C501/2"}, + {0x0496,"85C496"}, + {0x0597,"5513C"}, + {0x0601,"85C601"}, + {0x5107,"5107"}, + {0x5511,"5511/5512"}, + {0x5513,"5513"}, + {0x5571,"5571"}, + {0x5591,"5591/5592 Host"}, + {0x5597,"5597 [SiS5582]"}, + {0x7001,"7001"}, + }}, + {0x103a, "Seiko Epson Corporation", { + }}, + {0x103b, "Tatung Co. of America", { + }}, + {0x103c, "Hewlett-Packard Company", { + {0x1030,"J2585A"}, + {0x1031,"J2585B"}, + {0x2910,"E2910A"}, + {0x2925,"E2925A"}, + }}, + {0x103e, "Solliday Engineering", { + }}, + {0x103f, "Synopsys/Logic Modeling Group", { + }}, + {0x1040, "Accelgraphics Inc.", { + }}, + {0x1041, "Computrend", { + }}, + {0x1042, "Micron", { + {0x1000,"FDC 37C665"}, + {0x1001,"37C922"}, + {0x3000,"Samurai_0"}, + {0x3010,"Samurai_1"}, + {0x3020,"Samurai_IDE"}, + }}, + {0x1043, "Asustek Computer, Inc.", { + }}, + {0x1044, "Distributed Processing Technology", { + {0xa400,"SmartCache/Raid III or IV"}, + }}, + {0x1045, "OPTi Inc.", { + {0xc178,"92C178"}, + {0xc557,"82C557 [Viper-M]"}, + {0xc558,"82C558 [Viper-M ISA+IDE]"}, + {0xc621,"82C621"}, + {0xc700,"82C700"}, + {0xc701,"82C701 [FireStar Plus]"}, + {0xc814,"82C814 [Firebridge 1]"}, + {0xc822,"82C822"}, + {0xc824,"82C824"}, + {0xd568,"82C825 [Firebridge 2]"}, + }}, + {0x1046, "IPC Corporation, Ltd.", { + }}, + {0x1047, "Genoa Systems Corp", { + }}, + {0x1048, "Elsa AG", { + }}, + {0x1049, "Fountain Technologies, Inc.", { + }}, + {0x104a, "SGS Thomson Microelectronics", { + {0x0008,"STG 2000X"}, + {0x0009,"STG 1764X"}, + }}, + {0x104b, "BusLogic", { + {0x0140,"BT-946C (old) [multimaster 01]"}, + {0x1040,"BT-946C (BA80C30) [MultiMaster 10]"}, + {0x8130,"Flashpoint LT"}, + }}, + {0x104c, "Texas Instruments", { + {0x3d04,"TVP4010 [Permedia]"}, + {0x3d07,"TVP4020 [Permedia 2]"}, + {0xa001,"TDC1570"}, + {0xa100,"TDC1561"}, + {0xac10,"PCI1050"}, + {0xac11,"PCI1053"}, + {0xac12,"PCI1130"}, + {0xac13,"PCI1031"}, + {0xac15,"PCI1131"}, + {0xac16,"PCI-1250"}, + {0xac17,"PCI-1220"}, + }}, + {0x104d, "Sony Corporation", { + }}, + {0x104e, "Oak Technology, Inc", { + {0x0107,"OTI107"}, + }}, + {0x104f, "Co-time Computer Ltd", { + }}, + {0x1050, "Winbond Electronics Corp", { + {0x0940,"89C940"}, + }}, + {0x1051, "Anigma, Inc.", { + }}, + {0x1053, "Young Micro Systems", { + }}, + {0x1054, "Hitachi, Ltd", { + }}, + {0x1055, "EFAR Microsystems", { + }}, + {0x1056, "ICL", { + }}, + {0x1057, "Motorola Computer Group", { + {0x0001,"MPC105 [Eagle]"}, + {0x0002,"MPC106 [Grackle]"}, + {0x4801,"Raven"}, + }}, + {0x1058, "Electronics & Telecommunications RSH", { + }}, + {0x1059, "Teknor Industrial Computers Inc", { + }}, + {0x105a, "Promise Technology, Inc.", { + {0x4d33,"20246"}, + {0x5300,"DC5300"}, + }}, + {0x105b, "Foxconn International, Inc.", { + }}, + {0x105c, "Wipro Infotech Limited", { + }}, + {0x105d, "Number 9 Computer Company", { + {0x2309,"Imagine 128"}, + {0x2339,"Imagine 128-II"}, + {0x493d,"Imagine 128 T2R [Ticket to Ride]"}, + }}, + {0x105e, "Vtech Computers Ltd", { + }}, + {0x105f, "Infotronic America Inc", { + }}, + {0x1060, "United Microelectronics", { + {0x0001,"UM82C881"}, + {0x0002,"UM82C886"}, + {0x0101,"UM8673F"}, + {0x0881,"UM8881"}, + {0x0886,"UM8886F"}, + {0x0891,"UM8891A"}, + {0x1001,"UM886A"}, + {0x673a,"UM8886BF"}, + {0x8710,"UM8710"}, + {0x886a,"UM8886A"}, + {0x8881,"UM8881F"}, + {0x8886,"UM8886F"}, + {0x888a,"UM8886A"}, + {0x8891,"UM8891A"}, + {0x9017,"UM9017F"}, + {0xe881,"UM8881N"}, + {0xe886,"UM8886N"}, + {0xe891,"UM8891N"}, + }}, + {0x1061, "I.I.T.", { + {0x0001,"AGX016"}, + {0x0002,"IIT3204/3501"}, + }}, + {0x1062, "Maspar Computer Corp", { + }}, + {0x1063, "Ocean Office Automation", { + }}, + {0x1064, "Alcatel CIT", { + }}, + {0x1065, "Texas Microsystems", { + }}, + {0x1066, "PicoPower Technology", { + {0x0000,"PT80C826"}, + {0x0001,"PT86C52x [Vesuvius]"}, + {0x0002,"PT80C524 [Nile]"}, + }}, + {0x1067, "Mitsubishi Electronics", { + }}, + {0x1068, "Diversified Technology", { + }}, + {0x1069, "Mylex Corporation", { + {0x0001,"DAC960P"}, + }}, + {0x106a, "Aten Research Inc", { + }}, + {0x106b, "Apple Computer Inc.", { + {0x0001,"Bandit PowerPC host bridge"}, + {0x0002,"Grand Central I/O"}, + {0x000e,"Hydra Mac I/O"}, + }}, + {0x106c, "Hyundai Electronics America", { + }}, + {0x106d, "Sequent Computer Systems", { + }}, + {0x106e, "DFI, Inc", { + }}, + {0x106f, "City Gate Development Ltd", { + }}, + {0x1070, "Daewoo Telecom Ltd", { + }}, + {0x1071, "Mitac", { + }}, + {0x1072, "GIT Co Ltd", { + }}, + {0x1073, "Yamaha Corporation", { + {0x0002,"YGV615 [RPA3 3D-Graphics Controller]"}, + }}, + {0x1074, "NexGen Microsystems", { + {0x4e78,"82c501"}, + }}, + {0x1075, "Advanced Integrations Research", { + }}, + {0x1076, "Chaintech Computer Co. Ltd", { + }}, + {0x1077, "Q Logic", { + {0x1020,"ISP1020"}, + {0x1022,"ISP1022"}, + }}, + {0x1078, "Cyrix Corporation", { + {0x0000,"5510 [Grappa]"}, + {0x0001,"PCI_Master"}, + {0x0002,"5520 [Cognac]"}, + {0x0100,"5530_Legacy [Kahlua]"}, + {0x0101,"5530_SMI [Kahlua]"}, + {0x0102,"5530_IDE [Kahlua]"}, + {0x0103,"5530_Audio [Kahlua]"}, + {0x0104,"5530_Video [Kahlua]"}, + }}, + {0x1079, "I-Bus", { + }}, + {0x107a, "NetWorth", { + }}, + {0x107b, "Gateway 2000", { + }}, + {0x107c, "Goldstar", { + }}, + {0x107d, "LeadTek Research Inc.", { + {0x0000,"P86C850"}, + }}, + {0x107e, "Interphase Corporation", { + }}, + {0x107f, "Data Technology Corporation", { + {0x0802,"SL82C105"}, + }}, + {0x1080, "Contaq Microsystems", { + {0x0600,"82C599"}, + {0xc693,"82c693"}, + }}, + {0x1081, "Supermac Technology", { + }}, + {0x1082, "EFA Corporation of America", { + }}, + {0x1083, "Forex Computer Corporation", { + {0x0001,"FR710"}, + }}, + {0x1084, "Parador", { + }}, + {0x1085, "Tulip Computers Int.B.V.", { + }}, + {0x1086, "J. Bond Computer Systems", { + }}, + {0x1087, "Cache Computer", { + }}, + {0x1088, "Microcomputer Systems (M) Son", { + }}, + {0x1089, "Data General Corporation", { + }}, + {0x108a, "Bit3 Computer Corp.", { + }}, + {0x108c, "Oakleigh Systems Inc.", { + }}, + {0x108d, "Olicom", { + {0x0001,"OC-3136/3137"}, + {0x0011,"OC-2315"}, + {0x0012,"OC-2325"}, + {0x0013,"OC-2183/2185"}, + {0x0014,"OC-2326"}, + {0x0021,"OC-6151/6152 [RapidFire ATM PCI 155]"}, + }}, + {0x108e, "Sun Microsystems Computer Corp.", { + {0x1000,"EBUS"}, + {0x1001,"Happy Meal"}, + {0x5000,"Advanced PCI Bridge"}, + {0x8000,"PCI Bus Module"}, + {0xa000,"Ultra IIi PCI"}, + }}, + {0x108f, "Systemsoft", { + }}, + {0x1090, "Encore Computer Corporation", { + }}, + {0x1091, "Intergraph Corporation", { + }}, + {0x1092, "Diamond Multimedia Systems", { + }}, + {0x1093, "National Instruments", { + {0xc801,"PCI_GPIB"}, + }}, + {0x1094, "First International Computers", { + }}, + {0x1095, "CMD Technology Inc", { + {0x0640,"PCI0640"}, + {0x0643,"PCI0643"}, + {0x0646,"PCI0646"}, + {0x0650,"PBC0650A"}, + {0x0670,"670"}, + }}, + {0x1096, "Alacron", { + }}, + {0x1097, "Appian Technology", { + }}, + {0x1098, "Quantum Designs (H.K.) Ltd", { + {0x0001,"QD-8500"}, + {0x0002,"QD-8580"}, + }}, + {0x1099, "Samsung Electronics Co., Ltd", { + }}, + {0x109a, "Packard Bell", { + }}, + {0x109b, "Gemlight Computer Ltd.", { + }}, + {0x109c, "Megachips Corporation", { + }}, + {0x109d, "Zida Technologies Ltd.", { + }}, + {0x109e, "Brooktree Corporation", { + {0x0350,"Bt848"}, + {0x0351,"Bt849A"}, + {0x8472,"Bt8472"}, + {0x8474,"Bt8474"}, + }}, + {0x109f, "Trigem Computer Inc.", { + }}, + {0x10a0, "Meidensha Corporation", { + }}, + {0x10a1, "Juko Electronics Ind. Co. Ltd", { + }}, + {0x10a2, "Quantum Corporation", { + }}, + {0x10a3, "Everex Systems Inc", { + }}, + {0x10a4, "Globe Manufacturing Sales", { + }}, + {0x10a5, "Racal Interlan", { + }}, + {0x10a6, "Informtech Industrial Ltd.", { + }}, + {0x10a7, "Benchmarq Microelectronics", { + }}, + {0x10a8, "Sierra Semiconductor", { + {0x0000,"STB Horizon 64"}, + }}, + {0x10a9, "Silicon Graphics", { + }}, + {0x10aa, "ACC Microelectronics", { + {0x0000,"ACCM 2188"}, + }}, + {0x10ab, "Digicom", { + }}, + {0x10ac, "Honeywell IAC", { + }}, + {0x10ad, "Symphony Labs", { + {0x0001,"W83769F"}, + {0x0103,"SL82c103"}, + {0x0105,"SL82c105"}, + {0x0565,"W83C553"}, + }}, + {0x10ae, "Cornerstone Technology", { + }}, + {0x10af, "Micro Computer Systems Inc", { + }}, + {0x10b0, "CardExpert Technology", { + }}, + {0x10b1, "Cabletron Systems Inc", { + }}, + {0x10b2, "Raytheon Company", { + }}, + {0x10b3, "Databook Inc", { + {0x3106,"DB87144"}, + {0xb106,"DB87144"}, + }}, + {0x10b4, "STB Systems Inc", { + }}, + {0x10b5, "PLX Technology, Inc.", { + {0x9036,"9036"}, + {0x9060,"9060"}, + {0x906e,"9060ES"}, + {0x9080,"9080"}, + }}, + {0x10b6, "Madge Networks", { + {0x0001,"Smart"}, + {0x0002,"Smart 16/4 BM Mk2 PCI Ringnode"}, + {0x1001,"Collage 155 Server"}, + }}, + {0x10b7, "3Com Corporation", { + {0x0001,"3c985 1000BaseSX"}, + {0x3390,"Token Link Velocity"}, + {0x5900,"3c590 10BaseT [Vortex]"}, + {0x5950,"3c595 100BaseTX [Vortex]"}, + {0x5951,"3c595 100BaseT4 [Vortex]"}, + {0x5952,"3c595 100Base-MII [Vortex]"}, + {0x9000,"3c900 10BaseT [Boomerang]"}, + {0x9001,"3c900 Combo [Boomerang]"}, + {0x9050,"3c905 100BaseTX [Boomerang]"}, + {0x9051,"3c905 100BaseT4"}, + {0x9055,"3c905B 100BaseTX [Cyclone]"}, + }}, + {0x10b8, "Standard Microsystems", { + {0x0005,"9432 TX"}, + {0x1000,"37c665"}, + {0x1001,"37C922"}, + }}, + {0x10b9, "Acer Laboratories Inc.", { + {0x1435,"M1435"}, + {0x1445,"M1445"}, + {0x1449,"M1449"}, + {0x1451,"M1451"}, + {0x1461,"M1461"}, + {0x1489,"M1489"}, + {0x1511,"M1511"}, + {0x1513,"M1513"}, + {0x1521,"M1521"}, + {0x1523,"M1523"}, + {0x1531,"M1531"}, + {0x1533,"M1533"}, + {0x1541,"M1541"}, + {0x1543,"M1543"}, + {0x3141,"M3141"}, + {0x3143,"M3143"}, + {0x3145,"M3145"}, + {0x3147,"M3147"}, + {0x3149,"M3149"}, + {0x3151,"M3151"}, + {0x3307,"M3307"}, + {0x5215,"M4803"}, + {0x5217,"m5217h"}, + {0x5219,"M5219"}, + {0x5229,"M5229"}, + {0x5235,"m5225"}, + {0x5237,"M5237"}, + {0x7101,"M7101"}, + }}, + {0x10ba, "Mitsubishi Electric Corp.", { + }}, + {0x10bb, "Dapha Electronics Corporation", { + }}, + {0x10bc, "Advanced Logic Research", { + }}, + {0x10bd, "Surecom Technology", { + {0x0e34,"NE-34PCI LAN"}, + }}, + {0x10be, "Tseng Labs International Co.", { + }}, + {0x10bf, "Most Inc", { + }}, + {0x10c0, "Boca Research Inc.", { + }}, + {0x10c1, "ICM Co., Ltd.", { + }}, + {0x10c2, "Auspex Systems Inc.", { + }}, + {0x10c3, "Samsung Semiconductors, Inc.", { + }}, + {0x10c4, "Award Software International Inc.", { + }}, + {0x10c5, "Xerox Corporation", { + }}, + {0x10c6, "Rambus Inc.", { + }}, + {0x10c7, "Media Vision", { + }}, + {0x10c8, "Neomagic Corporation", { + {0x0001,"NM2070 [MagicGraph NM2070]"}, + {0x0002,"NM2090 [MagicGraph 128V]"}, + {0x0003,"NM2093 [MagicGraph 128ZV]"}, + {0x0004,"NM2160 [MagicGraph 128XD]"}, + }}, + {0x10c9, "Dataexpert Corporation", { + }}, + {0x10ca, "Fujitsu Microelectr., Inc.", { + }}, + {0x10cb, "Omron Corporation", { + }}, + {0x10cc, "Mentor ARC Inc", { + }}, + {0x10cd, "Advanced System Products, Inc", { + {0x1200,"ASC1200 [(abp940) Fast SCSI-II]"}, + {0x1300,"ABP940-U"}, + {0x2300,"ABP940-UW"}, + }}, + {0x10ce, "Radius", { + }}, + {0x10cf, "Citicorp TTI", { + {0x2001,"mb86605"}, + }}, + {0x10d0, "Fujitsu Limited", { + }}, + {0x10d1, "FuturePlus Systems Corp.", { + }}, + {0x10d2, "Molex Incorporated", { + }}, + {0x10d3, "Jabil Circuit Inc", { + }}, + {0x10d4, "Hualon Microelectronics", { + }}, + {0x10d5, "Autologic Inc.", { + }}, + {0x10d6, "Cetia", { + }}, + {0x10d7, "BCM Advanced Research", { + }}, + {0x10d8, "Advanced Peripherals Labs", { + }}, + {0x10d9, "Macronix, Inc.", { + {0x0512,"MX98713"}, + {0x0531,"MX987x5"}, + }}, + {0x10da, "Compaq IPG-Austin", { + }}, + {0x10db, "Rohm LSI Systems, Inc.", { + }}, + {0x10dc, "CERN/ECP/EDU", { + {0x0001,"STAR/RD24 SCI-PCI (PMC)"}, + {0x0002,"TAR/RD24 SCI-PCI (PMC) [ATT 2C15-3 (FPGA) SCI bridge on PCI 5 Volt card]"}, + {0x0021,"HIPPI destination"}, + {0x0022,"HIPPI source"}, + }}, + {0x10dd, "Evans & Sutherland", { + }}, + {0x10de, "Nvidia Corporation", { + {0x0008,"NV1"}, + {0x0009,"DAC64"}, + }}, + {0x10df, "Emulex Corporation", { + }}, + {0x10e0, "Integrated Micro Solutions Inc.", { + {0x5026,"IMS5026/27/28"}, + {0x8849,"8849"}, + {0x9128,"IMS9129"}, + }}, + {0x10e1, "Tekram Technology Co.,Ltd.", { + {0x690c,"690c"}, + {0xdc29,"DC290"}, + }}, + {0x10e2, "Aptix Corporation", { + }}, + {0x10e3, "Tundra Semiconductor Corp.", { + {0x0000,"CA91C042 [Universe]"}, + {0x0860,"CA91C860 [QSpan]"}, + }}, + {0x10e4, "Tandem Computers", { + }}, + {0x10e5, "Micro Industries Corporation", { + }}, + {0x10e6, "Gainbery Computer Products Inc.", { + }}, + {0x10e7, "Vadem", { + }}, + {0x10e8, "Applied Micro Circuits Corporation", { + {0x5920,"S5920"}, + {0x8043,"LANai4.x [Myrinet LANai interface chip]"}, + {0x8062,"S5933_PARASTATION"}, + {0x807d,"S5933 [Matchmaker]"}, + {0x809c,"S5933_HEPC3"}, + }}, + {0x10e9, "Alps Electric Co., Ltd.", { + }}, + {0x10ea, "Intergraphics Systems", { + {0x1680,"IGA-1680"}, + {0x1682,"IGA-1682"}, + }}, + {0x10eb, "Artists Graphics", { + {0x0101,"3GA"}, + }}, + {0x10ec, "Realtek Semiconductor Co., Ltd.", { + {0x8029,"8029"}, + {0x8129,"8129"}, + {0x8139,"8139"}, + }}, + {0x10ed, "Ascii Corporation", { + {0x7310,"V7310"}, + }}, + {0x10ee, "Xilinx, Inc.", { + }}, + {0x10ef, "Racore Computer Products, Inc.", { + }}, + {0x10f0, "Peritek Corporation", { + }}, + {0x10f1, "Tyan Computer", { + }}, + {0x10f2, "Achme Computer, Inc.", { + }}, + {0x10f3, "Alaris, Inc.", { + }}, + {0x10f4, "S-MOS Systems, Inc.", { + }}, + {0x10f5, "NKK Corporation", { + {0xa001,"NDR4000 [NR4600 Bridge]"}, + }}, + {0x10f6, "Creative Electronic Systems SA", { + }}, + {0x10f7, "Matsushita Electric Industrial Co., Ltd.", { + }}, + {0x10f8, "Altos India Ltd", { + }}, + {0x10f9, "PC Direct", { + }}, + {0x10fa, "Truevision", { + {0x000c,"TARGA 1000"}, + }}, + {0x10fb, "Thesys Gesellschaft f€r Mikroelektronik mbH", { + }}, + {0x10fc, "I-O Data Device, Inc.", { + }}, + {0x10fd, "Soyo Computer, Inc", { + }}, + {0x10fe, "Fast Multimedia AG", { + }}, + {0x10ff, "NCube", { + }}, + {0x1100, "Jazz Multimedia", { + }}, + {0x1101, "Initio Corporation", { + {0x9100,"320 P"}, + {0x9500,"360P"}, + }}, + {0x1102, "Creative Labs", { + }}, + {0x1103, "Triones Technologies, Inc.", { + }}, + {0x1104, "RasterOps Corp.", { + }}, + {0x1105, "Sigma Designs, Inc.", { + }}, + {0x1106, "VIA Technologies, Inc.", { + {0x0505,"VT 82C505"}, + {0x0561,"VT 82C561"}, + {0x0571,"VT82C586 IDE [Apollo]"}, + {0x0576,"VT 82C576 3V [Apollo Master]"}, + {0x0585,"VT82C585VP [Apollo VP1/VPX]"}, + {0x0586,"VT82C586 ISA [Apollo VP]"}, + {0x0595,"VT82C595 [Apollo VP2]"}, + {0x0597,"VT82C597 [Apollo VP3]"}, + {0x0926,"VT82C926 [Amazon]"}, + {0x1000,"82C570MV"}, + {0x1106,"82C570MV"}, + {0x1571,"VT 82C416MV"}, + {0x1595,"VT82C595/97 [Apollo VP2/97]"}, + {0x3038,"VT82C586B USB"}, + {0x3040,"VT82C586B ACPI"}, + {0x6100,"VT85C100A [Rhine II]"}, + {0x8597,"VT82C597 [Apollo VP3 AGP]"}, + }}, + {0x1107, "Stratus Computers", { + }}, + {0x1108, "Proteon, Inc.", { + {0x0100,"p1690plus_AA"}, + {0x0101,"p1690plus_AB"}, + }}, + {0x1109, "Cogent Data Technologies, Inc.", { + {0x1400,"EM110TX [EX110TX PCI Fast Ethernet Adapter]"}, + }}, + {0x110a, "Siemens Nixdorf AG", { + {0x6120,"SZB6120"}, + }}, + {0x110b, "Chromatic Research Inc.", { + }}, + {0x110c, "Mini-Max Technology, Inc.", { + }}, + {0x110d, "Znyx Advanced Systems", { + }}, + {0x110e, "CPU Technology", { + }}, + {0x110f, "Ross Technology", { + }}, + {0x1110, "Powerhouse Systems", { + }}, + {0x1111, "Santa Cruz Operation", { + }}, + {0x1112, "RNS - Div. of Meret Communications Inc", { + }}, + {0x1113, "Accton Technology Corporation", { + }}, + {0x1114, "Atmel Corporation", { + }}, + {0x1115, "3D Labs", { + }}, + {0x1116, "Data Translation", { + }}, + {0x1117, "Datacube, Inc", { + }}, + {0x1118, "Berg Electronics", { + }}, + {0x1119, "ICP Vortex Computersysteme GmbH", { + {0x0000,"GDT6000/6020/6050"}, + {0x0001,"GDT6000b/6010"}, + {0x0002,"GDT6110/6510"}, + {0x0003,"GDT6120/6520"}, + {0x0004,"GDT6530"}, + {0x0005,"GDT6550"}, + {0x0006,"GDT6x17"}, + {0x0007,"GDT6x27"}, + {0x0008,"GDT6537"}, + {0x0009,"GDT5557"}, + {0x000a,"GDT6x15"}, + {0x000b,"GDT6x25"}, + {0x000c,"GDT6535"}, + {0x000d,"GDT6555"}, + {0x0100,"GDT 6117RP/6517RP"}, + {0x0101,"GDT 6127RP/6527RP"}, + {0x0102,"GDT 6537RP"}, + {0x0103,"GDT 6557RP"}, + {0x0104,"GDT 6111RP/6511RP"}, + {0x0105,"GDT 6121RP/6521RP"}, + {0x0110,"GDT 6117RP1/6517RP1"}, + {0x0111,"GDT 6127RP1/6527RP1"}, + {0x0112,"GDT 6537RP1"}, + {0x0113,"GDT 6557RP1"}, + {0x0114,"GDT 6111RP1/6511RP1"}, + {0x0115,"GDT 6121RP1/6521RP1"}, + {0x0120,"GDT 6117RP2/6517RP2"}, + {0x0121,"GDT 6127RP2/6527RP2"}, + {0x0122,"GDT 6537RP2"}, + {0x0123,"GDT 6557RP2"}, + {0x0124,"GDT 6111RP2/6511RP2"}, + {0x0125,"GDT 6121RP2/6521RP2"}, + }}, + {0x111a, "Efficient Networks, Inc", { + {0x0000,"155P-MF1 (FPGA)"}, + {0x0002,"155P-MF1 (ASIC)"}, + }}, + {0x111b, "Teledyne Electronic Systems", { + }}, + {0x111c, "Tricord Systems Inc.", { + }}, + {0x111d, "Integrated Device Tech", { + }}, + {0x111e, "Eldec", { + }}, + {0x111f, "Precision Digital Images", { + }}, + {0x1120, "EMC Corporation", { + }}, + {0x1121, "Zilog", { + }}, + {0x1122, "Multi-tech Systems, Inc.", { + }}, + {0x1123, "Excellent Design, Inc.", { + }}, + {0x1124, "Leutron Vision AG", { + }}, + {0x1125, "Eurocore", { + }}, + {0x1127, "FORE Systems Inc", { + {0x0210,"PCA-200PC"}, + {0x0300,"PCA-200E"}, + }}, + {0x1129, "Firmworks", { + }}, + {0x112a, "Hermes Electronics Company, Ltd.", { + }}, + {0x112b, "Linotype - Hell AG", { + }}, + {0x112c, "Zenith Data Systems", { + }}, + {0x112d, "Ravicad", { + }}, + {0x112e, "Infomedia Microelectronics Inc.", { + }}, + {0x112f, "Imaging Technology Inc", { + {0x0000,"MVC IC-PCI"}, + }}, + {0x1130, "Computervision", { + }}, + {0x1131, "Philips Semiconductors", { + {0x7145,"SAA7145"}, + {0x7146,"SAA7146"}, + }}, + {0x1132, "Mitel Corp.", { + }}, + {0x1133, "Eicon Technology Corporation", { + {0xe001,"DIVA20PRO"}, + {0xe002,"DIVA20"}, + {0xe003,"DIVA20PRO_U"}, + {0xe004,"DIVA20_U"}, + }}, + {0x1134, "Mercury Computer Systems", { + }}, + {0x1135, "Fuji Xerox Co Ltd", { + }}, + {0x1136, "Momentum Data Systems", { + }}, + {0x1137, "Cisco Systems Inc", { + }}, + {0x1138, "Ziatech Corporation", { + {0x8905,"8905 [STD 32 Bridge]"}, + }}, + {0x1139, "Dynamic Pictures, Inc", { + }}, + {0x113a, "FWB Inc", { + }}, + {0x113b, "Network Computing Devices", { + }}, + {0x113c, "Cyclone Microsystems, Inc.", { + {0x0001,"PCI-SDK [PCI i960 Evaluation Platform]"}, + {0x0911,"PCI-911 [PCI-based i960Jx Intelligent I/O Controller]"}, + {0x0912,"PCI-912 [i960CF-based Intelligent I/O Controller]"}, + {0x0913,"PCI-913"}, + }}, + {0x113d, "Leading Edge Products Inc", { + }}, + {0x113e, "Sanyo Electric Co - Computer Engineering Dept", { + }}, + {0x113f, "Equinox Systems, Inc.", { + }}, + {0x1140, "Intervoice Inc", { + }}, + {0x1141, "Crest Microsystem Inc", { + }}, + {0x1142, "Alliance Semiconductor Corporation", { + {0x3210,"AP6410"}, + {0x6422,"AP6422"}, + {0x6424,"AT24"}, + {0x643d,"AT3D"}, + }}, + {0x1143, "NetPower, Inc", { + }}, + {0x1144, "Cincinnati Milacron", { + }}, + {0x1145, "Workbit Corporation", { + }}, + {0x1146, "Force Computers", { + }}, + {0x1147, "Interface Corp", { + }}, + {0x1148, "Schneider & Koch", { + }}, + {0x1149, "Win System Corporation", { + }}, + {0x114a, "VMIC", { + {0x7587,"VMIVME-7587"}, + }}, + {0x114b, "Canopus Co., Ltd", { + }}, + {0x114c, "Annabooks", { + }}, + {0x114d, "IC Corporation", { + }}, + {0x114e, "Nikon Systems Inc", { + }}, + {0x114f, "Digi International", { + {0x0002,"AccelePort EPC"}, + {0x0003,"RightSwitch SE-6"}, + {0x0004,"AccelePort Xem"}, + {0x0006,"AccelePort Xr,C/X"}, + {0x0009,"AccelePort Xr/J"}, + {0x000a,"AccelePort EPC/J"}, + {0x0027,"AccelePort Xr 920"}, + }}, + {0x1150, "Thinking Machines Corp", { + }}, + {0x1151, "JAE Electronics Inc.", { + }}, + {0x1152, "Megatek", { + }}, + {0x1153, "Land Win Electronic Corp", { + }}, + {0x1154, "Melco Inc", { + }}, + {0x1155, "Pine Technology Ltd", { + }}, + {0x1156, "Periscope Engineering", { + }}, + {0x1157, "Avsys Corporation", { + }}, + {0x1158, "Voarx R & D Inc", { + }}, + {0x1159, "Mutech Corp", { + {0x0001,"MV-1000"}, + }}, + {0x115a, "Harlequin Ltd", { + }}, + {0x115b, "Parallax Graphics", { + }}, + {0x115c, "Photron Ltd.", { + }}, + {0x115d, "Xircom", { + }}, + {0x115e, "Peer Protocols Inc", { + }}, + {0x115f, "Maxtor Corporation", { + }}, + {0x1160, "Megasoft Inc", { + }}, + {0x1161, "PFU Limited", { + }}, + {0x1162, "OA Laboratory Co Ltd", { + }}, + {0x1163, "Rendition", { + {0x0001,"Verite 1000 PCI"}, + {0x2000,"Verite V2100"}, + }}, + {0x1164, "Advanced Peripherals Technologies", { + }}, + {0x1165, "Imagraph Corporation", { + }}, + {0x1166, "Pequr Technology", { + }}, + {0x1167, "Mutoh Industries Inc", { + }}, + {0x1168, "Thine Electronics Inc", { + }}, + {0x1169, "Centre for Development of Advanced Computing", { + }}, + {0x116a, "Polaris Communications", { + }}, + {0x116b, "Connectware Inc", { + }}, + {0x116c, "Intelligent Resources Integrated Systems", { + }}, + {0x116d, "Martin-Marietta", { + }}, + {0x116e, "Electronics for Imaging", { + }}, + {0x116f, "Workstation Technology", { + }}, + {0x1170, "Inventec Corporation", { + }}, + {0x1171, "Loughborough Sound Images Plc", { + }}, + {0x1172, "Altera Corporation", { + }}, + {0x1173, "Adobe Systems, Inc", { + }}, + {0x1174, "Bridgeport Machines", { + }}, + {0x1175, "Mitron Computer Inc.", { + }}, + {0x1176, "SBE Incorporated", { + }}, + {0x1177, "Silicon Engineering", { + }}, + {0x1178, "Alfa, Inc.", { + }}, + {0x1179, "Toshiba America Info Systems", { + {0x0601,"601"}, + {0x060a,"ToPIC95"}, + {0x060f,"ToPIC97"}, + {0x0701,"Lucent DSP1645 [Mars]"}, + }}, + {0x117b, "L G Electronics, Inc.", { + }}, + {0x117c, "Atto Technology", { + }}, + {0x117d, "Becton & Dickinson", { + }}, + {0x117e, "T/R Systems", { + }}, + {0x117f, "Integrated Circuit Systems", { + }}, + {0x1180, "Ricoh Co Ltd", { + {0x0466,"RL5C466"}, + }}, + {0x1181, "Telmatics International", { + }}, + {0x1183, "Fujikura Ltd", { + }}, + {0x1184, "Forks Inc", { + }}, + {0x1185, "Dataworld International Ltd", { + }}, + {0x1186, "D-Link System Inc", { + }}, + {0x1187, "Advanced Technology Laboratories, Inc.", { + }}, + {0x1188, "Shima Seiki Manufacturing Ltd.", { + }}, + {0x1189, "Matsushita Electronics Co Ltd", { + }}, + {0x118a, "Hilevel Technology", { + }}, + {0x118b, "Hypertec Pty Limited", { + }}, + {0x118c, "Corollary, Inc", { + {0x0014,"PCIB [C-bus II to PCI bus host bridge chip]"}, + }}, + {0x118d, "BitFlow Inc", { + {0x0001,"n/a [Raptor-PCI framegrabber]"}, + }}, + {0x118e, "Hermstedt GmbH", { + }}, + {0x118f, "Green Logic", { + }}, + {0x1191, "Artop Electronic Corp", { + {0x0004,"ATP8400"}, + {0x0005,"ATP850UF"}, + }}, + {0x1192, "Densan Company Ltd", { + }}, + {0x1193, "Zeitnet Inc.", { + {0x0001,"1221"}, + {0x0002,"1225"}, + }}, + {0x1194, "Toucan Technology", { + }}, + {0x1195, "Ratoc System Inc", { + }}, + {0x1196, "Hytec Electronics Ltd", { + }}, + {0x1197, "Gage Applied Sciences, Inc.", { + }}, + {0x1198, "Lambda Systems Inc", { + }}, + {0x1199, "Attachmate Corporation", { + }}, + {0x119a, "Mind Share, Inc.", { + }}, + {0x119b, "Omega Micro Inc.", { + {0x1221,"82C092G"}, + }}, + {0x119c, "Information Technology Inst.", { + }}, + {0x119d, "Bug, Inc. Sapporo Japan", { + }}, + {0x119e, "Fujitsu Microelectronics Ltd.", { + }}, + {0x119f, "Bull HN Information Systems", { + }}, + {0x11a0, "Convex Computer Corporation", { + }}, + {0x11a1, "Hamamatsu Photonics K.K.", { + }}, + {0x11a2, "Sierra Research and Technology", { + }}, + {0x11a3, "Deuretzbacher GmbH & Co. Eng. KG", { + }}, + {0x11a4, "Barco Graphics NV", { + }}, + {0x11a5, "Microunity Systems Eng. Inc", { + }}, + {0x11a6, "Pure Data Ltd.", { + }}, + {0x11a7, "Power Computing Corp.", { + }}, + {0x11a8, "Systech Corp.", { + }}, + {0x11a9, "InnoSys Inc.", { + }}, + {0x11aa, "Actel", { + }}, + {0x11ab, "Galileo Technology Ltd.", { + {0x0146,"GT-64010"}, + {0x4801,"GT-48001"}, + }}, + {0x11ac, "Canon Information Systems Research Aust.", { + }}, + {0x11ad, "Lite-On Communications Inc", { + {0x0002,"LNE100TX"}, + }}, + {0x11ae, "Aztech System Ltd", { + }}, + {0x11af, "Avid Technology Inc.", { + }}, + {0x11b0, "V3 Semiconductor Inc.", { + {0x0292,"V292PBC [Am29030/40 Bridge]"}, + {0x0960,"V96xPBC"}, + {0xc960,"V96DPC"}, + }}, + {0x11b1, "Apricot Computers", { + }}, + {0x11b2, "Eastman Kodak", { + }}, + {0x11b3, "Barr Systems Inc.", { + }}, + {0x11b4, "Leitch Technology International", { + }}, + {0x11b5, "Radstone Technology Plc", { + }}, + {0x11b6, "United Video Corp", { + }}, + {0x11b8, "XPoint Technologies, Inc", { + }}, + {0x11b9, "Pathlight Technology Inc.", { + }}, + {0x11ba, "Videotron Corp", { + }}, + {0x11bb, "Pyramid Technology", { + }}, + {0x11bc, "Network Peripherals Inc", { + {0x0001,"NP-PCI"}, + }}, + {0x11bd, "Pinnacle Systems Inc.", { + }}, + {0x11be, "International Microcircuits Inc", { + }}, + {0x11bf, "Astrodesign, Inc.", { + }}, + {0x11c0, "Hewlett Packard", { + }}, + {0x11c1, "Lucent Microelectronics", { + {0x0440,"L56xMF"}, + }}, + {0x11c2, "Sand Microelectronics", { + }}, + {0x11c4, "Document Technologies, Inc", { + }}, + {0x11c5, "Shiva Corporation", { + }}, + {0x11c6, "Dainippon Screen Mfg. Co. Ltd", { + }}, + {0x11c7, "D.C.M. Data Systems", { + }}, + {0x11c8, "Dolphin Interconnect Solutions AS", { + {0x0658,"PSB"}, + }}, + {0x11c9, "Magma", { + }}, + {0x11ca, "LSI Systems, Inc", { + }}, + {0x11cb, "Specialix Research Ltd.", { + {0x2000,"PCI_9050"}, + {0x4000,"SUPI_1"}, + {0x8000,"T225"}, + }}, + {0x11cc, "Michels & Kleberhoff Computer GmbH", { + }}, + {0x11cd, "HAL Computer Systems, Inc.", { + }}, + {0x11ce, "Netaccess", { + }}, + {0x11cf, "Pioneer Electronic Corporation", { + }}, + {0x11d0, "Lockheed Martin Federal Systems-Manassas", { + }}, + {0x11d1, "Auravision", { + {0x01f7,"VxP524"}, + }}, + {0x11d2, "Intercom Inc.", { + }}, + {0x11d3, "Trancell Systems Inc", { + }}, + {0x11d4, "Analog Devices", { + }}, + {0x11d5, "Ikon Corporation", { + {0x0115,"10115"}, + {0x0117,"10117"}, + }}, + {0x11d6, "Tekelec Telecom", { + }}, + {0x11d7, "Trenton Technology, Inc.", { + }}, + {0x11d8, "Image Technologies Development", { + }}, + {0x11d9, "TEC Corporation", { + }}, + {0x11da, "Novell", { + }}, + {0x11db, "Sega Enterprises Ltd", { + }}, + {0x11dc, "Questra Corporation", { + }}, + {0x11dd, "Crosfield Electronics Limited", { + }}, + {0x11de, "Zoran Corporation", { + {0x6057,"ZR36057"}, + {0x6120,"ZR36120"}, + }}, + {0x11df, "New Wave PDG", { + }}, + {0x11e0, "Cray Communications A/S", { + }}, + {0x11e1, "GEC Plessey Semi Inc.", { + }}, + {0x11e2, "Samsung Information Systems America", { + }}, + {0x11e3, "Quicklogic Corporation", { + }}, + {0x11e4, "Second Wave Inc", { + }}, + {0x11e5, "IIX Consulting", { + }}, + {0x11e6, "Mitsui-Zosen System Research", { + }}, + {0x11e7, "Toshiba America, Elec. Company", { + }}, + {0x11e8, "Digital Processing Systems Inc.", { + }}, + {0x11e9, "Highwater Designs Ltd.", { + }}, + {0x11ea, "Elsag Bailey", { + }}, + {0x11eb, "Formation Inc.", { + }}, + {0x11ec, "Coreco Inc", { + }}, + {0x11ed, "Mediamatics", { + }}, + {0x11ee, "Dome Imaging Systems Inc", { + }}, + {0x11ef, "Nicolet Technologies B.V.", { + }}, + {0x11f0, "Compu-Shack GmbH", { + }}, + {0x11f1, "Symbios Logic Inc", { + }}, + {0x11f2, "Picture Tel Japan K.K.", { + }}, + {0x11f3, "Keithley Metrabyte", { + }}, + {0x11f4, "Kinetic Systems Corporation", { + {0x2915,"CAMAC controller"}, + }}, + {0x11f5, "Computing Devices International", { + }}, + {0x11f6, "Compex", { + {0x0112,"ENet100VG4"}, + {0x1401,"ReadyLink 2000"}, + }}, + {0x11f7, "Scientific Atlanta", { + }}, + {0x11f8, "PMC-Sierra Inc.", { + {0x7375,"PM7375 [LASAR-155 ATM SAR]"}, + }}, + {0x11f9, "I-Cube Inc", { + }}, + {0x11fa, "Kasan Electronics Company, Ltd.", { + }}, + {0x11fb, "Datel Inc", { + }}, + {0x11fc, "Silicon Magic", { + }}, + {0x11fd, "High Street Consultants", { + }}, + {0x11fe, "Comtrol Corporation", { + {0x0001,"RocketPort 8 Oct"}, + {0x0002,"RocketPort 8 Intf"}, + {0x0003,"RocketPort 16 Intf"}, + {0x0004,"RocketPort 32 Intf"}, + }}, + {0x11ff, "Scion Corporation", { + }}, + {0x1200, "CSS Corporation", { + }}, + {0x1201, "Vista Controls Corp", { + }}, + {0x1202, "Network General Corp.", { + }}, + {0x1203, "Bayer Corporation, Agfa Division", { + }}, + {0x1204, "Lattice Semiconductor Corporation", { + }}, + {0x1205, "Array Corporation", { + }}, + {0x1206, "Amdahl Corporation", { + }}, + {0x1208, "Parsytec GmbH", { + }}, + {0x1209, "SCI Systems Inc", { + }}, + {0x120a, "Synaptel", { + }}, + {0x120b, "Adaptive Solutions", { + }}, + {0x120c, "Technical Corp.", { + }}, + {0x120d, "Compression Labs, Inc.", { + }}, + {0x120e, "Cyclades Corporation", { + {0x0100,"Cyclom_Y"}, + {0x0200,"Cyclom_Z"}, + }}, + {0x120f, "Essential Communications", { + {0x0001,"Roadrunner serial HIPPI"}, + }}, + {0x1210, "Hyperparallel Technologies", { + }}, + {0x1211, "Braintech Inc", { + }}, + {0x1212, "Kingston Technology Corp.", { + }}, + {0x1213, "Applied Intelligent Systems, Inc.", { + }}, + {0x1214, "Performance Technologies, Inc.", { + }}, + {0x1215, "Interware Co., Ltd", { + }}, + {0x1216, "Purup Prepress A/S", { + }}, + {0x1217, "O2 Micro, Inc.", { + {0x6729,"6729"}, + {0x673a,"6730"}, + {0x6832,"6832"}, + }}, + {0x1218, "Hybricon Corp.", { + }}, + {0x1219, "First Virtual Corporation", { + }}, + {0x121a, "3Dfx Interactive, Inc.", { + {0x0001,"Voodoo"}, + {0x0002,"Voodoo2"}, + }}, + {0x121b, "Advanced Telecommunications Modules", { + }}, + {0x121c, "Nippon Texaco., Ltd", { + }}, + {0x121d, "Lippert Automationstechnik GmbH", { + }}, + {0x121e, "CSPI", { + }}, + {0x121f, "Arcus Technology, Inc.", { + }}, + {0x1220, "Ariel Corporation", { + }}, + {0x1221, "Contec Co., Ltd", { + }}, + {0x1222, "Ancor Communications, Inc.", { + }}, + {0x1223, "Heurikon/Computer Products", { + }}, + {0x1224, "Interactive Images", { + }}, + {0x1225, "Power I/O, Inc.", { + }}, + {0x1227, "Tech-Source", { + }}, + {0x1228, "Norsk Elektro Optikk A/S", { + }}, + {0x1229, "Data Kinesis Inc.", { + }}, + {0x122a, "Integrated Telecom", { + }}, + {0x122b, "LG Industrial Systems Co., Ltd", { + }}, + {0x122c, "Sican GmbH", { + }}, + {0x122d, "Aztech System Ltd", { + }}, + {0x122e, "Xyratex", { + }}, + {0x122f, "Andrew Corporation", { + }}, + {0x1230, "Fishcamp Engineering", { + }}, + {0x1231, "Woodward McCoach, Inc.", { + }}, + {0x1232, "GPT Limited", { + }}, + {0x1233, "Bus-Tech, Inc.", { + }}, + {0x1234, "Technical Corp.", { + }}, + {0x1235, "Risq Modular Systems, Inc.", { + }}, + {0x1236, "Sigma Designs Corporation", { + {0x6401,"REALmagic 64/GX (SD 6425)"}, + }}, + {0x1237, "Alta Technology Corporation", { + }}, + {0x1238, "Adtran", { + }}, + {0x1239, "3DO Company", { + }}, + {0x123a, "Visicom Laboratories, Inc.", { + }}, + {0x123b, "Seeq Technology, Inc.", { + }}, + {0x123c, "Century Systems, Inc.", { + }}, + {0x123d, "Engineering Design Team, Inc.", { + }}, + {0x123e, "Simutech, Inc.", { + }}, + {0x123f, "C-Cube Microsystems", { + {0x00e4,"MPEG"}, + }}, + {0x1240, "Marathon Technologies Corp.", { + }}, + {0x1241, "DSC Communications", { + }}, + {0x1243, "Delphax", { + }}, + {0x1244, "AVM Audiovisuelles MKTG & Computer System GmbH", { + }}, + {0x1245, "A.P.D., S.A.", { + }}, + {0x1246, "Dipix Technologies, Inc.", { + }}, + {0x1247, "Xylon Research, Inc.", { + }}, + {0x1248, "Central Data Corporation", { + }}, + {0x1249, "Samsung Electronics Co., Ltd.", { + }}, + {0x124a, "AEG Electrocom GmbH", { + }}, + {0x124b, "SBS/Greenspring Modular I/O", { + }}, + {0x124c, "Solitron Technologies, Inc.", { + }}, + {0x124d, "Stallion Technologies, Inc.", { + {0x0000,"EasyConnection 8/32 - PCI"}, + {0x0002,"EasyConnection 8/64 - PCI"}, + {0x0003,"EasyIO - PCI"}, + }}, + {0x124e, "Cylink", { + }}, + {0x124f, "Infotrend Technology, Inc.", { + }}, + {0x1250, "Hitachi Microcomputer System Ltd", { + }}, + {0x1251, "VLSI Solutions Oy", { + }}, + {0x1253, "Guzik Technical Enterprises", { + }}, + {0x1254, "Linear Systems Ltd.", { + }}, + {0x1255, "Optibase Ltd", { + {0x1110,"MPEG Forge"}, + {0x1210,"MPEG Fusion"}, + {0x2110,"VideoPlex"}, + {0x2120,"VideoPlex CC"}, + {0x2130,"VideoQuest"}, + }}, + {0x1256, "Perceptive Solutions, Inc.", { + }}, + {0x1257, "Vertex Networks, Inc.", { + }}, + {0x1258, "Gilbarco, Inc.", { + }}, + {0x1259, "Allied Telesyn International", { + }}, + {0x125a, "ABB Power Systems", { + }}, + {0x125b, "Asix Electronics Corporation", { + }}, + {0x125c, "Aurora Technologies, Inc.", { + }}, + {0x125d, "ESS Technology", { + }}, + {0x125e, "Specialvideo Engineering SRL", { + }}, + {0x125f, "Concurrent Technologies, Inc.", { + }}, + {0x1260, "Harris Semiconductor", { + }}, + {0x1261, "Matsushita-Kotobuki Electronics Industries, Ltd.", { + }}, + {0x1262, "ES Computer Company, Ltd.", { + }}, + {0x1263, "Sonic Solutions", { + }}, + {0x1264, "Aval Nagasaki Corporation", { + }}, + {0x1265, "Casio Computer Co., Ltd.", { + }}, + {0x1266, "Microdyne Corporation", { + }}, + {0x1267, "S. A. Telecommunications", { + {0x5352,"PCR2101"}, + {0x5a4b,"Telsat Turbo"}, + }}, + {0x1268, "Tektronix", { + }}, + {0x1269, "Thomson-CSF/TTM", { + }}, + {0x126a, "Lexmark International, Inc.", { + }}, + {0x126b, "Adax, Inc.", { + }}, + {0x126c, "Northern Telecom", { + }}, + {0x126d, "Splash Technology, Inc.", { + }}, + {0x126e, "Sumitomo Metal Industries, Ltd.", { + }}, + {0x126f, "Silicon Motion, Inc.", { + }}, + {0x1270, "Olympus Optical Co., Ltd.", { + }}, + {0x1271, "GW Instruments", { + }}, + {0x1272, "Telematics International", { + }}, + {0x1273, "Hughes Network Systems", { + {0x0002,"DirecPC"}, + }}, + {0x1274, "Ensoniq", { + {0x5000,"AudioPCI"}, + }}, + {0x1275, "Network Appliance Corporation", { + }}, + {0x1276, "Switched Network Technologies, Inc.", { + }}, + {0x1277, "Comstream", { + }}, + {0x1278, "Transtech Parallel Systems Ltd.", { + }}, + {0x1279, "Transmeta Corporation", { + }}, + {0x127a, "Rockwell International", { + }}, + {0x127b, "Pixera Corporation", { + }}, + {0x127c, "Crosspoint Solutions, Inc.", { + }}, + {0x127d, "Vela Research", { + }}, + {0x127e, "Winnov, L.P.", { + }}, + {0x127f, "Fujifilm", { + }}, + {0x1280, "Photoscript Group Ltd.", { + }}, + {0x1281, "Yokogawa Electric Corporation", { + }}, + {0x1282, "Davicom Semiconductor, Inc.", { + }}, + {0x1283, "Integrated Technology Express, Inc.", { + }}, + {0x1284, "Sahara Networks, Inc.", { + }}, + {0x1285, "Platform Technologies, Inc.", { + }}, + {0x1286, "Mazet GmbH", { + }}, + {0x1287, "M-Pact, Inc.", { + }}, + {0x1288, "Timestep Corporation", { + }}, + {0x1289, "AVC Technology, Inc.", { + }}, + {0x128a, "Asante Technologies, Inc.", { + }}, + {0x128b, "Transwitch Corporation", { + }}, + {0x128c, "Retix Corporation", { + }}, + {0x128d, "G2 Networks, Inc.", { + }}, + {0x128e, "Samho Multi Tech Ltd.", { + }}, + {0x128f, "Tateno Dennou, Inc.", { + }}, + {0x1290, "Sord Computer Corporation", { + }}, + {0x1291, "NCS Computer Italia", { + }}, + {0x1292, "Tritech Microelectronics Inc", { + }}, + {0x1293, "Media Reality Technology", { + }}, + {0x1294, "Rhetorex, Inc.", { + }}, + {0x1295, "Imagenation Corporation", { + }}, + {0x1296, "Kofax Image Products", { + }}, + {0x1297, "Holco Enterprise Co, Ltd/Shuttle Computer", { + }}, + {0x1298, "Spellcaster Telecommunications Inc.", { + }}, + {0x1299, "Knowledge Technology Lab.", { + }}, + {0x129b, "Image Access", { + }}, + {0x129c, "Jaycor", { + }}, + {0x129d, "Compcore Multimedia, Inc.", { + }}, + {0x129e, "Victor Company of Japan, Ltd.", { + }}, + {0x129f, "OEC Medical Systems, Inc.", { + }}, + {0x12a0, "Allen-Bradley Company", { + }}, + {0x12a1, "Simpact Associates, Inc.", { + }}, + {0x12a2, "Newgen Systems Corporation", { + }}, + {0x12a3, "Lucent Technologies", { + }}, + {0x12a4, "NTT Electronics Technology Company", { + }}, + {0x12a5, "Vision Dynamics Ltd.", { + }}, + {0x12a6, "Scalable Networks, Inc.", { + }}, + {0x12a7, "AMO GmbH", { + }}, + {0x12a8, "News Datacom", { + }}, + {0x12a9, "Xiotech Corporation", { + }}, + {0x12aa, "SDL Communications, Inc.", { + }}, + {0x12ab, "Yuan Yuan Enterprise Co., Ltd.", { + }}, + {0x12ac, "Measurex Corporation", { + }}, + {0x12ad, "Multidata GmbH", { + }}, + {0x12ae, "Alteon Networks Inc.", { + {0x0001,"AceNIC Gigabit Ethernet"}, + }}, + {0x12af, "TDK USA Corp", { + }}, + {0x12b0, "Jorge Scientific Corp", { + }}, + {0x12b1, "GammaLink", { + }}, + {0x12b2, "General Signal Networks", { + }}, + {0x12b3, "Inter-Face Co Ltd", { + }}, + {0x12b4, "FutureTel Inc", { + }}, + {0x12b5, "Granite Systems Inc.", { + }}, + {0x12b6, "Natural Microsystems", { + }}, + {0x12b7, "Cognex Modular Vision Systems Div. - Acumen Inc.", { + }}, + {0x12b8, "Korg", { + }}, + {0x12b9, "US Robotics", { + }}, + {0x12ba, "PMC Sierra", { + }}, + {0x12bb, "Nippon Unisoft Corporation", { + }}, + {0x12bc, "Array Microsystems", { + }}, + {0x12bd, "Computerm Corp.", { + }}, + {0x12be, "Anchor Chips Inc.", { + }}, + {0x12bf, "Fujifilm Microdevices", { + }}, + {0x12c0, "Infimed", { + }}, + {0x12c1, "GMM Research Corp", { + }}, + {0x12c2, "Mentec Limited", { + }}, + {0x12c3, "Holtek Microelectronics Inc", { + }}, + {0x12c4, "Connect Tech Inc", { + }}, + {0x12c5, "Picture Elements Incorporated", { + {0x0081,"PCIVST [PCI Grayscale Thresholding Engine]"}, + }}, + {0x12c6, "Mitani Corporation", { + }}, + {0x12c7, "Dialogic Corp", { + }}, + {0x12c8, "G Force Co, Ltd", { + }}, + {0x12c9, "Gigi Operations", { + }}, + {0x12ca, "Integrated Computing Engines", { + }}, + {0x12cb, "Antex Electronics Corporation", { + }}, + {0x12cc, "Pluto Technologies International", { + }}, + {0x12cd, "Aims Lab", { + }}, + {0x12ce, "Netspeed Inc.", { + }}, + {0x12cf, "Prophet Systems, Inc.", { + }}, + {0x12d0, "GDE Systems, Inc.", { + }}, + {0x12d1, "PSITech", { + }}, + {0x12d2, "NVidia / SGS Thomson (Joint Venture)", { + {0x0018,"Riva128"}, + }}, + {0x12d3, "Vingmed Sound A/S", { + }}, + {0x12d4, "DGM&S", { + }}, + {0x12d5, "Equator Technologies", { + }}, + {0x12d6, "Analogic Corp", { + }}, + {0x12d7, "Biotronic SRL", { + }}, + {0x12d8, "Pericom Semiconductor", { + }}, + {0x12d9, "Aculab PLC", { + }}, + {0x12da, "True Time Inc.", { + }}, + {0x12db, "Annapolis Micro Systems, Inc", { + }}, + {0x12dc, "Symicron Computer Communication Ltd.", { + }}, + {0x12dd, "Management Graphics", { + }}, + {0x12de, "Rainbow Technologies", { + }}, + {0x12df, "SBS Technologies Inc", { + }}, + {0x12e0, "Chase Research", { + }}, + {0x12e1, "Nintendo Co, Ltd", { + }}, + {0x12e2, "Datum Inc. Bancomm-Timing Division", { + }}, + {0x12e3, "Imation Corp - Medical Imaging Systems", { + }}, + {0x12e4, "Brooktrout Technology Inc", { + }}, + {0x12e5, "Apex Semiconductor Inc", { + }}, + {0x12e6, "Cirel Systems", { + }}, + {0x12e7, "Sunsgroup Corporation", { + }}, + {0x12e8, "Crisc Corp", { + }}, + {0x12e9, "GE Spacenet", { + }}, + {0x12ea, "Zuken", { + }}, + {0x12eb, "Aureal Semiconductor", { + }}, + {0x12ec, "3A International, Inc.", { + }}, + {0x12ed, "Optivision Inc.", { + }}, + {0x12ee, "Orange Micro", { + }}, + {0x12ef, "Vienna Systems", { + }}, + {0x12f0, "Pentek", { + }}, + {0x12f1, "Sorenson Vision Inc", { + }}, + {0x12f2, "Gammagraphx, Inc.", { + }}, + {0x12f3, "Radstone Technology", { + }}, + {0x12f4, "Megatel", { + }}, + {0x12f5, "Forks", { + }}, + {0x12f6, "Dawson France", { + }}, + {0x12f7, "Cognex", { + }}, + {0x12f8, "Electronic Design GmbH", { + }}, + {0x12f9, "Four Fold Ltd", { + }}, + {0x12fb, "Spectrum Signal Processing", { + }}, + {0x12fc, "Capital Equipment Corp", { + }}, + {0x12fd, "I2S", { + }}, + {0x12fe, "ESD Electronic System Design GmbH", { + }}, + {0x12ff, "Lexicon", { + }}, + {0x1300, "Harman International Industries Inc", { + }}, + {0x1302, "Computer Sciences Corp", { + }}, + {0x1303, "Innovative Integration", { + }}, + {0x1304, "Juniper Networks", { + }}, + {0x1305, "Netphone, Inc", { + }}, + {0x1306, "Duet Technologies", { + }}, + {0x1307, "Computer Boards", { + {0x0001,"DAS1602/16"}, + }}, + {0x1308, "Jato Technologies Inc.", { + }}, + {0x1309, "AB Semiconductor Ltd", { + }}, + {0x130a, "Mitsubishi Electric Microcomputer", { + }}, + {0x130b, "Colorgraphic Communications Corp", { + }}, + {0x130c, "Ambex Technologies, Inc", { + }}, + {0x130d, "Accelerix Inc", { + }}, + {0x130e, "Yamatake-Honeywell Co. Ltd", { + }}, + {0x130f, "Advanet Inc", { + }}, + {0x1310, "Gespac", { + }}, + {0x1311, "Videoserver, Inc", { + }}, + {0x1312, "Acuity Imaging, Inc", { + }}, + {0x1313, "Yaskawa Electric Co.", { + }}, + {0x1316, "Teradyne Inc", { + }}, + {0x1317, "Bridgecom, Inc", { + }}, + {0x1318, "Packet Engines Inc.", { + }}, + {0x1319, "Fortemedia, Inc", { + }}, + {0x131a, "Finisar Corp.", { + }}, + {0x131c, "Nippon Electro-Sensory Devices Corp", { + }}, + {0x131d, "Sysmic, Inc.", { + }}, + {0x131e, "Xinex Networks Inc", { + }}, + {0x131f, "Siig Inc", { + }}, + {0x1320, "Crypto AG", { + }}, + {0x1321, "Arcobel Graphics BV", { + }}, + {0x1322, "MTT Co., Ltd", { + }}, + {0x1323, "Dome Inc", { + }}, + {0x1324, "Sphere Communications", { + }}, + {0x1325, "Salix Technologies, Inc", { + }}, + {0x1326, "Seachange international", { + }}, + {0x1327, "Voss scientific", { + }}, + {0x1328, "quadrant international", { + }}, + {0x1329, "Productivity Enhancement", { + }}, + {0x132a, "Microcom Inc.", { + }}, + {0x132b, "Broadband Technologies", { + }}, + {0x132c, "Micrel Inc", { + }}, + {0x132d, "Integrated Silicon Solution, Inc.", { + }}, + {0x1330, "MMC Networks", { + }}, + {0x1331, "Radisys Corp.", { + }}, + {0x1332, "Micro Memory", { + }}, + {0x1334, "Redcreek Communications, Inc", { + }}, + {0x1335, "Videomail, Inc", { + }}, + {0x1337, "Third Planet Publishing", { + }}, + {0x1338, "BT Electronics", { + }}, + {0x133a, "Vtel Corp", { + }}, + {0x133b, "Softcom Microsystems", { + }}, + {0x133c, "Holontech Corp", { + }}, + {0x133d, "SS Technologies", { + }}, + {0x133e, "Virtual Computer Corp", { + }}, + {0x133f, "SCM Microsystems", { + }}, + {0x1340, "Atalla Corp", { + }}, + {0x1341, "Kyoto Microcomputer Co", { + }}, + {0x1342, "Promax Systems Inc", { + }}, + {0x1343, "Phylon Communications Inc", { + }}, + {0x1344, "Crucial Technology", { + }}, + {0x1345, "Arescom Inc", { + }}, + {0x1347, "Odetics", { + }}, + {0x1349, "Sumitomo Electric Industries, Ltd.", { + }}, + {0x134a, "DTC Technology Corp.", { + }}, + {0x134b, "ARK Research Corp.", { + }}, + {0x134c, "Chori Joho System Co. Ltd", { + }}, + {0x134d, "PCTel Inc", { + }}, + {0x134e, "CSTI", { + }}, + {0x134f, "Algo System Co Ltd", { + }}, + {0x1350, "Systec Co. Ltd", { + }}, + {0x1351, "Sonix Inc", { + }}, + {0x1353, "Dassault A.T.", { + }}, + {0x1354, "Dwave System Inc", { + }}, + {0x1355, "Kratos Analytical Ltd", { + }}, + {0x1356, "The Logical Co", { + }}, + {0x1359, "Prisa Networks", { + }}, + {0x135a, "Brain Boxes", { + }}, + {0x135b, "Giganet Inc", { + }}, + {0x135c, "Quatech Inc", { + }}, + {0x135d, "ABB Network Partner AB", { + }}, + {0x135e, "Sealevel Systems Inc", { + }}, + {0x135f, "I-Data International A-S", { + }}, + {0x1360, "Meinberg Funkuhren", { + }}, + {0x1361, "Soliton Systems K.K.", { + }}, + {0x1362, "Fujifacom Corporation", { + }}, + {0x1363, "Phoenix Technology Ltd", { + }}, + {0x1364, "ATM Communications Inc", { + }}, + {0x1365, "Hypercope GmbH", { + }}, + {0x1366, "Teijin Seiki Co. Ltd", { + }}, + {0x1367, "Hitachi Zosen Corporation", { + }}, + {0x1368, "Skyware Corporation", { + }}, + {0x1369, "Digigram", { + }}, + {0x136a, "High Soft Tech", { + }}, + {0x136b, "Kawasaki Steel Corporation", { + }}, + {0x136c, "Adtek System Science Co Ltd", { + }}, + {0x136d, "Gigalabs Inc", { + }}, + {0x136f, "Applied Magic Inc", { + }}, + {0x1370, "ATL Products", { + }}, + {0x1371, "CNet Technology Inc", { + }}, + {0x1373, "Silicon Vision Inc", { + }}, + {0x1374, "Silicom Ltd", { + }}, + {0x1375, "Argosystems Inc", { + }}, + {0x1376, "LMC", { + }}, + {0x1377, "Electronic Equipment Production & Distribution GmbH", { + }}, + {0x1378, "Telemann Co. Ltd", { + }}, + {0x1379, "Asahi Kasei Microsystems Co Ltd", { + }}, + {0x137a, "Mark of the Unicorn Inc", { + }}, + {0x137b, "PPT Vision", { + }}, + {0x137c, "Iwatsu Electric Co Ltd", { + }}, + {0x137d, "Dynachip Corporation", { + }}, + {0x137e, "Patriot Scientific Corporation", { + }}, + {0x137f, "Japan Satellite Systems Inc", { + }}, + {0x1380, "Sanritz Automation Co Ltd", { + }}, + {0x1381, "Brains Co. Ltd", { + }}, + {0x1382, "Marian - Electronic & Software", { + }}, + {0x1383, "Controlnet Inc", { + }}, + {0x1384, "Reality Simulation Systems Inc", { + }}, + {0x1385, "Netgear", { + }}, + {0x1386, "Video Domain Technologies", { + }}, + {0x1387, "Systran Corp", { + }}, + {0x1388, "Hitachi Information Technology Co Ltd", { + }}, + {0x1389, "Applicom International", { + }}, + {0x138a, "Fusion Micromedia Corp", { + }}, + {0x138b, "Tokimec Inc", { + }}, + {0x138c, "Silicon Reality", { + }}, + {0x138d, "Future Techno Designs pte Ltd", { + }}, + {0x138e, "Basler GmbH", { + }}, + {0x138f, "Patapsco Designs Inc", { + }}, + {0x1390, "Concept Development Inc", { + }}, + {0x1391, "Development Concepts Inc", { + }}, + {0x1392, "Medialight Inc", { + }}, + {0x1393, "Moxa Technologies Co Ltd", { + }}, + {0x1394, "Level One Communications", { + }}, + {0x1395, "Ambicom Inc", { + }}, + {0x1396, "Cipher Systems Inc", { + }}, + {0x1397, "Cologne Chip Designs GmbH", { + }}, + {0x1398, "Clarion co. Ltd", { + }}, + {0x1399, "Rios systems Co Ltd", { + }}, + {0x139a, "Alacritech Inc", { + }}, + {0x139b, "Mediasonic Multimedia Systems Ltd", { + }}, + {0x139c, "Quantum 3d Inc", { + }}, + {0x139d, "EPL limited", { + }}, + {0x139e, "Media4", { + }}, + {0x139f, "Aethra s.r.l.", { + }}, + {0x13a0, "Crystal Group Inc", { + }}, + {0x13a1, "Kawasaki Heavy Industries Ltd", { + }}, + {0x13a2, "Ositech Communications Inc", { + }}, + {0x13a3, "Hi-Fn", { + }}, + {0x13a4, "Rascom Inc", { + }}, + {0x13a5, "Audio Digital Imaging Inc", { + }}, + {0x13a6, "Videonics Inc", { + }}, + {0x13a7, "Teles AG", { + }}, + {0x13a8, "Exar Corp.", { + }}, + {0x13a9, "Siemens Medical Systems, Ultrasound Group", { + }}, + {0x13aa, "Broadband Networks Inc", { + }}, + {0x13ab, "Arcom Control Systems Ltd", { + }}, + {0x13ac, "Motion Media Technology Ltd", { + }}, + {0x13ad, "Nexus Inc", { + }}, + {0x13ae, "ALD Technology Ltd", { + }}, + {0x13af, "T.Sqware", { + }}, + {0x13b0, "Maxspeed Corp", { + }}, + {0x13b1, "Tamura corporation", { + }}, + {0x13b2, "Techno Chips Co. Ltd", { + }}, + {0x13b3, "Lanart Corporation", { + }}, + {0x13b4, "Wellbean Co Inc", { + }}, + {0x13b5, "ARM", { + }}, + {0x13b6, "Dlog GmbH", { + }}, + {0x13b7, "Logic Devices Inc", { + }}, + {0x13b8, "Nokia Telecommunications oy", { + }}, + {0x13b9, "Elecom Co Ltd", { + }}, + {0x13ba, "Oxford Instruments", { + }}, + {0x13bb, "Sanyo Technosound Co Ltd", { + }}, + {0x13bc, "Bitran Corporation", { + }}, + {0x13bd, "Sharp corporation", { + }}, + {0x13be, "Miroku Jyoho Service Co. Ltd", { + }}, + {0x13bf, "Sharewave Inc", { + }}, + {0x13c0, "Microgate Corporation", { + }}, + {0x13c1, "3ware Inc", { + }}, + {0x13c2, "Technotrend Systemtechnik GmbH", { + }}, + {0x13c3, "Janz Computer AG", { + }}, + {0x13c4, "Phase Metrics", { + }}, + {0x13c5, "Alphi Technology Corp", { + }}, + {0x13c6, "Condor Engineering Inc", { + }}, + {0x13c7, "Blue Chip Technology Ltd", { + }}, + {0x13c8, "Apptech Inc", { + }}, + {0x13c9, "Eaton Corporation", { + }}, + {0x13ca, "Iomega Corporation", { + }}, + {0x13cb, "Yano Electric Co Ltd", { + }}, + {0x13cc, "Metheus Corporation", { + }}, + {0x13cd, "Compatible Systems Corporation", { + }}, + {0x13ce, "Cocom A/S", { + }}, + {0x13cf, "Studio Audio & Video Ltd", { + }}, + {0x13d0, "Techsan Electronics Co Ltd", { + }}, + {0x13d1, "Abocom Systems Inc", { + }}, + {0x13d2, "Shark Multimedia Inc", { + }}, + {0x13d3, "IMC Networks", { + }}, + {0x13d4, "Graphics Microsystems Inc", { + }}, + {0x13d5, "Media 100 Inc", { + }}, + {0x13d6, "K.I. Technology Co Ltd", { + }}, + {0x13d7, "Toshiba Engineering Corporation", { + }}, + {0x13d8, "Phobos corporation", { + }}, + {0x13d9, "Apex PC Solutions Inc", { + }}, + {0x13da, "Intresource Systems pte Ltd", { + }}, + {0x13db, "Janich & Klass Computertechnik GmbH", { + }}, + {0x13dc, "Netboost Corporation", { + }}, + {0x13dd, "Multimedia Bundle Inc", { + }}, + {0x13de, "ABB Robotics Products AB", { + }}, + {0x13df, "E-Tech Inc", { + }}, + {0x13e0, "GVC Corporation", { + }}, + {0x13e1, "Silicom Multimedia Systems Inc", { + }}, + {0x13e2, "Dynamics Research Corporation", { + }}, + {0x13e3, "Nest Inc", { + }}, + {0x13e4, "Calculex Inc", { + }}, + {0x13e5, "Telesoft Design Ltd", { + }}, + {0x13e6, "Argosy research Inc", { + }}, + {0x13e7, "NAC Incorporated", { + }}, + {0x13e8, "Chip Express Corporation", { + }}, + {0x13e9, "Chip Express Corporation", { + }}, + {0x13ea, "Dallas Semiconductor", { + }}, + {0x13eb, "Hauppauge Computer Works Inc", { + }}, + {0x13ec, "Zydacron Inc", { + }}, + {0x13ed, "Raytheion E-Systems", { + }}, + {0x13ee, "Hayes Microcomputer Products Inc", { + }}, + {0x13ef, "Coppercom Inc", { + }}, + {0x13f0, "Sundance technology Inc", { + }}, + {0x13f1, "Oce' - Technologies B.V.", { + }}, + {0x13f2, "Ford Microelectronics Inc", { + }}, + {0x13f3, "Mcdata Corporation", { + }}, + {0x13f4, "Troika Design Inc", { + }}, + {0x13f5, "Kansai Electric Co. Ltd", { + }}, + {0x13f6, "C-Media Electronics Inc", { + }}, + {0x13f7, "Wildfire Communications", { + }}, + {0x13f8, "Ad Lib Multimedia Inc", { + }}, + {0x13f9, "NTT Advanced Technology Corp.", { + }}, + {0x13fa, "Pentland Systems Ltd", { + }}, + {0x13fb, "Aydin Corp", { + }}, + {0x13fc, "Computer Peripherals International", { + }}, + {0x13fd, "Micro Science Inc", { + }}, + {0x13fe, "Advantech Co. Ltd", { + }}, + {0x13ff, "Silicon Spice Inc", { + }}, + {0x1400, "Artx Inc", { + }}, + {0x1401, "CR-Systems A/S", { + }}, + {0x1402, "Meilhaus Electronic GmbH", { + }}, + {0x1403, "Ascor Inc", { + }}, + {0x1404, "Fundamental Software Inc", { + }}, + {0x1405, "Excalibur Systems Inc", { + }}, + {0x1406, "Oce' Printing Systems GmbH", { + }}, + {0x1407, "Lava Computer mfg Inc", { + }}, + {0x1408, "Aloka Co. Ltd", { + }}, + {0x1409, "Timedia Technology Co Ltd", { + }}, + {0x140a, "DSP Research Inc", { + }}, + {0x140b, "Ramix Inc", { + }}, + {0x140c, "Elmic Systems Inc", { + }}, + {0x140d, "Matsushita Electric Works Ltd", { + }}, + {0x140e, "Goepel Electronic GmbH", { + }}, + {0x140f, "Salient Systems Corp", { + }}, + {0x1410, "Midas lab Inc", { + }}, + {0x1411, "Ikos Systems Inc", { + }}, + {0x1412, "IC Ensemble Inc", { + }}, + {0x1413, "Addonics", { + }}, + {0x1414, "Microsoft Corporation", { + }}, + {0x1415, "Oxford Semiconductor Ltd", { + }}, + {0x1416, "Multiwave Innovation pte Ltd", { + }}, + {0x1417, "Convergenet Technologies Inc", { + }}, + {0x1418, "Kyushu electronics systems Inc", { + }}, + {0x1419, "Excel Switching Corp", { + }}, + {0x141a, "Apache Micro Peripherals Inc", { + }}, + {0x141b, "Zoom Telephonics Inc", { + }}, + {0x141d, "Digitan Systems Inc", { + }}, + {0x141e, "Fanuc Ltd", { + }}, + {0x141f, "Visiontech Ltd", { + }}, + {0x1420, "Psion Dacom plc", { + }}, + {0x1421, "Ads Technologies Inc", { + }}, + {0x1422, "Ygrec Systems Co Ltd", { + }}, + {0x1423, "Custom Technology Corp.", { + }}, + {0x1424, "Videoserver Connections", { + }}, + {0x1425, "ASIC Designers Inc", { + }}, + {0x1426, "Storage Technology Corp.", { + }}, + {0x1427, "Better On-Line Solutions", { + }}, + {0x1428, "Edec Co Ltd", { + }}, + {0x1429, "Unex Technology Corp.", { + }}, + {0x142a, "Kingmax Technology Inc", { + }}, + {0x142b, "Radiolan", { + }}, + {0x142c, "Minton Optic Industry Co Ltd", { + }}, + {0x142d, "Pix stream Inc", { + }}, + {0x142e, "Vitec Multimedia", { + }}, + {0x142f, "Radicom Research Inc", { + }}, + {0x1430, "ITT Aerospace/Communications Division", { + }}, + {0x1431, "Gilat Satellite Networks", { + }}, + {0x1432, "Edimax Computer Co.", { + }}, + {0x1433, "Eltec Elektronik GmbH", { + }}, + {0x1435, "Real Time Devices US Inc.", { + }}, + {0x1436, "CIS Technology Inc", { + }}, + {0x1668, "Action Tec Electronics Inc", { + }}, + {0x1b13, "Jaton Corp", { + }}, + {0x1c1c, "Symphony", { + {0x0001,"82C101"}, + }}, + {0x21c3, "21st Century Computer Corp.", { + }}, + {0x270b, "Xantel Corporation", { + }}, + {0x270f, "Chaintech Computer Co. Ltd", { + }}, + {0x3388, "Hint Corp", { + }}, + {0x3d3d, "3DLabs", { + {0x0001,"GLINT 300SX"}, + {0x0002,"GLINT 500TX"}, + {0x0003,"GLINT Delta"}, + {0x0004,"Permedia"}, + {0x0006,"GLINT MX"}, + }}, + {0x4005, "Avance Logic Inc.", { + {0x2064,"ALG2064i"}, + {0x2301,"ALG2301"}, + {0x2302,"ALG2302"}, + }}, + {0x4444, "Internext Compression Inc", { + }}, + {0x4680, "Umax Computer Corp", { + }}, + {0x4843, "Hercules Computer Technology Inc", { + }}, + {0x4978, "Axil Computer Inc", { + }}, + {0x4a14, "NetVin", { + {0x5000,"PCI NV5000SC"}, + }}, + {0x4ddc, "ILC Data Device Corp", { + }}, + {0x5053, "Voyetra Technologies", { + }}, + {0x5143, "Qualcomm Inc", { + }}, + {0x5333, "S3 Inc.", { + {0x0551,"Plato/PX (system)"}, + {0x5631,"86C325 [ViRGE]"}, + {0x8810,"86C764_0 [Trio 32 vers 0]"}, + {0x8811,"86C764_1 [Trio 32/64 vers 1]"}, + {0x8812,"Aurora64V+"}, + {0x8813,"86C764_3 [Trio 32/64 vers 3]"}, + {0x8814,"Trio64UV+"}, + {0x8815,"Aurora128"}, + {0x883d,"ViRGE/VX"}, + {0x8880,"Vision 868 vers 0"}, + {0x8881,"Vision 868 vers 1"}, + {0x8882,"Vision 868 vers 2"}, + {0x8883,"Vision 868 vers 3"}, + {0x88b0,"Vision 928 vers 0"}, + {0x88b1,"Vision 928 vers 1"}, + {0x88b2,"Vision 928 vers 2"}, + {0x88b3,"Vision 928 vers 3"}, + {0x88c0,"Vision 864 vers 0"}, + {0x88c1,"Vision 864 vers 1"}, + {0x88c2,"86C864"}, + {0x88c3,"86C864"}, + {0x88d0,"Vision 964 vers 0"}, + {0x88d1,"Vision 964 vers 1"}, + {0x88d2,"86C964"}, + {0x88d3,"86C964"}, + {0x88f0,"Vision 968"}, + {0x88f1,"86C968"}, + {0x88f2,"86C968"}, + {0x88f3,"86C968"}, + {0x8901,"Trio64V2/DX or /GX"}, + {0x8902,"Plato/PX (graphics)"}, + {0x8a01,"ViRGE/DX or /GX"}, + {0x8a10,"ViRGE/GX2"}, + {0x8c01,"ViRGE/MX"}, + {0x8c02,"ViRGE/MX+"}, + {0x8c03,"ViRGE/MX+MV"}, + {0xca00,"SonicVibes"}, + }}, + {0x5555, "Genroco, Inc", { + {0x0003,"TURBOstor HFP-832 [HiPPI NIC]"}, + }}, + {0x6374, "c't Magazin f€r Computertechnik", { + {0x6773,"GPPCI"}, + }}, + {0x6666, "Decision Computer International Co.", { + }}, + {0x8008, "Quancm Electronic GmbH", { + {0x0010,"WDOG1 [PCI-Watchdog 1]"}, + {0x0011,"PWDOG2 [Watchdog2/PCI]"}, + }}, + {0x8086, "Intel Corporation", { + {0x0482,"82375EB"}, + {0x0483,"82424ZX [Saturn]"}, + {0x0484,"82378IB [SIO ISA Bridge]"}, + {0x0486,"82430ZX [Aries]"}, + {0x04a3,"82434LX [Mercury/Neptune]"}, + {0x0960,"80960RP [i960 RP Microprocessor/Bridge]"}, + {0x1221,"82092AA_0"}, + {0x1222,"82092AA_1"}, + {0x1223,"SAA7116"}, + {0x1226,"82596"}, + {0x1227,"82865"}, + {0x1228,"82556"}, + {0x1229,"82557"}, + {0x122d,"430FX - 82437FX TSC [Triton I]"}, + {0x122e,"82371FB PIIX ISA [Triton I]"}, + {0x1230,"82371FB PIIX IDE [Triton I]"}, + {0x1234,"430MX - 82371MX MPIIX [430MX PCIset - 82371MX Mobile PCI I/O IDE Xcelerator (MPIIX)]"}, + {0x1235,"430MX - 82437MX MTSC [430MX PCIset - 82437MX Mobile System Controller (MTSC) and 82438MX Mobile Data Path (MTDP)]"}, + {0x1237,"440FX - 82441FX PMC [Natoma]"}, + {0x124b,"82380FB"}, + {0x1250,"430HX - 82439HX TXC [Triton II]"}, + {0x1960,"80960RP [i960RP Microprocessor]"}, + {0x7000,"82371SB PIIX3 ISA [Natoma/Triton II]"}, + {0x7010,"82371SB PIIX3 IDE [Natoma/Triton II]"}, + {0x7020,"82371SB PIIX3 USB [Natoma/Triton II]"}, + {0x7030,"430VX - 82437VX TVX [Triton VX]"}, + {0x7100,"430TX - 82439TX MTXC"}, + {0x7110,"82371AB PIIX4 ISA"}, + {0x7111,"82371AB PIIX4 IDE"}, + {0x7112,"82371AB PIIX4 USB"}, + {0x7113,"82371AB PIIX4 ACPI"}, + {0x7180,"440LX - 82443LX PAC Host"}, + {0x7181,"440LX - 82443LX PAC AGP"}, + {0x7190,"440BX - 82443BX Host"}, + {0x7191,"440BX - 82443BX AGP"}, + {0x7192,"440BX - 82443BX (AGP disabled)"}, + {0x7800,"i740"}, + {0x84c4,"82450KX [Orion]"}, + {0x84c5,"82450GX [Orion]"}, + }}, + {0x8800, "Trigem Computer Inc.", { + }}, + {0x8888, "Silicon Magic", { + }}, + {0x8e0e, "Computone Corporation", { + }}, + {0x8e2e, "KTI", { + {0x3000,"ET32P2"}, + }}, + {0x9004, "Adaptec", { + {0x1078,"AIC-7810"}, + {0x5078,"AIC-7850"}, + {0x5178,"7851"}, + {0x5278,"7852"}, + {0x5575,"2930"}, + {0x5578,"AIC-7855"}, + {0x5800,"AIC-5800"}, + {0x6075,"AIC-1480"}, + {0x6078,"AIC-7860"}, + {0x6178,"AIC-7861"}, + {0x6278,"AIC-7860"}, + {0x6378,"AIC-7860"}, + {0x7078,"AIC-7870"}, + {0x7178,"AIC-7871"}, + {0x7278,"AIC-7872"}, + {0x7378,"AIC-7873"}, + {0x7478,"AIC-7874 [AHA-2944]"}, + {0x7578,"7875"}, + {0x7678,"7876"}, + {0x7895,"AIC-7895"}, + {0x8078,"AIC-7880U"}, + {0x8178,"AIC-7881U"}, + {0x8278,"AIC-7882U"}, + {0x8378,"AIC-7883U"}, + {0x8478,"AIC-7884U"}, + {0x8578,"7885"}, + {0x8678,"7886"}, + {0x8b78,"ABA-1030"}, + }}, + {0x907f, "Atronics", { + {0x2015,"IDE-2015PL"}, + }}, + {0x9412, "Holtek", { + {0x6565,"6565"}, + }}, + {0xa200, "NEC Corporation", { + }}, + {0xa259, "Hewlett Packard", { + }}, + {0xa25b, "Hewlett Packard GmbH PL24-MKT", { + }}, + {0xa304, "Sony", { + }}, + {0xa727, "3Com Corporation", { + }}, + {0xaa42, "Scitex Digital Video", { + }}, + {0xb1b3, "Shiva Europe Limited", { + }}, + {0xc001, "TSI Telsys", { + }}, + {0xc0a9, "Micron/Crucial Technology", { + }}, + {0xc0de, "Motorola", { + }}, + {0xc0fe, "Motion Engineering, Inc.", { + }}, + {0xcafe, "Chrysalis-ITS", { + }}, + {0xd4d4, "Dy4 Systems Inc", { + }}, + {0xe159, "Tiger Jet Network Inc.", { + {0x0001,"300"}, + }}, + {0xecc0, "Echo Corporation", { + }}, + {0xedd8, "ARK Logic Inc", { + {0xa091,"1000PV [Stingray]"}, + {0xa099,"2000PV [Stingray]"}, + {0xa0a1,"2000MT"}, + {0xa0a9,"2000MI"}, + }}, +}; + diff --git a/os/pc/pcmciamodem.c b/os/pc/pcmciamodem.c new file mode 100644 index 00000000..800f406c --- /dev/null +++ b/os/pc/pcmciamodem.c @@ -0,0 +1,75 @@ +#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" + +/* + * PCMCIA modem. + * By default, this will set it up with the port and irq of + * COM2 unless a serialx=type=com line is found in plan9.ini. + * The assumption is that a laptop with a pcmcia will have only + * one com port. + */ + +enum { + Maxcard= 8, +}; + +static char* modems[] = { + "IBM 33.6 Data/Fax/Voice Modem", + "CM-56G", /* Xircom CreditCard Modem 56 - GlobalACCESS */ + "KeepInTouch", + "CEM56", + "MONTANA V.34 FAX/MODEM", /* Motorola */ + "REM10", + "GSM/GPRS", + "AirCard 555", + "Gold Card Global", /* Psion V90 Gold card */ + "Merlin UMTS Modem", /* Novatel card */ + 0, +}; + +void +pcmciamodemlink(void) +{ + ISAConf isa; + int i, j, slot, com2used, usingcom2; + + i = 0; + com2used = 0; + for(j = 0; modems[j]; j++){ + memset(&isa, 0, sizeof(isa)); + + /* look for a configuration line */ + for(; i < Maxcard; i++){ + if(isaconfig("serial", i, &isa)) + if(cistrcmp(isa.type, "com") == 0) + break; + memset(&isa, 0, sizeof(isa)); + } + + usingcom2 = 0; + if (isa.irq == 0 && isa.port == 0) { + if (com2used == 0) { + /* default is COM2 */ + isa.irq = 3; + isa.port = 0x2F8; + usingcom2 = 1; + } else + break; + } + slot = pcmspecial(modems[j], &isa); + if(slot >= 0){ + if(usingcom2) + com2used = 1; + if(ioalloc(isa.port, 8, 0, modems[j]) < 0) + print("%s port %lux already in use\n", modems[j], isa.port); + print("%s in pcmcia slot %d port 0x%lux irq %d\n", + modems[j], slot, isa.port, isa.irq); + } + } +} diff --git a/os/pc/piix4smbus.c b/os/pc/piix4smbus.c new file mode 100644 index 00000000..ced41001 --- /dev/null +++ b/os/pc/piix4smbus.c @@ -0,0 +1,213 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +// +// SMBus support for the PIIX4 +// +enum +{ + IntelVendID= 0x8086, + Piix4PMID= 0x7113, /* PIIX4 power management function */ + + // SMBus configuration registers (function 3) + SMBbase= 0x90, // 4 byte base address (bit 0 == 1, bit 3:1 == 0) + SMBconfig= 0xd2, + SMBintrselect= (7<<1), + SMIenable= (0<<1), // interrupts sent to SMI# + IRQ9enable= (4<<1), // intettupts sent to IRQ9 + SMBenable= (1<<0), // 1 enables + + // SMBus IO space registers + Hoststatus= 0x0, // (writing 1 bits reset the interrupt bits) + Failed= (1<<4), // transaction terminated by KILL + Bus_error= (1<<3), // transactio collision + Dev_error= (1<<2), // device error interrupt + Host_complete= (1<<1), // host command completion interrupt + Host_busy= (1<<0), // + Slavestatus= 0x1, // (writing 1 bits reset) + Alert_sts= (1<<5), // someone asserted SMBALERT# + Shdw2_sts= (1<<4), // slave accessed shadow 2 port + Shdw1_sts= (1<<3), // slave accessed shadow 1 port + Slv_sts= (1<<2), // slave accessed shadow 1 port + Slv_bsy= (1<<0), + Hostcontrol= 0x2, + Start= (1<<6), // start execution + Cmd_prot= (7<<2), // command protocol mask + Quick= (0<<2), // address only + Byte= (1<<2), // address + cmd + ByteData= (2<<2), // address + cmd + data + WordData= (3<<2), // address + cmd + data + data + Kill= (1<<1), // abort in progress command + Ienable= (1<<0), // enable completion interrupts + Hostcommand= 0x3, + Hostaddress= 0x4, + AddressMask= (0x7f<<1), // target address + Read= (1<<0), // 1 == read, 0 == write + Hostdata0= 0x5, + Hostdata1= 0x6, + Blockdata= 0x7, + Slavecontrol= 0x8, + Alert_en= (1<<3), // enable inter on SMBALERT# + Shdw2_en= (1<<2), // enable inter on external shadow 2 access + Shdw1_en= (1<<1), // enable inter on external shadow 1 access + Slv_en= (1<<0), // enable inter on access of host ctlr slave port + Shadowcommand= 0x9, + Slaveevent= 0xa, + Slavedata= 0xc, +}; + +static struct +{ + int rw; + int cmd; + int len; + int proto; +} proto[] = +{ + [SMBquick] { 0, 0, 0, Quick }, + [SMBsend] { 0, 1, 0, Byte }, + [SMBbytewrite] { 0, 1, 1, ByteData }, + [SMBwordwrite] { 0, 1, 2, WordData }, + [SMBrecv] { Read, 0, 1, Byte }, + [SMBbyteread] { Read, 1, 1, ByteData }, + [SMBwordread] { Read, 1, 2, WordData }, +}; + +static void +transact(SMBus *s, int type, int addr, int cmd, uchar *data) +{ + int tries, status; + char err[256]; + + if(type < 0 || type > nelem(proto)) + panic("piix4smbus: illegal transaction type %d", type); + + if(waserror()){ + qunlock(s); + nexterror(); + } + qlock(s); + + // wait a while for the host interface to be available + for(tries = 0; tries < 1000000; tries++){ + if((inb(s->base+Hoststatus) & Host_busy) == 0) + break; + sched(); + } + if(tries >= 1000000){ + // try aborting current transaction + outb(s->base+Hostcontrol, Kill); + for(tries = 0; tries < 1000000; tries++){ + if((inb(s->base+Hoststatus) & Host_busy) == 0) + break; + sched(); + } + if(tries >= 1000000){ + snprint(err, sizeof(err), "SMBus jammed: %2.2ux", inb(s->base+Hoststatus)); + error(err); + } + } + + // set up for transaction + outb(s->base+Hostaddress, (addr<<1)|proto[type].rw); + if(proto[type].cmd) + outb(s->base+Hostcommand, cmd); + if(proto[type].rw != Read){ + switch(proto[type].len){ + case 2: + outb(s->base+Hostdata1, data[1]); + // fall through + case 1: + outb(s->base+Hostdata0, data[0]); + break; + } + } + + + // reset the completion/error bits and start transaction + outb(s->base+Hoststatus, Failed|Bus_error|Dev_error|Host_complete); + outb(s->base+Hostcontrol, Start|proto[type].proto); + + // wait for completion + status = 0; + for(tries = 0; tries < 1000000; tries++){ + status = inb(s->base+Hoststatus); + if(status & (Failed|Bus_error|Dev_error|Host_complete)) + break; + sched(); + } + if((status & Host_complete) == 0){ + snprint(err, sizeof(err), "SMBus request failed: %2.2ux", status); + error(err); + } + + // get results + if(proto[type].rw == Read){ + switch(proto[type].len){ + case 2: + data[1] = inb(s->base+Hostdata1); + // fall through + case 1: + data[0] = inb(s->base+Hostdata0); + break; + } + } + qunlock(s); + poperror(); +} + +static SMBus smbusproto = +{ + .transact = transact, +}; + +// +// return 0 if this is a piix4 with an smbus interface +// +SMBus* +piix4smbus(void) +{ + Pcidev *p; + static SMBus *s; + + if(s != nil) + return s; + + p = pcimatch(nil, IntelVendID, Piix4PMID); + if(p == nil) + return nil; + + s = smalloc(sizeof(*s)); + memmove(s, &smbusproto, sizeof(*s)); + s->arg = p; + + // disable the smbus + pcicfgw8(p, SMBconfig, IRQ9enable|0); + + // see if bios gave us a viable port space + s->base = pcicfgr32(p, SMBbase) & ~1; +print("SMB base from bios is 0x%lux\n", s->base); + if(ioalloc(s->base, 0xd, 0, "piix4smbus") < 0){ + s->base = ioalloc(-1, 0xd, 2, "piix4smbus"); + if(s->base < 0){ + free(s); + print("piix4smbus: can't allocate io port\n"); + return nil; + } +print("SMB base ialloc is 0x%lux\n", s->base); + pcicfgw32(p, SMBbase, s->base|1); + } + + // disable SMBus interrupts, abort any transaction in progress + outb(s->base+Hostcontrol, Kill); + outb(s->base+Slavecontrol, 0); + + // enable the smbus + pcicfgw8(p, SMBconfig, IRQ9enable|SMBenable); + + return s; +} diff --git a/os/pc/pix b/os/pc/pix new file mode 100644 index 00000000..d856cfdb --- /dev/null +++ b/os/pc/pix @@ -0,0 +1,153 @@ +dev + root + cons + arch + env + mnt + pipe + prog + rtc + srv + dup + ssl + cap + +# draw screen vga vgax cga +# pointer +# vga + + ip bootp ip ipv6 ipaux iproute arp netlog ptclbsum386 iprouter plan9 nullmedium pktmedium + ether netif netaux + + sd + ds + uart + floppy dma + tinyfs +# mouse +# dbg x86break + +ip + il + tcp + udp + rudp + gre + ipifc + icmp + icmp6 + ipmux +lib + interp + keyring + sec + mp +# draw +# memlayer +# memdraw +# tk + math + kern + +link +# ps2mouse + ether82557 pci + ethermedium + loopbackmedium + netdevmedium + +misc +# vgaclgd542x +# vgas3 +cur vgasavage +# cga + sdata pci sdscsi + + uarti8250 + +mod + sys +# draw +# tk + keyring + math + +init + soeinit # it will do + +code + int kernel_pool_pcnt = 10; + int main_pool_pcnt = 40; + int heap_pool_pcnt = 20; + int image_pool_pcnt = 0; + int cflag=0; + int swcursor=0; + int consoleprint=0; + void screeninit(void){} + +port + alarm + alloc + allocb + chan + dev + dial + dis + discall + exception + exportfs + inferno + latin1 + nocache + nodynld + parse + pgrp + print + proc + qio + qlock + random + sysfile + taslock + xalloc + +root + /chan + /dev + /dis + /env + /fd / + /n + /n/remote + /net + /nvfs + /prog + /dis/lib + /dis/svc + /dis/wm + /osinit.dis + /dis/sh.dis + /dis/ls.dis + /dis/cat.dis + /dis/bind.dis + /dis/mount.dis + /dis/pwd.dis + /dis/echo.dis + /dis/cd.dis + /dis/lib/bufio.dis + /dis/lib/string.dis + /dis/lib/readdir.dis + /dis/lib/workdir.dis + /dis/lib/daytime.dis + /dis/lib/auth.dis + /dis/lib/ssl.dis + /dis/lib/filepat.dis + +# kfs + /dis/disk/kfs.dis + /dis/lib/arg.dis + /dis/lib/daytime.dis + /dis/lib/string.dis + /dis/lib/styx.dis + +# auth + /nvfs/default /usr/inferno/keyring/default diff --git a/os/pc/ps2mouse.c b/os/pc/ps2mouse.c new file mode 100644 index 00000000..49654bad --- /dev/null +++ b/os/pc/ps2mouse.c @@ -0,0 +1,84 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "io.h" + +/* + * mouse types + */ +enum +{ + Mouseother= 0, + Mouseserial= 1, + MousePS2= 2, +}; + +static int mousetype; + +/* + * ps/2 mouse message is three bytes + * + * byte 0 - 0 0 SDY SDX 1 M R L + * byte 1 - DX + * byte 2 - DY + * + * shift & left button is the same as middle button + */ +static void +ps2mouseputc(int c, int shift) +{ + static short msg[3]; + static int nb; + static uchar b[] = {0, 1, 4, 5, 2, 3, 6, 7, 0, 1, 2, 5, 2, 3, 6, 7 }; + int buttons, dx, dy; + + /* + * check byte 0 for consistency + */ + if(nb==0 && (c&0xc8)!=0x08) + return; + + msg[nb] = c; + if(++nb == 3){ + nb = 0; + if(msg[0] & 0x10) + msg[1] |= 0xFF00; + if(msg[0] & 0x20) + msg[2] |= 0xFF00; + + buttons = b[(msg[0]&7) | (shift ? 8 : 0)]; + dx = msg[1]; + dy = -msg[2]; + mousetrack(buttons, dx, dy, 1); + } + return; +} + +/* + * set up a ps2 mouse + */ +static void +ps2mouse(void) +{ + if(mousetype == MousePS2) + return; + + i8042auxenable(ps2mouseputc); + /* make mouse streaming, enabled */ + i8042auxcmd(0xEA); + i8042auxcmd(0xF4); + + mousetype = MousePS2; +} + +void +ps2mouselink(void) +{ + /* + * hack + */ + ps2mouse(); +} diff --git a/os/pc/ptclbsum386.s b/os/pc/ptclbsum386.s new file mode 100644 index 00000000..ba0a6a4d --- /dev/null +++ b/os/pc/ptclbsum386.s @@ -0,0 +1,126 @@ +TEXT ptclbsum(SB), $0 + MOVL addr+0(FP), SI + MOVL len+4(FP), CX + + XORL AX, AX /* sum */ + + TESTL $1, SI /* byte aligned? */ + MOVL SI, DI + JEQ _2align + + DECL CX + JLT _return + + MOVB 0x00(SI), AH + INCL SI + +_2align: + TESTL $2, SI /* word aligned? */ + JEQ _32loop + + CMPL CX, $2 /* less than 2 bytes? */ + JLT _1dreg + SUBL $2, CX + + XORL BX, BX + MOVW 0x00(SI), BX + ADDL BX, AX + ADCL $0, AX + LEAL 2(SI), SI + +_32loop: + CMPL CX, $0x20 + JLT _8loop + + MOVL CX, BP + SHRL $5, BP + ANDL $0x1F, CX + +_32loopx: + MOVL 0x00(SI), BX + MOVL 0x1C(SI), DX + ADCL BX, AX + MOVL 0x04(SI), BX + ADCL DX, AX + MOVL 0x10(SI), DX + ADCL BX, AX + MOVL 0x08(SI), BX + ADCL DX, AX + MOVL 0x14(SI), DX + ADCL BX, AX + MOVL 0x0C(SI), BX + ADCL DX, AX + MOVL 0x18(SI), DX + ADCL BX, AX + LEAL 0x20(SI), SI + ADCL DX, AX + + DECL BP + JNE _32loopx + + ADCL $0, AX + +_8loop: + CMPL CX, $0x08 + JLT _2loop + + MOVL CX, BP + SHRL $3, BP + ANDL $0x07, CX + +_8loopx: + MOVL 0x00(SI), BX + ADCL BX, AX + MOVL 0x04(SI), DX + ADCL DX, AX + + LEAL 0x08(SI), SI + DECL BP + JNE _8loopx + + ADCL $0, AX + +_2loop: + CMPL CX, $0x02 + JLT _1dreg + + MOVL CX, BP + SHRL $1, BP + ANDL $0x01, CX + +_2loopx: + MOVWLZX 0x00(SI), BX + ADCL BX, AX + + LEAL 0x02(SI), SI + DECL BP + JNE _2loopx + + ADCL $0, AX + +_1dreg: + TESTL $1, CX /* 1 byte left? */ + JEQ _fold + + XORL BX, BX + MOVB 0x00(SI), BX + ADDL BX, AX + ADCL $0, AX + +_fold: + MOVL AX, BX + SHRL $16, BX + JEQ _swab + + ANDL $0xFFFF, AX + ADDL BX, AX + JMP _fold + +_swab: + TESTL $1, addr+0(FP) + /*TESTL $1, DI*/ + JNE _return + XCHGB AH, AL + +_return: + RET diff --git a/os/pc/screen.c b/os/pc/screen.c new file mode 100644 index 00000000..e879b050 --- /dev/null +++ b/os/pc/screen.c @@ -0,0 +1,410 @@ +#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 <draw.h> +#include <memdraw.h> +#include <cursor.h> +#include "screen.h" + +#define RGB2K(r,g,b) ((156763*(r)+307758*(g)+59769*(b))>>19) + +Point ZP = {0, 0}; + +Rectangle physgscreenr; +Cursorinfo cursor; /* TO DO */ + +Memdata gscreendata; +Memimage *gscreen; + +VGAscr vgascreen[1]; + +Cursor arrow = { + { -1, -1 }, + { 0xFF, 0xFF, 0x80, 0x01, 0x80, 0x02, 0x80, 0x0C, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x08, 0x80, 0x04, + 0x80, 0x02, 0x80, 0x01, 0x80, 0x02, 0x8C, 0x04, + 0x92, 0x08, 0x91, 0x10, 0xA0, 0xA0, 0xC0, 0x40, + }, + { 0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFC, 0x7F, 0xF0, + 0x7F, 0xE0, 0x7F, 0xE0, 0x7F, 0xF0, 0x7F, 0xF8, + 0x7F, 0xFC, 0x7F, 0xFE, 0x7F, 0xFC, 0x73, 0xF8, + 0x61, 0xF0, 0x60, 0xE0, 0x40, 0x40, 0x00, 0x00, + }, +}; + +int +screensize(int x, int y, int z, ulong chan) +{ + VGAscr *scr; + + memimageinit(); + scr = &vgascreen[0]; + + /* + * BUG: need to check if any xalloc'ed memory needs to + * be given back if aperture is set. + */ + if(scr->aperture == 0){ + int width = (x*z)/BI2WD; + + gscreendata.bdata = xalloc(width*BY2WD*y); + if(gscreendata.bdata == 0) + error("screensize: vga soft memory"); +/* memset(gscreendata.bdata, 0x72, width*BY2WD*y); /* not really black */ + scr->useflush = 1; + scr->aperture = VGAMEM(); + scr->apsize = 1<<16; + } + else + gscreendata.bdata = KADDR(scr->aperture); + + if(gscreen) + freememimage(gscreen); + + gscreen = allocmemimaged(Rect(0,0,x,y), chan, &gscreendata); + vgaimageinit(chan); + if(gscreen == nil) + return -1; + + if(scr->dev && scr->dev->flush) + scr->useflush = 1; + + scr->palettedepth = 6; /* default */ + scr->gscreendata = &gscreendata; + scr->memdefont = getmemdefont(); + scr->gscreen = gscreen; + + physgscreenr = gscreen->r; + + drawcmap(); + return 0; +} + +int +screenaperture(int size, int align) +{ + VGAscr *scr; + ulong aperture; + + scr = &vgascreen[0]; + + if(size == 0){ + if(scr->aperture && scr->isupamem) + upafree(scr->aperture, scr->apsize); + scr->aperture = 0; + scr->isupamem = 0; + return 0; + } + if(scr->dev && scr->dev->linear){ + aperture = scr->dev->linear(scr, &size, &align); + if(aperture == 0) + return 1; + }else{ + aperture = upamalloc(0, size, align); + if(aperture == 0) + return 1; + + if(scr->aperture && scr->isupamem) + upafree(scr->aperture, scr->apsize); + scr->isupamem = 1; + } + + scr->aperture = aperture; + scr->apsize = size; + + return 0; +} + +uchar* +attachscreen(Rectangle* r, ulong* chan, int* d, int* width, int *softscreen) +{ + VGAscr *scr; + + scr = &vgascreen[0]; + if(scr->gscreen == nil || scr->gscreendata == nil) + return nil; + + *r = scr->gscreen->clipr; + *chan = scr->gscreen->chan; + *d = scr->gscreen->depth; + *width = scr->gscreen->width; + *softscreen = scr->useflush; + + return scr->gscreendata->bdata; +} + +/* + * It would be fair to say that this doesn't work for >8-bit screens. + */ +void +flushmemscreen(Rectangle r) +{ + VGAscr *scr; + uchar *sp, *disp, *sdisp, *edisp; + int y, len, incs, off, page; + + scr = &vgascreen[0]; + if(scr->dev && scr->dev->flush){ + scr->dev->flush(scr, r); + return; + } + if(scr->gscreen == nil || scr->useflush == 0) + return; + if(scr->dev == nil || scr->dev->page == nil) + return; + + if(rectclip(&r, scr->gscreen->r) == 0) + return; + + incs = scr->gscreen->width * BY2WD; + + switch(scr->gscreen->depth){ + default: + len = 0; + panic("flushmemscreen: depth\n"); + break; + case 8: + len = Dx(r); + break; + } + if(len < 1) + return; + + off = r.min.y*scr->gscreen->width*BY2WD+(r.min.x*scr->gscreen->depth)/8; + page = off/scr->apsize; + off %= scr->apsize; + disp = KADDR(scr->aperture); + sdisp = disp+off; + edisp = disp+scr->apsize; + + off = r.min.y*scr->gscreen->width*BY2WD+(r.min.x*scr->gscreen->depth)/8; + + sp = scr->gscreendata->bdata + off; + + scr->dev->page(scr, page); + for(y = r.min.y; y < r.max.y; y++) { + if(sdisp + incs < edisp) { + memmove(sdisp, sp, len); + sp += incs; + sdisp += incs; + } + else { + off = edisp - sdisp; + page++; + if(off <= len){ + if(off > 0) + memmove(sdisp, sp, off); + scr->dev->page(scr, page); + if(len - off > 0) + memmove(disp, sp+off, len - off); + } + else { + memmove(sdisp, sp, len); + scr->dev->page(scr, page); + } + sp += incs; + sdisp += incs - scr->apsize; + } + } +} + +void +getcolor(ulong p, ulong* pr, ulong* pg, ulong* pb) +{ + VGAscr *scr; + ulong x; + + scr = &vgascreen[0]; + if(scr->gscreen == nil) + return; + + switch(scr->gscreen->depth){ + default: + x = 0x0F; + break; + case 8: + x = 0xFF; + break; + } + p &= x; + + lock(&cursor); + *pr = scr->colormap[p][0]; + *pg = scr->colormap[p][1]; + *pb = scr->colormap[p][2]; + unlock(&cursor); +} + +int +setpalette(ulong p, ulong r, ulong g, ulong b) +{ + VGAscr *scr; + int d; + + scr = &vgascreen[0]; + d = scr->palettedepth; + + lock(&cursor); + scr->colormap[p][0] = r; + scr->colormap[p][1] = g; + scr->colormap[p][2] = b; + vgao(PaddrW, p); + vgao(Pdata, r>>(32-d)); + vgao(Pdata, g>>(32-d)); + vgao(Pdata, b>>(32-d)); + unlock(&cursor); + + return ~0; +} + +/* + * On some video cards (e.g. Mach64), the palette is used as the + * DAC registers for >8-bit modes. We don't want to set them when the user + * is trying to set a colormap and the card is in one of these modes. + */ +int +setcolor(ulong p, ulong r, ulong g, ulong b) +{ + VGAscr *scr; + int x; + + scr = &vgascreen[0]; + if(scr->gscreen == nil) + return 0; + + switch(scr->gscreen->depth){ + case 1: + case 2: + case 4: + x = 0x0F; + break; + case 8: + x = 0xFF; + break; + default: + return 0; + } + p &= x; + + return setpalette(p, r, g, b); +} + +int +cursoron(int dolock) +{ + VGAscr *scr; + int v; + + scr = &vgascreen[0]; + if(scr->cur == nil || scr->cur->move == nil) + return 0; + + if(dolock) + lock(&cursor); + v = scr->cur->move(scr, mousexy()); + if(dolock) + unlock(&cursor); + + return v; +} + +void +cursoroff(int) +{ +} + +void +setcursor(Cursor* curs) +{ + VGAscr *scr; + + scr = &vgascreen[0]; + if(scr->cur == nil || scr->cur->load == nil) + return; + + scr->cur->load(scr, curs); +} + +int hwaccel = 1; +int hwblank = 0; /* turned on by drivers that are known good */ +int panning = 0; + +int +hwdraw(Memdrawparam *par) +{ + VGAscr *scr; + Memimage *dst, *src; + int m; + + if(hwaccel == 0) + return 0; + + dst = par->dst; + scr = &vgascreen[0]; + if(dst == nil || dst->data == nil) + return 0; + + if(dst->data->bdata != gscreendata.bdata) + return 0; + + if(scr->fill==nil && scr->scroll==nil) + return 0; + + /* + * If we have an opaque mask and source is one opaque + * pixel we can convert to the destination format and just + * replicate with memset. + */ + m = Simplesrc|Simplemask|Fullmask; + if(scr->fill + && (par->state&m)==m + && ((par->srgba&0xFF) == 0xFF) + && (par->op&S) == S) + return scr->fill(scr, par->r, par->sdval); + + /* + * If no source alpha, an opaque mask, we can just copy the + * source onto the destination. If the channels are the same and + * the source is not replicated, memmove suffices. + */ + m = Simplemask|Fullmask; + src = par->src; + if(scr->scroll + && src->data->bdata==dst->data->bdata + && !(src->flags&Falpha) + && (par->state&m)==m + && (par->op&S) == S) + return scr->scroll(scr, par->r, par->sr); + + return 0; +} + +void +blankscreen(int blank) +{ + VGAscr *scr; + + scr = &vgascreen[0]; + if(hwblank){ + if(scr->blank) + scr->blank(scr, blank); + else + vgablank(scr, blank); + } +} + +void +cursorenable(void) +{ +} + +void +cursordisable(void) +{ +} diff --git a/os/pc/screen.h b/os/pc/screen.h new file mode 100644 index 00000000..bcc1f230 --- /dev/null +++ b/os/pc/screen.h @@ -0,0 +1,173 @@ +typedef struct Cursor Cursor; + +enum { + CURSWID = 16, + CURSHGT = 16, +}; + +struct Cursor +{ + Point offset; + uchar clr[CURSWID/BI2BY*CURSHGT]; + uchar set[CURSWID/BI2BY*CURSHGT]; +}; +typedef struct Cursorinfo Cursorinfo; +struct Cursorinfo { + Cursor; + Lock; +}; + +/* devmouse.c */ +extern void mousetrack(int, int, int, int); +extern Point mousexy(void); + +extern void mouseaccelerate(int); +extern int m3mouseputc(Queue*, int); +extern int m5mouseputc(Queue*, int); +extern int mouseputc(Queue*, int); + +extern Cursorinfo cursor; +extern Cursor arrow; + +/* + * Generic VGA registers. + */ +enum { + MiscW = 0x03C2, /* Miscellaneous Output (W) */ + MiscR = 0x03CC, /* Miscellaneous Output (R) */ + Status0 = 0x03C2, /* Input status 0 (R) */ + Status1 = 0x03DA, /* Input Status 1 (R) */ + FeatureR = 0x03CA, /* Feature Control (R) */ + FeatureW = 0x03DA, /* Feature Control (W) */ + + Seqx = 0x03C4, /* Sequencer Index, Data at Seqx+1 */ + Crtx = 0x03D4, /* CRT Controller Index, Data at Crtx+1 */ + Grx = 0x03CE, /* Graphics Controller Index, Data at Grx+1 */ + Attrx = 0x03C0, /* Attribute Controller Index and Data */ + + PaddrW = 0x03C8, /* Palette Address Register, write */ + Pdata = 0x03C9, /* Palette Data Register */ + Pixmask = 0x03C6, /* Pixel Mask Register */ + PaddrR = 0x03C7, /* Palette Address Register, read */ + Pstatus = 0x03C7, /* DAC Status (RO) */ + + Pcolours = 256, /* Palette */ + Pred = 0, + Pgreen = 1, + Pblue = 2, + + Pblack = 0x00, + Pwhite = 0xFF, +}; + +#define VGAMEM() 0xA0000 +#define vgai(port) inb(port) +#define vgao(port, data) outb(port, data) + +extern int vgaxi(long, uchar); +extern int vgaxo(long, uchar, uchar); + +/* + */ +typedef struct VGAdev VGAdev; +typedef struct VGAcur VGAcur; +typedef struct VGAscr VGAscr; + +struct VGAdev { + char* name; + + void (*enable)(VGAscr*); + void (*disable)(VGAscr*); + void (*page)(VGAscr*, int); + ulong (*linear)(VGAscr*, int*, int*); + void (*drawinit)(VGAscr*); + int (*fill)(VGAscr*, Rectangle, ulong); + void (*ovlctl)(VGAscr*, Chan*, void*, int); + int (*ovlwrite)(VGAscr*, void*, int, vlong); + void (*flush)(VGAscr*, Rectangle); +}; + +struct VGAcur { + char* name; + + void (*enable)(VGAscr*); + void (*disable)(VGAscr*); + void (*load)(VGAscr*, Cursor*); + int (*move)(VGAscr*, Point); + + int doespanning; +}; + +/* + */ +struct VGAscr { + Lock devlock; + VGAdev* dev; + + VGAcur* cur; + ulong storage; + Cursor; + + int useflush; + + ulong aperture; /* physical address */ + int isupamem; + int apsize; + + ulong io; /* device specific registers */ + + ulong colormap[Pcolours][3]; + int palettedepth; + + ulong *mmio; + Memimage* gscreen; + Memdata* gscreendata; + Memsubfont* memdefont; + + int (*fill)(VGAscr*, Rectangle, ulong); + int (*scroll)(VGAscr*, Rectangle, Rectangle); + void (*blank)(VGAscr*, int); + ulong id; /* internal identifier for driver use */ + int isblank; + int overlayinit; +}; + +extern VGAscr vgascreen[]; + +enum { + Backgnd = 0, /* black */ +}; + +/* mouse.c */ +extern void mousectl(Cmdbuf*); + +/* screen.c */ +extern int hwaccel; /* use hw acceleration; default on */ +extern int hwblank; /* use hw blanking; default on */ +extern int panning; /* use virtual screen panning; default off */ +extern void addvgaseg(char*, ulong, ulong); +extern uchar* attachscreen(Rectangle*, ulong*, int*, int*, int*); +extern void flushmemscreen(Rectangle); +extern int cursoron(int); +extern void cursoroff(int); +extern void setcursor(Cursor*); +extern int screensize(int, int, int, ulong); +extern int screenaperture(int, int); +extern Rectangle physgscreenr; /* actual monitor size */ +extern void blankscreen(int); + +/* devdraw.c */ +extern void deletescreenimage(void); +extern int drawhasclients(void); +extern ulong blanktime; +extern void setscreenimageclipr(Rectangle); +extern void drawflush(void); +extern int drawidletime(void); + +/* vga.c */ +extern void vgascreenwin(VGAscr*); +extern void vgaimageinit(ulong); +extern ulong vgapcilinear(VGAscr*, int*, int*, int, int); + +extern void drawblankscreen(int); +extern void vgablank(VGAscr*, int); diff --git a/os/pc/sd53c8xx.c b/os/pc/sd53c8xx.c new file mode 100644 index 00000000..88a679c8 --- /dev/null +++ b/os/pc/sd53c8xx.c @@ -0,0 +1,2138 @@ +/* + * NCR/Symbios/LSI Logic 53c8xx driver for Plan 9 + * Nigel Roles (nigel@9fs.org) + * + * 27/5/02 Fixed problems with transfers >= 256 * 512 + * + * 13/3/01 Fixed microcode to support targets > 7 + * + * 01/12/00 Removed previous comments. Fixed a small problem in + * mismatch recovery for targets with synchronous offsets of >=16 + * connected to >=875s. Thanks, Jean. + * + * Known problems + * + * Read/write mismatch recovery may fail on 53c1010s. Really need to get a manual. + */ + +#define MAXTARGET 16 /* can be 8 or 16 */ + +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include "../port/sd.h" +extern SDifc sd53c8xxifc; + +/**********************************/ +/* Portable configuration macros */ +/**********************************/ + +//#define BOOTDEBUG +//#define ASYNC_ONLY +//#define INTERNAL_SCLK +//#define ALWAYS_DO_WDTR +#define WMR_DEBUG + +/**********************************/ +/* CPU specific macros */ +/**********************************/ + +#define PRINTPREFIX "sd53c8xx: " + +#ifdef BOOTDEBUG + +#define KPRINT oprint +#define IPRINT intrprint +#define DEBUG(n) 1 +#define IFLUSH() iflush() + +#else + +#define KPRINT if(0) print +#define IPRINT if(0) print +#define DEBUG(n) (0) +#define IFLUSH() + +#endif /* BOOTDEBUG */ + +/*******************************/ +/* General */ +/*******************************/ + +#ifndef DMASEG +#define DMASEG(x) PCIWADDR(x) +#define legetl(x) (*(ulong*)(x)) +#define lesetl(x,v) (*(ulong*)(x) = (v)) +#define swabl(a,b,c) +#else +#endif /*DMASEG */ +#define DMASEG_TO_KADDR(x) KADDR((x)-PCIWINDOW) +#define KPTR(x) ((x) == 0 ? 0 : DMASEG_TO_KADDR(x)) + +#define MEGA 1000000L +#ifdef INTERNAL_SCLK +#define SCLK (33 * MEGA) +#else +#define SCLK (40 * MEGA) +#endif /* INTERNAL_SCLK */ +#define ULTRA_NOCLOCKDOUBLE_SCLK (80 * MEGA) + +#define MAXSYNCSCSIRATE (5 * MEGA) +#define MAXFASTSYNCSCSIRATE (10 * MEGA) +#define MAXULTRASYNCSCSIRATE (20 * MEGA) +#define MAXULTRA2SYNCSCSIRATE (40 * MEGA) +#define MAXASYNCCORERATE (25 * MEGA) +#define MAXSYNCCORERATE (25 * MEGA) +#define MAXFASTSYNCCORERATE (50 * MEGA) +#define MAXULTRASYNCCORERATE (80 * MEGA) +#define MAXULTRA2SYNCCORERATE (160 * MEGA) + + +#define X_MSG 1 +#define X_MSG_SDTR 1 +#define X_MSG_WDTR 3 + +struct na_patch { + unsigned lwoff; + unsigned char type; +}; + +typedef struct Ncr { + uchar scntl0; /* 00 */ + uchar scntl1; + uchar scntl2; + uchar scntl3; + + uchar scid; /* 04 */ + uchar sxfer; + uchar sdid; + uchar gpreg; + + uchar sfbr; /* 08 */ + uchar socl; + uchar ssid; + uchar sbcl; + + uchar dstat; /* 0c */ + uchar sstat0; + uchar sstat1; + uchar sstat2; + + uchar dsa[4]; /* 10 */ + + uchar istat; /* 14 */ + uchar istatpad[3]; + + uchar ctest0; /* 18 */ + uchar ctest1; + uchar ctest2; + uchar ctest3; + + uchar temp[4]; /* 1c */ + + uchar dfifo; /* 20 */ + uchar ctest4; + uchar ctest5; + uchar ctest6; + + uchar dbc[3]; /* 24 */ + uchar dcmd; /* 27 */ + + uchar dnad[4]; /* 28 */ + uchar dsp[4]; /* 2c */ + uchar dsps[4]; /* 30 */ + + uchar scratcha[4]; /* 34 */ + + uchar dmode; /* 38 */ + uchar dien; + uchar dwt; + uchar dcntl; + + uchar adder[4]; /* 3c */ + + uchar sien0; /* 40 */ + uchar sien1; + uchar sist0; + uchar sist1; + + uchar slpar; /* 44 */ + uchar slparpad0; + uchar macntl; + uchar gpcntl; + + uchar stime0; /* 48 */ + uchar stime1; + uchar respid; + uchar respidpad0; + + uchar stest0; /* 4c */ + uchar stest1; + uchar stest2; + uchar stest3; + + uchar sidl; /* 50 */ + uchar sidlpad[3]; + + uchar sodl; /* 54 */ + uchar sodlpad[3]; + + uchar sbdl; /* 58 */ + uchar sbdlpad[3]; + + uchar scratchb[4]; /* 5c */ +} Ncr; + +typedef struct Movedata { + uchar dbc[4]; + uchar pa[4]; +} Movedata; + +typedef enum NegoState { + NeitherDone, WideInit, WideResponse, WideDone, + SyncInit, SyncResponse, BothDone +} NegoState; + +typedef enum State { + Allocated, Queued, Active, Done +} State; + +typedef struct Dsa { + uchar stateb; + uchar result; + uchar dmablks; + uchar flag; /* setbyte(state,3,...) */ + + union { + ulong dmancr; /* For block transfer: NCR order (little-endian) */ + uchar dmaaddr[4]; + }; + + uchar target; /* Target */ + uchar pad0[3]; + + uchar lun; /* Logical Unit Number */ + uchar pad1[3]; + + uchar scntl3; + uchar sxfer; + uchar pad2[2]; + + uchar next[4]; /* chaining for SCRIPT (NCR byte order) */ + struct Dsa *freechain; /* chaining for freelist */ + Rendez; + uchar scsi_id_buf[4]; + Movedata msg_out_buf; + Movedata cmd_buf; + Movedata data_buf; + Movedata status_buf; + uchar msg_out[10]; /* enough to include SDTR */ + uchar status; + int p9status; + uchar parityerror; +} Dsa; + +typedef enum Feature { + BigFifo = 1, /* 536 byte fifo */ + BurstOpCodeFetch = 2, /* burst fetch opcodes */ + Prefetch = 4, /* prefetch 8 longwords */ + LocalRAM = 8, /* 4K longwords of local RAM */ + Differential = 16, /* Differential support */ + Wide = 32, /* Wide capable */ + Ultra = 64, /* Ultra capable */ + ClockDouble = 128, /* Has clock doubler */ + ClockQuad = 256, /* Has clock quadrupler (same as Ultra2) */ + Ultra2 = 256, +} Feature; + +typedef enum Burst { + Burst2 = 0, + Burst4 = 1, + Burst8 = 2, + Burst16 = 3, + Burst32 = 4, + Burst64 = 5, + Burst128 = 6 +} Burst; + +typedef struct Variant { + ushort did; + uchar maxrid; /* maximum allowed revision ID */ + char *name; + Burst burst; /* codings for max burst */ + uchar maxsyncoff; /* max synchronous offset */ + uchar registers; /* number of 32 bit registers */ + unsigned feature; +} Variant; + +static unsigned char cf2[] = { 6, 2, 3, 4, 6, 8, 12, 16 }; +#define NULTRA2SCF (sizeof(cf2)/sizeof(cf2[0])) +#define NULTRASCF (NULTRA2SCF - 2) +#define NSCF (NULTRASCF - 1) + +typedef struct Controller { + Lock; + struct { + uchar scntl3; + uchar stest2; + } bios; + uchar synctab[NULTRA2SCF - 1][8];/* table of legal tpfs */ + NegoState s[MAXTARGET]; + uchar scntl3[MAXTARGET]; + uchar sxfer[MAXTARGET]; + uchar cap[MAXTARGET]; /* capabilities byte from Identify */ + ushort capvalid; /* bit per target for validity of cap[] */ + ushort wide; /* bit per target set if wide negotiated */ + ulong sclk; /* clock speed of controller */ + uchar clockmult; /* set by synctabinit */ + uchar ccf; /* CCF bits */ + uchar tpf; /* best tpf value for this controller */ + uchar feature; /* requested features */ + int running; /* is the script processor running? */ + int ssm; /* single step mode */ + Ncr *n; /* pointer to registers */ + Variant *v; /* pointer to variant type */ + ulong *script; /* where the real script is */ + ulong scriptpa; /* where the real script is */ + Pcidev* pcidev; + SDev* sdev; + + struct { + Lock; + uchar head[4]; /* head of free list (NCR byte order) */ + Dsa *tail; + Dsa *freechain; + } dsalist; + + QLock q[MAXTARGET]; /* queues for each target */ +} Controller; + +#define SYNCOFFMASK(c) (((c)->v->maxsyncoff * 2) - 1) +#define SSIDMASK(c) (((c)->v->feature & Wide) ? 15 : 7) + +/* ISTAT */ +enum { Abrt = 0x80, Srst = 0x40, Sigp = 0x20, Sem = 0x10, Con = 0x08, Intf = 0x04, Sip = 0x02, Dip = 0x01 }; + +/* DSTAT */ +enum { Dfe = 0x80, Mdpe = 0x40, Bf = 0x20, Abrted = 0x10, Ssi = 0x08, Sir = 0x04, Iid = 0x01 }; + +/* SSTAT */ +enum { DataOut, DataIn, Cmd, Status, ReservedOut, ReservedIn, MessageOut, MessageIn }; + +static void setmovedata(Movedata*, ulong, ulong); +static void advancedata(Movedata*, long); +static int bios_set_differential(Controller *c); + +static char *phase[] = { + "data out", "data in", "command", "status", + "reserved out", "reserved in", "message out", "message in" +}; + +#ifdef BOOTDEBUG +#define DEBUGSIZE 10240 +char debugbuf[DEBUGSIZE]; +char *debuglast; + +static void +intrprint(char *format, ...) +{ + if (debuglast == 0) + debuglast = debugbuf; + debuglast = vseprint(debuglast, debugbuf + (DEBUGSIZE - 1), format, (&format + 1)); +} + +static void +iflush() +{ + int s; + char *endp; + s = splhi(); + if (debuglast == 0) + debuglast = debugbuf; + if (debuglast == debugbuf) { + splx(s); + return; + } + endp = debuglast; + splx(s); + screenputs(debugbuf, endp - debugbuf); + s = splhi(); + memmove(debugbuf, endp, debuglast - endp); + debuglast -= endp - debugbuf; + splx(s); +} + +static void +oprint(char *format, ...) +{ + int s; + + iflush(); + s = splhi(); + if (debuglast == 0) + debuglast = debugbuf; + debuglast = vseprint(debuglast, debugbuf + (DEBUGSIZE - 1), format, (&format + 1)); + splx(s); + iflush(); +} +#endif + +#include "sd53c8xx.i" + +static Dsa * +dsaalloc(Controller *c, int target, int lun) +{ + Dsa *d; + + ilock(&c->dsalist); + if ((d = c->dsalist.freechain) == 0) { + d = xalloc(sizeof(*d)); + if (DEBUG(1)) { + KPRINT(PRINTPREFIX "%d/%d: allocated new dsa %lux\n", target, lun, (ulong)d); + } + lesetl(d->next, 0); + lesetl(&d->stateb, A_STATE_ALLOCATED); + if (legetl(c->dsalist.head) == 0) + lesetl(c->dsalist.head, DMASEG(d)); /* ATOMIC?!? */ + else + lesetl(c->dsalist.tail->next, DMASEG(d)); /* ATOMIC?!? */ + c->dsalist.tail = d; + } + else { + if (DEBUG(1)) { + KPRINT(PRINTPREFIX "%d/%d: reused dsa %lux\n", target, lun, (ulong)d); + } + c->dsalist.freechain = d->freechain; + lesetl(&d->stateb, A_STATE_ALLOCATED); + } + iunlock(&c->dsalist); + d->target = target; + d->lun = lun; + return d; +} + +static void +dsafree(Controller *c, Dsa *d) +{ + ilock(&c->dsalist); + d->freechain = c->dsalist.freechain; + c->dsalist.freechain = d; + lesetl(&d->stateb, A_STATE_FREE); + iunlock(&c->dsalist); +} + +static Dsa * +dsafind(Controller *c, uchar target, uchar lun, uchar state) +{ + Dsa *d; + for (d = KPTR(legetl(c->dsalist.head)); d; d = KPTR(legetl(d->next))) { + if (d->target != 0xff && d->target != target) + continue; + if (lun != 0xff && d->lun != lun) + continue; + if (state != 0xff && d->stateb != state) + continue; + break; + } + return d; +} + +static void +dumpncrregs(Controller *c, int intr) +{ + int i; + Ncr *n = c->n; + int depth = c->v->registers / 4; + + if (intr) { + IPRINT("sa = %.8lux\n", c->scriptpa); + } + else { + KPRINT("sa = %.8lux\n", c->scriptpa); + } + for (i = 0; i < depth; i++) { + int j; + for (j = 0; j < 4; j++) { + int k = j * depth + i; + uchar *p; + + /* display little-endian to make 32-bit values readable */ + p = (uchar*)n+k*4; + if (intr) { + IPRINT(" %.2x%.2x%.2x%.2x %.2x %.2x", p[3], p[2], p[1], p[0], k * 4, (k * 4) + 0x80); + } + else { + KPRINT(" %.2x%.2x%.2x%.2x %.2x %.2x", p[3], p[2], p[1], p[0], k * 4, (k * 4) + 0x80); + } + USED(p); + } + if (intr) { + IPRINT("\n"); + } + else { + KPRINT("\n"); + } + } +} + +static int +chooserate(Controller *c, int tpf, int *scfp, int *xferpp) +{ + /* find lowest entry >= tpf */ + int besttpf = 1000; + int bestscfi = 0; + int bestxferp = 0; + int scf, xferp; + int maxscf; + + if (c->v->feature & Ultra2) + maxscf = NULTRA2SCF; + else if (c->v->feature & Ultra) + maxscf = NULTRASCF; + else + maxscf = NSCF; + + /* + * search large clock factors first since this should + * result in more reliable transfers + */ + for (scf = maxscf; scf >= 1; scf--) { + for (xferp = 0; xferp < 8; xferp++) { + unsigned char v = c->synctab[scf - 1][xferp]; + if (v == 0) + continue; + if (v >= tpf && v < besttpf) { + besttpf = v; + bestscfi = scf; + bestxferp = xferp; + } + } + } + if (besttpf == 1000) + return 0; + if (scfp) + *scfp = bestscfi; + if (xferpp) + *xferpp = bestxferp; + return besttpf; +} + +static void +synctabinit(Controller *c) +{ + int scf; + unsigned long scsilimit; + int xferp; + unsigned long cr, sr; + int tpf; + int fast; + int maxscf; + + if (c->v->feature & Ultra2) + maxscf = NULTRA2SCF; + else if (c->v->feature & Ultra) + maxscf = NULTRASCF; + else + maxscf = NSCF; + + /* + * for chips with no clock doubler, but Ultra capable (e.g. 860, or interestingly the + * first spin of the 875), assume 80MHz + * otherwise use the internal (33 Mhz) or external (40MHz) default + */ + + if ((c->v->feature & Ultra) != 0 && (c->v->feature & (ClockDouble | ClockQuad)) == 0) + c->sclk = ULTRA_NOCLOCKDOUBLE_SCLK; + else + c->sclk = SCLK; + + /* + * otherwise, if the chip is Ultra capable, but has a slow(ish) clock, + * invoke the doubler + */ + + if (SCLK <= 40000000) { + if (c->v->feature & ClockDouble) { + c->sclk *= 2; + c->clockmult = 1; + } + else if (c->v->feature & ClockQuad) { + c->sclk *= 4; + c->clockmult = 1; + } + else + c->clockmult = 0; + } + else + c->clockmult = 0; + + /* derive CCF from sclk */ + /* woebetide anyone with SCLK < 16.7 or > 80MHz */ + if (c->sclk <= 25 * MEGA) + c->ccf = 1; + else if (c->sclk <= 3750000) + c->ccf = 2; + else if (c->sclk <= 50 * MEGA) + c->ccf = 3; + else if (c->sclk <= 75 * MEGA) + c->ccf = 4; + else if ((c->v->feature & ClockDouble) && c->sclk <= 80 * MEGA) + c->ccf = 5; + else if ((c->v->feature & ClockQuad) && c->sclk <= 120 * MEGA) + c->ccf = 6; + else if ((c->v->feature & ClockQuad) && c->sclk <= 160 * MEGA) + c->ccf = 7; + + for (scf = 1; scf < maxscf; scf++) { + /* check for legal core rate */ + /* round up so we run slower for safety */ + cr = (c->sclk * 2 + cf2[scf] - 1) / cf2[scf]; + if (cr <= MAXSYNCCORERATE) { + scsilimit = MAXSYNCSCSIRATE; + fast = 0; + } + else if (cr <= MAXFASTSYNCCORERATE) { + scsilimit = MAXFASTSYNCSCSIRATE; + fast = 1; + } + else if ((c->v->feature & Ultra) && cr <= MAXULTRASYNCCORERATE) { + scsilimit = MAXULTRASYNCSCSIRATE; + fast = 2; + } + else if ((c->v->feature & Ultra2) && cr <= MAXULTRA2SYNCCORERATE) { + scsilimit = MAXULTRA2SYNCSCSIRATE; + fast = 3; + } + else + continue; + for (xferp = 11; xferp >= 4; xferp--) { + int ok; + int tp; + /* calculate scsi rate - round up again */ + /* start from sclk for accuracy */ + int totaldivide = xferp * cf2[scf]; + sr = (c->sclk * 2 + totaldivide - 1) / totaldivide; + if (sr > scsilimit) + break; + /* + * now work out transfer period + * round down now so that period is pessimistic + */ + tp = (MEGA * 1000) / sr; + /* + * bounds check it + */ + if (tp < 25 || tp > 255 * 4) + continue; + /* + * spot stupid special case for Ultra or Ultra2 + * while working out factor + */ + if (tp == 25) + tpf = 10; + else if (tp == 50) + tpf = 12; + else if (tp < 52) + continue; + else + tpf = tp / 4; + /* + * now check tpf looks sensible + * given core rate + */ + switch (fast) { + case 0: + /* scf must be ccf for SCSI 1 */ + ok = tpf >= 50 && scf == c->ccf; + break; + case 1: + ok = tpf >= 25 && tpf < 50; + break; + case 2: + /* + * must use xferp of 4, or 5 at a pinch + * for an Ultra transfer + */ + ok = xferp <= 5 && tpf >= 12 && tpf < 25; + break; + case 3: + ok = xferp == 4 && (tpf == 10 || tpf == 11); + break; + default: + ok = 0; + } + if (!ok) + continue; + c->synctab[scf - 1][xferp - 4] = tpf; + } + } + +#ifndef NO_ULTRA2 + if (c->v->feature & Ultra2) + tpf = 10; + else +#endif + if (c->v->feature & Ultra) + tpf = 12; + else + tpf = 25; + for (; tpf < 256; tpf++) { + if (chooserate(c, tpf, &scf, &xferp) == tpf) { + unsigned tp = tpf == 10 ? 25 : (tpf == 12 ? 50 : tpf * 4); + unsigned long khz = (MEGA + tp - 1) / (tp); + KPRINT(PRINTPREFIX "tpf=%d scf=%d.%.1d xferp=%d mhz=%ld.%.3ld\n", + tpf, cf2[scf] / 2, (cf2[scf] & 1) ? 5 : 0, + xferp + 4, khz / 1000, khz % 1000); + USED(khz); + if (c->tpf == 0) + c->tpf = tpf; /* note lowest value for controller */ + } + } +} + +static void +synctodsa(Dsa *dsa, Controller *c) +{ +/* + KPRINT("synctodsa(dsa=%lux, target=%d, scntl3=%.2lx sxfer=%.2x)\n", + dsa, dsa->target, c->scntl3[dsa->target], c->sxfer[dsa->target]); +*/ + dsa->scntl3 = c->scntl3[dsa->target]; + dsa->sxfer = c->sxfer[dsa->target]; +} + +static void +setsync(Dsa *dsa, Controller *c, int target, uchar ultra, uchar scf, uchar xferp, uchar reqack) +{ + c->scntl3[target] = + (c->scntl3[target] & 0x08) | (((scf << 4) | c->ccf | (ultra << 7)) & ~0x08); + c->sxfer[target] = (xferp << 5) | reqack; + c->s[target] = BothDone; + if (dsa) { + synctodsa(dsa, c); + c->n->scntl3 = c->scntl3[target]; + c->n->sxfer = c->sxfer[target]; + } +} + +static void +setasync(Dsa *dsa, Controller *c, int target) +{ + setsync(dsa, c, target, 0, c->ccf, 0, 0); +} + +static void +setwide(Dsa *dsa, Controller *c, int target, uchar wide) +{ + c->scntl3[target] = wide ? (1 << 3) : 0; + setasync(dsa, c, target); + c->s[target] = WideDone; +} + +static int +buildsdtrmsg(uchar *buf, uchar tpf, uchar offset) +{ + *buf++ = X_MSG; + *buf++ = 3; + *buf++ = X_MSG_SDTR; + *buf++ = tpf; + *buf = offset; + return 5; +} + +static int +buildwdtrmsg(uchar *buf, uchar expo) +{ + *buf++ = X_MSG; + *buf++ = 2; + *buf++ = X_MSG_WDTR; + *buf = expo; + return 4; +} + +static void +start(Controller *c, long entry) +{ + ulong p; + + if (c->running) + panic(PRINTPREFIX "start called while running"); + c->running = 1; + p = c->scriptpa + entry; + lesetl(c->n->dsp, p); + if (c->ssm) + c->n->dcntl |= 0x4; /* start DMA in SSI mode */ +} + +static void +ncrcontinue(Controller *c) +{ + if (c->running) + panic(PRINTPREFIX "ncrcontinue called while running"); + /* set the start DMA bit to continue execution */ + c->running = 1; + c->n->dcntl |= 0x4; +} + +static void +softreset(Controller *c) +{ + Ncr *n = c->n; + + n->istat = Srst; /* software reset */ + n->istat = 0; + /* general initialisation */ + n->scid = (1 << 6) | 7; /* respond to reselect, ID 7 */ + n->respid = 1 << 7; /* response ID = 7 */ + +#ifdef INTERNAL_SCLK + n->stest1 = 0x80; /* disable external scsi clock */ +#else + n->stest1 = 0x00; +#endif + + n->stime0 = 0xdd; /* about 0.5 second timeout on each device */ + n->scntl0 |= 0x8; /* Enable parity checking */ + + /* continued setup */ + n->sien0 = 0x8f; + n->sien1 = 0x04; + n->dien = 0x7d; + n->stest3 = 0x80; /* TolerANT enable */ + c->running = 0; + + if (c->v->feature & BigFifo) + n->ctest5 = (1 << 5); + n->dmode = c->v->burst << 6; /* set burst length bits */ + if (c->v->burst & 4) + n->ctest5 |= (1 << 2); /* including overflow into ctest5 bit 2 */ + if (c->v->feature & Prefetch) + n->dcntl |= (1 << 5); /* prefetch enable */ + else if (c->v->feature & BurstOpCodeFetch) + n->dmode |= (1 << 1); /* burst opcode fetch */ + if (c->v->feature & Differential) { + /* chip capable */ + if ((c->feature & Differential) || bios_set_differential(c)) { + /* user enabled, or some evidence bios set differential */ + if (n->sstat2 & (1 << 2)) + print(PRINTPREFIX "can't go differential; wrong cable\n"); + else { + n->stest2 = (1 << 5); + print(PRINTPREFIX "differential mode set\n"); + } + } + } + if (c->clockmult) { + n->stest1 |= (1 << 3); /* power up doubler */ + delay(2); + n->stest3 |= (1 << 5); /* stop clock */ + n->stest1 |= (1 << 2); /* enable doubler */ + n->stest3 &= ~(1 << 5); /* start clock */ + /* pray */ + } +} + +static void +msgsm(Dsa *dsa, Controller *c, int msg, int *cont, int *wakeme) +{ + uchar histpf, hisreqack; + int tpf; + int scf, xferp; + int len; + + Ncr *n = c->n; + + switch (c->s[dsa->target]) { + case SyncInit: + switch (msg) { + case A_SIR_MSG_SDTR: + /* reply to my SDTR */ + histpf = n->scratcha[2]; + hisreqack = n->scratcha[3]; + KPRINT(PRINTPREFIX "%d: SDTN response %d %d\n", + dsa->target, histpf, hisreqack); + + if (hisreqack == 0) + setasync(dsa, c, dsa->target); + else { + /* hisreqack should be <= c->v->maxsyncoff */ + tpf = chooserate(c, histpf, &scf, &xferp); + KPRINT(PRINTPREFIX "%d: SDTN: using %d %d\n", + dsa->target, tpf, hisreqack); + setsync(dsa, c, dsa->target, tpf < 25, scf, xferp, hisreqack); + } + *cont = -2; + return; + case A_SIR_EV_PHASE_SWITCH_AFTER_ID: + /* target ignored ATN for message after IDENTIFY - not SCSI-II */ + KPRINT(PRINTPREFIX "%d: illegal phase switch after ID message - SCSI-1 device?\n", dsa->target); + KPRINT(PRINTPREFIX "%d: SDTN: async\n", dsa->target); + setasync(dsa, c, dsa->target); + *cont = E_to_decisions; + return; + case A_SIR_MSG_REJECT: + /* rejection of my SDTR */ + KPRINT(PRINTPREFIX "%d: SDTN: rejected SDTR\n", dsa->target); + //async: + KPRINT(PRINTPREFIX "%d: SDTN: async\n", dsa->target); + setasync(dsa, c, dsa->target); + *cont = -2; + return; + } + break; + case WideInit: + switch (msg) { + case A_SIR_MSG_WDTR: + /* reply to my WDTR */ + KPRINT(PRINTPREFIX "%d: WDTN: response %d\n", + dsa->target, n->scratcha[2]); + setwide(dsa, c, dsa->target, n->scratcha[2]); + *cont = -2; + return; + case A_SIR_EV_PHASE_SWITCH_AFTER_ID: + /* target ignored ATN for message after IDENTIFY - not SCSI-II */ + KPRINT(PRINTPREFIX "%d: illegal phase switch after ID message - SCSI-1 device?\n", dsa->target); + setwide(dsa, c, dsa->target, 0); + *cont = E_to_decisions; + return; + case A_SIR_MSG_REJECT: + /* rejection of my SDTR */ + KPRINT(PRINTPREFIX "%d: WDTN: rejected WDTR\n", dsa->target); + setwide(dsa, c, dsa->target, 0); + *cont = -2; + return; + } + break; + + case NeitherDone: + case WideDone: + case BothDone: + switch (msg) { + case A_SIR_MSG_WDTR: { + uchar hiswide, mywide; + hiswide = n->scratcha[2]; + mywide = (c->v->feature & Wide) != 0; + KPRINT(PRINTPREFIX "%d: WDTN: target init %d\n", + dsa->target, hiswide); + if (hiswide < mywide) + mywide = hiswide; + KPRINT(PRINTPREFIX "%d: WDTN: responding %d\n", + dsa->target, mywide); + setwide(dsa, c, dsa->target, mywide); + len = buildwdtrmsg(dsa->msg_out, mywide); + setmovedata(&dsa->msg_out_buf, DMASEG(dsa->msg_out), len); + *cont = E_response; + c->s[dsa->target] = WideResponse; + return; + } + case A_SIR_MSG_SDTR: +#ifdef ASYNC_ONLY + *cont = E_reject; + return; +#else + /* target decides to renegotiate */ + histpf = n->scratcha[2]; + hisreqack = n->scratcha[3]; + KPRINT(PRINTPREFIX "%d: SDTN: target init %d %d\n", + dsa->target, histpf, hisreqack); + if (hisreqack == 0) { + /* he wants asynchronous */ + setasync(dsa, c, dsa->target); + tpf = 0; + } + else { + /* he wants synchronous */ + tpf = chooserate(c, histpf, &scf, &xferp); + if (hisreqack > c->v->maxsyncoff) + hisreqack = c->v->maxsyncoff; + KPRINT(PRINTPREFIX "%d: using %d %d\n", + dsa->target, tpf, hisreqack); + setsync(dsa, c, dsa->target, tpf < 25, scf, xferp, hisreqack); + } + /* build my SDTR message */ + len = buildsdtrmsg(dsa->msg_out, tpf, hisreqack); + setmovedata(&dsa->msg_out_buf, DMASEG(dsa->msg_out), len); + *cont = E_response; + c->s[dsa->target] = SyncResponse; + return; +#endif + } + break; + case WideResponse: + switch (msg) { + case A_SIR_EV_RESPONSE_OK: + c->s[dsa->target] = WideDone; + KPRINT(PRINTPREFIX "%d: WDTN: response accepted\n", dsa->target); + *cont = -2; + return; + case A_SIR_MSG_REJECT: + setwide(dsa, c, dsa->target, 0); + KPRINT(PRINTPREFIX "%d: WDTN: response REJECTed\n", dsa->target); + *cont = -2; + return; + } + break; + case SyncResponse: + switch (msg) { + case A_SIR_EV_RESPONSE_OK: + c->s[dsa->target] = BothDone; + KPRINT(PRINTPREFIX "%d: SDTN: response accepted (%s)\n", + dsa->target, phase[n->sstat1 & 7]); + *cont = -2; + return; /* chf */ + case A_SIR_MSG_REJECT: + setasync(dsa, c, dsa->target); + KPRINT(PRINTPREFIX "%d: SDTN: response REJECTed\n", dsa->target); + *cont = -2; + return; + } + break; + } + KPRINT(PRINTPREFIX "%d: msgsm: state %d msg %d\n", + dsa->target, c->s[dsa->target], msg); + *wakeme = 1; + return; +} + +static void +calcblockdma(Dsa *d, ulong base, ulong count) +{ + ulong blocks; + if (DEBUG(3)) + blocks = 0; + else { + blocks = count / A_BSIZE; + if (blocks > 255) + blocks = 255; + } + d->dmablks = blocks; + d->dmaaddr[0] = base; + d->dmaaddr[1] = base >> 8; + d->dmaaddr[2] = base >> 16; + d->dmaaddr[3] = base >> 24; + setmovedata(&d->data_buf, base + blocks * A_BSIZE, count - blocks * A_BSIZE); + d->flag = legetl(d->data_buf.dbc) == 0; +} + +static ulong +read_mismatch_recover(Controller *c, Ncr *n, Dsa *dsa) +{ + ulong dbc; + uchar dfifo = n->dfifo; + int inchip; + + dbc = (n->dbc[2]<<16)|(n->dbc[1]<<8)|n->dbc[0]; + if (n->ctest5 & (1 << 5)) + inchip = ((dfifo | ((n->ctest5 & 3) << 8)) - (dbc & 0x3ff)) & 0x3ff; + else + inchip = ((dfifo & 0x7f) - (dbc & 0x7f)) & 0x7f; + if (inchip) { + IPRINT(PRINTPREFIX "%d/%d: read_mismatch_recover: DMA FIFO = %d\n", + dsa->target, dsa->lun, inchip); + } + if (n->sxfer & SYNCOFFMASK(c)) { + /* SCSI FIFO */ + uchar fifo = n->sstat1 >> 4; + if (c->v->maxsyncoff > 8) + fifo |= (n->sstat2 & (1 << 4)); + if (fifo) { + inchip += fifo; + IPRINT(PRINTPREFIX "%d/%d: read_mismatch_recover: SCSI FIFO = %d\n", + dsa->target, dsa->lun, fifo); + } + } + else { + if (n->sstat0 & (1 << 7)) { + inchip++; + IPRINT(PRINTPREFIX "%d/%d: read_mismatch_recover: SIDL full\n", + dsa->target, dsa->lun); + } + if (n->sstat2 & (1 << 7)) { + inchip++; + IPRINT(PRINTPREFIX "%d/%d: read_mismatch_recover: SIDL msb full\n", + dsa->target, dsa->lun); + } + } + USED(inchip); + return dbc; +} + +static ulong +write_mismatch_recover(Controller *c, Ncr *n, Dsa *dsa) +{ + ulong dbc; + uchar dfifo = n->dfifo; + int inchip; + + dbc = (n->dbc[2]<<16)|(n->dbc[1]<<8)|n->dbc[0]; + USED(dsa); + if (n->ctest5 & (1 << 5)) + inchip = ((dfifo | ((n->ctest5 & 3) << 8)) - (dbc & 0x3ff)) & 0x3ff; + else + inchip = ((dfifo & 0x7f) - (dbc & 0x7f)) & 0x7f; +#ifdef WMR_DEBUG + if (inchip) { + IPRINT(PRINTPREFIX "%d/%d: write_mismatch_recover: DMA FIFO = %d\n", + dsa->target, dsa->lun, inchip); + } +#endif + if (n->sstat0 & (1 << 5)) { + inchip++; +#ifdef WMR_DEBUG + IPRINT(PRINTPREFIX "%d/%d: write_mismatch_recover: SODL full\n", dsa->target, dsa->lun); +#endif + } + if (n->sstat2 & (1 << 5)) { + inchip++; +#ifdef WMR_DEBUG + IPRINT(PRINTPREFIX "%d/%d: write_mismatch_recover: SODL msb full\n", dsa->target, dsa->lun); +#endif + } + if (n->sxfer & SYNCOFFMASK(c)) { + /* synchronous SODR */ + if (n->sstat0 & (1 << 6)) { + inchip++; +#ifdef WMR_DEBUG + IPRINT(PRINTPREFIX "%d/%d: write_mismatch_recover: SODR full\n", + dsa->target, dsa->lun); +#endif + } + if (n->sstat2 & (1 << 6)) { + inchip++; +#ifdef WMR_DEBUG + IPRINT(PRINTPREFIX "%d/%d: write_mismatch_recover: SODR msb full\n", + dsa->target, dsa->lun); +#endif + } + } + /* clear the dma fifo */ + n->ctest3 |= (1 << 2); + /* wait till done */ + while ((n->dstat & Dfe) == 0) + ; + return dbc + inchip; +} + +static void +sd53c8xxinterrupt(Ureg *ur, void *a) +{ + uchar istat; + ushort sist; + uchar dstat; + int wakeme = 0; + int cont = -1; + Dsa *dsa; + Controller *c = a; + Ncr *n = c->n; + + USED(ur); + if (DEBUG(1)) { + IPRINT(PRINTPREFIX "int\n"); + } + ilock(c); + istat = n->istat; + if (istat & Intf) { + Dsa *d; + int wokesomething = 0; + if (DEBUG(1)) { + IPRINT(PRINTPREFIX "Intfly\n"); + } + n->istat = Intf; + /* search for structures in A_STATE_DONE */ + for (d = KPTR(legetl(c->dsalist.head)); d; d = KPTR(legetl(d->next))) { + if (d->stateb == A_STATE_DONE) { + d->p9status = d->status; + if (DEBUG(1)) { + IPRINT(PRINTPREFIX "waking up dsa %lux\n", (ulong)d); + } + wakeup(d); + wokesomething = 1; + } + } + if (!wokesomething) { + IPRINT(PRINTPREFIX "nothing to wake up\n"); + } + } + + if ((istat & (Sip | Dip)) == 0) { + if (DEBUG(1)) { + IPRINT(PRINTPREFIX "int end %x\n", istat); + } + iunlock(c); + return; + } + + sist = (n->sist1<<8)|n->sist0; /* BUG? can two-byte read be inconsistent? */ + dstat = n->dstat; + dsa = (Dsa *)DMASEG_TO_KADDR(legetl(n->dsa)); + c->running = 0; + if (istat & Sip) { + if (DEBUG(1)) { + IPRINT("sist = %.4x\n", sist); + } + if (sist & 0x80) { + ulong addr; + ulong sa; + ulong dbc; + ulong tbc; + int dmablks; + ulong dmaaddr; + + addr = legetl(n->dsp); + sa = addr - c->scriptpa; + if (DEBUG(1) || DEBUG(2)) { + IPRINT(PRINTPREFIX "%d/%d: Phase Mismatch sa=%.8lux\n", + dsa->target, dsa->lun, sa); + } + /* + * now recover + */ + if (sa == E_data_in_mismatch) { + /* + * though this is a failure in the residue, there may have been blocks + * as well. if so, dmablks will not have been zeroed, since the state + * was not saved by the microcode. + */ + dbc = read_mismatch_recover(c, n, dsa); + tbc = legetl(dsa->data_buf.dbc) - dbc; + dsa->dmablks = 0; + n->scratcha[2] = 0; + advancedata(&dsa->data_buf, tbc); + if (DEBUG(1) || DEBUG(2)) { + IPRINT(PRINTPREFIX "%d/%d: transferred = %ld residue = %ld\n", + dsa->target, dsa->lun, tbc, legetl(dsa->data_buf.dbc)); + } + cont = E_data_mismatch_recover; + } + else if (sa == E_data_in_block_mismatch) { + dbc = read_mismatch_recover(c, n, dsa); + tbc = A_BSIZE - dbc; + /* recover current state from registers */ + dmablks = n->scratcha[2]; + dmaaddr = legetl(n->scratchb); + /* we have got to dmaaddr + tbc */ + /* we have dmablks * A_BSIZE - tbc + residue left to do */ + /* so remaining transfer is */ + IPRINT("in_block_mismatch: dmaaddr = 0x%lux tbc=%lud dmablks=%d\n", + dmaaddr, tbc, dmablks); + calcblockdma(dsa, dmaaddr + tbc, + dmablks * A_BSIZE - tbc + legetl(dsa->data_buf.dbc)); + /* copy changes into scratch registers */ + IPRINT("recalc: dmablks %d dmaaddr 0x%lx pa 0x%lx dbc %ld\n", + dsa->dmablks, legetl(dsa->dmaaddr), + legetl(dsa->data_buf.pa), legetl(dsa->data_buf.dbc)); + n->scratcha[2] = dsa->dmablks; + lesetl(n->scratchb, dsa->dmancr); + cont = E_data_block_mismatch_recover; + } + else if (sa == E_data_out_mismatch) { + dbc = write_mismatch_recover(c, n, dsa); + tbc = legetl(dsa->data_buf.dbc) - dbc; + dsa->dmablks = 0; + n->scratcha[2] = 0; + advancedata(&dsa->data_buf, tbc); + if (DEBUG(1) || DEBUG(2)) { + IPRINT(PRINTPREFIX "%d/%d: transferred = %ld residue = %ld\n", + dsa->target, dsa->lun, tbc, legetl(dsa->data_buf.dbc)); + } + cont = E_data_mismatch_recover; + } + else if (sa == E_data_out_block_mismatch) { + dbc = write_mismatch_recover(c, n, dsa); + tbc = legetl(dsa->data_buf.dbc) - dbc; + /* recover current state from registers */ + dmablks = n->scratcha[2]; + dmaaddr = legetl(n->scratchb); + /* we have got to dmaaddr + tbc */ + /* we have dmablks blocks - tbc + residue left to do */ + /* so remaining transfer is */ + IPRINT("out_block_mismatch: dmaaddr = %lux tbc=%lud dmablks=%d\n", + dmaaddr, tbc, dmablks); + calcblockdma(dsa, dmaaddr + tbc, + dmablks * A_BSIZE - tbc + legetl(dsa->data_buf.dbc)); + /* copy changes into scratch registers */ + n->scratcha[2] = dsa->dmablks; + lesetl(n->scratchb, dsa->dmancr); + cont = E_data_block_mismatch_recover; + } + else if (sa == E_id_out_mismatch) { + /* + * target switched phases while attention held during + * message out. The possibilities are: + * 1. It didn't like the last message. This is indicated + * by the new phase being message_in. Use script to recover + * + * 2. It's not SCSI-II compliant. The new phase will be other + * than message_in. We should also indicate that the device + * is asynchronous, if it's the SDTR that got ignored + * + * For now, if the phase switch is not to message_in, and + * and it happens after IDENTIFY and before SDTR, we + * notify the negotiation state machine. + */ + ulong lim = legetl(dsa->msg_out_buf.dbc); + uchar p = n->sstat1 & 7; + dbc = write_mismatch_recover(c, n, dsa); + tbc = lim - dbc; + IPRINT(PRINTPREFIX "%d/%d: msg_out_mismatch: %lud/%lud sent, phase %s\n", + dsa->target, dsa->lun, tbc, lim, phase[p]); + if (p != MessageIn && tbc == 1) { + msgsm(dsa, c, A_SIR_EV_PHASE_SWITCH_AFTER_ID, &cont, &wakeme); + } + else + cont = E_id_out_mismatch_recover; + } + else if (sa == E_cmd_out_mismatch) { + /* + * probably the command count is longer than the device wants ... + */ + ulong lim = legetl(dsa->cmd_buf.dbc); + uchar p = n->sstat1 & 7; + dbc = write_mismatch_recover(c, n, dsa); + tbc = lim - dbc; + IPRINT(PRINTPREFIX "%d/%d: cmd_out_mismatch: %lud/%lud sent, phase %s\n", + dsa->target, dsa->lun, tbc, lim, phase[p]); + USED(p, tbc); + cont = E_to_decisions; + } + else { + IPRINT(PRINTPREFIX "%d/%d: ma sa=%.8lux wanted=%s got=%s\n", + dsa->target, dsa->lun, sa, + phase[n->dcmd & 7], + phase[n->sstat1 & 7]); + dumpncrregs(c, 1); + dsa->p9status = SDeio; /* chf */ + wakeme = 1; + } + } + /*else*/ if (sist & 0x400) { + if (DEBUG(0)) { + IPRINT(PRINTPREFIX "%d/%d Sto\n", dsa->target, dsa->lun); + } + dsa->p9status = SDtimeout; + dsa->stateb = A_STATE_DONE; + softreset(c); + cont = E_issue_check; + wakeme = 1; + } + if (sist & 0x1) { + IPRINT(PRINTPREFIX "%d/%d: parity error\n", dsa->target, dsa->lun); + dsa->parityerror = 1; + } + if (sist & 0x4) { + IPRINT(PRINTPREFIX "%d/%d: unexpected disconnect\n", + dsa->target, dsa->lun); + dumpncrregs(c, 1); + //wakeme = 1; + dsa->p9status = SDeio; + } + } + if (istat & Dip) { + if (DEBUG(1)) { + IPRINT("dstat = %.2x\n", dstat); + } + /*else*/ if (dstat & Ssi) { + ulong *p = DMASEG_TO_KADDR(legetl(n->dsp)); + ulong w = (uchar *)p - (uchar *)c->script; + IPRINT("[%lux]", w); + USED(w); + cont = -2; /* restart */ + } + if (dstat & Sir) { + switch (legetl(n->dsps)) { + case A_SIR_MSG_IO_COMPLETE: + dsa->p9status = dsa->status; + wakeme = 1; + break; + case A_SIR_MSG_SDTR: + case A_SIR_MSG_WDTR: + case A_SIR_MSG_REJECT: + case A_SIR_EV_RESPONSE_OK: + msgsm(dsa, c, legetl(n->dsps), &cont, &wakeme); + break; + case A_SIR_MSG_IGNORE_WIDE_RESIDUE: + /* back up one in the data transfer */ + IPRINT(PRINTPREFIX "%d/%d: ignore wide residue %d, WSR = %d\n", + dsa->target, dsa->lun, n->scratcha[1], n->scntl2 & 1); + if (dsa->flag == 2) { + IPRINT(PRINTPREFIX "%d/%d: transfer over; residue ignored\n", + dsa->target, dsa->lun); + } + else { + calcblockdma(dsa, legetl(dsa->dmaaddr) - 1, + dsa->dmablks * A_BSIZE + legetl(dsa->data_buf.dbc) + 1); + } + cont = -2; + break; + case A_SIR_ERROR_NOT_MSG_IN_AFTER_RESELECT: + IPRINT(PRINTPREFIX "%d: not msg_in after reselect (%s)", + n->ssid & SSIDMASK(c), phase[n->sstat1 & 7]); + dsa = dsafind(c, n->ssid & SSIDMASK(c), -1, A_STATE_DISCONNECTED); + dumpncrregs(c, 1); + wakeme = 1; + break; + case A_SIR_NOTIFY_MSG_IN: + IPRINT(PRINTPREFIX "%d/%d: msg_in %d\n", + dsa->target, dsa->lun, n->sfbr); + cont = -2; + break; + case A_SIR_NOTIFY_DISC: + IPRINT(PRINTPREFIX "%d/%d: disconnect:", dsa->target, dsa->lun); + goto dsadump; + case A_SIR_NOTIFY_STATUS: + IPRINT(PRINTPREFIX "%d/%d: status\n", dsa->target, dsa->lun); + cont = -2; + break; + case A_SIR_NOTIFY_COMMAND: + IPRINT(PRINTPREFIX "%d/%d: commands\n", dsa->target, dsa->lun); + cont = -2; + break; + case A_SIR_NOTIFY_DATA_IN: + IPRINT(PRINTPREFIX "%d/%d: data in a %lx b %lx\n", + dsa->target, dsa->lun, legetl(n->scratcha), legetl(n->scratchb)); + cont = -2; + break; + case A_SIR_NOTIFY_BLOCK_DATA_IN: + IPRINT(PRINTPREFIX "%d/%d: block data in: a2 %x b %lx\n", + dsa->target, dsa->lun, n->scratcha[2], legetl(n->scratchb)); + cont = -2; + break; + case A_SIR_NOTIFY_DATA_OUT: + IPRINT(PRINTPREFIX "%d/%d: data out\n", dsa->target, dsa->lun); + cont = -2; + break; + case A_SIR_NOTIFY_DUMP: + IPRINT(PRINTPREFIX "%d/%d: dump\n", dsa->target, dsa->lun); + dumpncrregs(c, 1); + cont = -2; + break; + case A_SIR_NOTIFY_DUMP2: + IPRINT(PRINTPREFIX "%d/%d: dump2:", dsa->target, dsa->lun); + IPRINT(" sa %lux", legetl(n->dsp) - c->scriptpa); + IPRINT(" dsa %lux", legetl(n->dsa)); + IPRINT(" sfbr %ux", n->sfbr); + IPRINT(" a %lux", legetl(n->scratcha)); + IPRINT(" b %lux", legetl(n->scratchb)); + IPRINT(" ssid %ux", n->ssid); + IPRINT("\n"); + cont = -2; + break; + case A_SIR_NOTIFY_WAIT_RESELECT: + IPRINT(PRINTPREFIX "wait reselect\n"); + cont = -2; + break; + case A_SIR_NOTIFY_RESELECT: + IPRINT(PRINTPREFIX "reselect: ssid %.2x sfbr %.2x at %ld\n", + n->ssid, n->sfbr, TK2MS(m->ticks)); + cont = -2; + break; + case A_SIR_NOTIFY_ISSUE: + IPRINT(PRINTPREFIX "%d/%d: issue:", dsa->target, dsa->lun); + dsadump: + IPRINT(" tgt=%d", dsa->target); + IPRINT(" time=%ld", TK2MS(m->ticks)); + IPRINT("\n"); + cont = -2; + break; + case A_SIR_NOTIFY_ISSUE_CHECK: + IPRINT(PRINTPREFIX "issue check\n"); + cont = -2; + break; + case A_SIR_NOTIFY_SIGP: + IPRINT(PRINTPREFIX "responded to SIGP\n"); + cont = -2; + break; + case A_SIR_NOTIFY_DUMP_NEXT_CODE: { + ulong *dsp = DMASEG_TO_KADDR(legetl(n->dsp)); + int x; + IPRINT(PRINTPREFIX "code at %lux", dsp - c->script); + for (x = 0; x < 6; x++) { + IPRINT(" %.8lux", dsp[x]); + } + IPRINT("\n"); + USED(dsp); + cont = -2; + break; + } + case A_SIR_NOTIFY_WSR: + IPRINT(PRINTPREFIX "%d/%d: WSR set\n", dsa->target, dsa->lun); + cont = -2; + break; + case A_SIR_NOTIFY_LOAD_SYNC: + IPRINT(PRINTPREFIX "%d/%d: scntl=%.2x sxfer=%.2x\n", + dsa->target, dsa->lun, n->scntl3, n->sxfer); + cont = -2; + break; + case A_SIR_NOTIFY_RESELECTED_ON_SELECT: + if (DEBUG(2)) { + IPRINT(PRINTPREFIX "%d/%d: reselected during select\n", + dsa->target, dsa->lun); + } + cont = -2; + break; + case A_error_reselected: /* dsa isn't valid here */ + print(PRINTPREFIX "reselection error\n"); + dumpncrregs(c, 1); + for (dsa = KPTR(legetl(c->dsalist.head)); dsa; dsa = KPTR(legetl(dsa->next))) { + IPRINT(PRINTPREFIX "dsa target %d lun %d state %d\n", dsa->target, dsa->lun, dsa->stateb); + } + break; + default: + IPRINT(PRINTPREFIX "%d/%d: script error %ld\n", + dsa->target, dsa->lun, legetl(n->dsps)); + dumpncrregs(c, 1); + wakeme = 1; + } + } + /*else*/ if (dstat & Iid) { + ulong addr = legetl(n->dsp); + ulong dbc = (n->dbc[2]<<16)|(n->dbc[1]<<8)|n->dbc[0]; + IPRINT(PRINTPREFIX "%d/%d: Iid pa=%.8lux sa=%.8lux dbc=%lux\n", + dsa->target, dsa->lun, + addr, addr - c->scriptpa, dbc); + addr = (ulong)DMASEG_TO_KADDR(addr); + IPRINT("%.8lux %.8lux %.8lux\n", + *(ulong *)(addr - 12), *(ulong *)(addr - 8), *(ulong *)(addr - 4)); + USED(addr, dbc); + dsa->p9status = SDeio; + wakeme = 1; + } + /*else*/ if (dstat & Bf) { + IPRINT(PRINTPREFIX "%d/%d: Bus Fault\n", dsa->target, dsa->lun); + dumpncrregs(c, 1); + dsa->p9status = SDeio; + wakeme = 1; + } + } + if (cont == -2) + ncrcontinue(c); + else if (cont >= 0) + start(c, cont); + if (wakeme){ + if(dsa->p9status == SDnostatus) + dsa->p9status = SDeio; + wakeup(dsa); + } + iunlock(c); + if (DEBUG(1)) { + IPRINT(PRINTPREFIX "int end 1\n"); + } +} + +static int +done(void *arg) +{ + return ((Dsa *)arg)->p9status != SDnostatus; +} + +static void +setmovedata(Movedata *d, ulong pa, ulong bc) +{ + d->pa[0] = pa; + d->pa[1] = pa>>8; + d->pa[2] = pa>>16; + d->pa[3] = pa>>24; + d->dbc[0] = bc; + d->dbc[1] = bc>>8; + d->dbc[2] = bc>>16; + d->dbc[3] = bc>>24; +} + +static void +advancedata(Movedata *d, long v) +{ + lesetl(d->pa, legetl(d->pa) + v); + lesetl(d->dbc, legetl(d->dbc) - v); +} + +static void +dumpwritedata(uchar *data, int datalen) +{ + int i; + uchar *bp; + if (!DEBUG(0)){ + USED(data, datalen); + return; + } + + if (datalen) { + KPRINT(PRINTPREFIX "write:"); + for (i = 0, bp = data; i < 50 && i < datalen; i++, bp++) { + KPRINT("%.2ux", *bp); + } + if (i < datalen) { + KPRINT("..."); + } + KPRINT("\n"); + } +} + +static void +dumpreaddata(uchar *data, int datalen) +{ + int i; + uchar *bp; + if (!DEBUG(0)){ + USED(data, datalen); + return; + } + + if (datalen) { + KPRINT(PRINTPREFIX "read:"); + for (i = 0, bp = data; i < 50 && i < datalen; i++, bp++) { + KPRINT("%.2ux", *bp); + } + if (i < datalen) { + KPRINT("..."); + } + KPRINT("\n"); + } +} + +static void +busreset(Controller *c) +{ + int x, ntarget; + + /* bus reset */ + c->n->scntl1 |= (1 << 3); + delay(500); + c->n->scntl1 &= ~(1 << 3); + if(!(c->v->feature & Wide)) + ntarget = 8; + else + ntarget = MAXTARGET; + for (x = 0; x < ntarget; x++) { + setwide(0, c, x, 0); +#ifndef ASYNC_ONLY + c->s[x] = NeitherDone; +#endif + } + c->capvalid = 0; +} + +static void +reset(Controller *c) +{ + /* should wakeup all pending tasks */ + softreset(c); + busreset(c); +} + +static int +sd53c8xxrio(SDreq* r) +{ + Dsa *d; + uchar *bp; + Controller *c; + uchar target_expo, my_expo; + int bc, check, i, status, target; + + if((target = r->unit->subno) == 0x07) + return r->status = SDtimeout; /* assign */ + c = r->unit->dev->ctlr; + + check = 0; + d = dsaalloc(c, target, r->lun); + + qlock(&c->q[target]); /* obtain access to target */ +docheck: + /* load the transfer control stuff */ + d->scsi_id_buf[0] = 0; + d->scsi_id_buf[1] = c->sxfer[target]; + d->scsi_id_buf[2] = target; + d->scsi_id_buf[3] = c->scntl3[target]; + synctodsa(d, c); + + bc = 0; + + d->msg_out[bc] = 0x80 | r->lun; + +#ifndef NO_DISCONNECT + d->msg_out[bc] |= (1 << 6); +#endif + bc++; + + /* work out what to do about negotiation */ + switch (c->s[target]) { + default: + KPRINT(PRINTPREFIX "%d: strange nego state %d\n", target, c->s[target]); + c->s[target] = NeitherDone; + /* fall through */ + case NeitherDone: + if ((c->capvalid & (1 << target)) == 0) + break; + target_expo = (c->cap[target] >> 5) & 3; + my_expo = (c->v->feature & Wide) != 0; + if (target_expo < my_expo) + my_expo = target_expo; +#ifdef ALWAYS_DO_WDTR + bc += buildwdtrmsg(d->msg_out + bc, my_expo); + KPRINT(PRINTPREFIX "%d: WDTN: initiating expo %d\n", target, my_expo); + c->s[target] = WideInit; + break; +#else + if (my_expo) { + bc += buildwdtrmsg(d->msg_out + bc, (c->v->feature & Wide) ? 1 : 0); + KPRINT(PRINTPREFIX "%d: WDTN: initiating expo %d\n", target, my_expo); + c->s[target] = WideInit; + break; + } + KPRINT(PRINTPREFIX "%d: WDTN: narrow\n", target); + /* fall through */ +#endif + case WideDone: + if (c->cap[target] & (1 << 4)) { + KPRINT(PRINTPREFIX "%d: SDTN: initiating %d %d\n", target, c->tpf, c->v->maxsyncoff); + bc += buildsdtrmsg(d->msg_out + bc, c->tpf, c->v->maxsyncoff); + c->s[target] = SyncInit; + break; + } + KPRINT(PRINTPREFIX "%d: SDTN: async only\n", target); + c->s[target] = BothDone; + break; + + case BothDone: + break; + } + + setmovedata(&d->msg_out_buf, DMASEG(d->msg_out), bc); + setmovedata(&d->cmd_buf, DMASEG(r->cmd), r->clen); + calcblockdma(d, DMASEG(r->data), r->dlen); + + if (DEBUG(0)) { + KPRINT(PRINTPREFIX "%d/%d: exec: ", target, r->lun); + for (bp = r->cmd; bp < &r->cmd[r->clen]; bp++) { + KPRINT("%.2ux", *bp); + } + KPRINT("\n"); + if (!r->write) { + KPRINT(PRINTPREFIX "%d/%d: exec: limit=(%d)%ld\n", + target, r->lun, d->dmablks, legetl(d->data_buf.dbc)); + } + else + dumpwritedata(r->data, r->dlen); + } + + setmovedata(&d->status_buf, DMASEG(&d->status), 1); + + d->p9status = SDnostatus; + d->parityerror = 0; + + d->stateb = A_STATE_ISSUE; /* start operation */ + + ilock(c); + if (c->ssm) + c->n->dcntl |= 0x10; /* SSI */ + if (c->running) { + c->n->istat |= Sigp; + } + else { + start(c, E_issue_check); + } + iunlock(c); + + while(waserror()) + ; + tsleep(d, done, d, 600 * 1000); + poperror(); + + if (!done(d)) { + KPRINT(PRINTPREFIX "%d/%d: exec: Timed out\n", target, r->lun); + dumpncrregs(c, 0); + dsafree(c, d); + reset(c); + qunlock(&c->q[target]); + r->status = SDtimeout; + return r->status = SDtimeout; /* assign */ + } + + if((status = d->p9status) == SDeio) + c->s[target] = NeitherDone; + if (d->parityerror) { + status = SDeio; + } + + /* + * adjust datalen + */ + r->rlen = r->dlen; + if (DEBUG(0)) { + KPRINT(PRINTPREFIX "%d/%d: exec: before rlen adjust: dmablks %d flag %d dbc %lud\n", + target, r->lun, d->dmablks, d->flag, legetl(d->data_buf.dbc)); + } + r->rlen = r->dlen; + if (d->flag != 2) { + r->rlen -= d->dmablks * A_BSIZE; + r->rlen -= legetl(d->data_buf.dbc); + } + if(!r->write) + dumpreaddata(r->data, r->rlen); + if (DEBUG(0)) { + KPRINT(PRINTPREFIX "%d/%d: exec: p9status=%d status %d rlen %ld\n", + target, r->lun, d->p9status, status, r->rlen); + } + /* + * spot the identify + */ + if ((c->capvalid & (1 << target)) == 0 + && (status == SDok || status == SDcheck) + && r->cmd[0] == 0x12 && r->dlen >= 8) { + c->capvalid |= 1 << target; + bp = r->data; + c->cap[target] = bp[7]; + KPRINT(PRINTPREFIX "%d: capabilities %.2x\n", target, bp[7]); + } + if(!check && status == SDcheck && !(r->flags & SDnosense)){ + check = 1; + r->write = 0; + memset(r->cmd, 0, sizeof(r->cmd)); + r->cmd[0] = 0x03; + r->cmd[1] = r->lun<<5; + r->cmd[4] = sizeof(r->sense)-1; + r->clen = 6; + r->data = r->sense; + r->dlen = sizeof(r->sense)-1; + /* + * Clear out the microcode state + * so the Dsa can be re-used. + */ + lesetl(&d->stateb, A_STATE_ALLOCATED); + goto docheck; + } + qunlock(&c->q[target]); + dsafree(c, d); + + if(status == SDok && check){ + status = SDcheck; + r->flags |= SDvalidsense; + } + if(DEBUG(0)) + KPRINT(PRINTPREFIX "%d: r flags %8.8uX status %d rlen %ld\n", + target, r->flags, status, r->rlen); + if(r->flags & SDvalidsense){ + if(!DEBUG(0)) + KPRINT(PRINTPREFIX "%d: r flags %8.8uX status %d rlen %ld\n", + target, r->flags, status, r->rlen); + for(i = 0; i < r->rlen; i++) + KPRINT(" %2.2uX", r->sense[i]); + KPRINT("\n"); + } + return r->status = status; +} + +static void +cribbios(Controller *c) +{ + c->bios.scntl3 = c->n->scntl3; + c->bios.stest2 = c->n->stest2; + KPRINT(PRINTPREFIX "bios scntl3(%.2x) stest2(%.2x)\n", c->bios.scntl3, c->bios.stest2); +} + +static int +bios_set_differential(Controller *c) +{ + /* Concept lifted from FreeBSD - thanks Gerard */ + /* basically, if clock conversion factors are set, then there is + * evidence the bios had a go at the chip, and if so, it would + * have set the differential enable bit in stest2 + */ + return (c->bios.scntl3 & 7) != 0 && (c->bios.stest2 & 0x20) != 0; +} + +#define NCR_VID 0x1000 +#define NCR_810_DID 0x0001 +#define NCR_820_DID 0x0002 /* don't know enough about this one to support it */ +#define NCR_825_DID 0x0003 +#define NCR_815_DID 0x0004 +#define SYM_810AP_DID 0x0005 +#define SYM_860_DID 0x0006 +#define SYM_896_DID 0x000b +#define SYM_895_DID 0x000c +#define SYM_885_DID 0x000d /* ditto */ +#define SYM_875_DID 0x000f /* ditto */ +#define SYM_1010_DID 0x0020 +#define SYM_1011_DID 0x0021 +#define SYM_875J_DID 0x008f + +static Variant variant[] = { +{ NCR_810_DID, 0x0f, "NCR53C810", Burst16, 8, 24, 0 }, +{ NCR_810_DID, 0x1f, "SYM53C810ALV", Burst16, 8, 24, Prefetch }, +{ NCR_810_DID, 0xff, "SYM53C810A", Burst16, 8, 24, Prefetch }, +{ SYM_810AP_DID, 0xff, "SYM53C810AP", Burst16, 8, 24, Prefetch }, +{ NCR_815_DID, 0xff, "NCR53C815", Burst16, 8, 24, BurstOpCodeFetch }, +{ NCR_825_DID, 0x0f, "NCR53C825", Burst16, 8, 24, Wide|BurstOpCodeFetch|Differential }, +{ NCR_825_DID, 0xff, "SYM53C825A", Burst128, 16, 24, Prefetch|LocalRAM|BigFifo|Differential|Wide }, +{ SYM_860_DID, 0x0f, "SYM53C860", Burst16, 8, 24, Prefetch|Ultra }, +{ SYM_860_DID, 0xff, "SYM53C860LV", Burst16, 8, 24, Prefetch|Ultra }, +{ SYM_875_DID, 0x01, "SYM53C875r1", Burst128, 16, 24, Prefetch|LocalRAM|BigFifo|Differential|Wide|Ultra }, +{ SYM_875_DID, 0xff, "SYM53C875", Burst128, 16, 24, Prefetch|LocalRAM|BigFifo|Differential|Wide|Ultra|ClockDouble }, +{ SYM_875J_DID, 0xff, "SYM53C875j", Burst128, 16, 24, Prefetch|LocalRAM|BigFifo|Differential|Wide|Ultra|ClockDouble }, +{ SYM_885_DID, 0xff, "SYM53C885", Burst128, 16, 24, Prefetch|LocalRAM|BigFifo|Wide|Ultra|ClockDouble }, +{ SYM_895_DID, 0xff, "SYM53C895", Burst128, 16, 24, Prefetch|LocalRAM|BigFifo|Wide|Ultra|Ultra2 }, +{ SYM_896_DID, 0xff, "SYM53C896", Burst128, 16, 64, Prefetch|LocalRAM|BigFifo|Wide|Ultra|Ultra2 }, +{ SYM_1010_DID, 0xff, "SYM53C1010", Burst128, 16, 64, Prefetch|LocalRAM|BigFifo|Wide|Ultra|Ultra2 }, +{ SYM_1011_DID, 0xff, "SYM53C1010", Burst128, 16, 64, Prefetch|LocalRAM|BigFifo|Wide|Ultra|Ultra2 }, +}; + +#define offsetof(s, t) ((ulong)&((s *)0)->t) + +static int +xfunc(Controller *c, enum na_external x, unsigned long *v) +{ + switch (x) + { + case X_scsi_id_buf: + *v = offsetof(Dsa, scsi_id_buf[0]); return 1; + case X_msg_out_buf: + *v = offsetof(Dsa, msg_out_buf); return 1; + case X_cmd_buf: + *v = offsetof(Dsa, cmd_buf); return 1; + case X_data_buf: + *v = offsetof(Dsa, data_buf); return 1; + case X_status_buf: + *v = offsetof(Dsa, status_buf); return 1; + case X_dsa_head: + *v = DMASEG(&c->dsalist.head[0]); return 1; + case X_ssid_mask: + *v = SSIDMASK(c); return 1; + default: + print("xfunc: can't find external %d\n", x); + return 0; + } + return 1; +} + +static int +na_fixup(Controller *c, ulong pa_reg, + struct na_patch *patch, int patches, + int (*externval)(Controller*, int, ulong*)) +{ + int p; + int v; + ulong *script, pa_script; + unsigned long lw, lv; + + script = c->script; + pa_script = c->scriptpa; + for (p = 0; p < patches; p++) { + switch (patch[p].type) { + case 1: + /* script relative */ + script[patch[p].lwoff] += pa_script; + break; + case 2: + /* register i/o relative */ + script[patch[p].lwoff] += pa_reg; + break; + case 3: + /* data external */ + lw = script[patch[p].lwoff]; + v = (lw >> 8) & 0xff; + if (!(*externval)(c, v, &lv)) + return 0; + v = lv & 0xff; + script[patch[p].lwoff] = (lw & 0xffff00ffL) | (v << 8); + break; + case 4: + /* 32 bit external */ + lw = script[patch[p].lwoff]; + if (!(*externval)(c, lw, &lv)) + return 0; + script[patch[p].lwoff] = lv; + break; + case 5: + /* 24 bit external */ + lw = script[patch[p].lwoff]; + if (!(*externval)(c, lw & 0xffffff, &lv)) + return 0; + script[patch[p].lwoff] = (lw & 0xff000000L) | (lv & 0xffffffL); + break; + } + } + return 1; +} + +static SDev* +sd53c8xxpnp(void) +{ + char *cp; + Pcidev *p; + Variant *v; + int ba, nctlr; + void *scriptma; + Controller *ctlr; + SDev *sdev, *head, *tail; + ulong regpa, *script, scriptpa; + + if(cp = getconf("*maxsd53c8xx")) + nctlr = strtoul(cp, 0, 0); + else + nctlr = 32; + + p = nil; + head = tail = nil; + while((p = pcimatch(p, NCR_VID, 0)) != nil && nctlr > 0){ + for(v = variant; v < &variant[nelem(variant)]; v++){ + if(p->did == v->did && p->rid <= v->maxrid) + break; + } + if(v >= &variant[nelem(variant)]) { + print("no match\n"); + continue; + } + print(PRINTPREFIX "%s rev. 0x%2.2x intr=%d command=%4.4uX\n", + v->name, p->rid, p->intl, p->pcr); + + regpa = p->mem[1].bar; + ba = 2; + if(regpa & 0x04){ + if(p->mem[2].bar) + continue; + ba++; + } + regpa = upamalloc(regpa & ~0x0F, p->mem[1].size, 0); + if(regpa == 0) + continue; + + script = nil; + scriptpa = 0; + scriptma = nil; + if((v->feature & LocalRAM) && sizeof(na_script) <= 4096){ + scriptpa = p->mem[ba].bar; + if((scriptpa & 0x04) && p->mem[ba+1].bar){ + upafree(regpa, p->mem[1].size); + continue; + } + scriptpa = upamalloc(scriptpa & ~0x0F, + p->mem[ba].size, 0); + if(scriptpa) + script = KADDR(scriptpa); + } + if(scriptpa == 0){ + /* + * Either the map failed, or this chip does not have + * local RAM. It will need a copy of the microcode. + */ + scriptma = malloc(sizeof(na_script)); + if(scriptma == nil){ + upafree(regpa, p->mem[1].size); + continue; + } + scriptpa = DMASEG(scriptma); + script = scriptma; + } + + ctlr = malloc(sizeof(Controller)); + sdev = malloc(sizeof(SDev)); + if(ctlr == nil || sdev == nil){ +buggery: + if(ctlr) + free(ctlr); + if(sdev) + free(sdev); + if(scriptma) + free(scriptma); + else + upafree(scriptpa, p->mem[ba].size); + upafree(regpa, p->mem[1].size); + continue; + } + + ctlr->n = KADDR(regpa); + ctlr->v = v; + ctlr->script = script; + memmove(ctlr->script, na_script, sizeof(na_script)); + + /* + * Because we don't yet have an abstraction for the + * addresses as seen from the controller side (and on + * the 386 it doesn't matter), the follwong two lines + * are different between the 386 and alpha copies of + * this driver. + */ + ctlr->scriptpa = scriptpa; + if(!na_fixup(ctlr, regpa, na_patches, NA_PATCHES, xfunc)){ + print("script fixup failed\n"); + goto buggery; + } + swabl(ctlr->script, ctlr->script, sizeof(na_script)); + + ctlr->dsalist.freechain = 0; + lesetl(ctlr->dsalist.head, 0); + + ctlr->pcidev = p; + + sdev->ifc = &sd53c8xxifc; + sdev->ctlr = ctlr; + if(!(v->feature & Wide)) + sdev->nunit = 8; + else + sdev->nunit = MAXTARGET; + ctlr->sdev = sdev; + + if(head != nil) + tail->next = sdev; + else + head = sdev; + tail = sdev; + + nctlr--; + } + + return head; +} + +static SDev* +sd53c8xxid(SDev* sdev) +{ + return scsiid(sdev, &sd53c8xxifc); +} + +static int +sd53c8xxenable(SDev* sdev) +{ + Pcidev *pcidev; + Controller *ctlr; + char name[32]; + + ctlr = sdev->ctlr; + pcidev = ctlr->pcidev; + + pcisetbme(pcidev); + snprint(name, sizeof(name), "%s (%s)", sdev->name, sdev->ifc->name); + intrenable(pcidev->intl, sd53c8xxinterrupt, ctlr, pcidev->tbdf, name); + + ilock(ctlr); + synctabinit(ctlr); + cribbios(ctlr); + reset(ctlr); + iunlock(ctlr); + + return 1; +} + +SDifc sd53c8xxifc = { + "53c8xx", /* name */ + + sd53c8xxpnp, /* pnp */ + nil, /* legacy */ + sd53c8xxid, /* id */ + sd53c8xxenable, /* enable */ + nil, /* disable */ + + scsiverify, /* verify */ + scsionline, /* online */ + sd53c8xxrio, /* rio */ + nil, /* rctl */ + nil, /* wctl */ + + scsibio, /* bio */ + nil, /* probe */ + nil, /* clear */ + nil, /* stat */ +}; diff --git a/os/pc/sd53c8xx.i b/os/pc/sd53c8xx.i new file mode 100644 index 00000000..6ef50e02 --- /dev/null +++ b/os/pc/sd53c8xx.i @@ -0,0 +1,773 @@ +unsigned long na_script[] = { + /* extern scsi_id_buf */ + /* extern msg_out_buf */ + /* extern cmd_buf */ + /* extern data_buf */ + /* extern status_buf */ + /* extern msgin_buf */ + /* extern dsa_0 */ + /* extern dsa_1 */ + /* extern dsa_head */ + /* extern ssid_mask */ + /* SIR_MSG_IO_COMPLETE = 0 */ + /* error_not_cmd_complete = 1 */ + /* error_disconnected = 2 */ + /* error_reselected = 3 */ + /* error_unexpected_phase = 4 */ + /* error_weird_message = 5 */ + /* SIR_ERROR_NOT_MSG_IN_AFTER_RESELECT = 6 */ + /* error_not_identify_after_reselect = 7 */ + /* error_too_much_data = 8 */ + /* error_too_little_data = 9 */ + /* SIR_MSG_REJECT = 10 */ + /* SIR_MSG_SDTR = 11 */ + /* SIR_EV_RESPONSE_OK = 12 */ + /* error_sigp_set = 13 */ + /* SIR_EV_PHASE_SWITCH_AFTER_ID = 14 */ + /* SIR_MSG_WDTR = 15 */ + /* SIR_MSG_IGNORE_WIDE_RESIDUE = 16 */ + /* SIR_NOTIFY_DISC = 100 */ + /* SIR_NOTIFY_RESELECT = 101 */ + /* SIR_NOTIFY_MSG_IN = 102 */ + /* SIR_NOTIFY_STATUS = 103 */ + /* SIR_NOTIFY_DUMP = 104 */ + /* SIR_NOTIFY_DUMP2 = 105 */ + /* SIR_NOTIFY_SIGP = 106 */ + /* SIR_NOTIFY_ISSUE = 107 */ + /* SIR_NOTIFY_WAIT_RESELECT = 108 */ + /* SIR_NOTIFY_ISSUE_CHECK = 109 */ + /* SIR_NOTIFY_DUMP_NEXT_CODE = 110 */ + /* SIR_NOTIFY_COMMAND = 111 */ + /* SIR_NOTIFY_DATA_IN = 112 */ + /* SIR_NOTIFY_DATA_OUT = 113 */ + /* SIR_NOTIFY_BLOCK_DATA_IN = 114 */ + /* SIR_NOTIFY_WSR = 115 */ + /* SIR_NOTIFY_LOAD_SYNC = 116 */ + /* SIR_NOTIFY_RESELECTED_ON_SELECT = 117 */ + /* STATE_FREE = 0 */ + /* STATE_ALLOCATED = 1 */ + /* STATE_ISSUE = 2 */ + /* STATE_DISCONNECTED = 3 */ + /* STATE_DONE = 4 */ + /* RESULT_OK = 0 */ + /* MSG_IDENTIFY = 0x80 */ + /* MSG_DISCONNECT = 0x04 */ + /* MSG_SAVE_DATA_POINTER = 0x02 */ + /* MSG_RESTORE_POINTERS = 0x03 */ + /* MSG_IGNORE_WIDE_RESIDUE = 0x23 */ + /* X_MSG = 0x01 */ + /* X_MSG_SDTR = 0x01 */ + /* X_MSG_WDTR = 0x03 */ + /* MSG_REJECT = 0x07 */ + /* BSIZE = 512 */ +/* 0000 */ 0x80880000L, /* jump wait_for_reselection */ +/* 0004 */ 0x00000514L, +/* 0008 */ 0x88880000L, /* call load_sync */ +/* 000c */ 0x0000074cL, +/* 0010 */ 0x60000200L, /* clear target */ +/* 0014 */ 0x00000000L, +/* 0018 */ 0x47000000L, /* select atn from scsi_id_buf, reselected_on_select */ +/* 001c */ 0x000004ecL, +/* 0020 */ 0x878b0000L, /* jump start1, when msg_in */ +/* 0024 */ 0x00000000L, +/* 0028 */ 0x1e000000L, /* move from msg_out_buf, when msg_out */ +/* 002c */ 0x00000001L, +/* 0030 */ 0x868b0000L, /* jump start1, when msg_out */ +/* 0034 */ 0x00fffff0L, +/* 0038 */ 0x82830000L, /* jump to_decisions, when not cmd */ +/* 003c */ 0x000005f0L, +/* 0040 */ 0x60000008L, /* clear atn */ +/* 0044 */ 0x00000000L, +/* 0048 */ 0x1a000000L, /* move from cmd_buf, when cmd */ +/* 004c */ 0x00000002L, +/* 0050 */ 0x81830000L, /* jump to_decisions, when not data_in */ +/* 0054 */ 0x000005d8L, +/* 0058 */ 0xc0000004L, /* move memory 4, state, scratcha */ +/* 005c */ 0x00000678L, +/* 0060 */ 0x00000034L, +/* 0064 */ 0xc0000004L, /* move memory 4, dmaaddr, scratchb */ +/* 0068 */ 0x0000067cL, +/* 006c */ 0x0000005cL, +/* 0070 */ 0x72360000L, /* move scratcha2 to sfbr */ +/* 0074 */ 0x00000000L, +/* 0078 */ 0x808c0000L, /* jump data_in_normal, if 0 */ +/* 007c */ 0x00000078L, +/* 0080 */ 0x29000200L, /* move BSIZE, ptr dmaaddr, when data_in */ +/* 0084 */ 0x0000067cL, +/* 0088 */ 0x7e5d0200L, /* move scratchb1 + BSIZE / 256 to scratchb1 */ +/* 008c */ 0x00000000L, +/* 0090 */ 0x7f5e0000L, /* move scratchb2 + 0 to scratchb2 with carry */ +/* 0094 */ 0x00000000L, +/* 0098 */ 0x7f5f0000L, /* move scratchb3 + 0 to scratchb3 with carry */ +/* 009c */ 0x00000000L, +/* 00a0 */ 0x7e36ff00L, /* move scratcha2 + 255 to scratcha2 */ +/* 00a4 */ 0x00000000L, +/* 00a8 */ 0xc0000004L, /* move memory 4, scratchb, dmaaddr */ +/* 00ac */ 0x0000005cL, +/* 00b0 */ 0x0000067cL, +/* 00b4 */ 0x818b0000L, /* jump data_in_block_loop, when data_in */ +/* 00b8 */ 0x00ffffb4L, +/* 00bc */ 0xc0000004L, /* move memory 4, scratcha, state */ +/* 00c0 */ 0x00000034L, +/* 00c4 */ 0x00000678L, +/* 00c8 */ 0x88880000L, /* call save_state */ +/* 00cc */ 0x000005e0L, +/* 00d0 */ 0x80880000L, /* jump to_decisions */ +/* 00d4 */ 0x00000558L, +/* 00d8 */ 0xc0000004L, /* move memory 4, scratchb, dmaaddr */ +/* 00dc */ 0x0000005cL, +/* 00e0 */ 0x0000067cL, +/* 00e4 */ 0xc0000004L, /* move memory 4, scratcha, state */ +/* 00e8 */ 0x00000034L, +/* 00ec */ 0x00000678L, +/* 00f0 */ 0x80880000L, /* jump to_decisions */ +/* 00f4 */ 0x00000538L, +/* 00f8 */ 0x72370000L, /* move scratcha3 to sfbr */ +/* 00fc */ 0x00000000L, +/* 0100 */ 0x98040000L, /* int error_too_much_data, if not 0 */ +/* 0104 */ 0x00000008L, +/* 0108 */ 0x19000000L, /* move from data_buf, when data_in */ +/* 010c */ 0x00000003L, +/* 0110 */ 0x78370200L, /* move 2 to scratcha3 */ +/* 0114 */ 0x00000000L, +/* 0118 */ 0xc0000004L, /* move memory 4, scratcha, state */ +/* 011c */ 0x00000034L, +/* 0120 */ 0x00000678L, +/* 0124 */ 0x88880000L, /* call save_state */ +/* 0128 */ 0x00000584L, +/* 012c */ 0x80880000L, /* jump post_data_to_decisions */ +/* 0130 */ 0x0000052cL, +/* 0134 */ 0xc0000004L, /* move memory 4, state, scratcha */ +/* 0138 */ 0x00000678L, +/* 013c */ 0x00000034L, +/* 0140 */ 0xc0000004L, /* move memory 4, dmaaddr, scratchb */ +/* 0144 */ 0x0000067cL, +/* 0148 */ 0x0000005cL, +/* 014c */ 0x72360000L, /* move scratcha2 to sfbr */ +/* 0150 */ 0x00000000L, +/* 0154 */ 0x808c0000L, /* jump data_out_normal, if 0 */ +/* 0158 */ 0x0000005cL, +/* 015c */ 0xc0000004L, /* move memory 4, dmaaddr, scratchb */ +/* 0160 */ 0x0000067cL, +/* 0164 */ 0x0000005cL, +/* 0168 */ 0x28000200L, /* move BSIZE, ptr dmaaddr, when data_out */ +/* 016c */ 0x0000067cL, +/* 0170 */ 0x7e5d0200L, /* move scratchb1 + BSIZE / 256 to scratchb1 */ +/* 0174 */ 0x00000000L, +/* 0178 */ 0x7f5e0000L, /* move scratchb2 + 0 to scratchb2 with carry */ +/* 017c */ 0x00000000L, +/* 0180 */ 0x7f5f0000L, /* move scratchb3 + 0 to scratchb3 with carry */ +/* 0184 */ 0x00000000L, +/* 0188 */ 0x7e36ff00L, /* move scratcha2 + 255 to scratcha2 */ +/* 018c */ 0x00000000L, +/* 0190 */ 0xc0000004L, /* move memory 4, scratchb, dmaaddr */ +/* 0194 */ 0x0000005cL, +/* 0198 */ 0x0000067cL, +/* 019c */ 0x808b0000L, /* jump data_out_block_loop, when data_out */ +/* 01a0 */ 0x00ffffa8L, +/* 01a4 */ 0xc0000004L, /* move memory 4, scratcha, state */ +/* 01a8 */ 0x00000034L, +/* 01ac */ 0x00000678L, +/* 01b0 */ 0x80880000L, /* jump to_decisions */ +/* 01b4 */ 0x00000478L, +/* 01b8 */ 0x72370000L, /* move scratcha3 to sfbr */ +/* 01bc */ 0x00000000L, +/* 01c0 */ 0x98040000L, /* int error_too_little_data, if not 0 */ +/* 01c4 */ 0x00000009L, +/* 01c8 */ 0x18000000L, /* move from data_buf, when data_out */ +/* 01cc */ 0x00000003L, +/* 01d0 */ 0x78370200L, /* move 2 to scratcha3 */ +/* 01d4 */ 0x00000000L, +/* 01d8 */ 0xc0000004L, /* move memory 4, scratcha, state */ +/* 01dc */ 0x00000034L, +/* 01e0 */ 0x00000678L, +/* 01e4 */ 0x88880000L, /* call save_state */ +/* 01e8 */ 0x000004c4L, +/* 01ec */ 0x80880000L, /* jump post_data_to_decisions */ +/* 01f0 */ 0x0000046cL, +/* 01f4 */ 0x1b000000L, /* move from status_buf, when status */ +/* 01f8 */ 0x00000004L, +/* 01fc */ 0x9f030000L, /* int error_unexpected_phase, when not msg_in */ +/* 0200 */ 0x00000004L, +/* 0204 */ 0x0f000001L, /* move 1, scratcha, when msg_in */ +/* 0208 */ 0x00000034L, +/* 020c */ 0x808c0007L, /* jump rejected, if MSG_REJECT */ +/* 0210 */ 0x00000088L, +/* 0214 */ 0x808c0004L, /* jump disconnected, if MSG_DISCONNECT */ +/* 0218 */ 0x00000298L, +/* 021c */ 0x808c0002L, /* jump msg_in_skip, if MSG_SAVE_DATA_POINTER */ +/* 0220 */ 0x00000090L, +/* 0224 */ 0x808c0003L, /* jump msg_in_skip, if MSG_RESTORE_POINTERS */ +/* 0228 */ 0x00000088L, +/* 022c */ 0x808c0023L, /* jump ignore_wide, if MSG_IGNORE_WIDE_RESIDUE */ +/* 0230 */ 0x000001f0L, +/* 0234 */ 0x808c0001L, /* jump extended, if X_MSG */ +/* 0238 */ 0x00000088L, +/* 023c */ 0x98040000L, /* int error_not_cmd_complete, if not 0 */ +/* 0240 */ 0x00000001L, +/* 0244 */ 0x7c027e00L, /* move scntl2&0x7e to scntl2 */ +/* 0248 */ 0x00000000L, +/* 024c */ 0x60000040L, /* clear ack */ +/* 0250 */ 0x00000000L, +/* 0254 */ 0x48000000L, /* wait disconnect */ +/* 0258 */ 0x00000000L, +/* 025c */ 0xc0000004L, /* move memory 4, state, scratcha */ +/* 0260 */ 0x00000678L, +/* 0264 */ 0x00000034L, +/* 0268 */ 0x78340400L, /* move STATE_DONE to scratcha0 */ +/* 026c */ 0x00000000L, +/* 0270 */ 0x78350000L, /* move RESULT_OK to scratcha1 */ +/* 0274 */ 0x00000000L, +/* 0278 */ 0xc0000004L, /* move memory 4, scratcha, state */ +/* 027c */ 0x00000034L, +/* 0280 */ 0x00000678L, +/* 0284 */ 0x88880000L, /* call save_state */ +/* 0288 */ 0x00000424L, +/* 028c */ 0x98180000L, /* intfly 0 */ +/* 0290 */ 0x00000000L, +/* 0294 */ 0x80880000L, /* jump issue_check */ +/* 0298 */ 0x0000043cL, +/* 029c */ 0x98080000L, /* int SIR_MSG_REJECT */ +/* 02a0 */ 0x0000000aL, +/* 02a4 */ 0x60000040L, /* clear ack */ +/* 02a8 */ 0x00000000L, +/* 02ac */ 0x80880000L, /* jump to_decisions */ +/* 02b0 */ 0x0000037cL, +/* 02b4 */ 0x60000040L, /* clear ack */ +/* 02b8 */ 0x00000000L, +/* 02bc */ 0x80880000L, /* jump to_decisions */ +/* 02c0 */ 0x0000036cL, +/* 02c4 */ 0x60000040L, /* clear ack */ +/* 02c8 */ 0x00000000L, +/* 02cc */ 0x9f030000L, /* int error_unexpected_phase, when not msg_in */ +/* 02d0 */ 0x00000004L, +/* 02d4 */ 0x0f000001L, /* move 1, scratcha1, when msg_in */ +/* 02d8 */ 0x00000035L, +/* 02dc */ 0x808c0003L, /* jump ext_3, if 3 */ +/* 02e0 */ 0x00000030L, +/* 02e4 */ 0x808c0002L, /* jump ext_2, if 2 */ +/* 02e8 */ 0x00000098L, +/* 02ec */ 0x98040001L, /* int error_weird_message, if not 1 */ +/* 02f0 */ 0x00000005L, +/* 02f4 */ 0x60000040L, /* clear ack */ +/* 02f8 */ 0x00000000L, +/* 02fc */ 0x9f030000L, /* int error_unexpected_phase, when not msg_in */ +/* 0300 */ 0x00000004L, +/* 0304 */ 0x0f000001L, /* move 1, scratcha1, when msg_in */ +/* 0308 */ 0x00000035L, +/* 030c */ 0x80880000L, /* jump ext_done */ +/* 0310 */ 0x000000c8L, +/* 0314 */ 0x60000040L, /* ext_3: clear ack */ +/* 0318 */ 0x00000000L, +/* 031c */ 0x9f030000L, /* int error_unexpected_phase, when not msg_in */ +/* 0320 */ 0x00000004L, +/* 0324 */ 0x0f000001L, /* move 1, scratcha1, when msg_in */ +/* 0328 */ 0x00000035L, +/* 032c */ 0x60000040L, /* clear ack */ +/* 0330 */ 0x00000000L, +/* 0334 */ 0x9f030000L, /* int error_unexpected_phase, when not msg_in */ +/* 0338 */ 0x00000004L, +/* 033c */ 0x0f000001L, /* move 1, scratcha2, when msg_in */ +/* 0340 */ 0x00000036L, +/* 0344 */ 0x60000040L, /* clear ack */ +/* 0348 */ 0x00000000L, +/* 034c */ 0x9f030000L, /* int error_unexpected_phase, when not msg_in */ +/* 0350 */ 0x00000004L, +/* 0354 */ 0x0f000001L, /* move 1, scratcha3, when msg_in */ +/* 0358 */ 0x00000037L, +/* 035c */ 0x72350000L, /* move scratcha1 to sfbr */ +/* 0360 */ 0x00000000L, +/* 0364 */ 0x80840001L, /* jump ext_done, if not X_MSG_SDTR */ +/* 0368 */ 0x00000070L, +/* 036c */ 0x98080000L, /* sdtr: int SIR_MSG_SDTR */ +/* 0370 */ 0x0000000bL, +/* 0374 */ 0x60000040L, /* clear ack */ +/* 0378 */ 0x00000000L, +/* 037c */ 0x80880000L, /* jump to_decisions */ +/* 0380 */ 0x000002acL, +/* 0384 */ 0x60000040L, /* ext_2: clear ack */ +/* 0388 */ 0x00000000L, +/* 038c */ 0x9f030000L, /* int error_unexpected_phase, when not msg_in */ +/* 0390 */ 0x00000004L, +/* 0394 */ 0x0f000001L, /* move 1, scratcha1, when msg_in */ +/* 0398 */ 0x00000035L, +/* 039c */ 0x60000040L, /* clear ack */ +/* 03a0 */ 0x00000000L, +/* 03a4 */ 0x9f030000L, /* int error_unexpected_phase, when not msg_in */ +/* 03a8 */ 0x00000004L, +/* 03ac */ 0x0f000001L, /* move 1, scratcha2, when msg_in */ +/* 03b0 */ 0x00000036L, +/* 03b4 */ 0x72350000L, /* move scratcha1 to sfbr */ +/* 03b8 */ 0x00000000L, +/* 03bc */ 0x80840003L, /* jump ext_done, if not X_MSG_WDTR */ +/* 03c0 */ 0x00000018L, +/* 03c4 */ 0x98080000L, /* wdtr: int SIR_MSG_WDTR */ +/* 03c8 */ 0x0000000fL, +/* 03cc */ 0x60000040L, /* clear ack */ +/* 03d0 */ 0x00000000L, +/* 03d4 */ 0x80880000L, /* jump to_decisions */ +/* 03d8 */ 0x00000254L, +/* 03dc */ 0x58000008L, /* set atn */ +/* 03e0 */ 0x00000000L, +/* 03e4 */ 0x60000040L, /* clear ack */ +/* 03e8 */ 0x00000000L, +/* 03ec */ 0x78340700L, /* move MSG_REJECT to scratcha */ +/* 03f0 */ 0x00000000L, +/* 03f4 */ 0x9e030000L, /* int error_unexpected_phase, when not msg_out */ +/* 03f8 */ 0x00000004L, +/* 03fc */ 0x60000008L, /* clear atn */ +/* 0400 */ 0x00000000L, +/* 0404 */ 0x0e000001L, /* move 1, scratcha, when msg_out */ +/* 0408 */ 0x00000034L, +/* 040c */ 0x60000040L, /* clear ack */ +/* 0410 */ 0x00000000L, +/* 0414 */ 0x868b0000L, /* jump reject, when msg_out */ +/* 0418 */ 0x00ffffc0L, +/* 041c */ 0x80880000L, /* jump to_decisions */ +/* 0420 */ 0x0000020cL, +/* 0424 */ 0x60000040L, /* clear ack */ +/* 0428 */ 0x00000000L, +/* 042c */ 0x9f030000L, /* int error_unexpected_phase, when not msg_in */ +/* 0430 */ 0x00000004L, +/* 0434 */ 0x0f000001L, /* move 1, scratcha1, when msg_in */ +/* 0438 */ 0x00000035L, +/* 043c */ 0x98080000L, /* int SIR_MSG_IGNORE_WIDE_RESIDUE */ +/* 0440 */ 0x00000010L, +/* 0444 */ 0x60000040L, /* clear ack */ +/* 0448 */ 0x00000000L, +/* 044c */ 0x80880000L, /* jump to_decisions */ +/* 0450 */ 0x000001dcL, +/* 0454 */ 0x58000008L, /* set atn */ +/* 0458 */ 0x00000000L, +/* 045c */ 0x60000040L, /* clear ack */ +/* 0460 */ 0x00000000L, +/* 0464 */ 0x9e030000L, /* int error_unexpected_phase, when not msg_out */ +/* 0468 */ 0x00000004L, +/* 046c */ 0x1e000000L, /* move from msg_out_buf, when msg_out */ +/* 0470 */ 0x00000001L, +/* 0474 */ 0x868b0000L, /* jump response_repeat, when msg_out */ +/* 0478 */ 0x00fffff0L, +/* 047c */ 0x878b0000L, /* jump response_msg_in, when msg_in */ +/* 0480 */ 0x00000010L, +/* 0484 */ 0x98080000L, /* int SIR_EV_RESPONSE_OK */ +/* 0488 */ 0x0000000cL, +/* 048c */ 0x80880000L, /* jump to_decisions */ +/* 0490 */ 0x0000019cL, +/* 0494 */ 0x0f000001L, /* move 1, scratcha, when msg_in */ +/* 0498 */ 0x00000034L, +/* 049c */ 0x808c0007L, /* jump rejected, if MSG_REJECT */ +/* 04a0 */ 0x00fffdf8L, +/* 04a4 */ 0x98080000L, /* int SIR_EV_RESPONSE_OK */ +/* 04a8 */ 0x0000000cL, +/* 04ac */ 0x80880000L, /* jump msg_in_not_reject */ +/* 04b0 */ 0x00fffd60L, +/* 04b4 */ 0x7c027e00L, /* move scntl2&0x7e to scntl2 */ +/* 04b8 */ 0x00000000L, +/* 04bc */ 0x60000040L, /* clear ack */ +/* 04c0 */ 0x00000000L, +/* 04c4 */ 0x48000000L, /* wait disconnect */ +/* 04c8 */ 0x00000000L, +/* 04cc */ 0xc0000004L, /* move memory 4, state, scratcha */ +/* 04d0 */ 0x00000678L, +/* 04d4 */ 0x00000034L, +/* 04d8 */ 0x78340300L, /* move STATE_DISCONNECTED to scratcha0 */ +/* 04dc */ 0x00000000L, +/* 04e0 */ 0xc0000004L, /* move memory 4, scratcha, state */ +/* 04e4 */ 0x00000034L, +/* 04e8 */ 0x00000678L, +/* 04ec */ 0x88880000L, /* call save_state */ +/* 04f0 */ 0x000001bcL, +/* 04f4 */ 0x74020100L, /* move scntl2&0x01 to sfbr */ +/* 04f8 */ 0x00000000L, +/* 04fc */ 0x98040000L, /* int SIR_NOTIFY_WSR, if not 0 */ +/* 0500 */ 0x00000073L, +/* 0504 */ 0x80880000L, /* jump issue_check */ +/* 0508 */ 0x000001ccL, +/* 050c */ 0x98080000L, /* int SIR_NOTIFY_RESELECTED_ON_SELECT */ +/* 0510 */ 0x00000075L, +/* 0514 */ 0x80880000L, /* jump reselected */ +/* 0518 */ 0x00000008L, +/* 051c */ 0x54000000L, /* wait reselect sigp_set */ +/* 0520 */ 0x000001acL, +/* 0524 */ 0x60000200L, /* clear target */ +/* 0528 */ 0x00000000L, +/* 052c */ 0x9f030000L, /* int SIR_ERROR_NOT_MSG_IN_AFTER_RESELECT, when not msg_in */ +/* 0530 */ 0x00000006L, +/* 0534 */ 0x0f000001L, /* move 1, scratchb, when msg_in */ +/* 0538 */ 0x0000005cL, +/* 053c */ 0x98041f80L, /* int error_not_identify_after_reselect, if not MSG_IDENTIFY and mask 0x1f */ +/* 0540 */ 0x00000007L, +/* 0544 */ 0xc0000004L, /* move memory 4, dsa_head, dsa */ +/* 0548 */ 0x00000008L, +/* 054c */ 0x00000010L, +/* 0550 */ 0x72100000L, /* move dsa0 to sfbr */ +/* 0554 */ 0x00000000L, +/* 0558 */ 0x80840000L, /* jump find_dsa_1, if not 0 */ +/* 055c */ 0x00000030L, +/* 0560 */ 0x72110000L, /* move dsa1 to sfbr */ +/* 0564 */ 0x00000000L, +/* 0568 */ 0x80840000L, /* jump find_dsa_1, if not 0 */ +/* 056c */ 0x00000020L, +/* 0570 */ 0x72120000L, /* move dsa2 to sfbr */ +/* 0574 */ 0x00000000L, +/* 0578 */ 0x80840000L, /* jump find_dsa_1, if not 0 */ +/* 057c */ 0x00000010L, +/* 0580 */ 0x72130000L, /* move dsa3 to sfbr */ +/* 0584 */ 0x00000000L, +/* 0588 */ 0x980c0000L, /* int error_reselected, if 0 */ +/* 058c */ 0x00000003L, +/* 0590 */ 0x88880000L, /* call load_state */ +/* 0594 */ 0x000000f8L, +/* 0598 */ 0xc0000004L, /* move memory 4, state, scratcha */ +/* 059c */ 0x00000678L, +/* 05a0 */ 0x00000034L, +/* 05a4 */ 0x72340000L, /* move scratcha0 to sfbr */ +/* 05a8 */ 0x00000000L, +/* 05ac */ 0x80840003L, /* jump find_dsa_next, if not STATE_DISCONNECTED */ +/* 05b0 */ 0x00000038L, +/* 05b4 */ 0x740a0900L, /* move ssid & ssid_mask to sfbr */ +/* 05b8 */ 0x00000000L, +/* 05bc */ 0xc0000001L, /* move memory 1, targ, find_dsa_smc1 */ +/* 05c0 */ 0x00000680L, +/* 05c4 */ 0x000005c8L, +/* 05c8 */ 0x808400ffL, /* jump find_dsa_next, if not 255 */ +/* 05cc */ 0x0000001cL, +/* 05d0 */ 0xc0000001L, /* move memory 1, lun, find_dsa_smc2 */ +/* 05d4 */ 0x00000684L, +/* 05d8 */ 0x000005e4L, +/* 05dc */ 0x725c0000L, /* move scratchb0 to sfbr */ +/* 05e0 */ 0x00000000L, +/* 05e4 */ 0x808cf8ffL, /* jump reload_sync, if 255 and mask ~7 */ +/* 05e8 */ 0x00000034L, +/* 05ec */ 0xc0000004L, /* move memory 4, next, dsa */ +/* 05f0 */ 0x0000068cL, +/* 05f4 */ 0x00000010L, +/* 05f8 */ 0x80880000L, /* jump find_dsa_loop */ +/* 05fc */ 0x00ffff50L, +/* 0600 */ 0x60000008L, /* clear atn */ +/* 0604 */ 0x00000000L, +/* 0608 */ 0x878b0000L, /* jump msg_in_phase, when msg_in */ +/* 060c */ 0x00fffbf4L, +/* 0610 */ 0x98080000L, /* int SIR_MSG_REJECT */ +/* 0614 */ 0x0000000aL, +/* 0618 */ 0x80880000L, /* jump to_decisions */ +/* 061c */ 0x00000010L, +/* 0620 */ 0x88880000L, /* call load_sync */ +/* 0624 */ 0x00000134L, +/* 0628 */ 0x60000040L, /* clear ack */ +/* 062c */ 0x00000000L, +/* 0630 */ 0x818b0000L, /* jump data_in_phase, when data_in */ +/* 0634 */ 0x00fffa20L, +/* 0638 */ 0x828a0000L, /* jump cmd_phase, if cmd */ +/* 063c */ 0x00fffa00L, +/* 0640 */ 0x808a0000L, /* jump data_out_phase, if data_out */ +/* 0644 */ 0x00fffaecL, +/* 0648 */ 0x838a0000L, /* jump status_phase, if status */ +/* 064c */ 0x00fffba4L, +/* 0650 */ 0x878a0000L, /* jump msg_in_phase, if msg_in */ +/* 0654 */ 0x00fffbacL, +/* 0658 */ 0x98080000L, /* int error_unexpected_phase */ +/* 065c */ 0x00000004L, +/* 0660 */ 0x838b0000L, /* jump status_phase, when status */ +/* 0664 */ 0x00fffb8cL, +/* 0668 */ 0x878a0000L, /* jump msg_in_phase, if msg_in */ +/* 066c */ 0x00fffb94L, +/* 0670 */ 0x98080000L, /* int error_unexpected_phase */ +/* 0674 */ 0x00000004L, +/* 0678 */ 0x00000000L, /* state: defw 0 */ +/* 067c */ 0x00000000L, /* dmaaddr: defw 0 */ +/* 0680 */ 0x00000000L, /* targ: defw 0 */ +/* 0684 */ 0x00000000L, /* lun: defw 0 */ +/* 0688 */ 0x00000000L, /* sync: defw 0 */ +/* 068c */ 0x00000000L, /* next: defw 0 */ + /* dsa_load_len = dsa_load_end - dsa_copy */ + /* dsa_save_len = dsa_save_end - dsa_copy */ +/* 0690 */ 0xc0000004L, /* move memory 4, dsa, load_state_smc0 + 4 */ +/* 0694 */ 0x00000010L, +/* 0698 */ 0x000006a0L, +/* 069c */ 0xc0000018L, /* move memory dsa_load_len, 0, dsa_copy */ +/* 06a0 */ 0x00000000L, +/* 06a4 */ 0x00000678L, +/* 06a8 */ 0x90080000L, /* return */ +/* 06ac */ 0x00000000L, +/* 06b0 */ 0xc0000004L, /* move memory 4, dsa, save_state_smc0 + 8 */ +/* 06b4 */ 0x00000010L, +/* 06b8 */ 0x000006c4L, +/* 06bc */ 0xc0000008L, /* move memory dsa_save_len, dsa_copy, 0 */ +/* 06c0 */ 0x00000678L, +/* 06c4 */ 0x00000000L, +/* 06c8 */ 0x90080000L, /* return */ +/* 06cc */ 0x00000000L, +/* 06d0 */ 0x721a0000L, /* move ctest2 to sfbr */ +/* 06d4 */ 0x00000000L, +/* 06d8 */ 0xc0000004L, /* move memory 4, dsa_head, dsa */ +/* 06dc */ 0x00000008L, +/* 06e0 */ 0x00000010L, +/* 06e4 */ 0x72100000L, /* move dsa0 to sfbr */ +/* 06e8 */ 0x00000000L, +/* 06ec */ 0x80840000L, /* jump issue_check_1, if not 0 */ +/* 06f0 */ 0x00000030L, +/* 06f4 */ 0x72110000L, /* move dsa1 to sfbr */ +/* 06f8 */ 0x00000000L, +/* 06fc */ 0x80840000L, /* jump issue_check_1, if not 0 */ +/* 0700 */ 0x00000020L, +/* 0704 */ 0x72120000L, /* move dsa2 to sfbr */ +/* 0708 */ 0x00000000L, +/* 070c */ 0x80840000L, /* jump issue_check_1, if not 0 */ +/* 0710 */ 0x00000010L, +/* 0714 */ 0x72130000L, /* move dsa3 to sfbr */ +/* 0718 */ 0x00000000L, +/* 071c */ 0x808c0000L, /* jump wait_for_reselection, if 0 */ +/* 0720 */ 0x00fffdf8L, +/* 0724 */ 0x88880000L, /* call load_state */ +/* 0728 */ 0x00ffff64L, +/* 072c */ 0xc0000004L, /* move memory 4, state, scratcha */ +/* 0730 */ 0x00000678L, +/* 0734 */ 0x00000034L, +/* 0738 */ 0x72340000L, /* move scratcha0 to sfbr */ +/* 073c */ 0x00000000L, +/* 0740 */ 0x808c0002L, /* jump start, if STATE_ISSUE */ +/* 0744 */ 0x00fff8c0L, +/* 0748 */ 0xc0000004L, /* move memory 4, next, dsa */ +/* 074c */ 0x0000068cL, +/* 0750 */ 0x00000010L, +/* 0754 */ 0x80880000L, /* jump issue_check_loop */ +/* 0758 */ 0x00ffff88L, +/* 075c */ 0xc0000004L, /* move memory 4, sync, scratcha */ +/* 0760 */ 0x00000688L, +/* 0764 */ 0x00000034L, +/* 0768 */ 0x72340000L, /* move scratcha0 to sfbr */ +/* 076c */ 0x00000000L, +/* 0770 */ 0x6a030000L, /* move sfbr to scntl3 */ +/* 0774 */ 0x00000000L, +/* 0778 */ 0x72350000L, /* move scratcha1 to sfbr */ +/* 077c */ 0x00000000L, +/* 0780 */ 0x6a050000L, /* move sfbr to sxfer */ +/* 0784 */ 0x00000000L, +/* 0788 */ 0x90080000L, /* return */ +/* 078c */ 0x00000000L, +}; + +#define NA_SCRIPT_SIZE 484 + +struct na_patch na_patches[] = { + { 0x0006, 5 }, /* 00000018 */ + { 0x000b, 4 }, /* 0000002c */ + { 0x0013, 4 }, /* 0000004c */ + { 0x0017, 1 }, /* 0000005c */ + { 0x0018, 2 }, /* 00000060 */ + { 0x001a, 1 }, /* 00000068 */ + { 0x001b, 2 }, /* 0000006c */ + { 0x0021, 1 }, /* 00000084 */ + { 0x002b, 2 }, /* 000000ac */ + { 0x002c, 1 }, /* 000000b0 */ + { 0x0030, 2 }, /* 000000c0 */ + { 0x0031, 1 }, /* 000000c4 */ + { 0x0037, 2 }, /* 000000dc */ + { 0x0038, 1 }, /* 000000e0 */ + { 0x003a, 2 }, /* 000000e8 */ + { 0x003b, 1 }, /* 000000ec */ + { 0x0043, 4 }, /* 0000010c */ + { 0x0047, 2 }, /* 0000011c */ + { 0x0048, 1 }, /* 00000120 */ + { 0x004e, 1 }, /* 00000138 */ + { 0x004f, 2 }, /* 0000013c */ + { 0x0051, 1 }, /* 00000144 */ + { 0x0052, 2 }, /* 00000148 */ + { 0x0058, 1 }, /* 00000160 */ + { 0x0059, 2 }, /* 00000164 */ + { 0x005b, 1 }, /* 0000016c */ + { 0x0065, 2 }, /* 00000194 */ + { 0x0066, 1 }, /* 00000198 */ + { 0x006a, 2 }, /* 000001a8 */ + { 0x006b, 1 }, /* 000001ac */ + { 0x0073, 4 }, /* 000001cc */ + { 0x0077, 2 }, /* 000001dc */ + { 0x0078, 1 }, /* 000001e0 */ + { 0x007e, 4 }, /* 000001f8 */ + { 0x0082, 2 }, /* 00000208 */ + { 0x0098, 1 }, /* 00000260 */ + { 0x0099, 2 }, /* 00000264 */ + { 0x009f, 2 }, /* 0000027c */ + { 0x00a0, 1 }, /* 00000280 */ + { 0x00b6, 2 }, /* 000002d8 */ + { 0x00c2, 2 }, /* 00000308 */ + { 0x00ca, 2 }, /* 00000328 */ + { 0x00d0, 2 }, /* 00000340 */ + { 0x00d6, 2 }, /* 00000358 */ + { 0x00e6, 2 }, /* 00000398 */ + { 0x00ec, 2 }, /* 000003b0 */ + { 0x0102, 2 }, /* 00000408 */ + { 0x010e, 2 }, /* 00000438 */ + { 0x011c, 4 }, /* 00000470 */ + { 0x0126, 2 }, /* 00000498 */ + { 0x0134, 1 }, /* 000004d0 */ + { 0x0135, 2 }, /* 000004d4 */ + { 0x0139, 2 }, /* 000004e4 */ + { 0x013a, 1 }, /* 000004e8 */ + { 0x014e, 2 }, /* 00000538 */ + { 0x0152, 4 }, /* 00000548 */ + { 0x0153, 2 }, /* 0000054c */ + { 0x0167, 1 }, /* 0000059c */ + { 0x0168, 2 }, /* 000005a0 */ + { 0x016d, 3 }, /* 000005b4 */ + { 0x0170, 1 }, /* 000005c0 */ + { 0x0171, 1 }, /* 000005c4 */ + { 0x0175, 1 }, /* 000005d4 */ + { 0x0176, 1 }, /* 000005d8 */ + { 0x017c, 1 }, /* 000005f0 */ + { 0x017d, 2 }, /* 000005f4 */ + { 0x01a5, 2 }, /* 00000694 */ + { 0x01a6, 1 }, /* 00000698 */ + { 0x01a9, 1 }, /* 000006a4 */ + { 0x01ad, 2 }, /* 000006b4 */ + { 0x01ae, 1 }, /* 000006b8 */ + { 0x01b0, 1 }, /* 000006c0 */ + { 0x01b7, 4 }, /* 000006dc */ + { 0x01b8, 2 }, /* 000006e0 */ + { 0x01cc, 1 }, /* 00000730 */ + { 0x01cd, 2 }, /* 00000734 */ + { 0x01d3, 1 }, /* 0000074c */ + { 0x01d4, 2 }, /* 00000750 */ + { 0x01d8, 1 }, /* 00000760 */ + { 0x01d9, 2 }, /* 00000764 */ +}; +#define NA_PATCHES 80 + +enum na_external { + X_scsi_id_buf, + X_msg_out_buf, + X_cmd_buf, + X_data_buf, + X_status_buf, + X_msgin_buf, + X_dsa_0, + X_dsa_1, + X_dsa_head, + X_ssid_mask, +}; + +enum { + E_issue_check_next = 1864, + E_issue_check_1 = 1828, + E_issue_check_loop = 1764, + E_save_state_smc0 = 1724, + E_load_state_smc0 = 1692, + E_dsa_load_end = 1680, + E_sync = 1672, + E_dsa_save_end = 1664, + E_dsa_copy = 1656, + E_id_out_mismatch_recover = 1536, + E_next = 1676, + E_reload_sync = 1568, + E_find_dsa_smc2 = 1508, + E_lun = 1668, + E_find_dsa_smc1 = 1480, + E_targ = 1664, + E_find_dsa_next = 1516, + E_load_state = 1680, + E_find_dsa_1 = 1424, + E_find_dsa_loop = 1360, + E_find_dsa = 1348, + E_sigp_set = 1744, + E_reselected = 1316, + E_wsr_check = 1268, + E_response_msg_in = 1172, + E_response_repeat = 1132, + E_response = 1108, + E_reject = 988, + E_wdtr = 964, + E_sdtr = 876, + E_ext_done = 988, + E_ext_1 = 756, + E_ext_2 = 900, + E_ext_3 = 788, + E_issue_check = 1752, + E_extended = 708, + E_ignore_wide = 1060, + E_msg_in_skip = 692, + E_disconnected = 1204, + E_msg_in_not_reject = 532, + E_rejected = 668, + E_msg_in_phase = 516, + E_status_phase = 500, + E_data_out_mismatch = 464, + E_data_out_block_mismatch = 368, + E_data_out_normal = 440, + E_data_out_block_loop = 332, + E_data_out_phase = 308, + E_post_data_to_decisions = 1632, + E_data_in_mismatch = 272, + E_data_mismatch_recover = 228, + E_data_block_mismatch_recover = 216, + E_save_state = 1712, + E_data_in_block_mismatch = 136, + E_data_in_normal = 248, + E_data_in_block_loop = 112, + E_dmaaddr = 1660, + E_state = 1656, + E_data_in_phase = 88, + E_cmd_out_mismatch = 80, + E_cmd_phase = 64, + E_to_decisions = 1584, + E_id_out_mismatch = 48, + E_start1 = 40, + E_reselected_on_select = 1292, + E_load_sync = 1884, + E_start = 8, + E_wait_for_reselection = 1308, + E_idle = 0, +}; +#define A_dsa_save_len 8 +#define A_dsa_load_len 24 +#define A_BSIZE 512 +#define A_MSG_REJECT 7 +#define A_X_MSG_WDTR 3 +#define A_X_MSG_SDTR 1 +#define A_X_MSG 1 +#define A_MSG_IGNORE_WIDE_RESIDUE 35 +#define A_MSG_RESTORE_POINTERS 3 +#define A_MSG_SAVE_DATA_POINTER 2 +#define A_MSG_DISCONNECT 4 +#define A_MSG_IDENTIFY 128 +#define A_RESULT_OK 0 +#define A_STATE_DONE 4 +#define A_STATE_DISCONNECTED 3 +#define A_STATE_ISSUE 2 +#define A_STATE_ALLOCATED 1 +#define A_STATE_FREE 0 +#define A_SIR_NOTIFY_RESELECTED_ON_SELECT 117 +#define A_SIR_NOTIFY_LOAD_SYNC 116 +#define A_SIR_NOTIFY_WSR 115 +#define A_SIR_NOTIFY_BLOCK_DATA_IN 114 +#define A_SIR_NOTIFY_DATA_OUT 113 +#define A_SIR_NOTIFY_DATA_IN 112 +#define A_SIR_NOTIFY_COMMAND 111 +#define A_SIR_NOTIFY_DUMP_NEXT_CODE 110 +#define A_SIR_NOTIFY_ISSUE_CHECK 109 +#define A_SIR_NOTIFY_WAIT_RESELECT 108 +#define A_SIR_NOTIFY_ISSUE 107 +#define A_SIR_NOTIFY_SIGP 106 +#define A_SIR_NOTIFY_DUMP2 105 +#define A_SIR_NOTIFY_DUMP 104 +#define A_SIR_NOTIFY_STATUS 103 +#define A_SIR_NOTIFY_MSG_IN 102 +#define A_SIR_NOTIFY_RESELECT 101 +#define A_SIR_NOTIFY_DISC 100 +#define A_SIR_MSG_IGNORE_WIDE_RESIDUE 16 +#define A_SIR_MSG_WDTR 15 +#define A_SIR_EV_PHASE_SWITCH_AFTER_ID 14 +#define A_error_sigp_set 13 +#define A_SIR_EV_RESPONSE_OK 12 +#define A_SIR_MSG_SDTR 11 +#define A_SIR_MSG_REJECT 10 +#define A_error_too_little_data 9 +#define A_error_too_much_data 8 +#define A_error_not_identify_after_reselect 7 +#define A_SIR_ERROR_NOT_MSG_IN_AFTER_RESELECT 6 +#define A_error_weird_message 5 +#define A_error_unexpected_phase 4 +#define A_error_reselected 3 +#define A_error_disconnected 2 +#define A_error_not_cmd_complete 1 +#define A_SIR_MSG_IO_COMPLETE 0 diff --git a/os/pc/sd53c8xx.n b/os/pc/sd53c8xx.n new file mode 100644 index 00000000..d9dc2fa1 --- /dev/null +++ b/os/pc/sd53c8xx.n @@ -0,0 +1,448 @@ +// NCR 53c8xx driver for Plan 9 +// Nigel Roles (nigel@9fs.org) +// +// Microcode +// +// 27/5/02 Fixed problems with transfers >= 256 * 512 +// +// 13/3/01 Fixed microcode to support targets > 7 +// + +extern scsi_id_buf +extern msg_out_buf +extern cmd_buf +extern data_buf +extern status_buf +extern msgin_buf +extern dsa_0 +extern dsa_1 +extern dsa_head +extern ssid_mask + +SIR_MSG_IO_COMPLETE = 0 +error_not_cmd_complete = 1 +error_disconnected = 2 +error_reselected = 3 +error_unexpected_phase = 4 +error_weird_message = 5 +SIR_ERROR_NOT_MSG_IN_AFTER_RESELECT = 6 +error_not_identify_after_reselect = 7 +error_too_much_data = 8 +error_too_little_data = 9 +SIR_MSG_REJECT = 10 +SIR_MSG_SDTR = 11 +SIR_EV_RESPONSE_OK = 12 +error_sigp_set = 13 +SIR_EV_PHASE_SWITCH_AFTER_ID = 14 +SIR_MSG_WDTR = 15 +SIR_MSG_IGNORE_WIDE_RESIDUE = 16 +SIR_NOTIFY_DISC = 100 +SIR_NOTIFY_RESELECT = 101 +SIR_NOTIFY_MSG_IN = 102 +SIR_NOTIFY_STATUS = 103 +SIR_NOTIFY_DUMP = 104 +SIR_NOTIFY_DUMP2 = 105 +SIR_NOTIFY_SIGP = 106 +SIR_NOTIFY_ISSUE = 107 +SIR_NOTIFY_WAIT_RESELECT = 108 +SIR_NOTIFY_ISSUE_CHECK = 109 +SIR_NOTIFY_DUMP_NEXT_CODE = 110 +SIR_NOTIFY_COMMAND = 111 +SIR_NOTIFY_DATA_IN = 112 +SIR_NOTIFY_DATA_OUT = 113 +SIR_NOTIFY_BLOCK_DATA_IN = 114 +SIR_NOTIFY_WSR = 115 +SIR_NOTIFY_LOAD_SYNC = 116 +SIR_NOTIFY_RESELECTED_ON_SELECT = 117 + +STATE_FREE = 0 +STATE_ALLOCATED = 1 +STATE_ISSUE = 2 +STATE_DISCONNECTED = 3 +STATE_DONE = 4 + +RESULT_OK = 0 + +MSG_IDENTIFY = 0x80 +MSG_DISCONNECT = 0x04 +MSG_SAVE_DATA_POINTER = 0x02 +MSG_RESTORE_POINTERS = 0x03 +MSG_IGNORE_WIDE_RESIDUE = 0x23 +X_MSG = 0x01 +X_MSG_SDTR = 0x01 +X_MSG_WDTR = 0x03 +MSG_REJECT = 0x07 + +BSIZE = 512 +//BSIZE=4096 + +idle: + jump wait_for_reselection +start: + call load_sync +// move 13 to ctest0 +// int SIR_NOTIFY_ISSUE + clear target + select atn from scsi_id_buf, reselected_on_select // do I need to clear ATN here? + jump start1, when msg_in +start1: +// move 14 to ctest0 + move from msg_out_buf, when msg_out +id_out_mismatch: + jump start1, when msg_out // repeat on parity grounds + jump to_decisions, when not cmd +cmd_phase: +// int SIR_NOTIFY_COMMAND + clear atn + move from cmd_buf, when cmd +cmd_out_mismatch: + jump to_decisions, when not data_in +data_in_phase: + move memory 4, state, scratcha + move memory 4, dmaaddr, scratchb +// int SIR_NOTIFY_DATA_IN +data_in_block_loop: + move scratcha2 to sfbr + jump data_in_normal, if 0 +// int SIR_NOTIFY_BLOCK_DATA_IN + move BSIZE, ptr dmaaddr, when data_in // transfer BSIZE bytes +data_in_block_mismatch: + move scratchb1 + BSIZE / 256 to scratchb1 // add BSIZE to scratchb + move scratchb2 + 0 to scratchb2 with carry + move scratchb3 + 0 to scratchb3 with carry + move scratcha2 + 255 to scratcha2 // sub one from block count + move memory 4, scratchb, dmaaddr // save latest dmaddr + jump data_in_block_loop, when data_in + move memory 4, scratcha, state // save latest state + call save_state + jump to_decisions +data_block_mismatch_recover: + move memory 4, scratchb, dmaaddr // save latest dmaddr +data_mismatch_recover: + move memory 4, scratcha, state // save latest state + jump to_decisions // no need to save + // as interrupt routine + // did this +data_in_normal: + move scratcha3 to sfbr + int error_too_much_data, if not 0 + move from data_buf, when data_in +data_in_mismatch: + move 2 to scratcha3 + move memory 4, scratcha, state + call save_state + jump post_data_to_decisions +data_out_phase: +// int SIR_NOTIFY_DATA_OUT + move memory 4, state, scratcha + move memory 4, dmaaddr, scratchb +data_out_block_loop: + move scratcha2 to sfbr + jump data_out_normal, if 0 + move memory 4, dmaaddr, scratchb + move BSIZE, ptr dmaaddr, when data_out // transfer BSIZE bytes +data_out_block_mismatch: + move scratchb1 + BSIZE / 256 to scratchb1 // add BSIZE to scratchb + move scratchb2 + 0 to scratchb2 with carry + move scratchb3 + 0 to scratchb3 with carry + move scratcha2 + 255 to scratcha2 // sub one from block count + move memory 4, scratchb, dmaaddr // save latest dmaddr + jump data_out_block_loop, when data_out + move memory 4, scratcha, state // save latest state + jump to_decisions +data_out_normal: + move scratcha3 to sfbr + int error_too_little_data, if not 0 + move from data_buf, when data_out +data_out_mismatch: + move 2 to scratcha3 + move memory 4, scratcha, state + call save_state + jump post_data_to_decisions +status_phase: + move from status_buf, when status +// int SIR_NOTIFY_STATUS + int error_unexpected_phase, when not msg_in +msg_in_phase: + move 1, scratcha, when msg_in +// int SIR_NOTIFY_MSG_IN + jump rejected, if MSG_REJECT +msg_in_not_reject: + jump disconnected, if MSG_DISCONNECT + jump msg_in_skip, if MSG_SAVE_DATA_POINTER + jump msg_in_skip, if MSG_RESTORE_POINTERS + jump ignore_wide, if MSG_IGNORE_WIDE_RESIDUE + jump extended, if X_MSG + int error_not_cmd_complete, if not 0 + move scntl2&0x7e to scntl2 // take care not to clear WSR + clear ack + wait disconnect + // update state + move memory 4, state, scratcha + move STATE_DONE to scratcha0 + move RESULT_OK to scratcha1 + move memory 4, scratcha, state + call save_state +// int SIR_MSG_IO_COMPLETE + intfly 0 + jump issue_check + +rejected: + int SIR_MSG_REJECT + clear ack + jump to_decisions +msg_in_skip: + clear ack + jump to_decisions + +extended: + clear ack + int error_unexpected_phase, when not msg_in + move 1, scratcha1, when msg_in + jump ext_3, if 3 + jump ext_2, if 2 + int error_weird_message, if not 1 +ext_1: + clear ack + int error_unexpected_phase, when not msg_in + move 1, scratcha1, when msg_in + jump ext_done + +ext_3: clear ack + int error_unexpected_phase, when not msg_in + move 1, scratcha1, when msg_in + clear ack + int error_unexpected_phase, when not msg_in + move 1, scratcha2, when msg_in + clear ack + int error_unexpected_phase, when not msg_in + move 1, scratcha3, when msg_in + move scratcha1 to sfbr + jump ext_done, if not X_MSG_SDTR + +// the target sent SDTR - leave ACK asserted and signal kernel +// kernel will either restart at reject, or continue +sdtr: int SIR_MSG_SDTR + clear ack + jump to_decisions + +ext_2: clear ack + int error_unexpected_phase, when not msg_in + move 1, scratcha1, when msg_in + clear ack + int error_unexpected_phase, when not msg_in + move 1, scratcha2, when msg_in + move scratcha1 to sfbr + jump ext_done, if not X_MSG_WDTR + +wdtr: int SIR_MSG_WDTR + clear ack + jump to_decisions + +ext_done: +// ought to check message here, but instead reject all +// NB ATN set +reject: + set atn // get target's ATN + clear ack // finish ACK + move MSG_REJECT to scratcha // prepare message + int error_unexpected_phase, when not msg_out// didn't get ATN + clear atn // last byte coming + move 1, scratcha, when msg_out // send byte + clear ack // finish ACK + jump reject, when msg_out // parity error + jump to_decisions + +ignore_wide: + clear ack + int error_unexpected_phase, when not msg_in + move 1, scratcha1, when msg_in + int SIR_MSG_IGNORE_WIDE_RESIDUE + clear ack + jump to_decisions + +// sends a response to a message +response: + set atn + clear ack + int error_unexpected_phase, when not msg_out +response_repeat: + move from msg_out_buf, when msg_out + jump response_repeat, when msg_out // repeat on parity grounds +// now look for response +// msg_in could be a REJECT +// anything other message is something else so signal kernel first + jump response_msg_in, when msg_in + int SIR_EV_RESPONSE_OK // not a MSG_IN so OK + jump to_decisions + +response_msg_in: + move 1, scratcha, when msg_in + jump rejected, if MSG_REJECT // go and generate rej interrupt + int SIR_EV_RESPONSE_OK // not a REJECT so OK + jump msg_in_not_reject // try others + +disconnected: +// move 5 to ctest0 + move scntl2&0x7e to scntl2 // don't clear WSR + clear ack + wait disconnect + // UPDATE state to disconnected + move memory 4, state, scratcha + move STATE_DISCONNECTED to scratcha0 + move memory 4, scratcha, state + call save_state +wsr_check: + move scntl2&0x01 to sfbr + int SIR_NOTIFY_WSR, if not 0 +// int SIR_NOTIFY_DISC + jump issue_check + +reselected_on_select: + int SIR_NOTIFY_RESELECTED_ON_SELECT + jump reselected + +wait_for_reselection: +// move 11 to ctest0 +// int SIR_NOTIFY_WAIT_RESELECT + wait reselect sigp_set +reselected: +// move 12 to ctest0 + clear target + int SIR_ERROR_NOT_MSG_IN_AFTER_RESELECT, when not msg_in + move 1, scratchb, when msg_in + int error_not_identify_after_reselect, if not MSG_IDENTIFY and mask 0x1f +// int SIR_NOTIFY_RESELECT + // now locate the right DSA - note do not clear ACK, so target doesn't start + // synchronous transfer until we are ready +find_dsa: +// move 6 to ctest0 + move memory 4, dsa_head, dsa +find_dsa_loop: +// move 7 to ctest0 + move dsa0 to sfbr + jump find_dsa_1, if not 0 + move dsa1 to sfbr + jump find_dsa_1, if not 0 + move dsa2 to sfbr + jump find_dsa_1, if not 0 + move dsa3 to sfbr + int error_reselected, if 0 // couldn't match dsa (panic) +find_dsa_1: +// move 8 to ctest0 + // load state from DSA into dsa_copy + call load_state + move memory 4, state, scratcha // get dsastate in scratcha + move scratcha0 to sfbr // and state variable in sfbr + jump find_dsa_next, if not STATE_DISCONNECTED // wrong state + move ssid & ssid_mask to sfbr // get target ID + move memory 1, targ, find_dsa_smc1 // forge target comparison instruction +find_dsa_smc1: + jump find_dsa_next, if not 255 // jump if not matched + move memory 1, lun, find_dsa_smc2 // forge lun comparison instruction + move scratchb0 to sfbr // recover IDENTIFY message +find_dsa_smc2: + jump reload_sync, if 255 and mask ~7 // off we jolly well go +find_dsa_next: + move memory 4, next, dsa // find next + jump find_dsa_loop + +// id_out terminated early +// most likely the message wasn't recognised +// clear ATN and accept the message in +id_out_mismatch_recover: + clear atn + jump msg_in_phase, when msg_in + int SIR_MSG_REJECT + jump to_decisions + +// Reload synchronous registers after a reconnect. If the transfer is a synchronous read, then +// as soon as we clear ACK, the target will switch to data_in and start blasting data into the +// fifo. We need to be executing the 'jump when data_in' instruction before the target stops REQing +// since it is the REQ which latches the 'when'. The target will do 16 REQs before stopping, so +// we have 16 bytes (160uS) plus delays to do this after clearing ACK. Once the jump is executing, +// the target will wait, so as much debugging as you like can happen in data_in_phase, just don't +// stick any delays between 'clear ack' and 'jump data_in_phase, when data_in'. + +reload_sync: + call load_sync + clear ack +to_decisions: + jump data_in_phase, when data_in + jump cmd_phase, if cmd + jump data_out_phase, if data_out + jump status_phase, if status + jump msg_in_phase, if msg_in + int error_unexpected_phase +post_data_to_decisions: + jump status_phase, when status + jump msg_in_phase, if msg_in + int error_unexpected_phase + +// +// MULTI_TARGET +// +// following must mirror top of dsa structure +// the first section is loaded and saved, the +// second section loaded only +dsa_copy: +state: defw 0 // a0 is state, a1 result, a2 dma block count +dmaaddr: defw 0 // dma address for block moves +dsa_save_end: +targ: defw 0 // lsb is target +lun: defw 0 // lsb is lun +sync: defw 0 // lsb is scntl3, sxfer +next: defw 0 +dsa_load_end: +dsa_load_len = dsa_load_end - dsa_copy +dsa_save_len = dsa_save_end - dsa_copy + +load_state: + // load state from DSA into dsa_copy +// move 9 to ctest0 + move memory 4, dsa, load_state_smc0 + 4 +load_state_smc0: + move memory dsa_load_len, 0, dsa_copy +// move 20 to ctest0 + return +save_state: + move memory 4, dsa, save_state_smc0 + 8 +save_state_smc0: + move memory dsa_save_len, dsa_copy, 0 + return + +sigp_set: +// int SIR_NOTIFY_SIGP + move ctest2 to sfbr // clear SIGP +issue_check: +// int SIR_NOTIFY_ISSUE_CHECK +// move 1 to ctest0 + move memory 4, dsa_head, dsa +issue_check_loop: +// move 2 to ctest0 + move dsa0 to sfbr + jump issue_check_1, if not 0 + move dsa1 to sfbr + jump issue_check_1, if not 0 + move dsa2 to sfbr + jump issue_check_1, if not 0 + move dsa3 to sfbr + jump wait_for_reselection, if 0 // nothing to do +issue_check_1: +// move 3 to ctest0 + call load_state + move memory 4, state, scratcha // get dsastate in scratcha + move scratcha0 to sfbr // and state variable in sfbr + jump start, if STATE_ISSUE // right state +issue_check_next: +// move 4 to ctest0 + move memory 4, next, dsa // find next + jump issue_check_loop +load_sync: + move memory 4, sync, scratcha // load the sync stuff + move scratcha0 to sfbr // assuming load_state has been called + move sfbr to scntl3 + move scratcha1 to sfbr + move sfbr to sxfer +// int SIR_NOTIFY_LOAD_SYNC + return diff --git a/os/pc/sdata.c b/os/pc/sdata.c new file mode 100644 index 00000000..1e438986 --- /dev/null +++ b/os/pc/sdata.c @@ -0,0 +1,2206 @@ +#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" + +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<32-14> */ + Status = 7, /* (read) */ + Command = 7, /* (write) */ + + As = 2, /* Alternate Status (read) */ + Dc = 2, /* Device Control (write) */ +}; + +enum { /* Error */ + Med = 0x01, /* Media error */ + Ili = 0x01, /* command set specific (PACKET) */ + Nm = 0x02, /* No Media */ + Eom = 0x02, /* command set specific (PACKET) */ + Abrt = 0x04, /* Aborted command */ + Mcr = 0x08, /* Media Change Request */ + Idnf = 0x10, /* no user-accessible address */ + Mc = 0x20, /* Media Change */ + Unc = 0x40, /* Uncorrectable data error */ + Wp = 0x40, /* Write Protect */ + Icrc = 0x80, /* Interface CRC error */ +}; + +enum { /* Features */ + Dma = 0x01, /* data transfer via DMA (PACKET) */ + Ovl = 0x02, /* command overlapped (PACKET) */ +}; + +enum { /* Interrupt Reason */ + Cd = 0x01, /* Command/Data */ + Io = 0x02, /* I/O direction */ + Rel = 0x04, /* Bus Release */ +}; + +enum { /* Device/Head */ + Dev0 = 0xA0, /* Master */ + Dev1 = 0xB0, /* Slave */ + Lba = 0x40, /* LBA mode */ +}; + +enum { /* internal flags */ + Lba48 = 0x1, /* LBA48 mode */ + Lba48always = 0x2, /* ... */ +}; + +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, /* Bus Master IDE Active */ +}; + +enum { /* offsets into the identify info. */ + Iconfig = 0, /* general configuration */ + Ilcyl = 1, /* logical cylinders */ + Ilhead = 3, /* logical heads */ + Ilsec = 6, /* logical sectors per logical track */ + Iserial = 10, /* serial number */ + Ifirmware = 23, /* firmware revision */ + Imodel = 27, /* model number */ + Imaxrwm = 47, /* max. read/write multiple sectors */ + Icapabilities = 49, /* capabilities */ + Istandby = 50, /* device specific standby timer */ + Ipiomode = 51, /* PIO data transfer mode number */ + Ivalid = 53, + Iccyl = 54, /* cylinders if (valid&0x01) */ + Ichead = 55, /* heads if (valid&0x01) */ + Icsec = 56, /* sectors if (valid&0x01) */ + Iccap = 57, /* capacity if (valid&0x01) */ + Irwm = 59, /* read/write multiple */ + 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 { + ulong pa; /* Physical Base Address */ + int count; +} Prd; + +enum { + Nprd = SDmaxio/(64*1024)+2, +}; + +typedef struct Ctlr { + int cmdport; + int ctlport; + int irq; + int tbdf; + int bmiba; /* bus master interface base address */ + + Pcidev* pcidev; + void (*ienable)(Ctlr*); + void (*idisable)(Ctlr*); + SDev* sdev; + + Drive* drive[2]; + + Prd* prdt; /* physical region descriptor table */ + void* prdtbase; + + 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; + +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("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; + } + + 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\n", drive->dma, drive->rwm); + if(drive->flags&Lba48) + print("\tLLBA sectors %lld\n", drive->sectors); + } + + return drive; +} + +static void +atasrst(int ctlport) +{ + /* + * Srst is a big stick and may cause problems if further + * commands are tried before the drives become ready again. + * Also, there will be problems here if overlapped commands + * are ever supported. + */ + microdelay(5); + outb(ctlport+Dc, Srst); + microdelay(5); + outb(ctlport+Dc, 0); + microdelay(2*1000); +} + +static SDev* +ataprobe(int cmdport, int ctlport, int irq) +{ + Ctlr* ctlr; + SDev *sdev; + Drive *drive; + int dev, error, rhi, rlo; + + if(ioalloc(cmdport, 8, 0, "atacmd") < 0) { + 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 */ + + 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) +{ + if (cf->nports != 2) + error(Ebadarg); + + return ataprobe(cf->ports[0].port, cf->ports[1].port, cf->intnum); +} + +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 +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, 30*1000); + poperror(); + + done = ctlr->done; + qunlock(ctlr); + + if(!done || (drive->status & Err)) + return atasetsense(drive, SDcheck, 4, 8, drive->error); + return SDok; +} + +static int +atamodesense(Drive* drive, uchar* cmd) +{ + int len; + + /* + * Fake a vendor-specific request with page code 0, + * return the drive info. + */ + if((cmd[2] & 0x3F) != 0 && (cmd[2] & 0x3F) != 0x3F) + return atasetsense(drive, SDcheck, 0x05, 0x24, 0); + len = (cmd[7]<<8)|cmd[8]; + if(len == 0) + return SDok; + if(len < 8+sizeof(drive->info)) + return atasetsense(drive, SDcheck, 0x05, 0x1A, 0); + if(drive->data == nil || drive->dlen < len) + return atasetsense(drive, SDcheck, 0x05, 0x20, 1); + memset(drive->data, 0, 8); + drive->data[0] = sizeof(drive->info)>>8; + drive->data[1] = sizeof(drive->info); + memmove(drive->data+8, drive->info, sizeof(drive->info)); + drive->data += 8+sizeof(drive->info); + + return SDok; +} + +static void +atanop(Drive* drive, int subcommand) +{ + Ctlr* ctlr; + int as, cmdport, ctlport, timeo; + + /* + * Attempt to abort a command by using NOP. + * In response, the drive is supposed to set Abrt + * in the Error register, set (Drdy|Err) in Status + * and clear Bsy when done. However, some drives + * (e.g. ATAPI Zip) just go Bsy then clear Status + * when done, hence the timeout loop only on Bsy + * and the forced setting of drive->error. + */ + ctlr = drive->ctlr; + cmdport = ctlr->cmdport; + outb(cmdport+Features, subcommand); + outb(cmdport+Dh, drive->dev); + 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; + + pa = PCIWADDR(drive->data); + if(pa & 0x03) + return -1; + ctlr = drive->ctlr; + prd = ctlr->prdt; + + /* + * Sometimes drives identify themselves as being DMA capable + * although they are not on a busmastering controller. + */ + if(prd == nil){ + drive->dmactl = 0; + print("disabling dma: not on a busmastering controller\n"); + return -1; + } + + for(;;){ + prd->pa = pa; + count = 64*1024 - (pa & 0xFFFF); + if(count >= len){ + prd->count = PrdEOT|(len & 0xFFFF); + break; + } + prd->count = count; + len -= count; + pa += count; + prd++; + } + + bmiba = ctlr->bmiba; + outl(bmiba+Bmidtpx, PCIWADDR(ctlr->prdt)); + if(drive->write) + outb(ctlr->bmiba+Bmicx, 0); + else + outb(ctlr->bmiba+Bmicx, Rwcon); + bmisx = inb(bmiba+Bmisx); + outb(bmiba+Bmisx, bmisx|Ideints|Idedmae); + + return 0; +} + +static void +atadmastart(Ctlr* ctlr, int write) +{ + if(write) + outb(ctlr->bmiba+Bmicx, Ssbm); + else + outb(ctlr->bmiba+Bmicx, Rwcon|Ssbm); +} + +static 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); + + if(ataready(cmdport, ctlport, drive->dev, Bsy|Drq, 0, 107*1000) < 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) + r = SDtimeout; + else if(as & Chk) + r = SDcheck; + 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 > 10){ + 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, vlong lba) +{ + Ctlr *ctlr; + uchar cmd; + int as, c, cmdport, ctlport, h, len, s, use48; + + use48 = 0; + if((drive->flags&Lba48always) || (lba>>28) || 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, 0, 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) & 0xFF); + outb(cmdport+Count, drive->count & 0XFF); + outb(cmdport+Lbalo, (lba>>24) & 0xFF); + outb(cmdport+Lbalo, lba & 0xFF); + outb(cmdport+Lbamid, (lba>>32) & 0xFF); + outb(cmdport+Lbamid, (lba>>8) & 0xFF); + outb(cmdport+Lbahi, (lba>>40) & 0xFF); + outb(cmdport+Lbahi, (lba>>16) & 0xFF); + 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); + as = ataready(cmdport, ctlport, 0, Bsy, Drq|Err, 1000); + if(as < 0 || (as & Err)){ + iunlock(ctlr); + return -1; + } + len = drive->block; + if(drive->data+len > drive->limit) + len = drive->limit-drive->data; + outss(cmdport+Data, drive->data, len/2); + break; + + case Crd: + case Cwd: + atadmastart(ctlr, drive->write); + break; + } + 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) +{ + uchar *p; + Ctlr *ctlr; + int count, max; + vlong lba, len; + + /* + * Map SCSI commands into ATA commands for discs. + * Fail any command with a LUN except INQUIRY which + * will return 'logical unit not supported'. + */ + if((cmd[1]>>5) && cmd[0] != 0x12) + return atasetsense(drive, SDcheck, 0x05, 0x25, 0); + + switch(cmd[0]){ + default: + return atasetsense(drive, SDcheck, 0x05, 0x20, 0); + + case 0x00: /* test unit ready */ + return SDok; + + case 0x03: /* request sense */ + if(cmd[4] < sizeof(drive->sense)) + len = cmd[4]; + else + len = sizeof(drive->sense); + if(drive->data && drive->dlen >= len){ + memmove(drive->data, drive->sense, len); + drive->data += len; + } + return SDok; + + case 0x12: /* inquiry */ + if(cmd[4] < sizeof(drive->inquiry)) + len = cmd[4]; + else + len = sizeof(drive->inquiry); + if(drive->data && drive->dlen >= len){ + memmove(drive->data, drive->inquiry, len); + drive->data += len; + } + return SDok; + + case 0x1B: /* start/stop unit */ + /* + * NOP for now, can use the power management feature + * set later. + */ + return SDok; + + case 0x25: /* read capacity */ + if((cmd[1] & 0x01) || cmd[2] || cmd[3]) + return atasetsense(drive, SDcheck, 0x05, 0x24, 0); + if(drive->data == nil || drive->dlen < 8) + return atasetsense(drive, SDcheck, 0x05, 0x20, 1); + /* + * Read capacity returns the LBA of the last sector. + */ + len = drive->sectors-1; + p = drive->data; + *p++ = len>>24; + *p++ = len>>16; + *p++ = len>>8; + *p++ = len; + len = drive->secsize; + *p++ = len>>24; + *p++ = len>>16; + *p++ = len>>8; + *p = len; + drive->data += 8; + return SDok; + + case 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 += 8; + return SDok; + + case 0x28: /* read */ + case 0x2A: /* write */ + break; + + case 0x5A: + return atamodesense(drive, cmd); + } + + ctlr = drive->ctlr; + lba = (cmd[2]<<24)|(cmd[3]<<16)|(cmd[4]<<8)|cmd[5]; + count = (cmd[7]<<8)|cmd[8]; + if(drive->data == nil) + return SDok; + if(drive->dlen < count*drive->secsize) + count = drive->dlen/drive->secsize; + qlock(ctlr); + while(count){ + max = (drive->flags&Lba48) ? 65536 : 256; + if(count > max) + drive->count = max; + 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, 30*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; + int channel, ispc87415, pi, r; + SDev *legacy[2], *sdev, *head, *tail; + + legacy[0] = legacy[1] = head = tail = nil; + if(sdev = ataprobe(0x1F0, 0x3F4, IrqATA0)){ + head = tail = sdev; + legacy[0] = sdev; + } + if(sdev = ataprobe(0x170, 0x374, IrqATA1)){ + if(head != nil) + tail->next = sdev; + else + head = sdev; + tail = sdev; + legacy[1] = sdev; + } + + p = nil; + while(p = pcimatch(p, 0, 0)){ + /* + * Look for devices with the correct class and sub-class + * code and known device and vendor ID; add native-mode + * channels to the list to be probed, save info for the + * compatibility mode channels. + * Note that the legacy devices should not be considered + * PCI devices by the interrupt controller. + * For both native and legacy, save info for busmastering + * if capable. + * Promise Ultra ATA/66 (PDC20262) appears to + * 1) give a sub-class of 'other mass storage controller' + * instead of 'IDE controller', regardless of whether it's + * the only controller or not; + * 2) put 0 in the programming interface byte (probably + * as a consequence of 1) above). + * 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; + + 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 (0x3373<<16)|0x105A: /* Promise 20378 RAID */ + case (0x3149<<16)|0x1106: /* VIA VT8237 SATA/RAID */ + pi = 0x85; + break; + case (0x0004<<16)|0x1103: /* HighPoint HPT-370 */ + 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); + break; + case (0x0646<<16)|0x1095: /* CMD 646 */ + case (0x0571<<16)|0x1106: /* VIA 82C686 */ + case (0x0211<<16)|0x1166: /* ServerWorks IB6566 */ + 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) */ + 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; + 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 SDev* +ataid(SDev* sdev) +{ + int i; + Ctlr *ctlr; + char name[32]; + + /* + * Legacy controllers are always 'C' and 'D' and if + * they exist and have drives will be first in the list. + * If there are no active legacy controllers, native + * controllers start at 'C'. + */ + if(sdev == nil) + return nil; + ctlr = sdev->ctlr; + if(ctlr->cmdport == 0x1F0 || ctlr->cmdport == 0x170) + i = 2; + else + i = 0; + while(sdev){ + if(sdev->ifc == &sdataifc){ + ctlr = sdev->ctlr; + if(ctlr->cmdport == 0x1F0) + sdev->idno = 'C'; + else if(ctlr->cmdport == 0x170) + sdev->idno = 'D'; + else{ + sdev->idno = 'C'+i; + i++; + } + snprint(name, sizeof(name), "sd%c", sdev->idno); + kstrdup(&sdev->name, name); + } + sdev = sdev->next; + } + + return nil; +} + +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 = xspanalloc(Nprd*sizeof(Prd), 4, 4*1024); + ctlr->prdtbase = xalloc(Nprd * sizeof(Prd) + ALIGN); + ctlr->prdt = (Prd *)(((ulong)ctlr->prdtbase + ALIGN) & ~(ALIGN - 1)); + } + 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); + xfree(ctlr->prdtbase); + } + 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 */ + ataid, /* id */ + ataenable, /* enable */ + atadisable, /* disable */ + + scsiverify, /* verify */ + scsionline, /* online */ + atario, /* rio */ + atarctl, /* rctl */ + atawctl, /* wctl */ + + scsibio, /* bio */ + ataprobew, /* probe */ + ataclear, /* clear */ + atastat, /* stat */ +}; diff --git a/os/pc/sdmylex.c b/os/pc/sdmylex.c new file mode 100644 index 00000000..cb132991 --- /dev/null +++ b/os/pc/sdmylex.c @@ -0,0 +1,1249 @@ +/* + * Mylex MultiMaster (Buslogic BT-*) SCSI Host Adapter + * in both 24-bit and 32-bit mode. + * 24-bit mode works for Adaptec AHA-154xx series too. + * + * To do: + * allocate more Ccb's as needed, up to NMbox-1; + * add nmbox and nccb to Ctlr struct for the above; + * 64-bit LUN/explicit wide support necessary? + * + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" +#include "../port/error.h" + +#include "../port/sd.h" + +#define K2BPA(va, tbdf) PADDR(va) +#define BPA2K(pa, tbdf) KADDR(pa) + +extern SDifc sdmylexifc; + +enum { /* registers */ + Rcontrol = 0x00, /* WO: control register */ + Rstatus = 0x00, /* RO: status register */ + Rcpr = 0x01, /* WO: command/parameter register */ + Rdatain = 0x01, /* RO: data-in register */ + Rinterrupt = 0x02, /* RO: interrupt register */ +}; + +enum { /* Rcontrol */ + Rsbus = 0x10, /* SCSI Bus Reset */ + Rint = 0x20, /* Interrupt Reset */ + Rsoft = 0x40, /* Soft Reset */ + Rhard = 0x80, /* Hard Reset */ +}; + +enum { /* Rstatus */ + Cmdinv = 0x01, /* Command Invalid */ + Dirrdy = 0x04, /* Data In Register Ready */ + Cprbsy = 0x08, /* Command/Parameter Register Busy */ + Hardy = 0x10, /* Host Adapter Ready */ + Inreq = 0x20, /* Initialisation Required */ + Dfail = 0x40, /* Diagnostic Failure */ + Dact = 0x80, /* Diagnostic Active */ +}; + +enum { /* Rcpr */ + Cinitialise = 0x01, /* Initialise Mailbox */ + Cstart = 0x02, /* Start Mailbox Command */ + Cinquiry = 0x04, /* Adapter Inquiry */ + Ceombri = 0x05, /* Enable OMBR Interrupt */ + Cinquire = 0x0B, /* Inquire Configuration */ + Cextbios = 0x28, /* AHA-1542: extended BIOS info. */ + Cmbienable = 0x29, /* AHA-1542: Mailbox interface enable */ + Ciem = 0x81, /* Initialise Extended Mailbox */ + Ciesi = 0x8D, /* Inquire Extended Setup Information */ + Cerrm = 0x8F, /* Enable strict round-robin mode */ + Cwide = 0x96, /* Wide CCB */ +}; + +enum { /* Rinterrupt */ + Imbl = 0x01, /* Incoming Mailbox Loaded */ + Mbor = 0x02, /* Mailbox Out Ready */ + Cmdc = 0x04, /* Command Complete */ + Rsts = 0x08, /* SCSI Reset State */ + Intv = 0x80, /* Interrupt Valid */ +}; + +typedef struct Mbox24 Mbox24; +struct Mbox24 { + uchar code; /* action/completion code */ + uchar ccb[3]; /* CCB pointer (MSB, ..., LSB) */ +}; + +typedef struct Mbox32 Mbox32; +struct Mbox32 { + uchar ccb[4]; /* CCB pointer (LSB, ..., MSB) */ + uchar btstat; /* BT-7[45]7[SD] status */ + uchar sdstat; /* SCSI device status */ + uchar pad; + uchar code; /* action/completion code */ +}; + +enum { /* mailbox commands */ + Mbfree = 0x00, /* Mailbox not in use */ + + Mbostart = 0x01, /* Start a mailbox command */ + Mboabort = 0x02, /* Abort a mailbox command */ + + Mbiok = 0x01, /* CCB completed without error */ + Mbiabort = 0x02, /* CCB aborted at request of host */ + Mbinx = 0x03, /* Aborted CCB not found */ + Mbierror = 0x04, /* CCB completed with error */ +}; + +typedef struct Ccb24 Ccb24; +typedef struct Ccb32 Ccb32; +typedef union Ccb Ccb; + +typedef struct Ccb24 { + uchar opcode; /* Operation code */ + uchar datadir; /* Data direction control */ + uchar cdblen; /* Length of CDB */ + uchar senselen; /* Length of sense area */ + uchar datalen[3]; /* Data length (MSB, ..., LSB) */ + uchar dataptr[3]; /* Data pointer (MSB, ..., LSB) */ + uchar linkptr[3]; /* Link pointer (MSB, ..., LSB) */ + uchar linkid; /* command linking identifier */ + uchar btstat; /* BT-* adapter status */ + uchar sdstat; /* SCSI device status */ + uchar reserved[2]; /* */ + uchar cs[12+0xFF]; /* Command descriptor block + Sense */ + + void* data; /* buffer if address > 24-bits */ + + Rendez; + int done; /* command completed */ + + Ccb* ccb; /* link on free list */ +} Ccb24; + + +typedef struct Ccb32 { + uchar opcode; /* Operation code */ + uchar datadir; /* Data direction control */ + uchar cdblen; /* Length of CDB */ + uchar senselen; /* Length of sense area */ + uchar datalen[4]; /* Data length (LSB, ..., MSB) */ + uchar dataptr[4]; /* Data pointer (LSB, ..., MSB) */ + uchar reserved[2]; + uchar btstat; /* BT-* adapter status */ + uchar sdstat; /* SCSI device status */ + uchar targetid; /* Target ID */ + uchar luntag; /* LUN & tag */ + uchar cdb[12]; /* Command descriptor block */ + uchar ccbctl; /* CCB control */ + uchar linkid; /* command linking identifier */ + uchar linkptr[4]; /* Link pointer (LSB, ..., MSB) */ + uchar senseptr[4]; /* Sense pointer (LSB, ..., MSB) */ + uchar sense[0xFF]; /* Sense bytes */ + + Rendez; + int done; /* command completed */ + + Ccb* ccb; /* link on free list */ +} Ccb32; + +typedef union Ccb { + Ccb24; + Ccb32; +} Ccb; + +enum { /* opcode */ + OInitiator = 0x00, /* initiator CCB */ + Ordl = 0x03, /* initiator CCB with + * residual data length returned + */ +}; + +enum { /* datadir */ + CCBdatain = 0x08, /* inbound, length is checked */ + CCBdataout = 0x10, /* outbound, length is checked */ +}; + +enum { /* btstat */ + Eok = 0x00, /* normal completion with no errors */ +}; + +enum { /* luntag */ + TagEnable = 0x20, /* Tag enable */ + SQTag = 0x00, /* Simple Queue Tag */ + HQTag = 0x40, /* Head of Queue Tag */ + OQTag = 0x80, /* Ordered Queue Tag */ +}; + +enum { /* CCB control */ + NoDisc = 0x08, /* No disconnect */ + NoUnd = 0x10, /* No underrrun error report */ + NoData = 0x20, /* No data transfer */ + NoStat = 0x40, /* No CCB status if zero */ + NoIntr = 0x80, /* No Interrupts */ +}; + +typedef struct Ctlr Ctlr; +struct Ctlr { + int port; /* I/O port */ + int id; /* adapter SCSI id */ + int bus; /* 24 or 32 -bit */ + int irq; + int wide; + Pcidev* pcidev; + SDev* sdev; + int spurious; + + Lock issuelock; + + Lock ccblock; + QLock ccbq; + Rendez ccbr; + + Lock mboxlock; + void* mb; /* mailbox out + mailbox in */ + int mbox; /* current mailbox out index into mb */ + int mbix; /* current mailbox in index into mb */ + + Lock cachelock; + Ccb* ccb; /* list of free Ccb's */ + Ccb** cache; /* last completed Ccb */ +}; + +/* + * The number of mailboxes should be a multiple of 8 (4 for Mbox32) + * to ensure the boundary between the out and in mailboxes doesn't + * straddle a cache-line boundary. + * The number of Ccb's should be less than the number of mailboxes to + * ensure no queueing is necessary on mailbox allocation. + */ +enum { + NMbox = 8*8, /* number of Mbox's */ + NCcb = NMbox-1, /* number of Ccb's */ +}; + +#define PADDR24(a, n) ((PADDR(a)+(n)) <= (1<<24)) + +static void +ccbfree(Ctlr* ctlr, Ccb* ccb) +{ + lock(&ctlr->ccblock); + if(ctlr->bus == 24) + ((Ccb24*)ccb)->ccb = ctlr->ccb; + else + ((Ccb32*)ccb)->ccb = ctlr->ccb; + if(ctlr->ccb == nil) + wakeup(&ctlr->ccbr); + ctlr->ccb = ccb; + unlock(&ctlr->ccblock); +} + +static int +ccbavailable(void* a) +{ + return ((Ctlr*)a)->ccb != nil; +} + +static Ccb* +ccballoc(Ctlr* ctlr) +{ + Ccb *ccb; + + for(;;){ + lock(&ctlr->ccblock); + if((ccb = ctlr->ccb) != nil){ + if(ctlr->bus == 24) + ctlr->ccb = ((Ccb24*)ccb)->ccb; + else + ctlr->ccb = ((Ccb32*)ccb)->ccb; + unlock(&ctlr->ccblock); + break; + } + + unlock(&ctlr->ccblock); + qlock(&ctlr->ccbq); + if(waserror()){ + qunlock(&ctlr->ccbq); + continue; + } + sleep(&ctlr->ccbr, ccbavailable, ctlr); + qunlock(&ctlr->ccbq); + poperror(); + } + + return ccb; +} + +static int +done24(void* arg) +{ + return ((Ccb24*)arg)->done; +} + +static int +mylex24rio(SDreq* r) +{ + ulong p; + Ctlr *ctlr; + Ccb24 *ccb; + Mbox24 *mb; + uchar *data, lun, *sense; + int d, n, btstat, sdstat, target; + + ctlr = r->unit->dev->ctlr; + target = r->unit->subno; + lun = (r->cmd[1]>>5) & 0x07; + + /* + * Ctlr->cache holds the last completed Ccb for this target if it + * returned 'check condition'. + * If this command is a request-sense and there is valid sense data + * from the last completed Ccb, return it immediately. + */ + lock(&ctlr->cachelock); + if((ccb = ctlr->cache[target]) != nil){ + ctlr->cache[target] = nil; + if(r->cmd[0] == 0x03 + && ccb->sdstat == SDcheck && lun == ((ccb->cs[1]>>5) & 0x07)){ + unlock(&ctlr->cachelock); + if(r->dlen){ + sense = &ccb->cs[ccb->cdblen]; + n = 8+sense[7]; + if(n > r->dlen) + n = r->dlen; + memmove(r->data, sense, n); + r->rlen = n; + } + ccbfree(ctlr, (Ccb*)ccb); + return SDok; + } + } + unlock(&ctlr->cachelock); + if(ccb == nil) + ccb = ccballoc(ctlr); + + /* + * Check if the transfer is to memory above the 24-bit limit the + * controller can address. If it is, try to allocate a temporary + * buffer as a staging area. + */ + n = r->dlen; + if(n && !PADDR24(r->data, n)){ + data = mallocz(n, 0); + if(data == nil || !PADDR24(data, n)){ + if(data != nil){ + free(data); + ccb->data = nil; + } + ccbfree(ctlr, (Ccb*)ccb); + return SDmalloc; + } + if(r->write) + memmove(data, r->data, n); + ccb->data = r->data; + } + else + data = r->data; + + /* + * Fill in the ccb. + */ + ccb->opcode = Ordl; + + ccb->datadir = (target<<5)|lun; + if(n == 0) + ccb->datadir |= CCBdataout|CCBdatain; + else if(!r->write) + ccb->datadir |= CCBdatain; + else + ccb->datadir |= CCBdataout; + + ccb->cdblen = r->clen; + ccb->senselen = 0xFF; + + ccb->datalen[0] = n>>16; + ccb->datalen[1] = n>>8; + ccb->datalen[2] = n; + p = PADDR(data); + ccb->dataptr[0] = p>>16; + ccb->dataptr[1] = p>>8; + ccb->dataptr[2] = p; + + ccb->linkptr[0] = ccb->linkptr[1] = ccb->linkptr[2] = 0; + ccb->linkid = 0; + ccb->btstat = ccb->sdstat = 0; + ccb->reserved[0] = ccb->reserved[1] = 0; + + memmove(ccb->cs, r->cmd, r->clen); + + /* + * There's one more mbox than there there is + * ccb so there is always one free. + */ + lock(&ctlr->mboxlock); + mb = ctlr->mb; + mb += ctlr->mbox; + p = PADDR(ccb); + mb->ccb[0] = p>>16; + mb->ccb[1] = p>>8; + mb->ccb[2] = p; + mb->code = Mbostart; + ctlr->mbox++; + if(ctlr->mbox >= NMbox) + ctlr->mbox = 0; + + /* + * This command does not require Hardy + * and doesn't generate a Cmdc interrupt. + */ + ccb->done = 0; + outb(ctlr->port+Rcpr, Cstart); + unlock(&ctlr->mboxlock); + + /* + * Wait for the request to complete and return the status. + * Since the buffer is not reference counted cannot return + * until the DMA is done writing into the buffer so the caller + * cannot free the buffer prematurely. + */ + while(waserror()) + ; + sleep(ccb, done24, ccb); + poperror(); + + /* + * Save the status and patch up the number of + * bytes actually transferred. + * There's a firmware bug on some 956C controllers + * which causes the return count from a successful + * READ CAPACITY not be updated, so fix it here. + */ + sdstat = ccb->sdstat; + btstat = ccb->btstat; + + d = ccb->datalen[0]<<16; + d |= ccb->datalen[1]<<8; + d |= ccb->datalen[2]; + if(ccb->cs[0] == 0x25 && sdstat == SDok) + d = 0; + n -= d; + r->rlen = n; + + /* + * Tidy things up if a staging area was used for the data, + */ + if(ccb->data != nil){ + if(sdstat == SDok && btstat == 0 && !r->write) + memmove(ccb->data, data, n); + free(data); + ccb->data = nil; + } + + /* + * If there was a check-condition, save the + * ccb for a possible request-sense command. + */ + if(sdstat == SDcheck){ + if(r->flags & SDnosense){ + lock(&ctlr->cachelock); + if(ctlr->cache[target]) + ccbfree(ctlr, ctlr->cache[target]); + ctlr->cache[target] = (Ccb*)ccb; + unlock(&ctlr->cachelock); + return SDcheck; + } + sense = &ccb->cs[ccb->cdblen]; + n = 8+sense[7]; + if(n > sizeof(r->sense)-1) + n = sizeof(r->sense)-1; + memmove(r->sense, sense, n); + r->flags |= SDvalidsense; + } + ccbfree(ctlr, (Ccb*)ccb); + + if(btstat){ + if(btstat == 0x11) + return SDtimeout; + return SDeio; + } + return sdstat; +} + +static void +mylex24interrupt(Ureg*, void* arg) +{ + ulong pa; + Ctlr *ctlr; + Ccb24 *ccb; + Mbox24 *mb, *mbox; + int port, rinterrupt, rstatus; + + ctlr = arg; + port = ctlr->port; + + /* + * Save and clear the interrupt(s). The only + * interrupts expected are Cmdc, which is ignored, + * and Imbl which means something completed. + * There's one spurious interrupt left over from + * initialisation, ignore it. + */ + rinterrupt = inb(port+Rinterrupt); + rstatus = inb(port+Rstatus); + outb(port+Rcontrol, Rint); + if((rinterrupt & ~(Cmdc|Imbl)) != Intv && ctlr->spurious++) + print("%s: interrupt 0x%2.2ux\n", + ctlr->sdev->name, rinterrupt); + if((rinterrupt & Cmdc) && (rstatus & Cmdinv)) + print("%s: command invalid\n", ctlr->sdev->name); + + /* + * Look for something in the mail. + * If there is, save the status, free the mailbox + * and wakeup whoever. + */ + mb = ctlr->mb; + for(mbox = &mb[ctlr->mbix]; mbox->code; mbox = &mb[ctlr->mbix]){ + pa = (mbox->ccb[0]<<16)|(mbox->ccb[1]<<8)|mbox->ccb[2]; + ccb = BPA2K(pa, BUSUNKNOWN); + mbox->code = 0; + ccb->done = 1; + wakeup(ccb); + + ctlr->mbix++; + if(ctlr->mbix >= NMbox+NMbox) + ctlr->mbix = NMbox; + } +} + +static int +done32(void* arg) +{ + return ((Ccb32*)arg)->done; +} + +static int +mylex32rio(SDreq* r) +{ + ulong p; + uchar lun; + Ctlr *ctlr; + Ccb32 *ccb; + Mbox32 *mb; + int d, n, btstat, sdstat, target; + + ctlr = r->unit->dev->ctlr; + target = r->unit->subno; + lun = (r->cmd[1]>>5) & 0x07; + + /* + * Ctlr->cache holds the last completed Ccb for this target if it + * returned 'check condition'. + * If this command is a request-sense and there is valid sense data + * from the last completed Ccb, return it immediately. + */ + lock(&ctlr->cachelock); + if((ccb = ctlr->cache[target]) != nil){ + ctlr->cache[target] = nil; + if(r->cmd[0] == 0x03 + && ccb->sdstat == SDcheck && lun == (ccb->luntag & 0x07)){ + unlock(&ctlr->cachelock); + if(r->dlen){ + n = 8+ccb->sense[7]; + if(n > r->dlen) + n = r->dlen; + memmove(r->data, ccb->sense, n); + r->rlen = n; + } + ccbfree(ctlr, (Ccb*)ccb); + return SDok; + } + } + unlock(&ctlr->cachelock); + if(ccb == nil) + ccb = ccballoc(ctlr); + + /* + * Fill in the ccb. + */ + ccb->opcode = Ordl; + + n = r->dlen; + if(n == 0) + ccb->datadir = CCBdataout|CCBdatain; + else if(!r->write) + ccb->datadir = CCBdatain; + else + ccb->datadir = CCBdataout; + + ccb->cdblen = r->clen; + + ccb->datalen[0] = n; + ccb->datalen[1] = n>>8; + ccb->datalen[2] = n>>16; + ccb->datalen[3] = n>>24; + p = PADDR(r->data); + ccb->dataptr[0] = p; + ccb->dataptr[1] = p>>8; + ccb->dataptr[2] = p>>16; + ccb->dataptr[3] = p>>24; + + ccb->targetid = target; + ccb->luntag = lun; + if(r->unit->inquiry[7] & 0x02) + ccb->luntag |= SQTag|TagEnable; + memmove(ccb->cdb, r->cmd, r->clen); + ccb->btstat = ccb->sdstat = 0; + ccb->ccbctl = 0; + + /* + * There's one more mbox than there there is + * ccb so there is always one free. + */ + lock(&ctlr->mboxlock); + mb = ctlr->mb; + mb += ctlr->mbox; + p = PADDR(ccb); + mb->ccb[0] = p; + mb->ccb[1] = p>>8; + mb->ccb[2] = p>>16; + mb->ccb[3] = p>>24; + mb->code = Mbostart; + ctlr->mbox++; + if(ctlr->mbox >= NMbox) + ctlr->mbox = 0; + + /* + * This command does not require Hardy + * and doesn't generate a Cmdc interrupt. + */ + ccb->done = 0; + outb(ctlr->port+Rcpr, Cstart); + unlock(&ctlr->mboxlock); + + /* + * Wait for the request to complete and return the status. + * Since the buffer is not reference counted cannot return + * until the DMA is done writing into the buffer so the caller + * cannot free the buffer prematurely. + */ + while(waserror()) + ; + sleep(ccb, done32, ccb); + poperror(); + + /* + * Save the status and patch up the number of + * bytes actually transferred. + * There's a firmware bug on some 956C controllers + * which causes the return count from a successful + * READ CAPACITY not to be updated, so fix it here. + */ + sdstat = ccb->sdstat; + btstat = ccb->btstat; + + d = ccb->datalen[0]; + d |= (ccb->datalen[1]<<8); + d |= (ccb->datalen[2]<<16); + d |= (ccb->datalen[3]<<24); + if(ccb->cdb[0] == 0x25 && sdstat == SDok) + d = 0; + n -= d; + r->rlen = n; + + /* + * If there was a check-condition, save the + * ccb for a possible request-sense command. + */ + if(sdstat == SDcheck){ + if(r->flags & SDnosense){ + lock(&ctlr->cachelock); + if(ctlr->cache[target]) + ccbfree(ctlr, ctlr->cache[target]); + ctlr->cache[target] = (Ccb*)ccb; + unlock(&ctlr->cachelock); + return SDcheck; + } + n = 8+ccb->sense[7]; + if(n > sizeof(r->sense)-1) + n = sizeof(r->sense)-1; + memmove(r->sense, ccb->sense, n); + r->flags |= SDvalidsense; + } + ccbfree(ctlr, (Ccb*)ccb); + + if(btstat){ + if(btstat == 0x11) + return SDtimeout; + return SDeio; + } + return sdstat; +} + +static void +mylex32interrupt(Ureg*, void* arg) +{ + ulong pa; + Ctlr *ctlr; + Ccb32 *ccb; + Mbox32 *mb, *mbox; + int port, rinterrupt, rstatus; + + ctlr = arg; + port = ctlr->port; + + /* + * Save and clear the interrupt(s). The only + * interrupts expected are Cmdc, which is ignored, + * and Imbl which means something completed. + * There's one spurious interrupt left over from + * initialisation, ignore it. + */ + rinterrupt = inb(port+Rinterrupt); + rstatus = inb(port+Rstatus); + outb(port+Rcontrol, Rint); + if((rinterrupt & ~(Cmdc|Imbl)) != Intv && ctlr->spurious++) + print("%s: interrupt 0x%2.2ux\n", + ctlr->sdev->name, rinterrupt); + if((rinterrupt & Cmdc) && (rstatus & Cmdinv)) + print("%s: command invalid\n", ctlr->sdev->name); + + /* + * Look for something in the mail. + * If there is, free the mailbox and wakeup whoever. + */ + mb = ctlr->mb; + for(mbox = &mb[ctlr->mbix]; mbox->code; mbox = &mb[ctlr->mbix]){ + pa = (mbox->ccb[3]<<24) + |(mbox->ccb[2]<<16) + |(mbox->ccb[1]<<8) + |mbox->ccb[0]; + if(ctlr->pcidev) + ccb = BPA2K(pa, ctlr->pcidev->tbdf); + else + ccb = BPA2K(pa, BUSUNKNOWN); + mbox->code = 0; + ccb->done = 1; + wakeup(ccb); + + ctlr->mbix++; + if(ctlr->mbix >= NMbox+NMbox) + ctlr->mbix = NMbox; + } +} + +static int +mylexrio(SDreq* r) +{ + int subno; + Ctlr *ctlr; + + subno = r->unit->subno; + ctlr = r->unit->dev->ctlr; + if(subno == ctlr->id || (!ctlr->wide && subno >= 8)) + r->status = SDtimeout; + else if(ctlr->bus == 24) + r->status = mylex24rio(r); + else + r->status = mylex32rio(r); + return r->status; +} + +/* + * Issue a command to a controller. The command and its length is + * contained in cmd and cmdlen. If any data is to be + * returned, datalen should be non-zero, and the returned data + * will be placed in data. + * If Cmdc is set, bail out, the invalid command will be handled + * when the interrupt is processed. + */ +static void +issueio(int port, uchar* cmd, int cmdlen, uchar* data, int datalen) +{ + int len; + + if(cmd[0] != Cstart && cmd[0] != Ceombri){ + while(!(inb(port+Rstatus) & Hardy)) + ; + } + outb(port+Rcpr, cmd[0]); + + len = 1; + while(len < cmdlen){ + if(!(inb(port+Rstatus) & Cprbsy)){ + outb(port+Rcpr, cmd[len]); + len++; + } + if(inb(port+Rinterrupt) & Cmdc) + return; + } + + if(datalen){ + len = 0; + while(len < datalen){ + if(inb(port+Rstatus) & Dirrdy){ + data[len] = inb(port+Rdatain); + len++; + } + if(inb(port+Rinterrupt) & Cmdc) + return; + } + } +} + +/* + * Issue a command to a controller, wait for it to complete then + * try to reset the interrupt. Should only be called at initialisation. + */ +static int +issue(Ctlr* ctlr, uchar* cmd, int cmdlen, uchar* data, int datalen) +{ + int port; + uchar rinterrupt, rstatus; + static Lock mylexissuelock; + + port = ctlr->port; + + ilock(&ctlr->issuelock); + issueio(port, cmd, cmdlen, data, datalen); + + while(!((rinterrupt = inb(port+Rinterrupt)) & Cmdc)) + ; + + rstatus = inb(port+Rstatus); + outb(port+Rcontrol, Rint); + iunlock(&ctlr->issuelock); + + if((rinterrupt & Cmdc) && (rstatus & Cmdinv)) + return 0; + return 1; +} + +static SDev* +mylexprobe(int port, int irq) +{ + SDev *sdev; + Ctlr *ctlr; + uchar cmd[6], data[256]; + int clen, dlen, timeo; + + if(ioalloc(port, 0x3, 0, "mylex") < 0) + return nil; + ctlr = nil; + sdev = nil; + /* + * Attempt to hard-reset the board and reset + * the SCSI bus. If the board state doesn't settle to + * idle with mailbox initialisation required, either + * it isn't a compatible board or it's broken. + * If the controller has SCAM set this can take a while. + */ + if(getconf("*noscsireset") != nil) + outb(port+Rcontrol, Rhard); + else + outb(port+Rcontrol, Rhard|Rsbus); + for(timeo = 0; timeo < 100; timeo++){ + if(inb(port+Rstatus) == (Inreq|Hardy)) + break; + delay(100); + } + if(inb(port+Rstatus) != (Inreq|Hardy)){ +buggery: + if(ctlr != nil) + free(ctlr); + if (sdev != nil) + free(sdev); + iofree(port); + return nil; + } + + if((ctlr = malloc(sizeof(Ctlr))) == nil) + goto buggery; + ctlr->port = port; + ctlr->irq = irq; + ctlr->bus = 24; + ctlr->wide = 0; + + /* + * Try to determine if this is a 32-bit MultiMaster controller + * by attempting to obtain the extended inquiry information; + * this command is not implemented on Adaptec 154xx + * controllers. If successful, the first byte of the returned + * data is the host adapter bus type, 'E' for 32-bit EISA, + * PCI and VLB buses. + */ + cmd[0] = Ciesi; + cmd[1] = 4; + clen = 2; + dlen = 256; + if(issue(ctlr, cmd, clen, data, dlen)){ + if(data[0] == 'E') + ctlr->bus = 32; + ctlr->wide = data[0x0D] & 0x01; + } + else{ + /* + * Inconceivable though it may seem, a hard controller reset + * is necessary here to clear out the command queue. Every + * board seems to lock-up in a different way if you give an + * invalid command and then try to clear out the + * command/parameter and/or data-in register. + * Soft reset doesn't do the job either. Fortunately no + * serious initialisation has been done yet so there's nothing + * to tidy up. + */ + outb(port+Rcontrol, Rhard); + for(timeo = 0; timeo < 100; timeo++){ + if(inb(port+Rstatus) == (Inreq|Hardy)) + break; + delay(100); + } + if(inb(port+Rstatus) != (Inreq|Hardy)) + goto buggery; + } + + /* + * If the BIOS is enabled on the AHA-1542C/CF and BIOS options for + * support of drives > 1Gb, dynamic scanning of the SCSI bus or more + * than 2 drives under DOS 5.0 are enabled, the BIOS disables + * accepting Cmbinit to protect against running with drivers which + * don't support those options. In order to unlock the interface it + * is necessary to read a lock-code using Cextbios and write it back + * using Cmbienable; the lock-code is non-zero. + */ + cmd[0] = Cinquiry; + clen = 1; + dlen = 4; + if(issue(ctlr, cmd, clen, data, dlen) == 0) + goto buggery; + if(data[0] >= 0x43){ + cmd[0] = Cextbios; + clen = 1; + dlen = 2; + if(issue(ctlr, cmd, clen, data, dlen) == 0) + goto buggery; + + /* + * Lock-code returned in data[1]. If it's non-zero write + * it back along with bit 0 of byte 0 cleared to enable + * mailbox initialisation. + */ + if(data[1]){ + cmd[0] = Cmbienable; + cmd[1] = 0; + cmd[2] = data[1]; + clen = 3; + if(issue(ctlr, cmd, clen, 0, 0) == 0) + goto buggery; + } + } + + /* + * Get the id, DMA and IRQ info from the board. This will + * cause an interrupt which will hopefully not cause any + * trouble because the interrupt number isn't known yet. + * This is necessary as the DMA won't be set up if the + * board has the BIOS disabled. + * + * If the IRQ is already known, this must be a 32-bit PCI + * or EISA card, in which case the returned DMA and IRQ can + * be ignored. + */ + cmd[0] = Cinquire; + clen = 1; + dlen = 3; + if(issue(ctlr, cmd, clen, data, dlen) == 0) + goto buggery; + + ctlr->id = data[2] & 0x07; + if(ctlr->irq < 0){ + switch(data[0]){ /* DMA Arbitration Priority */ + case 0x80: /* Channel 7 */ + outb(0xD6, 0xC3); + outb(0xD4, 0x03); + break; + case 0x40: /* Channel 6 */ + outb(0xD6, 0xC2); + outb(0xD4, 0x02); + break; + case 0x20: /* Channel 5 */ + outb(0xD6, 0xC1); + outb(0xD4, 0x01); + break; + case 0x01: /* Channel 0 */ + outb(0x0B, 0xC0); + outb(0x0A, 0x00); + break; + default: + if(ctlr->bus == 24) + goto buggery; + break; + } + + switch(data[1]){ /* Interrupt Channel */ + case 0x40: + ctlr->irq = 15; + break; + case 0x20: + ctlr->irq = 14; + break; + case 0x08: + ctlr->irq = 12; + break; + case 0x04: + ctlr->irq = 11; + break; + case 0x02: + ctlr->irq = 10; + break; + case 0x01: + ctlr->irq = 9; + break; + default: + goto buggery; + } + } + + if((sdev = malloc(sizeof(SDev))) == nil) + goto buggery; + sdev->ifc = &sdmylexifc; + sdev->ctlr = ctlr; + ctlr->sdev = sdev; + if(!ctlr->wide) + sdev->nunit = 8; + else + sdev->nunit = 16; + + return sdev; +} + +static int mylexport[8] = { + 0x330, 0x334, 0x230, 0x234, 0x130, 0x134, 0x000, 0x000, +}; + +static SDev* +mylexpnp(void) +{ + Pcidev *p; + Ctlr *ctlr; + ISAConf isa; + int cfg, ctlrno, i, x; + SDev *sdev, *head, *tail; + + p = nil; + head = tail = nil; + while(p = pcimatch(p, 0x104B, 0)){ + if((sdev = mylexprobe(p->mem[0].bar & ~0x01, p->intl)) == nil) + continue; + + ctlr = sdev->ctlr; + ctlr->pcidev = p; + + if(head != nil) + tail->next = sdev; + else + head = sdev; + tail = sdev; + } + + if(strncmp(KADDR(0xFFFD9), "EISA", 4) == 0){ + for(cfg = 0x1000; cfg < MaxEISA*0x1000; cfg += 0x1000){ + x = 0; + for(i = 0; i < 4; i++) + x |= inb(cfg+CfgEISA+i)<<(i*8); + if(x != 0x0142B30A && x != 0x0242B30A) + continue; + + x = inb(cfg+0xC8C); + if((sdev = mylexprobe(mylexport[x & 0x07], -1)) == nil) + continue; + + if(head != nil) + tail->next = sdev; + else + head = sdev; + tail = sdev; + } + } + + for(ctlrno = 0; ctlrno < 4; ctlrno++){ + memset(&isa, 0, sizeof(isa)); + if(!isaconfig("scsi", ctlrno, &isa)) + continue; + if(strcmp(isa.type, "aha1542")) + continue; + if((sdev = mylexprobe(isa.port, -1)) == nil) + continue; + + if(head != nil) + tail->next = sdev; + else + head = sdev; + tail = sdev; + } + + return head; +} + +static SDev* +mylexid(SDev* sdev) +{ + return scsiid(sdev, &sdmylexifc); +} + +static int +mylex24enable(Ctlr* ctlr) +{ + ulong p; + Ccb24 *ccb, *ccbp; + uchar cmd[6], *v; + int len; + + len = (sizeof(Mbox24)*NMbox*2)+(sizeof(Ccb24)*NCcb); + v = xspanalloc(len, 32, 0); + + if(!PADDR24(ctlr, sizeof(Ctlr)) || !PADDR24(v, len)) + return 0; + + ctlr->mb = v; + v += sizeof(Mbox24)*NMbox*2; + + ccb = (Ccb24*)v; + for(ccbp = ccb; ccbp < &ccb[NCcb]; ccbp++){ + ccbp->ccb = ctlr->ccb; + ctlr->ccb = (Ccb*)ccbp; + } + + /* + * Initialise the software controller and + * set the board scanning the mailboxes. + */ + ctlr->mbix = NMbox; + + cmd[0] = Cinitialise; + cmd[1] = NMbox; + p = K2BPA(ctlr->mb, BUSUNKNOWN); + cmd[2] = p>>16; + cmd[3] = p>>8; + cmd[4] = p; + + return issue(ctlr, cmd, 5, 0, 0); +} + +static int +mylex32enable(Ctlr* ctlr) +{ + ulong p; + Ccb32 *ccb, *ccbp; + uchar cmd[6], *v; + + v = xspanalloc((sizeof(Mbox32)*NMbox*2)+(sizeof(Ccb32)*NCcb), 32, 0); + + ctlr->mb = v; + v += sizeof(Mbox32)*NMbox*2; + + ccb = (Ccb32*)v; + for(ccbp = ccb; ccbp < &ccb[NCcb]; ccbp++){ + /* + * Fill in some stuff that doesn't change. + */ + ccbp->senselen = sizeof(ccbp->sense); + p = PADDR(ccbp->sense); + ccbp->senseptr[0] = p; + ccbp->senseptr[1] = p>>8; + ccbp->senseptr[2] = p>>16; + ccbp->senseptr[3] = p>>24; + + ccbp->ccb = ctlr->ccb; + ctlr->ccb = (Ccb*)ccbp; + } + + /* + * Attempt wide mode setup. + */ + if(ctlr->wide){ + cmd[0] = Cwide; + cmd[1] = 1; + if(!issue(ctlr, cmd, 2, 0, 0)) + ctlr->wide = 0; + } + + /* + * Initialise the software controller and + * set the board scanning the mailboxes. + */ + ctlr->mbix = NMbox; + + cmd[0] = Ciem; + cmd[1] = NMbox; + if(ctlr->pcidev) + p = K2BPA(ctlr->mb, ctlr->tbdf); + else + p = K2BPA(ctlr->mb, BUSUNKNOWN); + cmd[2] = p; + cmd[3] = p>>8; + cmd[4] = p>>16; + cmd[5] = p>>24; + + return issue(ctlr, cmd, 6, 0, 0); +} + +static int +mylexenable(SDev* sdev) +{ + int tbdf; + Ctlr *ctlr; + void (*interrupt)(Ureg*, void*); + char name[32]; + + ctlr = sdev->ctlr; + if(ctlr->cache == nil){ + if((ctlr->cache = malloc(sdev->nunit*sizeof(Ccb*))) == nil) + return 0; + } + + tbdf = BUSUNKNOWN; + if(ctlr->bus == 32){ + if(ctlr->pcidev){ + tbdf = ctlr->pcidev->tbdf; + pcisetbme(ctlr->pcidev); + } + if(!mylex32enable(ctlr)) + return 0; + interrupt = mylex32interrupt; + } + else if(mylex24enable(ctlr)) + interrupt = mylex24interrupt; + else + return 0; + + snprint(name, sizeof(name), "sd%c (%s)", sdev->idno, sdev->ifc->name); + intrenable(ctlr->irq, interrupt, ctlr, tbdf, name); + + return 1; +} + +SDifc sdmylexifc = { + "mylex", /* name */ + + mylexpnp, /* pnp */ + nil, /* legacy */ + mylexid, /* id */ + mylexenable, /* enable */ + nil, /* disable */ + + scsiverify, /* verify */ + scsionline, /* online */ + mylexrio, /* rio */ + nil, /* rctl */ + nil, /* wctl */ + + scsibio, /* bio */ + nil, /* probe */ + nil, /* clear */ + nil, /* stat */ +}; diff --git a/os/pc/sdscsi.c b/os/pc/sdscsi.c new file mode 100644 index 00000000..0340564c --- /dev/null +++ b/os/pc/sdscsi.c @@ -0,0 +1,394 @@ +#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" + +static int +scsitest(SDreq* r) +{ + r->write = 0; + memset(r->cmd, 0, sizeof(r->cmd)); + r->cmd[1] = r->lun<<5; + r->clen = 6; + r->data = nil; + r->dlen = 0; + r->flags = 0; + + r->status = ~0; + + return r->unit->dev->ifc->rio(r); +} + +int +scsiverify(SDunit* unit) +{ + SDreq *r; + int i, status; + uchar *inquiry; + + if((r = malloc(sizeof(SDreq))) == nil) + return 0; + if((inquiry = sdmalloc(sizeof(unit->inquiry))) == nil){ + free(r); + return 0; + } + r->unit = unit; + r->lun = 0; /* ??? */ + + memset(unit->inquiry, 0, sizeof(unit->inquiry)); + r->write = 0; + r->cmd[0] = 0x12; + r->cmd[1] = r->lun<<5; + r->cmd[4] = sizeof(unit->inquiry)-1; + r->clen = 6; + r->data = inquiry; + r->dlen = sizeof(unit->inquiry)-1; + r->flags = 0; + + r->status = ~0; + if(unit->dev->ifc->rio(r) != SDok){ + free(r); + return 0; + } + memmove(unit->inquiry, inquiry, r->dlen); + free(inquiry); + + SET(status); + for(i = 0; i < 3; i++){ + while((status = scsitest(r)) == SDbusy) + ; + if(status == SDok || status != SDcheck) + break; + if(!(r->flags & SDvalidsense)) + break; + if((r->sense[2] & 0x0F) != 0x02) + continue; + + /* + * Unit is 'not ready'. + * If it is in the process of becoming ready or needs + * an initialising command, set status so it will be spun-up + * below. + * If there's no medium, that's OK too, but don't + * try to spin it up. + */ + if(r->sense[12] == 0x04){ + if(r->sense[13] == 0x02 || r->sense[13] == 0x01){ + status = SDok; + break; + } + } + if(r->sense[12] == 0x3A) + break; + } + + if(status == SDok){ + /* + * Try to ensure a direct-access device is spinning. + * Don't wait for completion, ignore the result. + */ + if((unit->inquiry[0] & 0x1F) == 0){ + memset(r->cmd, 0, sizeof(r->cmd)); + r->write = 0; + r->cmd[0] = 0x1B; + r->cmd[1] = (r->lun<<5)|0x01; + r->cmd[4] = 1; + r->clen = 6; + r->data = nil; + r->dlen = 0; + r->flags = 0; + + r->status = ~0; + unit->dev->ifc->rio(r); + } + } + free(r); + + if(status == SDok || status == SDcheck) + return 1; + return 0; +} + +static int +scsirio(SDreq* r) +{ + /* + * Perform an I/O request, returning + * -1 failure + * 0 ok + * 1 no medium present + * 2 retry + * The contents of r may be altered so the + * caller should re-initialise if necesary. + */ + r->status = ~0; + switch(r->unit->dev->ifc->rio(r)){ + default: + return -1; + case SDcheck: + if(!(r->flags & SDvalidsense)) + return -1; + switch(r->sense[2] & 0x0F){ + case 0x00: /* no sense */ + case 0x01: /* recovered error */ + return 2; + case 0x06: /* check condition */ + /* + * 0x28 - not ready to ready transition, + * medium may have changed. + * 0x29 - power on or some type of reset. + */ + if(r->sense[12] == 0x28 && r->sense[13] == 0) + return 2; + if(r->sense[12] == 0x29) + return 2; + return -1; + case 0x02: /* not ready */ + /* + * If no medium present, bail out. + * If unit is becoming ready, rather than not + * not ready, wait a little then poke it again. */ + if(r->sense[12] == 0x3A) + return 1; + if(r->sense[12] != 0x04 || r->sense[13] != 0x01) + return -1; + + while(waserror()) + ; + tsleep(&up->sleep, return0, 0, 500); + poperror(); + scsitest(r); + return 2; + default: + return -1; + } + return -1; + case SDok: + return 0; + } + return -1; +} + +int +scsionline(SDunit* unit) +{ + SDreq *r; + uchar *p; + int ok, retries; + + if((r = malloc(sizeof(SDreq))) == nil) + return 0; + if((p = sdmalloc(8)) == nil){ + free(r); + return 0; + } + + ok = 0; + + r->unit = unit; + r->lun = 0; /* ??? */ + for(retries = 0; retries < 10; retries++){ + /* + * Read-capacity is mandatory for DA, WORM, CD-ROM and + * MO. It may return 'not ready' if type DA is not + * spun up, type MO or type CD-ROM are not loaded or just + * plain slow getting their act together after a reset. + */ + r->write = 0; + memset(r->cmd, 0, sizeof(r->cmd)); + r->cmd[0] = 0x25; + r->cmd[1] = r->lun<<5; + r->clen = 10; + r->data = p; + r->dlen = 8; + r->flags = 0; + + r->status = ~0; + switch(scsirio(r)){ + default: + break; + case 0: + unit->sectors = (p[0]<<24)|(p[1]<<16)|(p[2]<<8)|p[3]; + if(unit->sectors == 0) + continue; + /* + * Read-capacity returns the LBA of the last sector, + * therefore the number of sectors must be incremented. + */ + unit->sectors++; + unit->secsize = (p[4]<<24)|(p[5]<<16)|(p[6]<<8)|p[7]; + + /* + * 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; + ok = 1; + break; + case 1: + ok = 1; + break; + case 2: + continue; + } + break; + } + free(p); + free(r); + + if(ok) + return ok+retries; + else + 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; +} + +long +scsibio(SDunit* unit, int lun, int write, void* data, long nb, long bno) +{ + SDreq *r; + long rlen; + + if((r = malloc(sizeof(SDreq))) == nil) + error(Enomem); + r->unit = unit; + r->lun = lun; +again: + r->write = write; + if(write == 0) + r->cmd[0] = 0x28; + else + r->cmd[0] = 0x2A; + r->cmd[1] = (lun<<5); + r->cmd[2] = bno>>24; + r->cmd[3] = bno>>16; + r->cmd[4] = bno>>8; + r->cmd[5] = bno; + r->cmd[6] = 0; + r->cmd[7] = nb>>8; + r->cmd[8] = nb; + r->cmd[9] = 0; + r->clen = 10; + r->data = data; + r->dlen = nb*unit->secsize; + r->flags = 0; + + r->status = ~0; + switch(scsirio(r)){ + default: + rlen = -1; + break; + case 0: + rlen = r->rlen; + break; + case 2: + rlen = -1; + if(!(r->flags & SDvalidsense)) + break; + switch(r->sense[2] & 0x0F){ + default: + break; + case 0x06: /* check condition */ + /* + * Check for a removeable media change. + * If so, mark it by zapping the geometry info + * to force an online request. + */ + if(r->sense[12] != 0x28 || r->sense[13] != 0) + break; + if(unit->inquiry[1] & 0x80) + unit->sectors = 0; + break; + case 0x02: /* not ready */ + /* + * If unit is becoming ready, + * rather than not not ready, try again. + */ + if(r->sense[12] == 0x04 && r->sense[13] == 0x01) + goto again; + break; + } + break; + } + free(r); + + return rlen; +} + +SDev* +scsiid(SDev* sdev, SDifc* ifc) +{ + char name[32]; + static char idno[16] = "0123456789abcdef"; + static char *p = idno; + + while(sdev){ + if(sdev->ifc == ifc){ + sdev->idno = *p++; + snprint(name, sizeof(name), "sd%c", sdev->idno); + kstrdup(&sdev->name, name); + if(p >= &idno[sizeof(idno)]) + break; + } + sdev = sdev->next; + } + + return nil; +} diff --git a/os/pc/trap.c b/os/pc/trap.c new file mode 100644 index 00000000..465c9bab --- /dev/null +++ b/os/pc/trap.c @@ -0,0 +1,571 @@ +#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" + +int (*breakhandler)(Ureg *ur, Proc*); + +static void debugbpt(Ureg*, void*); +static void fault386(Ureg*, void*); +static void doublefault(Ureg*, void*); +static void unexpected(Ureg*, void*); +static void _dumpstack(Ureg*); + +static Lock vctllock; +static Vctl *vctl[256]; + +enum +{ + Ntimevec = 20 /* number of time buckets for each intr */ +}; +ulong intrtimes[256][Ntimevec]; + +void +intrenable(int irq, void (*f)(Ureg*, void*), void* a, int tbdf, char *name) +{ + int vno; + Vctl *v; + + if(f == nil){ + print("intrenable: nil handler for %d, tbdf 0x%uX for %s\n", + irq, tbdf, name); + return; + } + + v = xalloc(sizeof(Vctl)); + v->isintr = 1; + v->irq = irq; + v->tbdf = tbdf; + v->f = f; + v->a = a; + strncpy(v->name, name, KNAMELEN-1); + v->name[KNAMELEN-1] = 0; + + ilock(&vctllock); + vno = arch->intrenable(v); + if(vno == -1){ + iunlock(&vctllock); + print("intrenable: couldn't enable irq %d, tbdf 0x%uX for %s\n", + irq, tbdf, v->name); + xfree(v); + return; + } + if(vctl[vno]){ + if(vctl[vno]->isr != v->isr || vctl[vno]->eoi != v->eoi) + panic("intrenable: handler: %s %s %luX %luX %luX %luX\n", + vctl[vno]->name, v->name, + vctl[vno]->isr, v->isr, vctl[vno]->eoi, v->eoi); + v->next = vctl[vno]; + } + vctl[vno] = v; + iunlock(&vctllock); +} + +int +intrdisable(int irq, void (*f)(Ureg *, void *), void *a, int tbdf, char *name) +{ + Vctl **pv, *v; + int vno; + + /* + * For now, none of this will work with the APIC code, + * there is no mapping between irq and vector as the IRQ + * is pretty meaningless. + */ + if(arch->intrvecno == nil) + return -1; + vno = arch->intrvecno(irq); + ilock(&vctllock); + pv = &vctl[vno]; + while (*pv && + ((*pv)->irq != irq || (*pv)->tbdf != tbdf || (*pv)->f != f || (*pv)->a != a || + strcmp((*pv)->name, name))) + pv = &((*pv)->next); + assert(*pv); + + v = *pv; + *pv = (*pv)->next; /* Link out the entry */ + + if(vctl[vno] == nil && arch->intrdisable != nil) + arch->intrdisable(irq); + iunlock(&vctllock); + xfree(v); + return 0; +} + +static long +irqallocread(Chan*, void *vbuf, long n, vlong offset) +{ + char *buf, *p, str[2*(11+1)+KNAMELEN+1+1]; + int m, vno; + long oldn; + Vctl *v; + + if(n < 0 || offset < 0) + error(Ebadarg); + + oldn = n; + buf = vbuf; + for(vno=0; vno<nelem(vctl); vno++){ + for(v=vctl[vno]; v; v=v->next){ + m = snprint(str, sizeof str, "%11d %11d %.*s\n", vno, v->irq, KNAMELEN, v->name); + if(m <= offset) /* if do not want this, skip entry */ + offset -= m; + else{ + /* skip offset bytes */ + m -= offset; + p = str+offset; + offset = 0; + + /* write at most max(n,m) bytes */ + if(m > n) + m = n; + memmove(buf, p, m); + n -= m; + buf += m; + + if(n == 0) + return oldn; + } + } + } + return oldn - n; +} + +void +trapenable(int vno, void (*f)(Ureg*, void*), void* a, char *name) +{ + Vctl *v; + + if(vno < 0 || vno >= VectorPIC) + panic("trapenable: vno %d\n", vno); + v = xalloc(sizeof(Vctl)); + v->tbdf = BUSUNKNOWN; + v->f = f; + v->a = a; + strncpy(v->name, name, KNAMELEN); + v->name[KNAMELEN-1] = 0; + + lock(&vctllock); + if(vctl[vno]) + v->next = vctl[vno]->next; + vctl[vno] = v; + unlock(&vctllock); +} + +static void +nmienable(void) +{ + int x; + + /* + * Hack: should be locked with NVRAM access. + */ + outb(0x70, 0x80); /* NMI latch clear */ + outb(0x70, 0); + + x = inb(0x61) & 0x07; /* Enable NMI */ + outb(0x61, 0x08|x); + outb(0x61, x); +} + +void +trapinit(void) +{ + int d1, v; + ulong vaddr; + Segdesc *idt; + + idt = (Segdesc*)IDTADDR; + vaddr = (ulong)vectortable; + for(v = 0; v < 256; v++){ + d1 = (vaddr & 0xFFFF0000)|SEGP; + switch(v){ + + case VectorBPT: + d1 |= SEGPL(3)|SEGIG; + break; + + case VectorSYSCALL: + d1 |= SEGPL(3)|SEGIG; + break; + + default: + d1 |= SEGPL(0)|SEGIG; + break; + } + idt[v].d0 = (vaddr & 0xFFFF)|(KESEL<<16); + idt[v].d1 = d1; + vaddr += 6; + } + + /* + * Special traps. + * Syscall() is called directly without going through trap(). + */ + trapenable(VectorBPT, debugbpt, 0, "debugpt"); + trapenable(VectorPF, fault386, 0, "fault386"); + trapenable(Vector2F, doublefault, 0, "doublefault"); + trapenable(Vector15, unexpected, 0, "unexpected"); + + nmienable(); + + addarchfile("irqalloc", 0444, irqallocread, nil); +} + +static char* excname[32] = { + "divide error", + "debug exception", + "nonmaskable interrupt", + "breakpoint", + "overflow", + "bounds check", + "invalid opcode", + "coprocessor not available", + "double fault", + "coprocessor segment overrun", + "invalid TSS", + "segment not present", + "stack exception", + "general protection violation", + "page fault", + "15 (reserved)", + "coprocessor error", + "alignment check", + "machine check", + "19 (reserved)", + "20 (reserved)", + "21 (reserved)", + "22 (reserved)", + "23 (reserved)", + "24 (reserved)", + "25 (reserved)", + "26 (reserved)", + "27 (reserved)", + "28 (reserved)", + "29 (reserved)", + "30 (reserved)", + "31 (reserved)", +}; + +/* + * keep histogram of interrupt service times + */ +void +intrtime(Mach*, int vno) +{ + USED(vno); +} + +/* + * All traps come here. It is slower to have all traps call trap() + * rather than directly vectoring the handler. However, this avoids a + * lot of code duplication and possible bugs. The only exception is + * VectorSYSCALL. + * Trap is called with interrupts disabled via interrupt-gates. + */ +void +trap(Ureg* ureg) +{ + int i, vno; + char buf[ERRMAX]; + Vctl *ctl, *v; + Mach *mach; + + vno = ureg->trap; + if(ctl = vctl[vno]){ + if(ctl->isintr){ + m->intr++; + if(vno >= VectorPIC && vno != VectorSYSCALL) + m->lastintr = ctl->irq; + } + + if(ctl->isr) + ctl->isr(vno); + for(v = ctl; v != nil; v = v->next){ + if(v->f) + v->f(ureg, v->a); + } + if(ctl->eoi) + ctl->eoi(vno); + + if(ctl->isintr){ + if(up && ctl->irq != IrqTIMER && ctl->irq != IrqCLOCK) + preemption(0); + } + } + else if(vno <= nelem(excname) && up->type == Interp){ + spllo(); + sprint(buf, "sys: trap: %s", excname[vno]); + error(buf); + } + else if(vno >= VectorPIC && vno != VectorSYSCALL){ + /* + * An unknown interrupt. + * Check for a default IRQ7. This can happen when + * the IRQ input goes away before the acknowledge. + * In this case, a 'default IRQ7' is generated, but + * the corresponding bit in the ISR isn't set. + * In fact, just ignore all such interrupts. + */ + + /* call all interrupt routines, just in case */ + for(i = VectorPIC; i <= MaxIrqLAPIC; i++){ + ctl = vctl[i]; + if(ctl == nil) + continue; + if(!ctl->isintr) + continue; + for(v = ctl; v != nil; v = v->next){ + if(v->f) + v->f(ureg, v->a); + } + /* should we do this? */ + if(ctl->eoi) + ctl->eoi(i); + } + + /* clear the interrupt */ + i8259isr(vno); + + if(0)print("cpu%d: spurious interrupt %d, last %d", + m->machno, vno, m->lastintr); + if(0)if(conf.nmach > 1){ + for(i = 0; i < 32; i++){ + if(!(active.machs & (1<<i))) + continue; + mach = MACHP(i); + if(m->machno == mach->machno) + continue; + print(" cpu%d: last %d", + mach->machno, mach->lastintr); + } + print("\n"); + } + m->spuriousintr++; + return; + } + else{ + if(vno == VectorNMI){ + nmienable(); + if(m->machno != 0){ + print("cpu%d: PC %8.8luX\n", + m->machno, ureg->pc); + for(;;); + } + } + dumpregs(ureg); + if(vno < nelem(excname)) + panic("%s", excname[vno]); + panic("unknown trap/intr: %d\n", vno); + } + + /* delaysched set because we held a lock or because our quantum ended */ + if(up && up->delaysched){ + sched(); + splhi(); + } +} + +/* + * dump registers + */ +void +dumpregs2(Ureg* ureg) +{ + if(up) + print("cpu%d: registers for %s %lud\n", + m->machno, up->text, up->pid); + else + print("cpu%d: registers for kernel\n", m->machno); + print("FLAGS=%luX TRAP=%luX ECODE=%luX PC=%luX", + ureg->flags, ureg->trap, ureg->ecode, ureg->pc); + print(" SS=%4.4luX USP=%luX\n", ureg->ss & 0xFFFF, ureg->usp); + print(" AX %8.8luX BX %8.8luX CX %8.8luX DX %8.8luX\n", + ureg->ax, ureg->bx, ureg->cx, ureg->dx); + print(" SI %8.8luX DI %8.8luX BP %8.8luX\n", + ureg->si, ureg->di, ureg->bp); + print(" CS %4.4luX DS %4.4luX ES %4.4luX FS %4.4luX GS %4.4luX\n", + ureg->cs & 0xFFFF, ureg->ds & 0xFFFF, ureg->es & 0xFFFF, + ureg->fs & 0xFFFF, ureg->gs & 0xFFFF); +} + +void +dumpregs(Ureg* ureg) +{ + extern ulong etext; + vlong mca, mct; + + dumpregs2(ureg); + + /* + * Processor control registers. + * If machine check exception, time stamp counter, page size extensions + * or enhanced virtual 8086 mode extensions are supported, there is a + * CR4. If there is a CR4 and machine check extensions, read the machine + * check address and machine check type registers if RDMSR supported. + */ + print(" CR0 %8.8lux CR2 %8.8lux CR3 %8.8lux", + getcr0(), getcr2(), getcr3()); + if(m->cpuiddx & 0x9A){ + print(" CR4 %8.8lux", getcr4()); + if((m->cpuiddx & 0xA0) == 0xA0){ + rdmsr(0x00, &mca); + rdmsr(0x01, &mct); + print("\n MCA %8.8llux MCT %8.8llux", mca, mct); + } + } + print("\n ur %lux up %lux\n", ureg, up); +} + +/* + * Fill in enough of Ureg to get a stack trace, and call a function. + * Used by debugging interface rdb. + */ +void +callwithureg(void (*fn)(Ureg*)) +{ + Ureg ureg; + ureg.pc = getcallerpc(&fn); + ureg.sp = (ulong)&fn; + fn(&ureg); +} + +static void +_dumpstack(Ureg *ureg) +{ + ulong l, v, i, estack; + extern ulong etext; + + print("ktrace /kernel/path %.8lux %.8lux\n", ureg->pc, ureg->sp); + i = 0; + if(up + && (ulong)&l >= (ulong)up->kstack + && (ulong)&l <= (ulong)up->kstack+KSTACK) + estack = (ulong)up->kstack+KSTACK; + else if((ulong)&l >= (ulong)m->stack + && (ulong)&l <= (ulong)m+BY2PG) + estack = (ulong)m+MACHSIZE; + else + return; + + for(l=(ulong)&l; l<estack; l+=4){ + v = *(ulong*)l; + if(KTZERO < v && v < (ulong)&etext){ + /* + * we could Pick off general CALL (((uchar*)v)[-5] == 0xE8) + * and CALL indirect through AX (((uchar*)v)[-2] == 0xFF && ((uchar*)v)[-2] == 0xD0), + * but this is too clever and misses faulting address. + */ + print("%.8lux=%.8lux ", l, v); + i++; + } + if(i == 4){ + i = 0; + print("\n"); + } + } + if(i) + print("\n"); +} + +void +dumpstack(void) +{ + callwithureg(_dumpstack); +} + +static void +debugbpt(Ureg* ureg, void*) +{ + char buf[ERRMAX]; + + if(breakhandler != nil){ + breakhandler(ureg, up); + return; + } + if(up == 0) + panic("kernel bpt"); + /* restore pc to instruction that caused the trap */ + ureg->pc--; + sprint(buf, "sys: breakpoint"); + error(buf); +} + +static void +doublefault(Ureg*, void*) +{ + panic("double fault"); +} + +static void +unexpected(Ureg* ureg, void*) +{ + print("unexpected trap %lud; ignoring\n", ureg->trap); +} + +static void +fault386(Ureg* ureg, void*) +{ + ulong addr; + int read, user; + char buf[ERRMAX]; + + addr = getcr2(); + user = (ureg->cs & 0xFFFF) == UESEL; + if(!user && mmukmapsync(addr)) + return; + read = !(ureg->ecode & 2); + spllo(); + snprint(buf, sizeof(buf), "trap: fault %s pc=0x%lux addr=0x%lux", + read ? "read" : "write", ureg->pc, addr); + if(up->type == Interp) + disfault(ureg, buf); + dumpregs(ureg); + panic("fault: %s\n", buf); +} + + + + + +static void +linkproc(void) +{ + spllo(); + up->kpfun(up->arg); + pexit("kproc dying", 0); +} + +void +kprocchild(Proc* p, void (*func)(void*), void* arg) +{ + /* + * gotolabel() needs a word on the stack in + * which to place the return PC used to jump + * to linkproc(). + */ + p->sched.pc = (ulong)linkproc; + p->sched.sp = (ulong)p->kstack+KSTACK-BY2WD; + + p->kpfun = func; + p->arg = arg; +} + + + +ulong +dbgpc(Proc *p) +{ + Ureg *ureg; + + ureg = p->dbgreg; + if(ureg == 0) + return 0; + + return ureg->pc; +} diff --git a/os/pc/tv.h b/os/pc/tv.h new file mode 100644 index 00000000..181563b8 --- /dev/null +++ b/os/pc/tv.h @@ -0,0 +1,15 @@ +static ushort +swab16(ushort u) { + return u; +} + +#define ScreenWidth 640 /* screen width */ +#define ScreenHeight 480 /* screen height */ +#define XCorrection 9 /* correction for x axes (trial and error) */ +#define YCorrection 32 /* correction for y axes (trial and error) */ +#define HSync 1 +#define VSync 1 + +#define AudioChip TEA6320T /* new board has TEA6320T */ + +#define EISA(a) (a) diff --git a/os/pc/uarti8250.c b/os/pc/uarti8250.c new file mode 100644 index 00000000..2d6c0da2 --- /dev/null +++ b/os/pc/uarti8250.c @@ -0,0 +1,740 @@ +#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/uart.h" + +/* + * 8250 UART and compatibles. + */ +enum { + Uart0 = 0x3F8, /* COM1 */ + Uart0IRQ = 4, + Uart1 = 0x2F8, /* COM2 */ + Uart1IRQ = 3, + + UartFREQ = 1843200, +}; + +enum { /* I/O ports */ + Rbr = 0, /* Receiver Buffer (RO) */ + Thr = 0, /* Transmitter Holding (WO) */ + Ier = 1, /* Interrupt Enable */ + Iir = 2, /* Interrupt Identification (RO) */ + Fcr = 2, /* FIFO Control (WO) */ + Lcr = 3, /* Line Control */ + Mcr = 4, /* Modem Control */ + Lsr = 5, /* Line Status */ + Msr = 6, /* Modem Status */ + Scr = 7, /* Scratch Pad */ + Dll = 0, /* Divisor Latch LSB */ + Dlm = 1, /* Divisor Latch MSB */ +}; + +enum { /* Ier */ + Erda = 0x01, /* Enable Received Data Available */ + Ethre = 0x02, /* Enable Thr Empty */ + Erls = 0x04, /* Enable Receiver Line Status */ + Ems = 0x08, /* Enable Modem Status */ +}; + +enum { /* Iir */ + Ims = 0x00, /* Ms interrupt */ + Ip = 0x01, /* Interrupt Pending (not) */ + Ithre = 0x02, /* Thr Empty */ + Irda = 0x04, /* Received Data Available */ + Irls = 0x06, /* Receiver Line Status */ + Ictoi = 0x0C, /* Character Time-out Indication */ + IirMASK = 0x3F, + Ifena = 0xC0, /* FIFOs enabled */ +}; + +enum { /* Fcr */ + FIFOena = 0x01, /* FIFO enable */ + FIFOrclr = 0x02, /* clear Rx FIFO */ + FIFOtclr = 0x04, /* clear Tx FIFO */ + FIFO1 = 0x00, /* Rx FIFO trigger level 1 byte */ + FIFO4 = 0x40, /* 4 bytes */ + FIFO8 = 0x80, /* 8 bytes */ + FIFO14 = 0xC0, /* 14 bytes */ +}; + +enum { /* Lcr */ + Wls5 = 0x00, /* Word Length Select 5 bits/byte */ + Wls6 = 0x01, /* 6 bits/byte */ + Wls7 = 0x02, /* 7 bits/byte */ + Wls8 = 0x03, /* 8 bits/byte */ + WlsMASK = 0x03, + Stb = 0x04, /* 2 stop bits */ + Pen = 0x08, /* Parity Enable */ + Eps = 0x10, /* Even Parity Select */ + Stp = 0x20, /* Stick Parity */ + Brk = 0x40, /* Break */ + Dlab = 0x80, /* Divisor Latch Access Bit */ +}; + +enum { /* Mcr */ + Dtr = 0x01, /* Data Terminal Ready */ + Rts = 0x02, /* Ready To Send */ + Out1 = 0x04, /* no longer in use */ + Ie = 0x08, /* IRQ Enable */ + Dm = 0x10, /* Diagnostic Mode loopback */ +}; + +enum { /* Lsr */ + Dr = 0x01, /* Data Ready */ + Oe = 0x02, /* Overrun Error */ + Pe = 0x04, /* Parity Error */ + Fe = 0x08, /* Framing Error */ + Bi = 0x10, /* Break Interrupt */ + Thre = 0x20, /* Thr Empty */ + Temt = 0x40, /* Tramsmitter Empty */ + FIFOerr = 0x80, /* error in receiver FIFO */ +}; + +enum { /* Msr */ + Dcts = 0x01, /* Delta Cts */ + Ddsr = 0x02, /* Delta Dsr */ + Teri = 0x04, /* Trailing Edge of Ri */ + Ddcd = 0x08, /* Delta Dcd */ + Cts = 0x10, /* Clear To Send */ + Dsr = 0x20, /* Data Set Ready */ + Ri = 0x40, /* Ring Indicator */ + Dcd = 0x80, /* Data Set Ready */ +}; + +typedef struct Ctlr { + int io; + int irq; + int tbdf; + int iena; + + uchar sticky[8]; + + Lock; + int hasfifo; + int checkfifo; + int fena; +} Ctlr; + +extern PhysUart i8250physuart; + +static Ctlr i8250ctlr[2] = { +{ .io = Uart0, + .irq = Uart0IRQ, + .tbdf = BUSUNKNOWN, }, + +{ .io = Uart1, + .irq = Uart1IRQ, + .tbdf = BUSUNKNOWN, }, +}; + +static Uart i8250uart[2] = { +{ .regs = &i8250ctlr[0], + .name = "COM1", + .freq = UartFREQ, + .phys = &i8250physuart, + .special= 0, + .next = &i8250uart[1], }, + +{ .regs = &i8250ctlr[1], + .name = "COM2", + .freq = UartFREQ, + .phys = &i8250physuart, + .special= 0, + .next = nil, }, +}; + +#define csr8r(c, r) inb((c)->io+(r)) +#define csr8w(c, r, v) outb((c)->io+(r), (c)->sticky[(r)]|(v)) + +static long +i8250status(Uart* uart, void* buf, long n, long offset) +{ + char *p; + Ctlr *ctlr; + uchar ier, lcr, mcr, msr; + + ctlr = uart->regs; + p = malloc(READSTR); + mcr = ctlr->sticky[Mcr]; + msr = csr8r(ctlr, Msr); + ier = ctlr->sticky[Ier]; + lcr = ctlr->sticky[Lcr]; + snprint(p, READSTR, + "b%d c%d d%d e%d l%d m%d p%c r%d s%d i%d\n" + "dev(%d) type(%d) framing(%d) overruns(%d) " + "berr(%d) serr(%d)%s%s%s%s\n", + + uart->baud, + uart->hup_dcd, + (msr & Dsr) != 0, + uart->hup_dsr, + (lcr & WlsMASK) + 5, + (ier & Ems) != 0, + (lcr & Pen) ? ((lcr & Eps) ? 'e': 'o'): 'n', + (mcr & Rts) != 0, + (lcr & Stb) ? 2: 1, + ctlr->fena, + + uart->dev, + uart->type, + uart->ferr, + uart->oerr, + uart->berr, + uart->serr, + (msr & Cts) ? " cts": "", + (msr & Dsr) ? " dsr": "", + (msr & Dcd) ? " dcd": "", + (msr & Ri) ? " ring": "" + ); + n = readstr(offset, buf, n, p); + free(p); + + return n; +} + +static void +i8250fifo(Uart* uart, int level) +{ + Ctlr *ctlr; + + ctlr = uart->regs; + if(ctlr->hasfifo == 0) + return; + + /* + * Changing the FIFOena bit in Fcr flushes data + * from both receive and transmit FIFOs; there's + * no easy way to guarantee not losing data on + * the receive side, but it's possible to wait until + * the transmitter is really empty. + */ + ilock(ctlr); + while(!(csr8r(ctlr, Lsr) & Temt)) + ; + + /* + * Set the trigger level, default is the max. + * value. + * Some UARTs require FIFOena to be set before + * other bits can take effect, so set it twice. + */ + ctlr->fena = level; + switch(level){ + case 0: + break; + case 1: + level = FIFO1|FIFOena; + break; + case 4: + level = FIFO4|FIFOena; + break; + case 8: + level = FIFO8|FIFOena; + break; + default: + level = FIFO14|FIFOena; + break; + } + csr8w(ctlr, Fcr, level); + csr8w(ctlr, Fcr, level); + iunlock(ctlr); +} + +static void +i8250dtr(Uart* uart, int on) +{ + Ctlr *ctlr; + + /* + * Toggle DTR. + */ + ctlr = uart->regs; + if(on) + ctlr->sticky[Mcr] |= Dtr; + else + ctlr->sticky[Mcr] &= ~Dtr; + csr8w(ctlr, Mcr, 0); +} + +static void +i8250rts(Uart* uart, int on) +{ + Ctlr *ctlr; + + /* + * Toggle RTS. + */ + ctlr = uart->regs; + if(on) + ctlr->sticky[Mcr] |= Rts; + else + ctlr->sticky[Mcr] &= ~Rts; + csr8w(ctlr, Mcr, 0); +} + +static void +i8250modemctl(Uart* uart, int on) +{ + Ctlr *ctlr; + + ctlr = uart->regs; + ilock(&uart->tlock); + if(on){ + ctlr->sticky[Ier] |= Ems; + csr8w(ctlr, Ier, ctlr->sticky[Ier]); + uart->modem = 1; + uart->cts = csr8r(ctlr, Msr) & Cts; + } + else{ + ctlr->sticky[Ier] &= ~Ems; + csr8w(ctlr, Ier, ctlr->sticky[Ier]); + uart->modem = 0; + uart->cts = 1; + } + iunlock(&uart->tlock); + + /* modem needs fifo */ + (*uart->phys->fifo)(uart, on); +} + +static int +i8250parity(Uart* uart, int parity) +{ + int lcr; + Ctlr *ctlr; + + ctlr = uart->regs; + lcr = ctlr->sticky[Lcr] & ~(Eps|Pen); + + switch(parity){ + case 'e': + lcr |= Eps|Pen; + break; + case 'o': + lcr |= Pen; + break; + case 'n': + break; + default: + return -1; + } + ctlr->sticky[Lcr] = lcr; + csr8w(ctlr, Lcr, 0); + + uart->parity = parity; + + return 0; +} + +static int +i8250stop(Uart* uart, int stop) +{ + int lcr; + Ctlr *ctlr; + + ctlr = uart->regs; + lcr = ctlr->sticky[Lcr] & ~Stb; + + switch(stop){ + case 1: + break; + case 2: + lcr |= Stb; + break; + default: + return -1; + } + ctlr->sticky[Lcr] = lcr; + csr8w(ctlr, Lcr, 0); + + uart->stop = stop; + + return 0; +} + +static int +i8250bits(Uart* uart, int bits) +{ + int lcr; + Ctlr *ctlr; + + ctlr = uart->regs; + lcr = ctlr->sticky[Lcr] & ~WlsMASK; + + switch(bits){ + case 5: + lcr |= Wls5; + break; + case 6: + lcr |= Wls6; + break; + case 7: + lcr |= Wls7; + break; + case 8: + lcr |= Wls8; + break; + default: + return -1; + } + ctlr->sticky[Lcr] = lcr; + csr8w(ctlr, Lcr, 0); + + uart->bits = bits; + + return 0; +} + +static int +i8250baud(Uart* uart, int baud) +{ + ulong bgc; + Ctlr *ctlr; + + /* + * Set the Baud rate by calculating and setting the Baud rate + * Generator Constant. This will work with fairly non-standard + * Baud rates. + */ + if(uart->freq == 0 || baud <= 0) + return -1; + bgc = (uart->freq+8*baud-1)/(16*baud); + + ctlr = uart->regs; + csr8w(ctlr, Lcr, Dlab); + outb(ctlr->io+Dlm, bgc>>8); + outb(ctlr->io+Dll, bgc); + csr8w(ctlr, Lcr, 0); + + uart->baud = baud; + + return 0; +} + +static void +i8250break(Uart* uart, int ms) +{ + Ctlr *ctlr; + + /* + * Send a break. + */ + if(ms <= 0) + ms = 200; + + ctlr = uart->regs; + csr8w(ctlr, Lcr, Brk); + tsleep(&up->sleep, return0, 0, ms); + csr8w(ctlr, Lcr, 0); +} + +static void +i8250kick(Uart* uart) +{ + int i; + Ctlr *ctlr; + + if(uart->cts == 0 || uart->blocked) + return; + + /* + * 128 here is an arbitrary limit to make sure + * we don't stay in this loop too long. If the + * chip's output queue is longer than 128, too + * bad -- presotto + */ + ctlr = uart->regs; + for(i = 0; i < 128; i++){ + if(!(csr8r(ctlr, Lsr) & Thre)) + break; + if(uart->op >= uart->oe && uartstageoutput(uart) == 0) + break; + outb(ctlr->io+Thr, *(uart->op++)); + } +} + +static void +i8250interrupt(Ureg*, void* arg) +{ + Ctlr *ctlr; + Uart *uart; + int iir, lsr, old, r; + + uart = arg; + + ctlr = uart->regs; + for(iir = csr8r(ctlr, Iir); !(iir & Ip); iir = csr8r(ctlr, Iir)){ + switch(iir & IirMASK){ + case Ims: /* Ms interrupt */ + r = csr8r(ctlr, Msr); + if(r & Dcts){ + ilock(&uart->tlock); + old = uart->cts; + uart->cts = r & Cts; + if(old == 0 && uart->cts) + uart->ctsbackoff = 2; + iunlock(&uart->tlock); + } + if(r & Ddsr){ + old = r & Dsr; + if(uart->hup_dsr && uart->dsr && !old) + uart->dohup = 1; + uart->dsr = old; + } + if(r & Ddcd){ + old = r & Dcd; + if(uart->hup_dcd && uart->dcd && !old) + uart->dohup = 1; + uart->dcd = old; + } + break; + case Ithre: /* Thr Empty */ + uartkick(uart); + break; + case Irda: /* Received Data Available */ + case Irls: /* Receiver Line Status */ + case Ictoi: /* Character Time-out Indication */ + /* + * Consume any received data. + * If the received byte came in with a break, + * parity or framing error, throw it away; + * overrun is an indication that something has + * already been tossed. + */ + while((lsr = csr8r(ctlr, Lsr)) & Dr){ + if(lsr & (FIFOerr|Oe)) + uart->oerr++; + if(lsr & Pe) + uart->perr++; + if(lsr & Fe) + uart->ferr++; + r = csr8r(ctlr, Rbr); + if(!(lsr & (Bi|Fe|Pe))) + uartrecv(uart, r); + } + break; + + default: + iprint("weird uart interrupt 0x%2.2uX\n", iir); + break; + } + } +} + +static void +i8250disable(Uart* uart) +{ + Ctlr *ctlr; + + /* + * Turn off DTR and RTS, disable interrupts and fifos. + */ + (*uart->phys->dtr)(uart, 0); + (*uart->phys->rts)(uart, 0); + (*uart->phys->fifo)(uart, 0); + + ctlr = uart->regs; + ctlr->sticky[Ier] = 0; + csr8w(ctlr, Ier, ctlr->sticky[Ier]); + + if(ctlr->iena != 0){ + if(intrdisable(ctlr->irq, i8250interrupt, uart, ctlr->tbdf, uart->name) == 0) + ctlr->iena = 0; + } +} + +static void +i8250enable(Uart* uart, int ie) +{ + Ctlr *ctlr; + + ctlr = uart->regs; + + /* + * Check if there is a FIFO. + * Changing the FIFOena bit in Fcr flushes data + * from both receive and transmit FIFOs; there's + * no easy way to guarantee not losing data on + * the receive side, but it's possible to wait until + * the transmitter is really empty. + * Also, reading the Iir outwith i8250interrupt() + * can be dangerous, but this should only happen + * once, before interrupts are enabled. + */ + ilock(ctlr); + if(!ctlr->checkfifo){ + /* + * Wait until the transmitter is really empty. + */ + while(!(csr8r(ctlr, Lsr) & Temt)) + ; + csr8w(ctlr, Fcr, FIFOena); + if(csr8r(ctlr, Iir) & Ifena) + ctlr->hasfifo = 1; + csr8w(ctlr, Fcr, 0); + ctlr->checkfifo = 1; + } + iunlock(ctlr); + + /* + * Enable interrupts and turn on DTR and RTS. + * Be careful if this is called to set up a polled serial line + * early on not to try to enable interrupts as interrupt- + * -enabling mechanisms might not be set up yet. + */ + if(ie){ + if(ctlr->iena == 0){ + intrenable(ctlr->irq, i8250interrupt, uart, ctlr->tbdf, uart->name); + ctlr->iena = 1; + } + ctlr->sticky[Ier] = Ethre|Erda; + ctlr->sticky[Mcr] |= Ie; + } + else{ + ctlr->sticky[Ier] = 0; + ctlr->sticky[Mcr] = 0; + } + csr8w(ctlr, Ier, ctlr->sticky[Ier]); + csr8w(ctlr, Mcr, ctlr->sticky[Mcr]); + + (*uart->phys->dtr)(uart, 1); + (*uart->phys->rts)(uart, 1); + + /* + * During startup, the i8259 interrupt controller is reset. + * This may result in a lost interrupt from the i8250 uart. + * The i8250 thinks the interrupt is still outstanding and does not + * generate any further interrupts. The workaround is to call the + * interrupt handler to clear any pending interrupt events. + * Note: this must be done after setting Ier. + */ + if(ie) + i8250interrupt(nil, uart); +} + +void* +i8250alloc(int io, int irq, int tbdf) +{ + Ctlr *ctlr; + + if((ctlr = malloc(sizeof(Ctlr))) != nil){ + ctlr->io = io; + ctlr->irq = irq; + ctlr->tbdf = tbdf; + } + + return ctlr; +} + +static Uart* +i8250pnp(void) +{ + return i8250uart; +} + +static int +i8250getc(Uart *uart) +{ + Ctlr *ctlr; + + ctlr = uart->regs; + while(!(csr8r(ctlr, Lsr)&Dr)) + delay(1); + return csr8r(ctlr, Rbr); +} + +static void +i8250putc(Uart *uart, int c) +{ + int i; + Ctlr *ctlr; + + ctlr = uart->regs; + for(i = 0; !(csr8r(ctlr, Lsr)&Thre) && i < 128; i++) + delay(1); + outb(ctlr->io+Thr, c); + for(i = 0; !(csr8r(ctlr, Lsr)&Thre) && i < 128; i++) + delay(1); +} + +PhysUart i8250physuart = { + .name = "i8250", + .pnp = i8250pnp, + .enable = i8250enable, + .disable = i8250disable, + .kick = i8250kick, + .dobreak = i8250break, + .baud = i8250baud, + .bits = i8250bits, + .stop = i8250stop, + .parity = i8250parity, + .modemctl = i8250modemctl, + .rts = i8250rts, + .dtr = i8250dtr, + .status = i8250status, + .fifo = i8250fifo, + .getc = i8250getc, + .putc = i8250putc, +}; + +void +i8250console(void) +{ + Uart *uart; + int n; + char *cmd, *p; + + if((p = getconf("console")) == nil) + return; + n = strtoul(p, &cmd, 0); + if(p == cmd) + return; + switch(n){ + default: + return; + case 0: + uart = &i8250uart[0]; + break; + case 1: + uart = &i8250uart[1]; + break; + } + + (*uart->phys->enable)(uart, 0); + uartctl(uart, "b9600 l8 pn s1"); + if(*cmd != '\0') + uartctl(uart, cmd); + + consuart = uart; + uart->console = 1; +} + +void +i8250mouse(char* which, int (*putc)(Queue*, int), int setb1200) +{ + char *p; + int port; + + port = strtol(which, &p, 0); + if(p == which || port < 0 || port > 1) + error(Ebadarg); + uartmouse(&i8250uart[port], putc, setb1200); +} + +void +i8250setmouseputc(char* which, int (*putc)(Queue*, int)) +{ + char *p; + int port; + + port = strtol(which, &p, 0); + if(p == which || port < 0 || port > 1) + error(Ebadarg); + uartsetmouseputc(&i8250uart[port], putc); + +} diff --git a/os/pc/uartisa.c b/os/pc/uartisa.c new file mode 100644 index 00000000..de4a8dcc --- /dev/null +++ b/os/pc/uartisa.c @@ -0,0 +1,98 @@ +#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/uart.h" + +extern PhysUart i8250physuart; +extern PhysUart isaphysuart; +extern void* i8250alloc(int, int, int); + +static Uart* +uartisa(int ctlrno, ISAConf* isa) +{ + int io; + void *ctlr; + Uart *uart; + char buf[64]; + + io = isa->port; + snprint(buf, sizeof(buf), "%s%d", isaphysuart.name, ctlrno); + if(ioalloc(io, 8, 0, buf) < 0){ + print("uartisa: I/O 0x%uX in use\n", io); + return nil; + } + + uart = malloc(sizeof(Uart)); + ctlr = i8250alloc(io, isa->irq, BUSUNKNOWN); + if(ctlr == nil){ + iofree(io); + free(uart); + return nil; + } + + uart->regs = ctlr; + snprint(buf, sizeof(buf), "COM%d", ctlrno+1); + kstrdup(&uart->name, buf); + uart->freq = isa->freq; + uart->phys = &i8250physuart; + + return uart; +} + +static Uart* +uartisapnp(void) +{ + int ctlrno; + ISAConf isa; + Uart *head, *tail, *uart; + + /* + * Look for up to 4 discrete UARTs on the ISA bus. + * All suitable devices are configured to simply point + * to the generic i8250 driver. + */ + head = tail = nil; + for(ctlrno = 2; ctlrno < 6; ctlrno++){ + memset(&isa, 0, sizeof(isa)); + if(!isaconfig("uart", ctlrno, &isa)) + continue; + if(strcmp(isa.type, "isa") != 0) + continue; + if(isa.port == 0 || isa.irq == 0) + continue; + if(isa.freq == 0) + isa.freq = 1843200; + uart = uartisa(ctlrno, &isa); + if(uart == nil) + continue; + if(head != nil) + tail->next = uart; + else + head = uart; + tail = uart; + } + + return head; +} + +PhysUart isaphysuart = { + .name = "UartISA", + .pnp = uartisapnp, + .enable = nil, + .disable = nil, + .kick = nil, + .dobreak = nil, + .baud = nil, + .bits = nil, + .stop = nil, + .parity = nil, + .modemctl = nil, + .rts = nil, + .dtr = nil, + .status = nil, + .fifo = nil, +}; diff --git a/os/pc/uartpci.c b/os/pc/uartpci.c new file mode 100644 index 00000000..e84592b0 --- /dev/null +++ b/os/pc/uartpci.c @@ -0,0 +1,137 @@ +#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/uart.h" + +extern PhysUart i8250physuart; +extern PhysUart pciphysuart; +extern void* i8250alloc(int, int, int); + +static Uart* +uartpci(int ctlrno, Pcidev* p, int barno, int n, int freq, char* name) +{ + int i, io; + void *ctlr; + char buf[64]; + Uart *head, *uart; + + io = p->mem[barno].bar & ~0x01; + snprint(buf, sizeof(buf), "%s%d", pciphysuart.name, ctlrno); + if(ioalloc(io, p->mem[barno].size, 0, buf) < 0){ + print("uartpci: I/O 0x%uX in use\n", io); + return nil; + } + + head = uart = malloc(sizeof(Uart)*n); + + for(i = 0; i < n; i++){ + ctlr = i8250alloc(io, p->intl, p->tbdf); + io += 8; + if(ctlr == nil) + continue; + + uart->regs = ctlr; + snprint(buf, sizeof(buf), "%s.%8.8uX", name, p->tbdf); + kstrdup(&uart->name, buf); + uart->freq = freq; + uart->phys = &i8250physuart; + if(uart != head) + (uart-1)->next = uart; + uart++; + } + + return head; +} + +static Uart* +uartpcipnp(void) +{ + Pcidev *p; + char *name; + int ctlrno, n, subid; + Uart *head, *tail, *uart; + + /* + * Loop through all PCI devices looking for simple serial + * controllers (ccrb == 0x07) and configure the ones which + * are familiar. All suitable devices are configured to + * simply point to the generic i8250 driver. + */ + head = tail = nil; + ctlrno = 0; + for(p = pcimatch(nil, 0, 0); p != nil; p = pcimatch(p, 0, 0)){ + if(p->ccrb != 0x07 || p->ccru != 0) + continue; + + switch((p->did<<16)|p->vid){ + default: + continue; + case (0x9050<<16)|0x10B5: /* Perle PCI-Fast4 series */ + case (0x9030<<16)|0x10B5: /* Perle Ultraport series */ + /* + * These devices consists of a PLX bridge (the above + * PCI VID+DID) behind which are some 16C654 UARTs. + * Must check the subsystem VID and DID for correct + * match. + */ + subid = pcicfgr16(p, PciSVID); + subid |= pcicfgr16(p, PciSID)<<16; + switch(subid){ + default: + continue; + case (0x0011<<16)|0x12E0: /* Perle PCI-Fast16 */ + n = 16; + name = "PCI-Fast16"; + break; + case (0x0021<<16)|0x12E0: /* Perle PCI-Fast8 */ + n = 8; + name = "PCI-Fast8"; + break; + case (0x0031<<16)|0x12E0: /* Perle PCI-Fast4 */ + n = 4; + name = "PCI-Fast4"; + break; + case (0x0021<<16)|0x155F: /* Perle Ultraport8 */ + n = 8; + name = "Ultraport8"; /* 16C754 UARTs */ + break; + } + uart = uartpci(ctlrno, p, 2, n, 7372800, name); + if(uart == nil) + continue; + break; + } + + if(head != nil) + tail->next = uart; + else + head = uart; + for(tail = uart; tail->next != nil; tail = tail->next) + ; + ctlrno++; + } + + return head; +} + +PhysUart pciphysuart = { + .name = "UartPCI", + .pnp = uartpcipnp, + .enable = nil, + .disable = nil, + .kick = nil, + .dobreak = nil, + .baud = nil, + .bits = nil, + .stop = nil, + .parity = nil, + .modemctl = nil, + .rts = nil, + .dtr = nil, + .status = nil, + .fifo = nil, +}; diff --git a/os/pc/usb.h b/os/pc/usb.h new file mode 100644 index 00000000..32da4d51 --- /dev/null +++ b/os/pc/usb.h @@ -0,0 +1,160 @@ +typedef struct Ctlr Ctlr; +typedef struct Endpt Endpt; +typedef struct Udev Udev; +typedef struct Usbhost Usbhost; + +enum +{ + MaxUsb = 10, /* max number of USB Host Controller Interfaces (Usbhost*) */ + MaxUsbDev = 32, /* max number of attached USB devices, including root hub (Udev*) */ + + /* + * USB packet definitions... + */ + TokIN = 0x69, + TokOUT = 0xE1, + TokSETUP = 0x2D, + + /* request type */ + RH2D = 0<<7, + RD2H = 1<<7, + Rstandard = 0<<5, + Rclass = 1<<5, + Rvendor = 2<<5, + Rdevice = 0, + Rinterface = 1, + Rendpt = 2, + Rother = 3, +}; + +#define Class(csp) ((csp)&0xff) +#define Subclass(csp) (((csp)>>8)&0xff) +#define Proto(csp) (((csp)>>16)&0xff) +#define CSP(c, s, p) ((c) | ((s)<<8) | ((p)<<16)) + +/* + * device endpoint + */ +struct Endpt +{ + Ref; + Lock; + int x; /* index in Udev.ep */ + int id; /* hardware endpoint address */ + int maxpkt; /* maximum packet size (from endpoint descriptor) */ + int data01; /* 0=DATA0, 1=DATA1 */ + uchar eof; + ulong csp; + uchar mode; /* OREAD, OWRITE, ORDWR */ + uchar nbuf; /* number of buffers allowed */ + uchar iso; + uchar debug; + uchar active; /* listed for examination by interrupts */ + int setin; + /* ISO related: */ + int hz; + int remain; /* for packet size calculations */ + int samplesz; + int sched; /* schedule index; -1 if undefined or aperiodic */ + int pollms; /* polling interval in msec */ + int psize; /* (remaining) size of this packet */ + int off; /* offset into packet */ + /* Real-time iso stuff */ + ulong foffset; /* file offset (to detect seeks) */ + ulong poffset; /* offset of next packet to be queued */ + ulong toffset; /* offset associated with time */ + vlong time; /* timeassociated with offset */ + int buffered; /* bytes captured but unread, or written but unsent */ + /* end ISO stuff */ + + Udev* dev; /* owning device */ + + ulong nbytes; + ulong nblocks; + + void *private; + + // all the rest could (should?) move to the driver private structure; except perhaps err + QLock rlock; + Rendez rr; + Queue* rq; + QLock wlock; + Rendez wr; + Queue* wq; + + int ntd; + char* err; // needs to be global for unstall; fix? + + Endpt* activef; /* active endpoint list */ +}; + +/* device parameters */ +enum +{ + /* Udev.state */ + Disabled = 0, + Attached, + Enabled, + Assigned, + Configured, + + /* Udev.class */ + Noclass = 0, + Hubclass = 9, +}; + +/* + * active USB device + */ +struct Udev +{ + Ref; + Lock; + Usbhost *uh; + int x; /* index in usbdev[] */ + int busy; + int state; + int id; + uchar port; /* port number on connecting hub */ + ulong csp; + ushort vid; /* vendor id */ + ushort did; /* product id */ + int ls; + int npt; + Endpt* ep[16]; /* active end points */ + Udev* ports; /* active ports, if hub */ + Udev* next; /* next device on this hub */ +}; + +/* + * One of these per active Host Controller Interface (HCI) + */ +struct Usbhost +{ + ISAConf; /* hardware info */ + int tbdf; /* type+busno+devno+funcno */ + + QLock; /* protects namespace state */ + int idgen; /* version number to distinguish new connections */ + Udev* dev[MaxUsbDev]; /* device endpoints managed by this HCI */ + + void (*init)(Usbhost*); + void (*interrupt)(Ureg*, void*); + + void (*portinfo)(Usbhost*, char*, char*); + void (*portreset)(Usbhost*, int); + void (*portenable)(Usbhost*, int, int); + + void (*epalloc)(Usbhost*, Endpt*); + void (*epfree)(Usbhost*, Endpt*); + void (*epopen)(Usbhost*, Endpt*); + void (*epclose)(Usbhost*, Endpt*); + void (*epmode)(Usbhost*, Endpt*); + + long (*read)(Usbhost*, Endpt*, void*, long, vlong); + long (*write)(Usbhost*, Endpt*, void*, long, vlong, int); + + void *ctlr; +}; + +extern void addusbtype(char*, int(*)(Usbhost*)); diff --git a/os/pc/usbuhci.c b/os/pc/usbuhci.c new file mode 100644 index 00000000..4d65726c --- /dev/null +++ b/os/pc/usbuhci.c @@ -0,0 +1,1538 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#include "usb.h" + +#define XPRINT if(debug)print + +static int Chatty = 0; +static int debug = 0; + +static char Estalled[] = "usb endpoint stalled"; + +/* + * UHCI interface registers and bits + */ +enum +{ + /* i/o space */ + Cmd = 0, + Status = 2, + Usbintr = 4, + Frnum = 6, + Flbaseadd = 8, + SOFMod = 0xC, + Portsc0 = 0x10, + Portsc1 = 0x12, + + /* port status */ + Suspend = 1<<12, + PortReset = 1<<9, + SlowDevice = 1<<8, + ResumeDetect = 1<<6, + PortChange = 1<<3, /* write 1 to clear */ + PortEnable = 1<<2, + StatusChange = 1<<1, /* write 1 to clear */ + DevicePresent = 1<<0, + + NFRAME = 1024, + FRAMESIZE= NFRAME*sizeof(ulong), /* fixed by hardware; aligned to same */ + + Vf = 1<<2, /* TD only */ + IsQH = 1<<1, + Terminate = 1<<0, + + /* TD.status */ + SPD = 1<<29, + ErrLimit0 = 0<<27, + ErrLimit1 = 1<<27, + ErrLimit2 = 2<<27, + ErrLimit3 = 3<<27, + LowSpeed = 1<<26, + IsoSelect = 1<<25, + IOC = 1<<24, + Active = 1<<23, + Stalled = 1<<22, + DataBufferErr = 1<<21, + Babbling = 1<<20, + NAKed = 1<<19, + CRCorTimeout = 1<<18, + BitstuffErr = 1<<17, + AnyError = (Stalled | DataBufferErr | Babbling | NAKed | CRCorTimeout | BitstuffErr), + + /* TD.dev */ + IsDATA1 = 1<<19, + + /* TD.flags (software) */ + CancelTD= 1<<0, + IsoClean= 1<<2, +}; + +static struct +{ + int bit; + char *name; +} +portstatus[] = +{ + { Suspend, "suspend", }, + { PortReset, "reset", }, + { SlowDevice, "lowspeed", }, + { ResumeDetect, "resume", }, + { PortChange, "portchange", }, + { PortEnable, "enable", }, + { StatusChange, "statuschange", }, + { DevicePresent, "present", }, +}; + +typedef struct Ctlr Ctlr; +typedef struct Endptx Endptx; +typedef struct QH QH; +typedef struct TD TD; + +/* + * software structures + */ +struct Ctlr +{ + Lock; /* protects state shared with interrupt (eg, free list) */ + Ctlr* next; + Pcidev* pcidev; + int active; + + int io; + ulong* frames; /* frame list */ + ulong* frameld; /* real time load on each of the frame list entries */ + QLock resetl; /* lock controller during USB reset */ + + TD* tdpool; + TD* freetd; + QH* qhpool; + QH* freeqh; + + QH* ctlq; /* queue for control i/o */ + QH* bwsop; /* empty bandwidth sop (to PIIX4 errata specifications) */ + QH* bulkq; /* queue for bulk i/o (points back to bandwidth sop) */ + QH* recvq; /* receive queues for bulk i/o */ + + Udev* ports[2]; + + struct { + Lock; + Endpt* f; + } activends; + + long usbints; /* debugging */ + long framenumber; + long frameptr; + long usbbogus; +}; + +#define IN(x) ins(ctlr->io+(x)) +#define OUT(x, v) outs(ctlr->io+(x), (v)) + +static Ctlr* ctlrhead; +static Ctlr* ctlrtail; + +struct Endptx +{ + QH* epq; /* queue of TDs for this endpoint */ + + /* ISO related: */ + void* tdalloc; + void* bpalloc; + uchar* bp0; /* first block in array */ + TD * td0; /* first td in array */ + TD * etd; /* pointer into circular list of TDs for isochronous ept */ + TD * xtd; /* next td to be cleaned */ +}; + +/* + * UHCI hardware structures, aligned on 16-byte boundary + */ +struct TD +{ + ulong link; + ulong status; /* controller r/w */ + ulong dev; + ulong buffer; + + /* software */ + ulong flags; + union{ + Block* bp; /* non-iso */ + ulong offset; /* iso */ + }; + Endpt* ep; + TD* next; +}; +#define TFOL(p) ((TD*)KADDR((ulong)(p) & ~(0xF|PCIWINDOW))) + +struct QH +{ + ulong head; + ulong entries; /* address of next TD or QH to process (updated by controller) */ + + /* software */ + QH* hlink; + TD* first; + QH* next; /* free list */ + TD* last; + ulong _d1; /* fillers */ + ulong _d2; +}; +#define QFOL(p) ((QH*)KADDR((ulong)(p) & ~(0xF|PCIWINDOW))) + +static TD * +alloctd(Ctlr *ctlr) +{ + TD *t; + + ilock(ctlr); + t = ctlr->freetd; + if(t == nil) + panic("alloctd"); /* TO DO */ + ctlr->freetd = t->next; + t->next = nil; + iunlock(ctlr); + t->ep = nil; + t->bp = nil; + t->status = 0; + t->link = Terminate; + t->buffer = 0; + t->flags = 0; + return t; +} + +static void +freetd(Ctlr *ctlr, TD *t) +{ + t->ep = nil; + if(t->bp) + freeb(t->bp); + t->bp = nil; + ilock(ctlr); + t->buffer = 0xdeadbeef; + t->next = ctlr->freetd; + ctlr->freetd = t; + iunlock(ctlr); +} + +static void +dumpdata(Block *b, int n) +{ + int i; + + XPRINT("\tb %8.8lux[%d]: ", (ulong)b->rp, n); + if(n > 16) + n = 16; + for(i=0; i<n; i++) + XPRINT(" %2.2ux", b->rp[i]); + XPRINT("\n"); +} + +static void +dumptd(TD *t, int follow) +{ + int i, n; + char buf[20], *s; + TD *t0; + + t0 = t; + while(t){ + i = t->dev & 0xFF; + if(i == TokOUT || i == TokSETUP) + n = ((t->dev>>21) + 1) & 0x7FF; + else if((t->status & Active) == 0) + n = (t->status + 1) & 0x7FF; + else + n = 0; + s = buf; + if(t->status & Active) + *s++ = 'A'; + if(t->status & Stalled) + *s++ = 'S'; + if(t->status & DataBufferErr) + *s++ = 'D'; + if(t->status & Babbling) + *s++ = 'B'; + if(t->status & NAKed) + *s++ = 'N'; + if(t->status & CRCorTimeout) + *s++ = 'T'; + if(t->status & BitstuffErr) + *s++ = 'b'; + if(t->status & LowSpeed) + *s++ = 'L'; + *s = 0; + XPRINT("td %8.8lux: ", t); + XPRINT("l=%8.8lux s=%8.8lux d=%8.8lux b=%8.8lux %8.8lux f=%8.8lux\n", + t->link, t->status, t->dev, t->buffer, t->bp?(ulong)t->bp->rp:0, t->flags); + XPRINT("\ts=%s,ep=%ld,d=%ld,D=%ld\n", + buf, (t->dev>>15)&0xF, (t->dev>>8)&0xFF, (t->dev>>19)&1); + if(debug && t->bp && (t->flags & CancelTD) == 0) + dumpdata(t->bp, n); + if(!follow || t->link & Terminate || t->link & IsQH) + break; + t = TFOL(t->link); + if(t == t0) + break; /* looped */ + } +} + +static TD * +alloctde(Ctlr *ctlr, Endpt *e, int pid, int n) +{ + TD *t; + int tog, id; + + t = alloctd(ctlr); + id = (e->x<<7)|(e->dev->x&0x7F); + tog = 0; + if(e->data01 && pid != TokSETUP) + tog = IsDATA1; + t->ep = e; + t->status = ErrLimit3 | Active | IOC; /* or put IOC only on last? */ + if(e->dev->ls) + t->status |= LowSpeed; + t->dev = ((n-1)<<21) | ((id&0x7FF)<<8) | pid | tog; + return t; +} + +static QH * +allocqh(Ctlr *ctlr) +{ + QH *qh; + + ilock(ctlr); + qh = ctlr->freeqh; + if(qh == nil) + panic("allocqh"); /* TO DO */ + ctlr->freeqh = qh->next; + qh->next = nil; + iunlock(ctlr); + qh->head = Terminate; + qh->entries = Terminate; + qh->hlink = nil; + qh->first = nil; + qh->last = nil; + return qh; +} + +static void +freeqh(Ctlr *ctlr, QH *qh) +{ + ilock(ctlr); + qh->next = ctlr->freeqh; + ctlr->freeqh = qh; + iunlock(ctlr); +} + +static void +dumpqh(QH *q) +{ + int i; + QH *q0; + + q0 = q; + for(i = 0; q != nil && i < 10; i++){ + XPRINT("qh %8.8lux: %8.8lux %8.8lux\n", q, q->head, q->entries); + if((q->entries & Terminate) == 0) + dumptd(TFOL(q->entries), 1); + if(q->head & Terminate) + break; + if((q->head & IsQH) == 0){ + XPRINT("head:"); + dumptd(TFOL(q->head), 1); + break; + } + q = QFOL(q->head); + if(q == q0) + break; /* looped */ + } +} + +static void +queuetd(Ctlr *ctlr, QH *q, TD *t, int vf, char *why) +{ + TD *lt; + + for(lt = t; lt->next != nil; lt = lt->next) + lt->link = PCIWADDR(lt->next) | vf; + lt->link = Terminate; + ilock(ctlr); + XPRINT("queuetd %s: t=%p lt=%p q=%p first=%p last=%p entries=%.8lux\n", + why, t, lt, q, q->first, q->last, q->entries); + if(q->first != nil){ + q->last->link = PCIWADDR(t) | vf; + q->last->next = t; + }else{ + q->first = t; + q->entries = PCIWADDR(t); + } + q->last = lt; + XPRINT(" t=%p q=%p first=%p last=%p entries=%.8lux\n", + t, q, q->first, q->last, q->entries); + dumpqh(q); + iunlock(ctlr); +} + +static void +cleantd(Ctlr *ctlr, TD *t, int discard) +{ + Block *b; + int n, err; + + XPRINT("cleanTD: %8.8lux %8.8lux %8.8lux %8.8lux\n", t->link, t->status, t->dev, t->buffer); + if(t->ep != nil && t->ep->debug) + dumptd(t, 0); + if(t->status & Active) + panic("cleantd Active"); + err = t->status & (AnyError&~NAKed); + /* TO DO: on t->status&AnyError, q->entries will not have advanced */ + if (err) { + XPRINT("cleanTD: Error %8.8lux %8.8lux %8.8lux %8.8lux\n", t->link, t->status, t->dev, t->buffer); +// print("cleanTD: Error %8.8lux %8.8lux %8.8lux %8.8lux\n", t->link, t->status, t->dev, t->buffer); + } + switch(t->dev&0xFF){ + case TokIN: + if(discard || (t->flags & CancelTD) || t->ep == nil || t->ep->x!=0&&err){ + if(t->ep != nil){ + if(err != 0) + t->ep->err = err==Stalled? Estalled: Eio; + wakeup(&t->ep->rr); /* in case anyone cares */ + } + break; + } + b = t->bp; + n = (t->status + 1) & 0x7FF; + if(n > b->lim - b->wp) + n = 0; + b->wp += n; + if(Chatty) + dumpdata(b, n); + t->bp = nil; + t->ep->nbytes += n; + t->ep->nblocks++; + qpass(t->ep->rq, b); /* TO DO: flow control */ + wakeup(&t->ep->rr); /* TO DO */ + break; + case TokSETUP: + XPRINT("cleanTD: TokSETUP %lux\n", &t->ep); + /* don't really need to wakeup: subsequent IN or OUT gives status */ + if(t->ep != nil) { + wakeup(&t->ep->wr); /* TO DO */ + XPRINT("cleanTD: wakeup %lux\n", &t->ep->wr); + } + break; + case TokOUT: + /* TO DO: mark it done somewhere */ + XPRINT("cleanTD: TokOut %lux\n", &t->ep); + if(t->ep != nil){ + if(t->bp){ + n = BLEN(t->bp); + t->ep->nbytes += n; + t->ep->nblocks++; + } + if(t->ep->x!=0 && err != 0) + t->ep->err = err==Stalled? Estalled: Eio; + if(--t->ep->ntd < 0) + panic("cleantd ntd"); + wakeup(&t->ep->wr); /* TO DO */ + XPRINT("cleanTD: wakeup %lux\n", &t->ep->wr); + } + break; + } + freetd(ctlr, t); +} + +static void +cleanq(Ctlr *ctlr, QH *q, int discard, int vf) +{ + TD *t, *tp; + + ilock(ctlr); + tp = nil; + for(t = q->first; t != nil;){ + XPRINT("cleanq: %8.8lux %8.8lux %8.8lux %8.8lux %8.8lux %8.8lux\n", t->link, t->status, t->dev, t->buffer, t->flags, t->next); + if(t->status & Active){ + if(t->status & NAKed){ + t->status = (t->status & ~NAKed) | IOC; /* ensure interrupt next frame */ + tp = t; + t = t->next; + continue; + } + if(t->flags & CancelTD){ + XPRINT("cancelTD: %8.8lux\n", (ulong)t); + t->status = (t->status & ~Active) | IOC; /* ensure interrupt next frame */ + tp = t; + t = t->next; + continue; + } + tp = t; + t = t->next; + continue; + } + t->status &= ~IOC; + if (tp == nil) { + q->first = t->next; + if(q->first != nil) + q->entries = PCIWADDR(q->first); + else + q->entries = Terminate; + } else { + tp->next = t->next; + if (t->next != nil) + tp->link = PCIWADDR(t->next) | vf; + else + tp->link = Terminate; + } + if (q->last == t) + q->last = tp; + iunlock(ctlr); + cleantd(ctlr, t, discard); + ilock(ctlr); + if (tp) + t = tp->next; + else + t = q->first; + XPRINT("t = %8.8lux\n", t); + dumpqh(q); + } + if(q->first && q->entries != PCIWADDR(q->first)){ + ctlr->usbbogus++; + q->entries = PCIWADDR(q->first); + } + iunlock(ctlr); +} + +static void +canceltds(Ctlr *ctlr, QH *q, Endpt *e) +{ + TD *t; + + if(q != nil){ + ilock(ctlr); + for(t = q->first; t != nil; t = t->next) + if(t->ep == e) + t->flags |= CancelTD; + iunlock(ctlr); + XPRINT("cancel:\n"); + dumpqh(q); + } +} + +static void +eptcancel(Ctlr *ctlr, Endpt *e) +{ + Endptx *x; + + if(e == nil) + return; + x = e->private; + canceltds(ctlr, x->epq, e); + canceltds(ctlr, ctlr->ctlq, e); + canceltds(ctlr, ctlr->bulkq, e); +} + +static void +eptactivate(Ctlr *ctlr, Endpt *e) +{ + ilock(&ctlr->activends); + if(e->active == 0){ + XPRINT("activate 0x%p\n", e); + e->active = 1; + e->activef = ctlr->activends.f; + ctlr->activends.f = e; + } + iunlock(&ctlr->activends); +} + +static void +eptdeactivate(Ctlr *ctlr, Endpt *e) +{ + Endpt **l; + + /* could be O(1) but not worth it yet */ + ilock(&ctlr->activends); + if(e->active){ + e->active = 0; + XPRINT("deactivate 0x%p\n", e); + for(l = &ctlr->activends.f; *l != e; l = &(*l)->activef) + if(*l == nil){ + iunlock(&ctlr->activends); + panic("usb eptdeactivate"); + } + *l = e->activef; + } + iunlock(&ctlr->activends); +} + +static void +queueqh(Ctlr *ctlr, QH *qh) +{ + QH *q; + + // See if it's already queued + for (q = ctlr->recvq->next; q; q = q->hlink) + if (q == qh) + return; + if ((qh->hlink = ctlr->recvq->next) == nil) + qh->head = Terminate; + else + qh->head = PCIWADDR(ctlr->recvq->next) | IsQH; + ctlr->recvq->next = qh; + ctlr->recvq->entries = PCIWADDR(qh) | IsQH; +} + +static QH* +qxmit(Ctlr *ctlr, Endpt *e, Block *b, int pid) +{ + TD *t; + int n, vf; + QH *qh; + Endptx *x; + + x = e->private; + if(b != nil){ + n = BLEN(b); + t = alloctde(ctlr, e, pid, n); + t->bp = b; + t->buffer = PCIWADDR(b->rp); + }else + t = alloctde(ctlr, e, pid, 0); + ilock(ctlr); + e->ntd++; + iunlock(ctlr); + if(e->debug) pprint("QTD: %8.8lux n=%ld\n", t, b?BLEN(b): 0); + vf = 0; + if(e->x == 0){ + qh = ctlr->ctlq; + vf = 0; + }else if((qh = x->epq) == nil || e->mode != OWRITE){ + qh = ctlr->bulkq; + vf = Vf; + } + queuetd(ctlr, qh, t, vf, "qxmit"); + return qh; +} + +static QH* +qrcv(Ctlr *ctlr, Endpt *e) +{ + TD *t; + Block *b; + QH *qh; + int vf; + Endptx *x; + + x = e->private; + t = alloctde(ctlr, e, TokIN, e->maxpkt); + b = allocb(e->maxpkt); + t->bp = b; + t->buffer = PCIWADDR(b->wp); + vf = 0; + if(e->x == 0){ + qh = ctlr->ctlq; + }else if((qh = x->epq) == nil || e->mode != OREAD){ + qh = ctlr->bulkq; + vf = Vf; + } + queuetd(ctlr, qh, t, vf, "qrcv"); + return qh; +} + +static int +usbsched(Ctlr *ctlr, int pollms, ulong load) +{ + int i, d, q; + ulong best, worst; + + best = 1000000; + q = -1; + for (d = 0; d < pollms; d++){ + worst = 0; + for (i = d; i < NFRAME; i++){ + if (ctlr->frameld[i] + load > worst) + worst = ctlr->frameld[i] + load; + } + if (worst < best){ + best = worst; + q = d; + } + } + return q; +} + +static int +schedendpt(Ctlr *ctlr, Endpt *e) +{ + TD *td; + Endptx *x; + uchar *bp; + int i, id, ix, size, frnum; + + if(!e->iso || e->sched >= 0) + return 0; + + if (e->active){ + return -1; + } + e->off = 0; + e->sched = usbsched(ctlr, e->pollms, e->maxpkt); + if(e->sched < 0) + return -1; + + x = e->private; + if (x->tdalloc || x->bpalloc) + panic("usb: tdalloc/bpalloc"); + x->tdalloc = mallocz(0x10 + NFRAME*sizeof(TD), 1); + x->bpalloc = mallocz(0x10 + e->maxpkt*NFRAME/e->pollms, 1); + x->td0 = (TD*)(((ulong)x->tdalloc + 0xf) & ~0xf); + x->bp0 = (uchar *)(((ulong)x->bpalloc + 0xf) & ~0xf); + frnum = (IN(Frnum) + 1) & 0x3ff; + frnum = (frnum & ~(e->pollms - 1)) + e->sched; + x->xtd = &x->td0[(frnum+8)&0x3ff]; /* Next td to finish */ + x->etd = nil; + e->remain = 0; + e->nbytes = 0; + td = x->td0; + for(i = e->sched; i < NFRAME; i += e->pollms){ + bp = x->bp0 + e->maxpkt*i/e->pollms; + td->buffer = PCIWADDR(bp); + td->ep = e; + td->next = &td[1]; + ctlr->frameld[i] += e->maxpkt; + td++; + } + td[-1].next = x->td0; + for(i = e->sched; i < NFRAME; i += e->pollms){ + ix = (frnum+i) & 0x3ff; + td = &x->td0[ix]; + + id = (e->x<<7)|(e->dev->x&0x7F); + if (e->mode == OREAD) + /* enable receive on this entry */ + td->dev = ((e->maxpkt-1)<<21) | ((id&0x7FF)<<8) | TokIN; + else{ + size = (e->hz + e->remain)*e->pollms/1000; + e->remain = (e->hz + e->remain)*e->pollms%1000; + size *= e->samplesz; + td->dev = ((size-1)<<21) | ((id&0x7FF)<<8) | TokOUT; + } + td->status = ErrLimit1 | Active | IsoSelect | IOC; + td->link = ctlr->frames[ix]; + td->flags |= IsoClean; + ctlr->frames[ix] = PCIWADDR(td); + } + return 0; +} + +static void +unschedendpt(Ctlr *ctlr, Endpt *e) +{ + int q; + TD *td; + Endptx *x; + ulong *addr; + + if(!e->iso || e->sched < 0) + return; + + x = e->private; + if (x->tdalloc == nil) + panic("tdalloc"); + for (q = e->sched; q < NFRAME; q += e->pollms){ + td = x->td0++; + addr = &ctlr->frames[q]; + while(*addr != PADDR(td)) { + if(*addr & IsQH) + panic("usb: TD expected"); + addr = &TFOL(*addr)->link; + } + *addr = td->link; + ctlr->frameld[q] -= e->maxpkt; + } + free(x->tdalloc); + free(x->bpalloc); + x->tdalloc = nil; + x->bpalloc = nil; + x->etd = nil; + x->td0 = nil; + e->sched = -1; +} + +static void +epalloc(Usbhost *uh, Endpt *e) +{ + Endptx *x; + + x = malloc(sizeof(Endptx)); + e->private = x; + x->epq = allocqh(uh->ctlr); + if(x->epq == nil) + panic("devendptx"); +} + +static void +epfree(Usbhost *uh, Endpt *e) +{ + Ctlr *ctlr; + Endptx *x; + + ctlr = uh->ctlr; + x = e->private; + if(x->epq != nil) + freeqh(ctlr, x->epq); +} + +static void +epopen(Usbhost *uh, Endpt *e) +{ + Ctlr *ctlr; + + ctlr = uh->ctlr; + if(e->iso && e->active) + error("already open"); + if(schedendpt(ctlr, e) < 0){ + if(e->active) + error("cannot schedule USB endpoint, active"); + else + error("cannot schedule USB endpoint"); + } + eptactivate(ctlr, e); +} + +static void +epclose(Usbhost *uh, Endpt *e) +{ + Ctlr *ctlr; + + ctlr = uh->ctlr; + eptdeactivate(ctlr, e); + unschedendpt(ctlr, e); +} + +static void +epmode(Usbhost *uh, Endpt *e) +{ + Ctlr *ctlr; + Endptx *x; + + ctlr = uh->ctlr; + x = e->private; + if(e->iso) { + if(x->epq != nil) { + freeqh(ctlr, x->epq); + x->epq = nil; + } + } + else { + /* Each bulk device gets a queue head hanging off the + * bulk queue head + */ + if(x->epq == nil) { + x->epq = allocqh(ctlr); + if(x->epq == nil) + panic("epbulk: allocqh"); + } + queueqh(ctlr, x->epq); + } +} + +static int ioport[] = {-1, Portsc0, Portsc1}; + +static void +portreset(Usbhost *uh, int port) +{ + int i, p; + Ctlr *ctlr; + + ctlr = uh->ctlr; + if(port != 1 && port != 2) + error(Ebadarg); + + /* should check that device not being configured on other port? */ + p = ioport[port]; + qlock(&ctlr->resetl); + if(waserror()){ + qunlock(&ctlr->resetl); + nexterror(); + } + XPRINT("r: %x\n", IN(p)); + ilock(ctlr); + OUT(p, PortReset); + delay(12); /* BUG */ + XPRINT("r2: %x\n", IN(p)); + OUT(p, IN(p) & ~PortReset); + XPRINT("r3: %x\n", IN(p)); + OUT(p, IN(p) | PortEnable); + microdelay(64); + for(i=0; i<1000 && (IN(p) & PortEnable) == 0; i++) + ; + XPRINT("r': %x %d\n", IN(p), i); + OUT(p, (IN(p) & ~PortReset)|PortEnable); + iunlock(ctlr); + poperror(); + qunlock(&ctlr->resetl); +} + +static void +portenable(Usbhost *uh, int port, int on) +{ + int w, p; + Ctlr *ctlr; + + ctlr = uh->ctlr; + if(port != 1 && port != 2) + error(Ebadarg); + + /* should check that device not being configured on other port? */ + p = ioport[port]; + qlock(&ctlr->resetl); + if(waserror()){ + qunlock(&ctlr->resetl); + nexterror(); + } + ilock(ctlr); + w = IN(p); + if(on) + w |= PortEnable; + else + w &= ~PortEnable; + OUT(p, w); + microdelay(64); + iunlock(ctlr); + XPRINT("e: %x\n", IN(p)); + poperror(); + qunlock(&ctlr->resetl); +} + +static void +portinfo(Usbhost *uh, char *s, char *se) +{ + int x, i, j; + Ctlr *ctlr; + + ctlr = uh->ctlr; + for(i = 1; i <= 2; i++) { + ilock(ctlr); + x = IN(ioport[i]); + if((x & (PortChange|StatusChange)) != 0) + OUT(ioport[i], x); + iunlock(ctlr); + s = seprint(s, se, "%d %ux", i, x); + for(j = 0; j < nelem(portstatus); j++) { + if((x & portstatus[j].bit) != 0) + s = seprint(s, se, " %s", portstatus[j].name); + } + s = seprint(s, se, "\n"); + } +} + +static void +cleaniso(Endpt *e, int frnum) +{ + TD *td; + int id, n, i; + Endptx *x; + uchar *bp; + + x = e->private; + td = x->xtd; + if (td->status & Active) + return; + id = (e->x<<7)|(e->dev->x&0x7F); + do { + if (td->status & AnyError) + XPRINT("usbisoerror 0x%lux\n", td->status); + n = (td->status + 1) & 0x3ff; + e->nbytes += n; + if ((td->flags & IsoClean) == 0) + e->nblocks++; + if (e->mode == OREAD){ + e->buffered += n; + e->poffset += (td->status + 1) & 0x3ff; + td->offset = e->poffset; + td->dev = ((e->maxpkt -1)<<21) | ((id&0x7FF)<<8) | TokIN; + e->toffset = td->offset; + }else{ + if ((td->flags & IsoClean) == 0){ + e->buffered -= n; + if (e->buffered < 0){ +// print("e->buffered %d?\n", e->buffered); + e->buffered = 0; + } + } + e->toffset = td->offset; + n = (e->hz + e->remain)*e->pollms/1000; + e->remain = (e->hz + e->remain)*e->pollms%1000; + n *= e->samplesz; + td->dev = ((n -1)<<21) | ((id&0x7FF)<<8) | TokOUT; + td->offset = e->poffset; + e->poffset += n; + } + td = td->next; + if (x->xtd == td){ + XPRINT("@"); + break; + } + } while ((td->status & Active) == 0); + e->time = todget(nil); + x->xtd = td; + for (n = 2; n < 4; n++){ + i = ((frnum + n)&0x3ff); + td = x->td0 + i; + bp = x->bp0 + e->maxpkt*i/e->pollms; + if (td->status & Active) + continue; + + if (e->mode == OWRITE){ + if (td == x->etd) { + XPRINT("*"); + memset(bp+e->off, 0, e->maxpkt-e->off); + if (e->off == 0) + td->flags |= IsoClean; + else + e->buffered += (((td->dev>>21) +1) & 0x3ff) - e->off; + x->etd = nil; + }else if ((td->flags & IsoClean) == 0){ + XPRINT("-"); + memset(bp, 0, e->maxpkt); + td->flags |= IsoClean; + } + } else { + /* Unread bytes are now lost */ + e->buffered -= (td->status + 1) & 0x3ff; + } + td->status = ErrLimit1 | Active | IsoSelect | IOC; + } + wakeup(&e->wr); +} + +static void +interrupt(Ureg*, void *a) +{ + QH *q; + Ctlr *ctlr; + Endpt *e; + Endptx *x; + int s, frnum; + Usbhost *uh; + + uh = a; + ctlr = uh->ctlr; + s = IN(Status); + ctlr->frameptr = inl(ctlr->io+Flbaseadd); + ctlr->framenumber = IN(Frnum) & 0x3ff; + OUT(Status, s); + if ((s & 0x1f) == 0) + return; + ctlr->usbints++; + frnum = IN(Frnum) & 0x3ff; + if (s & 0x1a) { + XPRINT("cmd #%x sofmod #%x\n", IN(Cmd), inb(ctlr->io+SOFMod)); + XPRINT("sc0 #%x sc1 #%x\n", IN(Portsc0), IN(Portsc1)); + } + + ilock(&ctlr->activends); + for(e = ctlr->activends.f; e != nil; e = e->activef) { + x = e->private; + if(!e->iso && x->epq != nil) { + XPRINT("cleanq(ctlr, x->epq, 0, 0)\n"); + cleanq(ctlr, x->epq, 0, 0); + } + if(e->iso) { + XPRINT("cleaniso(e)\n"); + cleaniso(e, frnum); + } + } + iunlock(&ctlr->activends); + XPRINT("cleanq(ctlr, ctlr->ctlq, 0, 0)\n"); + cleanq(ctlr, ctlr->ctlq, 0, 0); + XPRINT("cleanq(ctlr, ctlr->bulkq, 0, Vf)\n"); + cleanq(ctlr, ctlr->bulkq, 0, Vf); + XPRINT("clean recvq\n"); + for (q = ctlr->recvq->next; q; q = q->hlink) { + XPRINT("cleanq(ctlr, q, 0, Vf)\n"); + cleanq(ctlr, q, 0, Vf); + } +} + +static int +eptinput(void *arg) +{ + Endpt *e; + + e = arg; + return e->eof || e->err || qcanread(e->rq); +} + +static int +isoreadyx(Endptx *x) +{ + return x->etd == nil || (x->etd != x->xtd && (x->etd->status & Active) == 0); +} + +static int +isoready(void *arg) +{ + int ret; + Ctlr *ctlr; + Endpt *e; + Endptx *x; + + e = arg; + ctlr = e->dev->uh->ctlr; + x = e->private; + ilock(&ctlr->activends); + ret = isoreadyx(x); + iunlock(&ctlr->activends); + return ret; +} + +static long +isoio(Ctlr *ctlr, Endpt *e, void *a, long n, ulong offset, int w) +{ + TD *td; + Endptx *x; + int i, frnum; + uchar *p, *q, *bp; + volatile int isolock; + + x = e->private; + qlock(&e->rlock); + isolock = 0; + if(waserror()){ + if (isolock){ + isolock = 0; + iunlock(&ctlr->activends); + } + qunlock(&e->rlock); + eptcancel(ctlr, e); + nexterror(); + } + p = a; + if (offset != 0 && offset != e->foffset){ + iprint("offset %lud, foffset %lud\n", offset, e->foffset); + /* Seek to a specific position */ + frnum = (IN(Frnum) + 8) & 0x3ff; + td = x->td0 +frnum; + if (offset < td->offset) + error("ancient history"); + while (offset > e->toffset){ + tsleep(&e->wr, return0, 0, 500); + } + while (offset >= td->offset + ((w?(td->dev >> 21):td->status) + 1) & 0x7ff){ + td = td->next; + if (td == x->xtd) + iprint("trouble\n"); + } + ilock(&ctlr->activends); + isolock = 1; + e->off = td->offset - offset; + if (e->off >= e->maxpkt){ + iprint("I can't program: %d\n", e->off); + e->off = 0; + } + x->etd = td; + e->foffset = offset; + } + do { + if (isolock == 0){ + ilock(&ctlr->activends); + isolock = 1; + } + td = x->etd; + if (td == nil || e->off == 0){ + if (td == nil){ + XPRINT("0"); + if (w){ + frnum = (IN(Frnum) + 1) & 0x3ff; + td = x->td0 + frnum; + while(td->status & Active) + td = td->next; + }else{ + frnum = (IN(Frnum) - 4) & 0x3ff; + td = x->td0 + frnum; + while(td->next != x->xtd) + td = td->next; + } + x->etd = td; + e->off = 0; + }else{ + /* New td, make sure it's ready */ + while (isoreadyx(x) == 0){ + isolock = 0; + iunlock(&ctlr->activends); + sleep(&e->wr, isoready, e); + ilock(&ctlr->activends); + isolock = 1; + } + if (x->etd == nil){ + XPRINT("!"); + continue; + } + } + if (w) + e->psize = ((td->dev >> 21) + 1) & 0x7ff; + else + e->psize = (x->etd->status + 1) & 0x7ff; + if(e->psize > e->maxpkt) + panic("packet size > maximum"); + } + if((i = n) >= e->psize) + i = e->psize; + if (w) + e->buffered += i; + else{ + e->buffered -= i; + if (e->buffered < 0) + e->buffered = 0; + } + isolock = 0; + iunlock(&ctlr->activends); + td->flags &= ~IsoClean; + bp = x->bp0 + (td - x->td0) * e->maxpkt / e->pollms; + q = bp + e->off; + if (w){ + memmove(q, p, i); + }else{ + memmove(p, q, i); + } + p += i; + n -= i; + e->off += i; + e->psize -= i; + if (e->psize){ + if (n != 0) + panic("usb iso: can't happen"); + break; + } + if(w) + td->offset = offset + (p-(uchar*)a) - (((td->dev >> 21) + 1) & 0x7ff); + td->status = ErrLimit3 | Active | IsoSelect | IOC; + x->etd = td->next; + e->off = 0; + } while(n > 0); + n = p-(uchar*)a; + e->foffset += n; + poperror(); + if (isolock) + iunlock(&ctlr->activends); + qunlock(&e->rlock); + return n; +} + +static long +read(Usbhost *uh, Endpt *e, void *a, long n, vlong offset) +{ + long l, i; + Block *b; + Ctlr *ctlr; + uchar *p; + + ctlr = uh->ctlr; + if(e->iso) + return isoio(ctlr, e, a, n, (ulong)offset, 0); + + XPRINT("qlock(%p)\n", &e->rlock); + qlock(&e->rlock); + XPRINT("got qlock(%p)\n", &e->rlock); + if(waserror()){ + qunlock(&e->rlock); + eptcancel(ctlr, e); + nexterror(); + } + p = a; + do { + if(e->eof) { + XPRINT("e->eof\n"); + break; + } + if(e->err) + error(e->err); + qrcv(ctlr, e); + if(!e->iso) + e->data01 ^= 1; + sleep(&e->rr, eptinput, e); + if(e->err) + error(e->err); + b = qget(e->rq); /* TO DO */ + if(b == nil) { + XPRINT("b == nil\n"); + break; + } + if(waserror()){ + freeb(b); + nexterror(); + } + l = BLEN(b); + if((i = l) > n) + i = n; + if(i > 0){ + memmove(p, b->rp, i); + p += i; + } + poperror(); + freeb(b); + n -= i; + if (l != e->maxpkt) + break; + } while (n > 0); + poperror(); + qunlock(&e->rlock); + return p-(uchar*)a; +} + +static int +qisempty(void *arg) +{ + return ((QH*)arg)->entries & Terminate; +} + +static long +write(Usbhost *uh, Endpt *e, void *a, long n, vlong offset, int tok) +{ + int i, j; + QH *qh; + Block *b; + Ctlr *ctlr; + uchar *p; + + ctlr = uh->ctlr; + if(e->iso) + return isoio(ctlr, e, a, n, (ulong)offset, 1); + + p = a; + qlock(&e->wlock); + if(waserror()){ + qunlock(&e->wlock); + eptcancel(ctlr, e); + nexterror(); + } + do { + if(e->err) + error(e->err); + if((i = n) >= e->maxpkt) + i = e->maxpkt; + b = allocb(i); + if(waserror()){ + freeb(b); + nexterror(); + } + XPRINT("out [%d]", i); + for (j = 0; j < i; j++) XPRINT(" %.2x", p[j]); + XPRINT("\n"); + memmove(b->wp, p, i); + b->wp += i; + p += i; + n -= i; + poperror(); + qh = qxmit(ctlr, e, b, tok); + tok = TokOUT; + e->data01 ^= 1; + if(e->ntd >= e->nbuf) { +XPRINT("qh %s: q=%p first=%p last=%p entries=%.8lux\n", + "writeusb sleep", qh, qh->first, qh->last, qh->entries); + XPRINT("write: sleep %lux\n", &e->wr); + sleep(&e->wr, qisempty, qh); + XPRINT("write: awake\n"); + } + } while(n > 0); + poperror(); + qunlock(&e->wlock); + return p-(uchar*)a; +} + +static void +init(Usbhost* uh) +{ + Ctlr *ctlr; + + ctlr = uh->ctlr; + ilock(ctlr); + outl(ctlr->io+Flbaseadd, PCIWADDR(ctlr->frames)); + OUT(Frnum, 0); + OUT(Usbintr, 0xF); /* enable all interrupts */ + XPRINT("cmd 0x%x sofmod 0x%x\n", IN(Cmd), inb(ctlr->io+SOFMod)); + XPRINT("sc0 0x%x sc1 0x%x\n", IN(Portsc0), IN(Portsc1)); + if((IN(Cmd)&1)==0) + OUT(Cmd, 1); /* run */ +// pprint("at: c=%x s=%x c0=%x\n", IN(Cmd), IN(Status), IN(Portsc0)); + iunlock(ctlr); +} + +static void +scanpci(void) +{ + int io; + Ctlr *ctlr; + Pcidev *p; + static int already = 0; + + if(already) + return; + already = 1; + p = nil; + while(p = pcimatch(p, 0, 0)) { + /* + * Find UHCI controllers. Class = 12 (serial controller), + * Sub-class = 3 (USB) and Programming Interface = 0. + */ + if(p->ccrb != 0x0C || p->ccru != 0x03 || p->ccrp != 0x00) + continue; + io = p->mem[4].bar & ~0x0F; + if(io == 0) { + print("usbuhci: failed to map registers\n"); + continue; + } + if(ioalloc(io, p->mem[4].size, 0, "usbuhci") < 0){ + print("usbuhci: port %d in use\n", io); + continue; + } + if(p->intl == 0xFF || p->intl == 0) { + print("usbuhci: no irq assigned for port %d\n", io); + continue; + } + + XPRINT("usbuhci: %x/%x port 0x%ux size 0x%x irq %d\n", + p->vid, p->did, io, p->mem[4].size, p->intl); + + ctlr = malloc(sizeof(Ctlr)); + ctlr->pcidev = p; + ctlr->io = io; + if(ctlrhead != nil) + ctlrtail->next = ctlr; + else + ctlrhead = ctlr; + ctlrtail = ctlr; + } +} + +static int +reset(Usbhost *uh) +{ + int i; + TD *t; + ulong io; + Ctlr *ctlr; + Pcidev *p; + + scanpci(); + + /* + * Any adapter matches if no uh->port is supplied, + * otherwise the ports must match. + */ + for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){ + if(ctlr->active) + continue; + if(uh->port == 0 || uh->port == ctlr->io){ + ctlr->active = 1; + break; + } + } + if(ctlr == nil) + return -1; + + io = ctlr->io; + p = ctlr->pcidev; + + uh->ctlr = ctlr; + uh->port = io; + uh->irq = p->intl; + uh->tbdf = p->tbdf; + + XPRINT("usbcmd\t0x%.4x\nusbsts\t0x%.4x\nusbintr\t0x%.4x\nfrnum\t0x%.2x\n", + IN(Cmd), IN(Status), IN(Usbintr), inb(io+Frnum)); + XPRINT("frbaseadd\t0x%.4x\nsofmod\t0x%x\nportsc1\t0x%.4x\nportsc2\t0x%.4x\n", + IN(Flbaseadd), inb(io+SOFMod), IN(Portsc0), IN(Portsc1)); + + OUT(Cmd, 0); /* stop */ + while((IN(Status) & (1<<5)) == 0) /* wait for halt */ + ; + OUT(Status, 0xFF); /* clear pending interrupts */ + pcicfgw16(p, 0xc0, 0x2000); /* legacy support register: turn off lunacy mode */ + + if(0){ + i = inb(io+SOFMod); + OUT(Cmd, 4); /* global reset */ + delay(15); + OUT(Cmd, 0); /* end reset */ + delay(4); + outb(io+SOFMod, i); + } + + ctlr->tdpool = xspanalloc(128*sizeof(TD), 16, 0); + for(i=128; --i>=0;){ + ctlr->tdpool[i].next = ctlr->freetd; + ctlr->freetd = &ctlr->tdpool[i]; + } + ctlr->qhpool = xspanalloc(64*sizeof(QH), 16, 0); + for(i=64; --i>=0;){ + ctlr->qhpool[i].next = ctlr->freeqh; + ctlr->freeqh = &ctlr->qhpool[i]; + } + + /* + * the last entries of the periodic (interrupt & isochronous) scheduling TD entries + * points to the control queue and the bandwidth sop for bulk traffic. + * this is looped following the instructions in PIIX4 errata 29773804.pdf: + * a QH links to a looped but inactive TD as its sole entry, + * with its head entry leading on to the bulk traffic, the last QH of which + * links back to the empty QH. + */ + ctlr->ctlq = allocqh(ctlr); + ctlr->bwsop = allocqh(ctlr); + ctlr->bulkq = allocqh(ctlr); + ctlr->recvq = allocqh(ctlr); + t = alloctd(ctlr); /* inactive TD, looped */ + t->link = PCIWADDR(t); + ctlr->bwsop->entries = PCIWADDR(t); + + ctlr->ctlq->head = PCIWADDR(ctlr->bulkq) | IsQH; + ctlr->bulkq->head = PCIWADDR(ctlr->recvq) | IsQH; + ctlr->recvq->head = PCIWADDR(ctlr->bwsop) | IsQH; + if (1) /* don't use loop back */ + ctlr->bwsop->head = Terminate; + else /* set up loop back */ + ctlr->bwsop->head = PCIWADDR(ctlr->bwsop) | IsQH; + + ctlr->frames = xspanalloc(FRAMESIZE, FRAMESIZE, 0); + ctlr->frameld = xallocz(FRAMESIZE, 1); + for (i = 0; i < NFRAME; i++) + ctlr->frames[i] = PCIWADDR(ctlr->ctlq) | IsQH; + + /* + * Linkage to the generic USB driver. + */ + uh->init = init; + uh->interrupt = interrupt; + + uh->portinfo = portinfo; + uh->portreset = portreset; + uh->portenable = portenable; + + uh->epalloc = epalloc; + uh->epfree = epfree; + uh->epopen = epopen; + uh->epclose = epclose; + uh->epmode = epmode; + + uh->read = read; + uh->write = write; + + return 0; +} + +void +usbuhcilink(void) +{ + addusbtype("uhci", reset); +} diff --git a/os/pc/vga.c b/os/pc/vga.c new file mode 100644 index 00000000..0d6c7743 --- /dev/null +++ b/os/pc/vga.c @@ -0,0 +1,241 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#define Image IMAGE +#include <draw.h> +#include <memdraw.h> +#include <cursor.h> +#include "screen.h" + +static Memimage* back; +static Memimage *conscol; + +static Point curpos; +static Rectangle window; +static int *xp; +static int xbuf[256]; +static Lock vgascreenlock; +int drawdebug; + +void +vgaimageinit(ulong chan) +{ + if(back == nil){ + back = allocmemimage(Rect(0,0,1,1), chan); /* RSC BUG */ + if(back == nil) + panic("back alloc"); /* RSC BUG */ + back->flags |= Frepl; + back->clipr = Rect(-0x3FFFFFF, -0x3FFFFFF, 0x3FFFFFF, 0x3FFFFFF); + memfillcolor(back, DBlack); + } + + if(conscol == nil){ + conscol = allocmemimage(Rect(0,0,1,1), chan); /* RSC BUG */ + if(conscol == nil) + panic("conscol alloc"); /* RSC BUG */ + conscol->flags |= Frepl; + conscol->clipr = Rect(-0x3FFFFFF, -0x3FFFFFF, 0x3FFFFFF, 0x3FFFFFF); + memfillcolor(conscol, DWhite); + } +} + +static void +vgascroll(VGAscr* scr) +{ + int h, o; + Point p; + Rectangle r; + + h = scr->memdefont->height; + o = 8*h; + r = Rpt(window.min, Pt(window.max.x, window.max.y-o)); + p = Pt(window.min.x, window.min.y+o); + memimagedraw(scr->gscreen, r, scr->gscreen, p, nil, p, S); + r = Rpt(Pt(window.min.x, window.max.y-o), window.max); + memimagedraw(scr->gscreen, r, back, ZP, nil, ZP, S); + + curpos.y -= o; +} + +static void +vgascreenputc(VGAscr* scr, char* buf, Rectangle *flushr) +{ + Point p; + int h, w, pos; + Rectangle r; + +// drawdebug = 1; + if(xp < xbuf || xp >= &xbuf[sizeof(xbuf)]) + xp = xbuf; + + h = scr->memdefont->height; + switch(buf[0]){ + + case '\n': + if(curpos.y+h >= window.max.y){ + vgascroll(scr); + *flushr = window; + } + curpos.y += h; + vgascreenputc(scr, "\r", flushr); + break; + + case '\r': + xp = xbuf; + curpos.x = window.min.x; + break; + + case '\t': + p = memsubfontwidth(scr->memdefont, " "); + w = p.x; + if(curpos.x >= window.max.x-4*w) + vgascreenputc(scr, "\n", flushr); + + pos = (curpos.x-window.min.x)/w; + pos = 4-(pos%4); + *xp++ = curpos.x; + r = Rect(curpos.x, curpos.y, curpos.x+pos*w, curpos.y + h); + memimagedraw(scr->gscreen, r, back, back->r.min, nil, back->r.min, S); + curpos.x += pos*w; + break; + + case '\b': + if(xp <= xbuf) + break; + xp--; + r = Rect(*xp, curpos.y, curpos.x, curpos.y+h); + memimagedraw(scr->gscreen, r, back, back->r.min, nil, ZP, S); + combinerect(flushr, r); + curpos.x = *xp; + break; + + case '\0': + break; + + default: + p = memsubfontwidth(scr->memdefont, buf); + w = p.x; + + if(curpos.x >= window.max.x-w) + vgascreenputc(scr, "\n", flushr); + + *xp++ = curpos.x; + r = Rect(curpos.x, curpos.y, curpos.x+w, curpos.y+h); + memimagedraw(scr->gscreen, r, back, back->r.min, nil, back->r.min, S); + memimagestring(scr->gscreen, curpos, conscol, ZP, scr->memdefont, buf); + combinerect(flushr, r); + curpos.x += w; + } +// drawdebug = 0; +} + +static void +vgascreenputs(char* s, int n) +{ + int i; + Rune r; + char buf[4]; + VGAscr *scr; + Rectangle flushr; + + scr = &vgascreen[0]; + + if(!islo()){ + /* + * Don't deadlock trying to + * print in an interrupt. + */ + if(!canlock(&vgascreenlock)) + return; + } + else + lock(&vgascreenlock); + + flushr = Rect(10000, 10000, -10000, -10000); + + while(n > 0){ + i = chartorune(&r, s); + if(i == 0){ + s++; + --n; + continue; + } + memmove(buf, s, i); + buf[i] = 0; + n -= i; + s += i; + vgascreenputc(scr, buf, &flushr); + } + flushmemscreen(flushr); + + unlock(&vgascreenlock); +} + +void +vgascreenwin(VGAscr* scr) +{ + int h, w; + + h = scr->memdefont->height; + w = scr->memdefont->info[' '].width; + + window = insetrect(scr->gscreen->r, 48); + window.max.x = window.min.x+((window.max.x-window.min.x)/w)*w; + window.max.y = window.min.y+((window.max.y-window.min.y)/h)*h; + curpos = window.min; + + screenputs = vgascreenputs; +} + +/* + * Supposedly this is the way to turn DPMS + * monitors off using just the VGA registers. + * Unfortunately, it seems to mess up the video mode + * on the cards I've tried. + */ +void +vgablank(VGAscr*, int blank) +{ + uchar seq1, crtc17; + + if(blank) { + seq1 = 0x00; + crtc17 = 0x80; + } else { + seq1 = 0x20; + crtc17 = 0x00; + } + + outs(Seqx, 0x0100); /* synchronous reset */ + seq1 |= vgaxi(Seqx, 1) & ~0x20; + vgaxo(Seqx, 1, seq1); + crtc17 |= vgaxi(Crtx, 0x17) & ~0x80; + delay(10); + vgaxo(Crtx, 0x17, crtc17); + outs(Crtx, 0x0300); /* end synchronous reset */ +} + +void +cornerstring(char *s) +{ + int h, w; + VGAscr *scr; + Rectangle r; + Point p; + + scr = &vgascreen[0]; + if(scr->aperture == 0 || screenputs != vgascreenputs) + return; + p = memsubfontwidth(scr->memdefont, s); + w = p.x; + h = scr->memdefont->height; + + r = Rect(0, 0, w, h); + memimagedraw(scr->gscreen, r, back, back->r.min, nil, back->r.min, S); + memimagestring(scr->gscreen, r.min, conscol, ZP, scr->memdefont, s); +// flushmemscreen(r); +} diff --git a/os/pc/vga.h b/os/pc/vga.h new file mode 100644 index 00000000..685a8ee5 --- /dev/null +++ b/os/pc/vga.h @@ -0,0 +1,75 @@ +/* + * Generic VGA registers. + */ +enum { + MiscW = 0x03C2, /* Miscellaneous Output (W) */ + MiscR = 0x03CC, /* Miscellaneous Output (R) */ + Status0 = 0x03C2, /* Input status 0 (R) */ + Status1 = 0x03DA, /* Input Status 1 (R) */ + FeatureR = 0x03CA, /* Feature Control (R) */ + FeatureW = 0x03DA, /* Feature Control (W) */ + + Seqx = 0x03C4, /* Sequencer Index, Data at Seqx+1 */ + Crtx = 0x03D4, /* CRT Controller Index, Data at Crtx+1 */ + Grx = 0x03CE, /* Graphics Controller Index, Data at Grx+1 */ + Attrx = 0x03C0, /* Attribute Controller Index and Data */ + + PaddrW = 0x03C8, /* Palette Address Register, write */ + Pdata = 0x03C9, /* Palette Data Register */ + Pixmask = 0x03C6, /* Pixel Mask Register */ + PaddrR = 0x03C7, /* Palette Address Register, read */ + Pstatus = 0x03C7, /* DAC Status (RO) */ + + Pcolours = 256, /* Palette */ + Pred = 0, + Pgreen = 1, + Pblue = 2, + + Pblack = 0x00, + Pwhite = 0xFF, +}; + +#define vgai(port) inb(port) +#define vgao(port, data) outb(port, data) + +extern int vgaxi(long, uchar); +extern int vgaxo(long, uchar, uchar); + +typedef struct Cursor Cursor; +struct Cursor +{ + Point offset; + uchar clr[2*16]; + uchar set[2*16]; +}; + +/* + * First pass at tidying this up... + */ +typedef struct Mode { + int x; + int y; + int d; + + ulong aperture; /* this is a physical address */ + int apsize; + int apshift; +} Mode; + +/* + * Definitions of known VGA controllers. + */ +typedef struct Vgac Vgac; +struct Vgac { + char* name; + void (*page)(int); + void (*init)(Mode*); + int (*ident)(void); + void (*enable)(void); + void (*disable)(void); + void (*move)(int, int); + void (*load)(Cursor*); + Vgac* link; +}; + +extern void addvgaclink(Vgac*); diff --git a/os/pc/vga3dfx.c b/os/pc/vga3dfx.c new file mode 100644 index 00000000..cce27185 --- /dev/null +++ b/os/pc/vga3dfx.c @@ -0,0 +1,258 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#define Image IMAGE +#include <draw.h> +#include <memdraw.h> +#include <cursor.h> +#include "screen.h" + +typedef struct { + int vidProcCfg; + int hwCurPatAddr; + int hwCurLoc; + int hwCurC0; + int hwCurC1; +} Cursor3dfx; + +enum { + dramInit0 = 0x18, + dramInit1 = 0x1C, + + hwCur = 0x5C, +}; + +static ulong +tdfxlinear(VGAscr* scr, int* size, int* align) +{ + Pcidev *p; + int oapsize, wasupamem; + ulong aperture, oaperture; + + oaperture = scr->aperture; + oapsize = scr->apsize; + wasupamem = scr->isupamem; + + aperture = 0; + if(p = pcimatch(nil, 0x121A, 0)){ + switch(p->did){ + case 0x0003: /* Banshee */ + case 0x0005: /* Avenger (a.k.a. Voodoo3) */ + case 0x0009: /* Voodoo5 */ + aperture = p->mem[1].bar & ~0x0F; + *size = p->mem[1].size; + break; + default: + break; + } + } + + if(wasupamem){ + if(oaperture == aperture) + return oaperture; + upafree(oaperture, oapsize); + } + scr->isupamem = 0; + + aperture = upamalloc(aperture, *size, *align); + if(aperture == 0){ + if(wasupamem && upamalloc(oaperture, oapsize, 0)){ + aperture = oaperture; + scr->isupamem = 1; + } + else + scr->isupamem = 0; + } + else + scr->isupamem = 1; + + return aperture; +} + +static void +tdfxenable(VGAscr* scr) +{ + Pcidev *p; + ulong aperture; + int align, i, *mmio, size; + + /* + * Only once, can't be disabled for now. + * scr->io holds the physical address of + * the MMIO registers. + */ + if(scr->io) + return; + if(p = pcimatch(nil, 0x121A, 0)){ + switch(p->did){ + case 0x0003: /* Banshee */ + case 0x0005: /* Avenger (a.k.a. Voodoo3) */ + break; + default: + return; + } + } + else + return; + scr->io = upamalloc(p->mem[0].bar & ~0x0F, p->mem[0].size, 0); + if(scr->io == 0) + return; + + addvgaseg("3dfxmmio", (ulong)scr->io, p->mem[0].size); + + size = p->mem[1].size; + align = 0; + aperture = tdfxlinear(scr, &size, &align); + if(aperture){ + scr->aperture = aperture; + scr->apsize = size; + addvgaseg("3dfxscreen", aperture, size); + } + + /* + * Find a place for the cursor data in display memory. + * If SDRAM then there's 16MB memory else it's SGRAM + * and can count it based on the power-on straps - + * chip size can be 8Mb or 16Mb, and there can be 4 or + * 8 of them. + * Use the last 1KB of the framebuffer. + */ + mmio = KADDR(scr->io + dramInit0); + if(*(mmio+1) & 0x40000000) + i = 16*1024*1024; + else{ + if(*mmio & 0x08000000) + i = 16*1024*1024/8; + else + i = 8*1024*1024/8; + if(*mmio & 0x04000000) + i *= 8; + else + i *= 4; + } + scr->storage = i - 1024; +} + +static void +tdfxcurdisable(VGAscr* scr) +{ + Cursor3dfx *cursor3dfx; + + if(scr->io == 0) + return; + cursor3dfx = KADDR(scr->io+hwCur); + cursor3dfx->vidProcCfg &= ~0x08000000; +} + +static void +tdfxcurload(VGAscr* scr, Cursor* curs) +{ + int y; + uchar *p; + Cursor3dfx *cursor3dfx; + + if(scr->io == 0) + return; + cursor3dfx = KADDR(scr->io+hwCur); + + /* + * Disable the cursor then load the new image in + * the top-left of the 64x64 array. + * The cursor data is stored in memory as 128-bit + * words consisting of plane 0 in the least significant 64-bits + * and plane 1 in the most significant. + * The X11 cursor truth table is: + * p0 p1 colour + * 0 0 transparent + * 0 1 transparent + * 1 0 hwCurC0 + * 1 1 hwCurC1 + * Unused portions of the image have been initialised to be + * transparent. + */ + cursor3dfx->vidProcCfg &= ~0x08000000; + p = KADDR(scr->aperture + scr->storage); + for(y = 0; y < 16; y++){ + *p++ = curs->clr[2*y]|curs->set[2*y]; + *p++ = curs->clr[2*y+1]|curs->set[2*y+1]; + p += 6; + *p++ = curs->set[2*y]; + *p++ = curs->set[2*y+1]; + p += 6; + } + + /* + * Save the cursor hotpoint and enable the cursor. + * The 0,0 cursor point is bottom-right. + */ + scr->offset.x = 63+curs->offset.x; + scr->offset.y = 63+curs->offset.y; + cursor3dfx->vidProcCfg |= 0x08000000; +} + +static int +tdfxcurmove(VGAscr* scr, Point p) +{ + Cursor3dfx *cursor3dfx; + + if(scr->io == 0) + return 1; + cursor3dfx = KADDR(scr->io+hwCur); + + cursor3dfx->hwCurLoc = ((p.y+scr->offset.y)<<16)|(p.x+scr->offset.x); + + return 0; +} + +static void +tdfxcurenable(VGAscr* scr) +{ + Cursor3dfx *cursor3dfx; + + tdfxenable(scr); + if(scr->io == 0) + return; + cursor3dfx = KADDR(scr->io+hwCur); + + /* + * Cursor colours. + */ + cursor3dfx->hwCurC0 = 0xFFFFFFFF; + cursor3dfx->hwCurC1 = 0x00000000; + + /* + * Initialise the 64x64 cursor to be transparent (X11 mode). + */ + cursor3dfx->hwCurPatAddr = scr->storage; + memset(KADDR(scr->aperture + scr->storage), 0, 64*16); + + /* + * Load, locate and enable the 64x64 cursor in X11 mode. + */ + tdfxcurload(scr, &arrow); + tdfxcurmove(scr, ZP); + cursor3dfx->vidProcCfg |= 0x08000002; +} + +VGAdev vga3dfxdev = { + "3dfx", + + tdfxenable, + nil, + nil, + tdfxlinear, +}; + +VGAcur vga3dfxcur = { + "3dfxhwgc", + + tdfxcurenable, + tdfxcurdisable, + tdfxcurload, + tdfxcurmove, +}; diff --git a/os/pc/vgaark2000pv.c b/os/pc/vgaark2000pv.c new file mode 100644 index 00000000..d42eccc3 --- /dev/null +++ b/os/pc/vgaark2000pv.c @@ -0,0 +1,190 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#define Image IMAGE +#include <draw.h> +#include <memdraw.h> +#include <cursor.h> +#include "screen.h" + +static int +ark2000pvpageset(VGAscr*, int page) +{ + uchar seq15; + + seq15 = vgaxi(Seqx, 0x15); + vgaxo(Seqx, 0x15, page); + vgaxo(Seqx, 0x16, page); + + return seq15; +} + +static void +ark2000pvpage(VGAscr* scr, int page) +{ + lock(&scr->devlock); + ark2000pvpageset(scr, page); + unlock(&scr->devlock); +} + +static void +ark2000pvdisable(VGAscr*) +{ + uchar seq20; + + seq20 = vgaxi(Seqx, 0x20) & ~0x08; + vgaxo(Seqx, 0x20, seq20); +} + +static void +ark2000pvenable(VGAscr* scr) +{ + uchar seq20; + ulong storage; + + /* + * Disable the cursor then configure for X-Windows style, + * 32x32 and 4/8-bit colour depth. + * Set cursor colours for 4/8-bit. + */ + seq20 = vgaxi(Seqx, 0x20) & ~0x1F; + vgaxo(Seqx, 0x20, seq20); + seq20 |= 0x18; + + vgaxo(Seqx, 0x26, 0x00); + vgaxo(Seqx, 0x27, 0x00); + vgaxo(Seqx, 0x28, 0x00); + vgaxo(Seqx, 0x29, 0xFF); + vgaxo(Seqx, 0x2A, 0xFF); + vgaxo(Seqx, 0x2B, 0xFF); + + /* + * Cursor storage is a 256 byte or 1Kb block located in the last + * 16Kb of video memory. Crt25 is the index of which block. + */ + storage = (vgaxi(Seqx, 0x10)>>6) & 0x03; + storage = (1024*1024)<<storage; + storage -= 256; + scr->storage = storage; + vgaxo(Seqx, 0x25, 0x3F); + + /* + * Enable the cursor. + */ + vgaxo(Seqx, 0x20, seq20); +} + +static void +ark2000pvload(VGAscr* scr, Cursor* curs) +{ + uchar *p, seq10; + int opage, x, y; + + /* + * Is linear addressing turned on? This will determine + * how we access the cursor storage. + */ + seq10 = vgaxi(Seqx, 0x10); + opage = 0; + p = KADDR(scr->aperture); + if(!(seq10 & 0x10)){ + lock(&scr->devlock); + opage = ark2000pvpageset(scr, scr->storage>>16); + p += (scr->storage & 0xFFFF); + } + else + p += scr->storage; + + /* + * The cursor is set in X11 mode which gives the following + * truth table: + * and xor colour + * 0 0 underlying pixel colour + * 0 1 underlying pixel colour + * 1 0 background colour + * 1 1 foreground colour + * Put the cursor into the top-left of the 32x32 array. + * The manual doesn't say what the data layout in memory is - + * this worked out by trial and error. + */ + for(y = 0; y < 32; y++){ + for(x = 0; x < 32/8; x++){ + if(x < 16/8 && y < 16){ + *p++ = curs->clr[2*y + x]|curs->set[2*y + x]; + *p++ = curs->set[2*y + x]; + } + else { + *p++ = 0x00; + *p++ = 0x00; + } + } + } + + if(!(seq10 & 0x10)){ + ark2000pvpageset(scr, opage); + unlock(&scr->devlock); + } + + /* + * Save the cursor hotpoint. + */ + scr->offset = curs->offset; +} + +static int +ark2000pvmove(VGAscr* scr, Point p) +{ + int x, xo, y, yo; + + /* + * Mustn't position the cursor offscreen even partially, + * or it might disappear. Therefore, if x or y is -ve, adjust the + * cursor origins instead. + */ + if((x = p.x+scr->offset.x) < 0){ + xo = -x; + x = 0; + } + else + xo = 0; + if((y = p.y+scr->offset.y) < 0){ + yo = -y; + y = 0; + } + else + yo = 0; + + /* + * Load the new values. + */ + vgaxo(Seqx, 0x2C, xo); + vgaxo(Seqx, 0x2D, yo); + vgaxo(Seqx, 0x21, (x>>8) & 0x0F); + vgaxo(Seqx, 0x22, x & 0xFF); + vgaxo(Seqx, 0x23, (y>>8) & 0x0F); + vgaxo(Seqx, 0x24, y & 0xFF); + + return 0; +} + +VGAdev vgaark2000pvdev = { + "ark2000pv", + + 0, + 0, + ark2000pvpage, + 0, +}; + +VGAcur vgaark2000pvcur = { + "ark2000pvhwgc", + + ark2000pvenable, + ark2000pvdisable, + ark2000pvload, + ark2000pvmove, +}; diff --git a/os/pc/vgabt485.c b/os/pc/vgabt485.c new file mode 100644 index 00000000..1c155e6d --- /dev/null +++ b/os/pc/vgabt485.c @@ -0,0 +1,245 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#define Image IMAGE +#include <draw.h> +#include <memdraw.h> +#include <cursor.h> +#include "screen.h" + +/* + * Hardware graphics cursor support for + * Brooktree Bt485 Monolithic True-Color RAMDAC. + * Assumes hooked up to an S3 86C928. + * + * BUGS: + * 64x64x2 cursor always used; + * no support for interlaced mode. + */ +enum { + AddrW = 0x00, /* Address register; palette/cursor RAM write */ + Palette = 0x01, /* 6/8-bit color palette data */ + Pmask = 0x02, /* Pixel mask register */ + AddrR = 0x03, /* Address register; palette/cursor RAM read */ + ColorW = 0x04, /* Address register; cursor/overscan color write */ + Color = 0x05, /* Cursor/overscan color data */ + Cmd0 = 0x06, /* Command register 0 */ + ColorR = 0x07, /* Address register; cursor/overscan color read */ + Cmd1 = 0x08, /* Command register 1 */ + Cmd2 = 0x09, /* Command register 2 */ + Status = 0x0A, /* Status */ + Cmd3 = 0x1A, /* Command register 3 */ + Cram = 0x0B, /* Cursor RAM array data */ + Cxlr = 0x0C, /* Cursor x-low register */ + Cxhr = 0x0D, /* Cursor x-high register */ + Cylr = 0x0E, /* Cursor y-low register */ + Cyhr = 0x0F, /* Cursor y-high register */ + + Nreg = 0x10, +}; + +/* + * Lower 2-bits of indirect DAC register + * addressing. + */ +static ushort dacxreg[4] = { + PaddrW, Pdata, Pixmask, PaddrR +}; + +static uchar +bt485io(uchar reg) +{ + uchar crt55, cr0; + + crt55 = vgaxi(Crtx, 0x55) & 0xFC; + if((reg & 0x0F) == Status){ + /* + * 1,2: Set indirect addressing for Status or + * Cmd3 - set bit7 of Cr0. + */ + vgaxo(Crtx, 0x55, crt55|((Cmd0>>2) & 0x03)); + cr0 = vgai(dacxreg[Cmd0 & 0x03])|0x80; + vgao(dacxreg[Cmd0 & 0x03], cr0); + + /* + * 3,4: Set the index into the Write register, + * index == 0x00 for Status, 0x01 for Cmd3. + */ + vgaxo(Crtx, 0x55, crt55|((AddrW>>2) & 0x03)); + vgao(dacxreg[AddrW & 0x03], (reg == Status) ? 0x00: 0x01); + + /* + * 5,6: Get the contents of the appropriate + * register at 0x0A. + */ + } + + return crt55; +} + +static uchar +bt485i(uchar reg) +{ + uchar crt55, r; + + crt55 = bt485io(reg); + vgaxo(Crtx, 0x55, crt55|((reg>>2) & 0x03)); + r = vgai(dacxreg[reg & 0x03]); + vgaxo(Crtx, 0x55, crt55); + + return r; +} + +static void +bt485o(uchar reg, uchar data) +{ + uchar crt55; + + crt55 = bt485io(reg); + vgaxo(Crtx, 0x55, crt55|((reg>>2) & 0x03)); + vgao(dacxreg[reg & 0x03], data); + vgaxo(Crtx, 0x55, crt55); +} + +static void +bt485disable(VGAscr*) +{ + uchar r; + + /* + * Disable + * cursor mode 3; + * cursor control enable for Bt485 DAC; + * the hardware cursor external operation mode. + */ + r = bt485i(Cmd2) & ~0x03; + bt485o(Cmd2, r); + + r = vgaxi(Crtx, 0x45) & ~0x20; + vgaxo(Crtx, 0x45, r); + + r = vgaxi(Crtx, 0x55) & ~0x20; + vgaxo(Crtx, 0x55, r); +} + +static void +bt485enable(VGAscr*) +{ + uchar r; + + /* + * Turn cursor off. + */ + r = bt485i(Cmd2) & 0xFC; + bt485o(Cmd2, r); + + /* + * Overscan colour, + * cursor colour 1 (white), + * cursor colour 2, 3 (black). + */ + bt485o(ColorW, 0x00); + bt485o(Color, Pwhite); bt485o(Color, Pwhite); bt485o(Color, Pwhite); + + bt485o(Color, Pwhite); bt485o(Color, Pwhite); bt485o(Color, Pwhite); + + bt485o(Color, Pblack); bt485o(Color, Pblack); bt485o(Color, Pblack); + bt485o(Color, Pblack); bt485o(Color, Pblack); bt485o(Color, Pblack); + + /* + * Finally, enable + * the hardware cursor external operation mode; + * cursor control enable for Bt485 DAC. + * The #9GXE cards seem to need the 86C928 Bt485 support + * enabled in order to work at all in enhanced mode. + */ + + r = vgaxi(Crtx, 0x55)|0x20; + vgaxo(Crtx, 0x55, r); + + r = vgaxi(Crtx, 0x45)|0x20; + vgaxo(Crtx, 0x45, r); +} + +static void +bt485load(VGAscr* scr, Cursor* curs) +{ + uchar r; + int x, y; + + /* + * Turn cursor off; + * put cursor into 64x64x2 mode and clear MSBs of address; + * clear LSBs of address; + */ + r = bt485i(Cmd2) & 0xFC; + bt485o(Cmd2, r); + + r = (bt485i(Cmd3) & 0xFC)|0x04; + bt485o(Cmd3, r); + + bt485o(AddrW, 0x00); + + /* + * Now load the cursor RAM array, both planes. + * The cursor is 16x16, the array 64x64; put + * the cursor in the top left. The 0,0 cursor + * point is bottom-right, so positioning will + * have to take that into account. + */ + for(y = 0; y < 64; y++){ + for(x = 0; x < 64/8; x++){ + if(x < 16/8 && y < 16) + bt485o(Cram, curs->clr[x+y*2]); + else + bt485o(Cram, 0x00); + } + } + for(y = 0; y < 64; y++){ + for(x = 0; x < 64/8; x++){ + if(x < 16/8 && y < 16) + bt485o(Cram, curs->set[x+y*2]); + else + bt485o(Cram, 0x00); + } + } + + /* + * Initialise the cursor hot-point + * and enable the cursor. + */ + scr->offset.x = 64+curs->offset.x; + scr->offset.y = 64+curs->offset.y; + + r = (bt485i(Cmd2) & 0xFC)|0x01; + bt485o(Cmd2, r); +} + +static int +bt485move(VGAscr* scr, Point p) +{ + int x, y; + + x = p.x+scr->offset.x; + y = p.y+scr->offset.y; + + bt485o(Cxlr, x & 0xFF); + bt485o(Cxhr, (x>>8) & 0x0F); + bt485o(Cylr, y & 0xFF); + bt485o(Cyhr, (y>>8) & 0x0F); + + return 0; +} + +VGAcur vgabt485cur = { + "bt485hwgc", + + bt485enable, + bt485disable, + bt485load, + bt485move, +}; diff --git a/os/pc/vgaclgd542x.c b/os/pc/vgaclgd542x.c new file mode 100644 index 00000000..6dd95e9e --- /dev/null +++ b/os/pc/vgaclgd542x.c @@ -0,0 +1,291 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#define Image IMAGE +#include <draw.h> +#include <memdraw.h> +#include <cursor.h> +#include "screen.h" + +static int +clgd542xpageset(VGAscr*, int page) +{ + uchar gr09; + int opage; + + if(vgaxi(Seqx, 0x07) & 0xF0) + page = 0; + gr09 = vgaxi(Grx, 0x09); + if(vgaxi(Grx, 0x0B) & 0x20){ + vgaxo(Grx, 0x09, page<<2); + opage = gr09>>2; + } + else{ + vgaxo(Grx, 0x09, page<<4); + opage = gr09>>4; + } + + return opage; +} + +static void +clgd542xpage(VGAscr* scr, int page) +{ + lock(&scr->devlock); + clgd542xpageset(scr, page); + unlock(&scr->devlock); +} + +static ulong +clgd542xlinear(VGAscr* scr, int* size, int* align) +{ + ulong aperture, oaperture; + int oapsize, wasupamem; + Pcidev *p; + + oaperture = scr->aperture; + oapsize = scr->apsize; + wasupamem = scr->isupamem; + if(wasupamem) + upafree(oaperture, oapsize); + scr->isupamem = 0; + + if(p = pcimatch(nil, 0x1013, 0)){ + aperture = p->mem[0].bar & ~0x0F; + *size = p->mem[0].size; + } + else + aperture = 0; + + aperture = upamalloc(aperture, *size, *align); + if(aperture == 0){ + if(wasupamem && upamalloc(oaperture, oapsize, 0)) + scr->isupamem = 1; + } + else + scr->isupamem = 1; + + return aperture; +} + +static void +clgd542xdisable(VGAscr*) +{ + uchar sr12; + + sr12 = vgaxi(Seqx, 0x12); + vgaxo(Seqx, 0x12, sr12 & ~0x01); +} + +static void +clgd542xenable(VGAscr* scr) +{ + uchar sr12; + int mem, x; + + /* + * Disable the cursor. + */ + sr12 = vgaxi(Seqx, 0x12); + vgaxo(Seqx, 0x12, sr12 & ~0x01); + + /* + * Cursor colours. + * Can't call setcolor here as cursor is already locked. + */ + vgaxo(Seqx, 0x12, sr12|0x02); + vgao(PaddrW, 0x00); + vgao(Pdata, Pwhite); + vgao(Pdata, Pwhite); + vgao(Pdata, Pwhite); + vgao(PaddrW, 0x0F); + vgao(Pdata, Pblack); + vgao(Pdata, Pblack); + vgao(Pdata, Pblack); + vgaxo(Seqx, 0x12, sr12); + + mem = 0; + switch(vgaxi(Crtx, 0x27) & ~0x03){ + + case 0x88: /* CL-GD5420 */ + case 0x8C: /* CL-GD5422 */ + case 0x94: /* CL-GD5424 */ + case 0x80: /* CL-GD5425 */ + case 0x90: /* CL-GD5426 */ + case 0x98: /* CL-GD5427 */ + case 0x9C: /* CL-GD5429 */ + /* + * The BIOS leaves the memory size in Seq0A, bits 4 and 3. + * See Technical Reference Manual Appendix E1, Section 1.3.2. + * + * The storage area for the 64x64 cursors is the last 16Kb of + * display memory. + */ + mem = (vgaxi(Seqx, 0x0A)>>3) & 0x03; + break; + + case 0xA0: /* CL-GD5430 */ + case 0xA8: /* CL-GD5434 */ + case 0xAC: /* CL-GD5436 */ + case 0xB8: /* CL-GD5446 */ + case 0x30: /* CL-GD7543 */ + /* + * Attempt to intuit the memory size from the DRAM control + * register. Minimum is 512KB. + * If DRAM bank switching is on then there's double. + */ + x = vgaxi(Seqx, 0x0F); + mem = (x>>3) & 0x03; + if(x & 0x80) + mem++; + break; + + default: /* uh, ah dunno */ + break; + } + scr->storage = ((256<<mem)-16)*1024; + + /* + * Set the current cursor to index 0 + * and turn the 64x64 cursor on. + */ + vgaxo(Seqx, 0x13, 0); + vgaxo(Seqx, 0x12, sr12|0x05); +} + +static void +clgd542xinitcursor(VGAscr* scr, int xo, int yo, int index) +{ + uchar *p, seq07; + uint p0, p1; + int opage, x, y; + + /* + * Is linear addressing turned on? This will determine + * how we access the cursor storage. + */ + seq07 = vgaxi(Seqx, 0x07); + opage = 0; + p = KADDR(scr->aperture); + if(!(seq07 & 0xF0)){ + lock(&scr->devlock); + opage = clgd542xpageset(scr, scr->storage>>16); + p += (scr->storage & 0xFFFF); + } + else + p += scr->storage; + p += index*1024; + + for(y = yo; y < 16; y++){ + p0 = scr->set[2*y]; + p1 = scr->set[2*y+1]; + if(xo){ + p0 = (p0<<xo)|(p1>>(8-xo)); + p1 <<= xo; + } + *p++ = p0; + *p++ = p1; + + for(x = 16; x < 64; x += 8) + *p++ = 0x00; + + p0 = scr->clr[2*y]|scr->set[2*y]; + p1 = scr->clr[2*y+1]|scr->set[2*y+1]; + if(xo){ + p0 = (p0<<xo)|(p1>>(8-xo)); + p1 <<= xo; + } + *p++ = p0; + *p++ = p1; + + for(x = 16; x < 64; x += 8) + *p++ = 0x00; + } + while(y < 64+yo){ + for(x = 0; x < 64; x += 8){ + *p++ = 0x00; + *p++ = 0x00; + } + y++; + } + + if(!(seq07 & 0xF0)){ + clgd542xpageset(scr, opage); + unlock(&scr->devlock); + } +} + +static void +clgd542xload(VGAscr* scr, Cursor* curs) +{ + uchar sr12; + + /* + * Disable the cursor. + */ + sr12 = vgaxi(Seqx, 0x12); + vgaxo(Seqx, 0x12, sr12 & ~0x01); + + memmove(&scr->Cursor, curs, sizeof(Cursor)); + clgd542xinitcursor(scr, 0, 0, 0); + + /* + * Enable the cursor. + */ + vgaxo(Seqx, 0x13, 0); + vgaxo(Seqx, 0x12, sr12|0x05); +} + +static int +clgd542xmove(VGAscr* scr, Point p) +{ + int index, x, xo, y, yo; + + index = 0; + if((x = p.x+scr->offset.x) < 0){ + xo = -x; + x = 0; + } + else + xo = 0; + if((y = p.y+scr->offset.y) < 0){ + yo = -y; + y = 0; + } + else + yo = 0; + + if(xo || yo){ + clgd542xinitcursor(scr, xo, yo, 1); + index = 1; + } + vgaxo(Seqx, 0x13, index<<2); + + vgaxo(Seqx, 0x10|((x & 0x07)<<5), (x>>3) & 0xFF); + vgaxo(Seqx, 0x11|((y & 0x07)<<5), (y>>3) & 0xFF); + + return 0; +} + +VGAdev vgaclgd542xdev = { + "clgd542x", + + 0, + 0, + clgd542xpage, + clgd542xlinear, +}; + +VGAcur vgaclgd542xcur = { + "clgd542xhwgc", + + clgd542xenable, + clgd542xdisable, + clgd542xload, + clgd542xmove, +}; diff --git a/os/pc/vgaclgd546x.c b/os/pc/vgaclgd546x.c new file mode 100644 index 00000000..62bcd82a --- /dev/null +++ b/os/pc/vgaclgd546x.c @@ -0,0 +1,277 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#define Image IMAGE +#include <draw.h> +#include <memdraw.h> +#include <cursor.h> +#include "screen.h" + +typedef struct Cursor546x Cursor546x; +struct Cursor546x { + ushort x; + ushort y; + ushort preset; + ushort enable; + ushort addr; +}; + +enum { + PaletteState = 0xB0, + CursorMMIO = 0xE0, +}; + +static ulong +clgd546xlinear(VGAscr* scr, int* size, int* align) +{ + ulong aperture, oaperture; + int oapsize, wasupamem; + Pcidev *p; + + oaperture = scr->aperture; + oapsize = scr->apsize; + wasupamem = scr->isupamem; + + aperture = 0; + if(p = pcimatch(nil, 0x1013, 0)){ + switch(p->did){ + case 0xD0: + case 0xD4: + case 0xD6: + aperture = p->mem[0].bar & ~0x0F; + *size = p->mem[0].size; + break; + default: + break; + } + } + + if(wasupamem){ + if(oaperture == aperture) + return oaperture; + upafree(oaperture, oapsize); + } + scr->isupamem = 0; + + aperture = upamalloc(aperture, *size, *align); + if(aperture == 0){ + if(wasupamem && upamalloc(oaperture, oapsize, 0)){ + aperture = oaperture; + scr->isupamem = 1; + } + else + scr->isupamem = 0; + } + else + scr->isupamem = 1; + + return aperture; +} +static void +clgd546xenable(VGAscr* scr) +{ + Pcidev *p; + int size, align; + ulong aperture; + + /* + * Only once, can't be disabled for now. + * scr->io holds the virtual address of + * the MMIO registers. + */ + if(scr->io) + return; + if(p = pcimatch(nil, 0x1013, 0)){ + switch(p->did){ + case 0xD0: + case 0xD4: + case 0xD6: + break; + default: + return; + } + } + else + return; + scr->io = upamalloc(p->mem[1].bar & ~0x0F, p->mem[1].size, 0); + if(scr->io == 0) + return; + addvgaseg("clgd546xmmio", scr->io, p->mem[1].size); + + scr->io = (ulong)KADDR(scr->io); + + size = p->mem[0].size; + align = 0; + aperture = clgd546xlinear(scr, &size, &align); + if(aperture) { + scr->aperture = aperture; + scr->apsize = size; + addvgaseg("clgd546xscreen", aperture, size); + } +} + +static void +clgd546xcurdisable(VGAscr* scr) +{ + Cursor546x *cursor546x; + + if(scr->io == 0) + return; + cursor546x = (Cursor546x*)(scr->io+CursorMMIO); + cursor546x->enable = 0; +} + +static void +clgd546xcurload(VGAscr* scr, Cursor* curs) +{ + int c, i, m, y; + uchar *p; + Cursor546x *cursor546x; + + if(scr->io == 0) + return; + cursor546x = (Cursor546x*)(scr->io+CursorMMIO); + + /* + * Disable the cursor then change only the bits + * that need it. + */ + cursor546x->enable = 0; + p = (uchar*)(scr->aperture + scr->storage); + for(y = 0; y < 16; y++){ + c = curs->set[2*y]; + m = 0; + for(i = 0; i < 8; i++){ + if(c & (1<<(7-i))) + m |= 1<<i; + } + *p++ = m; + c = curs->set[2*y + 1]; + m = 0; + for(i = 0; i < 8; i++){ + if(c & (1<<(7-i))) + m |= 1<<i; + } + *p++ = m; + p += 6; + c = curs->set[2*y]|curs->clr[2*y]; + m = 0; + for(i = 0; i < 8; i++){ + if(c & (1<<(7-i))) + m |= 1<<i; + } + *p++ = m; + c = curs->set[2*y + 1]|curs->clr[2*y + 1]; + m = 0; + for(i = 0; i < 8; i++){ + if(c & (1<<(7-i))) + m |= 1<<i; + } + *p++ = m; + p += 6; + } + + /* + * Save the cursor hotpoint and enable the cursor. + */ + scr->offset = curs->offset; + cursor546x->enable = 1; +} + +static int +clgd546xcurmove(VGAscr* scr, Point p) +{ + int x, xo, y, yo; + Cursor546x *cursor546x; + + if(scr->io == 0) + return 1; + cursor546x = (Cursor546x*)(scr->io+CursorMMIO); + + if((x = p.x+scr->offset.x) < 0){ + xo = -x; + x = 0; + } + else + xo = 0; + if((y = p.y+scr->offset.y) < 0){ + yo = -y; + y = 0; + } + else + yo = 0; + + cursor546x->preset = (xo<<8)|yo; + cursor546x->x = x; + cursor546x->y = y; + + return 0; +} + +static void +clgd546xcurenable(VGAscr* scr) +{ + uchar *p; + Cursor546x *cursor546x; + + clgd546xenable(scr); + if(scr->io == 0) + return; + cursor546x = (Cursor546x*)(scr->io+CursorMMIO); + + /* + * Cursor colours. + * Can't call setcolor here as cursor is already locked. + */ + p = (uchar*)(scr->io+PaletteState); + *p |= 0x08; + vgao(PaddrW, 0x00); + vgao(Pdata, Pwhite); + vgao(Pdata, Pwhite); + vgao(Pdata, Pwhite); + vgao(PaddrW, 0x0F); + vgao(Pdata, Pblack); + vgao(Pdata, Pblack); + vgao(Pdata, Pblack); + *p &= ~0x08; + + /* + * Find a place for the cursor data in display memory. + * 2 cursor images might be needed, 1KB each so use the last + * 2KB of the framebuffer and initialise them to be + * transparent. + */ + scr->storage = ((vgaxi(Seqx, 0x14) & 0x07)+1)*1024*1022; + cursor546x->addr = (scr->storage>>10)<<2; + memset((uchar*)(scr->aperture + scr->storage), 0, 2*64*16); + + /* + * Load, locate and enable the 64x64 cursor. + */ + clgd546xcurload(scr, &arrow); + clgd546xcurmove(scr, ZP); + cursor546x->enable = 1; +} + +VGAdev vgaclgd546xdev = { + "clgd546x", + + clgd546xenable, + nil, + nil, + clgd546xlinear, +}; + +VGAcur vgaclgd546xcur = { + "clgd546xhwgc", + + clgd546xcurenable, + clgd546xcurdisable, + clgd546xcurload, + clgd546xcurmove, +}; diff --git a/os/pc/vgact65545.c b/os/pc/vgact65545.c new file mode 100644 index 00000000..9a1467ac --- /dev/null +++ b/os/pc/vgact65545.c @@ -0,0 +1,149 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#define Image IMAGE +#include <draw.h> +#include <memdraw.h> +#include <cursor.h> +#include "screen.h" + +static void +ct65545page(VGAscr*, int page) +{ + outb(0x3D6, 0x10); + outb(0x3D7, page<<6); +} + +static void +ct65545disable(VGAscr*) +{ + outl(0xA3D0, 0); +} + +static void +ct65545enable(VGAscr* scr) +{ + ulong storage; + + /* + * Find a place for the cursor data in display memory. + * Must be on a 1024-byte boundary. + */ + storage = ROUND(scr->gscreen->width*BY2WD*scr->gscreen->r.max.y, 1024); + outl(0xB3D0, storage); + scr->storage = storage; + + /* + * Set the colours. + * Enable the cursor. + */ + outl(0xA7D0, 0xFFFF0000); + outl(0xA3D0, 2); +} + +static void +ct65545initcursor(VGAscr* scr, int xo, int yo, int index) +{ + uchar *mem; + uint and, clr, set, xor; + int i, x, y; + + mem = KADDR(scr->aperture); + mem += scr->storage + index*1024; + + for(y = yo; y < 16; y++){ + clr = (scr->clr[2*y]<<8)|scr->clr[2*y+1]; + set = (scr->set[2*y]<<8)|scr->set[2*y+1]; + if(xo){ + clr <<= xo; + set <<= xo; + } + + and = 0; + xor = 0; + for(i = 0; i < 16; i++){ + if(set & (1<<i)){ + /* nothing to do */ + } + else if(clr & (1<<i)) + xor |= 1<<i; + else + and |= 1<<i; + } + *mem++ = and>>8; + *mem++ = xor>>8; + *mem++ = and; + *mem++ = xor; + + for(x = 16; x < 64; x += 8){ + *mem++ = 0xFF; + *mem++ = 0x00; + } + } + while(y < 64+yo){ + for(x = 0; x < 64; x += 8){ + *mem++ = 0xFF; + *mem++ = 0x00; + } + y++; + } +} + +static void +ct65545load(VGAscr* scr, Cursor* curs) +{ + memmove(&scr->Cursor, curs, sizeof(Cursor)); + ct65545initcursor(scr, 0, 0, 0); +} + +static int +ct65545move(VGAscr* scr, Point p) +{ + int index, x, xo, y, yo; + + index = 0; + if((x = p.x+scr->offset.x) < 0){ + xo = -x; + x = 0; + } + else + xo = 0; + if((y = p.y+scr->offset.y) < 0){ + yo = -y; + y = 0; + } + else + yo = 0; + + if(xo || yo){ + ct65545initcursor(scr, xo, yo, 1); + index = 1; + } + outl(0xB3D0, scr->storage + index*1024); + + outl(0xAFD0, (y<<16)|x); + + return 0; +} + +VGAdev vgact65545dev = { + "ct65540", /* BUG: really 65545 */ + + 0, + 0, + ct65545page, + 0, +}; + +VGAcur vgact65545cur = { + "ct65545hwgc", + + ct65545enable, + ct65545disable, + ct65545load, + ct65545move, +}; diff --git a/os/pc/vgacyber938x.c b/os/pc/vgacyber938x.c new file mode 100644 index 00000000..7c86679c --- /dev/null +++ b/os/pc/vgacyber938x.c @@ -0,0 +1,225 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#define Image IMAGE +#include <draw.h> +#include <memdraw.h> +#include <cursor.h> +#include "screen.h" + +enum { + CursorON = 0xC8, + CursorOFF = 0x00, +}; + +static int +cyber938xpageset(VGAscr*, int page) +{ + int opage; + + opage = inb(0x3D8); + + outb(0x3D8, page); + outb(0x3D9, page); + + return opage; +} + +static void +cyber938xpage(VGAscr* scr, int page) +{ + lock(&scr->devlock); + cyber938xpageset(scr, page); + unlock(&scr->devlock); +} + +static ulong +cyber938xlinear(VGAscr* scr, int* size, int* align) +{ + ulong aperture, oaperture; + int oapsize, wasupamem; + int osize; + Pcidev *p; + + osize = *size; + oaperture = scr->aperture; + oapsize = scr->apsize; + wasupamem = scr->isupamem; + if(wasupamem) + upafree(oaperture, oapsize); + scr->isupamem = 0; + scr->mmio = 0; + + if(p = pcimatch(nil, 0x1023, 0)){ + aperture = p->mem[0].bar & ~0x0F; + *size = p->mem[0].size; + /* + * Heuristic to detect the MMIO space. We're flying blind + * here, with only the XFree86 source to guide us. + */ + if(p->mem[1].size == 0x20000) + scr->mmio = (ulong*)(p->mem[1].bar & ~0x0F); + } + else + aperture = 0; + + aperture = upamalloc(aperture, *size, *align); + if(aperture == 0){ + if(wasupamem && upamalloc(oaperture, oapsize, 0)) + scr->isupamem = 1; + } + else + scr->isupamem = 1; + + if(aperture) + addvgaseg("cyber938xscreen", aperture, osize); + if(scr->mmio) + addvgaseg("cyber938xmmio", (ulong)scr->mmio, 0x20000); + + return aperture; +} + +static void +cyber938xcurdisable(VGAscr*) +{ + vgaxo(Crtx, 0x50, CursorOFF); +} + +static void +cyber938xcurload(VGAscr* scr, Cursor* curs) +{ + uchar *p; + int islinear, opage, y; + + cyber938xcurdisable(scr); + + opage = 0; + p = KADDR(scr->aperture); + islinear = vgaxi(Crtx, 0x21) & 0x20; + if(!islinear){ + lock(&scr->devlock); + opage = cyber938xpageset(scr, scr->storage>>16); + p += (scr->storage & 0xFFFF); + } + else + p += scr->storage; + + for(y = 0; y < 16; y++){ + *p++ = curs->set[2*y]|curs->clr[2*y]; + *p++ = curs->set[2*y + 1]|curs->clr[2*y + 1]; + *p++ = 0x00; + *p++ = 0x00; + *p++ = curs->set[2*y]; + *p++ = curs->set[2*y + 1]; + *p++ = 0x00; + *p++ = 0x00; + } + memset(p, 0, (32-y)*8); + + if(!islinear){ + cyber938xpageset(scr, opage); + unlock(&scr->devlock); + } + + /* + * Save the cursor hotpoint and enable the cursor. + */ + scr->offset = curs->offset; + vgaxo(Crtx, 0x50, CursorON); +} + +static int +cyber938xcurmove(VGAscr* scr, Point p) +{ + int x, xo, y, yo; + + /* + * Mustn't position the cursor offscreen even partially, + * or it might disappear. Therefore, if x or y is -ve, adjust the + * cursor origins instead. + */ + if((x = p.x+scr->offset.x) < 0){ + xo = -x; + x = 0; + } + else + xo = 0; + if((y = p.y+scr->offset.y) < 0){ + yo = -y; + y = 0; + } + else + yo = 0; + + /* + * Load the new values. + */ + vgaxo(Crtx, 0x46, xo); + vgaxo(Crtx, 0x47, yo); + vgaxo(Crtx, 0x40, x & 0xFF); + vgaxo(Crtx, 0x41, (x>>8) & 0xFF); + vgaxo(Crtx, 0x42, y & 0xFF); + vgaxo(Crtx, 0x43, (y>>8) & 0xFF); + + return 0; +} + +static void +cyber938xcurenable(VGAscr* scr) +{ + int i; + ulong storage; + + cyber938xcurdisable(scr); + + /* + * Cursor colours. + */ + for(i = 0x48; i < 0x4C; i++) + vgaxo(Crtx, i, 0x00); + for(i = 0x4C; i < 0x50; i++) + vgaxo(Crtx, i, 0xFF); + + /* + * Find a place for the cursor data in display memory. + */ + storage = ((scr->gscreen->width*BY2WD*scr->gscreen->r.max.y+1023)/1024); + vgaxo(Crtx, 0x44, storage & 0xFF); + vgaxo(Crtx, 0x45, (storage>>8) & 0xFF); + storage *= 1024; + scr->storage = storage; + + /* + * Load, locate and enable the 32x32 cursor. + * (64x64 is bit 0, X11 format is bit 6 and cursor + * enable is bit 7). Bit 3 needs to be set on 9382 + * chips otherwise even the white bits are black. + */ + cyber938xcurload(scr, &arrow); + cyber938xcurmove(scr, ZP); + vgaxo(Crtx, 0x50, CursorON); +} + +VGAdev vgacyber938xdev = { + "cyber938x", + + nil, /* enable */ + nil, /* disable */ + cyber938xpage, /* page */ + cyber938xlinear, /* linear */ + nil, /* drawinit */ +}; + +VGAcur vgacyber938xcur = { + "cyber938xhwgc", + + cyber938xcurenable, /* enable */ + cyber938xcurdisable, /* disable */ + cyber938xcurload, /* load */ + cyber938xcurmove, /* move */ +}; diff --git a/os/pc/vgaet4000.c b/os/pc/vgaet4000.c new file mode 100644 index 00000000..f3ede446 --- /dev/null +++ b/os/pc/vgaet4000.c @@ -0,0 +1,270 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#define Image IMAGE +#include <draw.h> +#include <memdraw.h> +#include <cursor.h> +#include "screen.h" + +static void +setet4000page(int page) +{ + uchar p; + + p = page & 0x0F; + p |= p<<4; + outb(0x3CD, p); + + p = (page & 0x30); + p |= p>>4; + outb(0x3CB, p); +} + +static void +et4000page(VGAscr *scr, int page) +{ + lock(&scr->devlock); + setet4000page(page); + unlock(&scr->devlock); +} + +static void +et4000disable(VGAscr*) +{ + uchar imaF7; + + outb(0x217A, 0xF7); + imaF7 = inb(0x217B) & ~0x80; + outb(0x217B, imaF7); +} + +static void +et4000enable(VGAscr *scr) +{ + uchar imaF7; + + et4000disable(scr); + + /* + * Configure CRTCB for Sprite, 64x64, + * CRTC pixel overlay. + */ + outb(0x217A, 0xEF); + outb(0x217B, 0x02); + + /* + * Cursor goes in the top left corner + * of the Sprite area, so the horizontal and + * vertical presets are 0. + */ + outb(0x217A, 0xE2); + outb(0x217B, 0x00); + outb(0x217A, 0xE3); + outb(0x217B, 0x00); + + outb(0x217A, 0xE6); + outb(0x217B, 0x00); + outb(0x217A, 0xE7); + outb(0x217B, 0x00); + + /* + * Find a place for the cursor data in display memory. + * Must be on a "doubleword" boundary, but put it on a + * 1024-byte boundary so that there's no danger of it + * crossing a page. + */ + scr->storage = (scr->gscreen->width*BY2WD*scr->gscreen->r.max.y+1023)/1024; + scr->storage *= 1024/4; + outb(0x217A, 0xE8); + outb(0x217B, scr->storage & 0xFF); + outb(0x217A, 0xE9); + outb(0x217B, (scr->storage>>8) & 0xFF); + outb(0x217A, 0xEA); + outb(0x217B, (scr->storage>>16) & 0x0F); + scr->storage *= 4; + + /* + * Row offset in "quadwords". Must be 2 for Sprite. + * Bag the pixel-panning. + * Colour depth, must be 2 for Sprite. + */ + outb(0x217A, 0xEB); + outb(0x217B, 0x02); + outb(0x217A, 0xEC); + outb(0x217B, 0x00); + + outb(0x217A, 0xED); + outb(0x217B, 0x00); + + outb(0x217A, 0xEE); +// if(vgascreen.ldepth == 3) + outb(0x217B, 0x01); +// else +// outb(0x217B, 0x00); + + /* + * Enable the CRTCB/Sprite. + */ + outb(0x217A, 0xF7); + imaF7 = inb(0x217B); + outb(0x217B, 0x80|imaF7); +} + +static void +et4000load(VGAscr *scr, Cursor *c) +{ + uchar p0, p1, *mem; + int i, x, y; + ushort p; + uchar clr[2*16], set[2*16]; + + /* + * Lock the display memory so we can update the + * cursor bitmap if necessary. + */ + lock(&scr->devlock); + + /* + * Disable the cursor. + * Set the display page (do we need to restore + * the current contents when done?) and the + * pointer to the two planes. What if this crosses + * into a new page? + */ + et4000disable(scr); + + setet4000page(scr->storage>>16); + mem = (uchar*)KADDR(scr->aperture) + (scr->storage & 0xFFFF); + + /* + * Initialise the 64x64 cursor RAM array. There are 2 planes, + * p0 and p1. Data is written 4 pixels per byte, with p1 the + * MS bit of each pixel. + * The cursor mode gives the following truth table: + * p1 p0 colour + * 0 0 Sprite Colour 0 (defined as 0x00) + * 0 1 Sprite Colour 1 (defined as 0xFF) + * 1 0 Transparent (allow CRTC pixel pass through) + * 1 1 Invert (allow CRTC pixel invert through) + * Put the cursor into the top-left of the 64x64 array. + * + * This is almost certainly wrong, since it has not + * been updated for the 3rd edition color values. + */ + memmove(clr, c->clr, sizeof(clr)); +// pixreverse(clr, sizeof(clr), 0); + memmove(set, c->set, sizeof(set)); +// pixreverse(set, sizeof(set), 0); + for(y = 0; y < 64; y++){ + for(x = 0; x < 64/8; x++){ + if(x < 16/8 && y < 16){ + p0 = clr[x+y*2]; + p1 = set[x+y*2]; + + p = 0x0000; + for(i = 0; i < 8; i++){ + if(p1 & (1<<(7-i))){ + /* nothing to do */ + } + else if(p0 & (1<<(7-i))) + p |= 0x01<<(2*i); + else + p |= 0x02<<(2*i); + } + *mem++ = p & 0xFF; + *mem++ = (p>>8) & 0xFF; + } + else { + *mem++ = 0xAA; + *mem++ = 0xAA; + } + } + } + + /* + * enable the cursor. + */ + outb(0x217A, 0xF7); + p = inb(0x217B)|0x80; + outb(0x217B, p); + + unlock(&scr->devlock); +} + +static int +et4000move(VGAscr *scr, Point p) +{ + int x, xo, y, yo; + + if(canlock(&scr->devlock) == 0) + return 1; + + /* + * Mustn't position the cursor offscreen even partially, + * or it disappears. Therefore, if x or y is -ve, adjust the + * cursor presets instead. + */ + if((x = p.x+scr->offset.x) < 0){ + xo = -x; + x = 0; + } + else + xo = 0; + if((y = p.y+scr->offset.y) < 0){ + yo = -y; + y = 0; + } + else + yo = 0; + + /* + * The cursor image is jerky if we don't do this. + * The cursor information is probably fetched from + * display memory during the horizontal blank active + * time and it doesn't like it if the coordinates + * are changed underneath. + */ + while((vgai(Status1) & 0x08) == 0) + ; + + outb(0x217A, 0xE2); + outb(0x217B, xo); + + outb(0x217A, 0xE6); + outb(0x217B, yo); + + outb(0x217A, 0xE1); + outb(0x217B, (x>>8) & 0xFF); + outb(0x217A, 0xE0); + outb(0x217B, x & 0xFF); + outb(0x217A, 0xE5); + outb(0x217B, (y>>8) & 0xFF); + outb(0x217A, 0xE4); + outb(0x217B, y & 0xFF); + + unlock(&scr->devlock); + return 0; +} + +VGAcur vgaet4000cur = { + "et4000hwgc", + + et4000enable, + et4000disable, + et4000load, + et4000move, +}; + +VGAdev vgaet4000dev = { + "et4000", + + 0, + 0, + et4000page, + 0 +}; diff --git a/os/pc/vgahiqvideo.c b/os/pc/vgahiqvideo.c new file mode 100644 index 00000000..6ae02b04 --- /dev/null +++ b/os/pc/vgahiqvideo.c @@ -0,0 +1,274 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#define Image IMAGE +#include <draw.h> +#include <memdraw.h> +#include <cursor.h> +#include "screen.h" + +enum { + Xrx = 0x3D6, /* Configuration Extensions Index */ +}; + +static uchar +hiqvideoxi(long port, uchar index) +{ + uchar data; + + outb(port, index); + data = inb(port+1); + + return data; +} + +static void +hiqvideoxo(long port, uchar index, uchar data) +{ + outb(port, index); + outb(port+1, data); +} + +static ulong +hiqvideolinear(VGAscr* scr, int* size, int* align) +{ + ulong aperture, oaperture; + int oapsize, wasupamem; + Pcidev *p; + + oaperture = scr->aperture; + oapsize = scr->apsize; + wasupamem = scr->isupamem; + + aperture = 0; + if(p = pcimatch(nil, 0x102C, 0)){ + switch(p->did){ + case 0x00C0: /* 69000 HiQVideo */ + case 0x00E0: /* 65550 HiQV32 */ + case 0x00E4: /* 65554 HiQV32 */ + case 0x00E5: /* 65555 HiQV32 */ + aperture = p->mem[0].bar & ~0x0F; + *size = p->mem[0].size; + break; + default: + break; + } + } + + if(wasupamem){ + if(oaperture == aperture) + return oaperture; + upafree(oaperture, oapsize); + } + scr->isupamem = 0; + + aperture = upamalloc(aperture, *size, *align); + if(aperture == 0){ + if(wasupamem && upamalloc(oaperture, oapsize, 0)){ + aperture = oaperture; + scr->isupamem = 1; + } + else + scr->isupamem = 0; + } + else + scr->isupamem = 1; + + return aperture; +} + +static void +hiqvideoenable(VGAscr* scr) +{ + Pcidev *p; + int align, size, vmsize; + ulong aperture; + + /* + * Only once, can't be disabled for now. + */ + if(scr->io) + return; + if(p = pcimatch(nil, 0x102C, 0)){ + switch(p->did){ + case 0x00C0: /* 69000 HiQVideo */ + vmsize = 2*1024*1024; + break; + case 0x00E0: /* 65550 HiQV32 */ + case 0x00E4: /* 65554 HiQV32 */ + case 0x00E5: /* 65555 HiQV32 */ + switch((hiqvideoxi(Xrx, 0x43)>>1) & 0x03){ + default: + case 0: + vmsize = 1*1024*1024; + break; + case 1: + vmsize = 2*1024*1024; + break; + } + break; + default: + return; + } + } + else + return; + + size = p->mem[0].size; + align = 0; + aperture = hiqvideolinear(scr, &size, &align); + if(aperture) { + scr->aperture = aperture; + scr->apsize = size; + addvgaseg("hiqvideoscreen", aperture, size); + } + + /* + * Find a place for the cursor data in display memory. + * Must be on a 4096-byte boundary. + * scr->io holds the physical address of the cursor + * storage area in the framebuffer region. + */ + scr->storage = vmsize-4096; + scr->io = scr->aperture+scr->storage; +} + +static void +hiqvideocurdisable(VGAscr*) +{ + hiqvideoxo(Xrx, 0xA0, 0x10); +} + +static void +hiqvideocurload(VGAscr* scr, Cursor* curs) +{ + uchar *p; + int x, y; + + /* + * Disable the cursor. + */ + hiqvideocurdisable(scr); + + if(scr->io == 0) + return; + p = KADDR(scr->io); + + for(y = 0; y < 16; y += 2){ + *p++ = ~(curs->clr[2*y]|curs->set[2*y]); + *p++ = ~(curs->clr[2*y+1]|curs->set[2*y+1]); + *p++ = 0xFF; + *p++ = 0xFF; + *p++ = ~(curs->clr[2*y+2]|curs->set[2*y+2]); + *p++ = ~(curs->clr[2*y+3]|curs->set[2*y+3]); + *p++ = 0xFF; + *p++ = 0xFF; + *p++ = curs->set[2*y]; + *p++ = curs->set[2*y+1]; + *p++ = 0x00; + *p++ = 0x00; + *p++ = curs->set[2*y+2]; + *p++ = curs->set[2*y+3]; + *p++ = 0x00; + *p++ = 0x00; + } + while(y < 32){ + for(x = 0; x < 64; x += 8) + *p++ = 0xFF; + for(x = 0; x < 64; x += 8) + *p++ = 0x00; + y += 2; + } + + /* + * Save the cursor hotpoint and enable the cursor. + */ + scr->offset = curs->offset; + hiqvideoxo(Xrx, 0xA0, 0x11); +} + +static int +hiqvideocurmove(VGAscr* scr, Point p) +{ + int x, y; + + if(scr->io == 0) + return 1; + + if((x = p.x+scr->offset.x) < 0) + x = 0x8000|(-x & 0x07FF); + if((y = p.y+scr->offset.y) < 0) + y = 0x8000|(-y & 0x07FF); + + hiqvideoxo(Xrx, 0xA4, x & 0xFF); + hiqvideoxo(Xrx, 0xA5, (x>>8) & 0xFF); + hiqvideoxo(Xrx, 0xA6, y & 0xFF); + hiqvideoxo(Xrx, 0xA7, (y>>8) & 0xFF); + + return 0; +} + +static void +hiqvideocurenable(VGAscr* scr) +{ + uchar xr80; + + hiqvideoenable(scr); + if(scr->io == 0) + return; + + /* + * Disable the cursor. + */ + hiqvideocurdisable(scr); + + /* + * Cursor colours. + * Can't call setcolor here as cursor is already locked. + * When done make sure the cursor enable in Xr80 is set. + */ + xr80 = hiqvideoxi(Xrx, 0x80); + hiqvideoxo(Xrx, 0x80, xr80|0x01); + vgao(PaddrW, 0x04); + vgao(Pdata, Pwhite); + vgao(Pdata, Pwhite); + vgao(Pdata, Pwhite); + vgao(Pdata, Pblack); + vgao(Pdata, Pblack); + vgao(Pdata, Pblack); + hiqvideoxo(Xrx, 0x80, xr80|0x10); + + hiqvideoxo(Xrx, 0xA2, (scr->storage>>12)<<4); + hiqvideoxo(Xrx, 0xA3, (scr->storage>>16) & 0x3F); + + /* + * Load, locate and enable the 32x32 cursor. + * Cursor enable in Xr80 better be set already. + */ + hiqvideocurload(scr, &arrow); + hiqvideocurmove(scr, ZP); + hiqvideoxo(Xrx, 0xA0, 0x11); +} + +VGAdev vgahiqvideodev = { + "hiqvideo", + + hiqvideoenable, /* enable */ + nil, /* disable */ + nil, /* page */ + hiqvideolinear, /* linear */ +}; + +VGAcur vgahiqvideocur = { + "hiqvideohwgc", + + hiqvideocurenable, /* enable */ + hiqvideocurdisable, /* disable */ + hiqvideocurload, /* load */ + hiqvideocurmove, /* move */ +}; diff --git a/os/pc/vgai81x.c b/os/pc/vgai81x.c new file mode 100644 index 00000000..ac840fe6 --- /dev/null +++ b/os/pc/vgai81x.c @@ -0,0 +1,282 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#define Image IMAGE +#include <draw.h> +#include <memdraw.h> +#include <cursor.h> +#include "screen.h" + +typedef struct +{ + ushort ctl; + ushort pad; + ulong base; + ulong pos; +} CursorI81x; + +enum { + Fbsize = 8*MB, + + hwCur = 0x70080, +}; + +static Pcidev * +i81xpcimatch(void) +{ + Pcidev *p; + + p = nil; + while((p = pcimatch(p, 0x8086, 0)) != nil){ + switch(p->did){ + default: + continue; + case 0x7121: + case 0x7123: + case 0x7125: + case 0x1102: + case 0x1112: + case 0x1132: + case 0x3577: /* IBM R31 uses intel 830M chipset */ + return p; + } + } + return nil; +} + +static ulong +i81xlinear(VGAscr* scr, int* size, int* align) +{ + Pcidev *p; + int oapsize, wasupamem; + ulong aperture, oaperture, fbuf, fbend, *rp; + + oaperture = scr->aperture; + oapsize = scr->apsize; + wasupamem = scr->isupamem; + + aperture = 0; + p = i81xpcimatch(); + if(p != nil) { + aperture = p->mem[0].bar & ~0x0F; + *size = p->mem[0].size; + if(*size > Fbsize) + *size = Fbsize; + } + + if(wasupamem){ + if(oaperture == aperture) + return oaperture; + upafree(oaperture, oapsize); + } + scr->isupamem = 0; + + aperture = upamalloc(aperture, *size, *align); + if(aperture == 0){ + if(wasupamem && upamalloc(oaperture, oapsize, 0)){ + aperture = oaperture; + scr->isupamem = 1; + } + else + scr->isupamem = 0; + } + else + scr->isupamem = 1; + + /* allocate space for frame buffer, populate page table */ + if(oapsize == 0) { + fbuf = PADDR(xspanalloc(*size, BY2PG, 0)); + fbend = PGROUND(fbuf+*size); + rp = KADDR(scr->io+0x10000); + while(fbuf < fbend) { + *rp++ = fbuf | (1<<0); + fbuf += BY2PG; + } + } + return aperture; +} + +static void +i81xenable(VGAscr* scr) +{ + Pcidev *p; + int align, size; + Mach *mach0; + ulong aperture, pgtbl, *rp, cursor, *pte; + + /* + * Only once, can't be disabled for now. + * scr->io holds the physical address of + * the MMIO registers. + */ + if(scr->io) + return; + p = i81xpcimatch(); + if(p == nil) + return; + scr->io = upamalloc(p->mem[1].bar & ~0x0F, p->mem[1].size, 0); + if(scr->io == 0) + return; + + /* allocate page table */ + pgtbl = PADDR(xspanalloc(64*1024, BY2PG, 0)); + rp = KADDR(scr->io+0x2020); + *rp = pgtbl | 1; + + addvgaseg("i81xmmio", (ulong)scr->io, p->mem[0].size); + + size = p->mem[0].size; + align = 0; + aperture = i81xlinear(scr, &size, &align); + if(aperture){ + scr->aperture = aperture; + scr->apsize = size; + addvgaseg("i81xscreen", aperture, size); + } + + /* + * allocate space for the cursor data in system memory. + * must be uncached. + */ + cursor = (ulong)xspanalloc(BY2PG, BY2PG, 0); + mach0 = MACHP(0); + pte = mmuwalk(mach0->pdb, cursor, 2, 0); + if(pte == nil) + panic("i81x cursor"); + *pte |= PTEUNCACHED; + scr->storage = PADDR(cursor); +} + +static void +i81xcurdisable(VGAscr* scr) +{ + CursorI81x *hwcurs; + + if(scr->io == 0) + return; + hwcurs = KADDR(scr->io+hwCur); + hwcurs->ctl = (1<<4); +} + +static void +i81xcurload(VGAscr* scr, Cursor* curs) +{ + int y; + uchar *p; + CursorI81x *hwcurs; + + if(scr->io == 0) + return; + hwcurs = KADDR(scr->io+hwCur); + + /* + * Disable the cursor then load the new image in + * the top-left of the 32x32 array. + * Unused portions of the image have been initialised to be + * transparent. + */ + hwcurs->ctl = (1<<4); + p = KADDR(scr->storage); + for(y = 0; y < 16; y += 2) { + *p++ = ~(curs->clr[2*y]|curs->set[2*y]); + *p++ = ~(curs->clr[2*y+1]|curs->set[2*y+1]); + p += 2; + *p++ = ~(curs->clr[2*y+2]|curs->set[2*y+2]); + *p++ = ~(curs->clr[2*y+3]|curs->set[2*y+3]); + p += 2; + *p++ = curs->set[2*y]; + *p++ = curs->set[2*y+1]; + p += 2; + *p++ = curs->set[2*y+2]; + *p++ = curs->set[2*y+3]; + p += 2; + } + + /* + * Save the cursor hotpoint and enable the cursor. + * The 0,0 cursor point is top-left. + */ + scr->offset.x = curs->offset.x; + scr->offset.y = curs->offset.y; + hwcurs->ctl = (1<<4)|1; +} + +static int +i81xcurmove(VGAscr* scr, Point p) +{ + int x, y; + ulong pos; + CursorI81x *hwcurs; + + if(scr->io == 0) + return 1; + hwcurs = KADDR(scr->io+hwCur); + + x = p.x+scr->offset.x; + y = p.y+scr->offset.y; + pos = 0; + if(x < 0) { + pos |= (1<<15); + x = -x; + } + if(y < 0) { + pos |= (1<<31); + y = -y; + } + pos |= ((y&0x7ff)<<16)|(x&0x7ff); + hwcurs->pos = pos; + + return 0; +} + +static void +i81xcurenable(VGAscr* scr) +{ + int i; + uchar *p; + CursorI81x *hwcurs; + + i81xenable(scr); + if(scr->io == 0) + return; + hwcurs = KADDR(scr->io+hwCur); + + /* + * Initialise the 32x32 cursor to be transparent in 2bpp mode. + */ + hwcurs->base = scr->storage; + p = KADDR(scr->storage); + for(i = 0; i < 32/2; i++) { + memset(p, 0xff, 8); + memset(p+8, 0, 8); + p += 16; + } + /* + * Load, locate and enable the 32x32 cursor in 2bpp mode. + */ + i81xcurload(scr, &arrow); + i81xcurmove(scr, ZP); +} + +VGAdev vgai81xdev = { + "i81x", + + i81xenable, + nil, + nil, + i81xlinear, +}; + +VGAcur vgai81xcur = { + "i81xhwgc", + + i81xcurenable, + i81xcurdisable, + i81xcurload, + i81xcurmove, +}; diff --git a/os/pc/vgamach64xx.c b/os/pc/vgamach64xx.c new file mode 100644 index 00000000..322c449c --- /dev/null +++ b/os/pc/vgamach64xx.c @@ -0,0 +1,1250 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#define Image IMAGE +#include <draw.h> +#include <memdraw.h> +#include <cursor.h> +#include "screen.h" + +char Eunsupportedformat[] = "unsupported video format"; +char Enotconfigured[] = "device not configured"; + +#define SCALE_ZERO_EXTEND 0x0 +#define SCALE_DYNAMIC 0x1 +#define SCALE_RED_TEMP_6500K 0x0 +#define SCALE_RED_TEMP_9800K 0x2 +#define SCALE_HORZ_BLEND 0x0 +#define SCALE_HORZ_REP 0x4 +#define SCALE_VERT_BLEND 0x0 +#define SCALE_VERT_REP 0x8 +#define SCALE_BANDWIDTH_NORMAL 0x0 +#define SCALE_BANDWIDTH_EXCEEDED 0x4000000 +#define SCALE_BANDWIDTH_RESET 0x4000000 +#define SCALE_CLK_ACTIVITY 0x0 +#define SCALE_CLK_CONTINUOUS 0x20000000 +#define OVERLAY_DISABLE 0x0 +#define OVERLAY_ENABLE 0x40000000 +#define SCALE_DISABLE 0x0 +#define SCALE_ENABLE 0x80000000 + +#define SCALER_FRAME_READ_MODE_FULL 0x0 +#define SCALER_BUF_MODE_SINGLE 0x0 +#define SCALER_BUF_MODE_DOUBLE 0x40000 +#define SCALER_BUF_NEXT_0 0x0 +#define SCALER_BUF_NEXT_1 0x80000 +#define SCALER_BUF_STATUS_0 0x0 +#define SCALER_BUF_STATUS_1 0x100000 + +#define OVERLAY_MIX_G_CMP 0x0 +#define OVERLAY_MIX_ALWAYS_G 0x100 +#define OVERLAY_MIX_ALWAYS_V 0x200 +#define OVERLAY_MIX_NOT_G 0x300 +#define OVERLAY_MIX_NOT_V 0x400 +#define OVERLAY_MIX_G_XOR_V 0x500 +#define OVERLAY_MIX_NOT_G_XOR_V 0x600 +#define OVERLAY_MIX_V_CMP 0x700 +#define OVERLAY_MIX_NOT_G_OR_NOT_V 0x800 +#define OVERLAY_MIX_G_OR_NOT_V 0x900 +#define OVERLAY_MIX_NOT_G_OR_V 0xA00 +#define OVERLAY_MIX_G_OR_V 0xB00 +#define OVERLAY_MIX_G_AND_V 0xC00 +#define OVERLAY_MIX_NOT_G_AND_V 0xD00 +#define OVERLAY_MIX_G_AND_NOT_V 0xE00 +#define OVERLAY_MIX_NOT_G_AND_NOT_V 0xF00 +#define OVERLAY_EXCLUSIVE_NORMAL 0x0 +#define OVERLAY_EXCLUSIVE_V_ONLY 0x80000000 + +#define VIDEO_IN_8BPP 0x2 +#define VIDEO_IN_16BPP 0x4 +#define VIDEO_IN_32BPP 0x6 +#define VIDEO_IN_VYUY422 0xB /*16 bpp */ +#define VIDEO_IN_YVYU422 0xC /* 16 bpp */ +#define SCALE_IN_15BPP 0x30000 /* aRGB 1555 */ +#define SCALE_IN_16BPP 0x40000 /* RGB 565 */ +#define SCALE_IN_32BPP 0x60000 /* aRGB 8888 */ +#define SCALE_IN_YUV9 0x90000 /* planar */ +#define SCALE_IN_YUV12 0xA0000 /* planar */ +#define SCALE_IN_VYUY422 0xB0000 /* 16 bpp */ +#define SCALE_IN_YVYU422 0xC0000 /* 16 bpp */ +#define HOST_YUV_APERTURE_UPPER 0x0 +#define HOST_YUV_APERTURE_LOWER 0x20000000 +#define HOST_MEM_MODE_Y 0x40000000 +#define HOST_MEM_MODE_U 0x80000000 +#define HOST_MEM_MODE_V 0xC0000000 +#define HOST_MEM_MODE_NORMAL HOST_YUV_APERTURE_UPPER + +static Chan *ovl_chan; /* Channel of controlling process */ +static int ovl_width; /* Width of input overlay buffer */ +static int ovl_height; /* Height of input overlay buffer */ +static int ovl_format; /* Overlay format */ +static ulong ovl_fib; /* Frame in bytes */ + +enum { + VTGTB1S1 = 0x01, // Asic description for VTB1S1 and GTB1S1. + VT4GTIIC = 0x3A, // asic descr for VT4 and RAGE IIC + GTB1U1 = 0x19, // Asic description for GTB1U1. + GTB1S2 = 0x41, // Asic description for GTB1S2. + GTB2U1 = 0x1A, + GTB2U2 = 0x5A, + GTB2U3 = 0x9A, + GTIIIC1U1 = 0x1B, // 3D RAGE PRO asic descrp. + GTIIIC1U2 = 0x5B, // 3D RAGE PRO asic descrp. + GTIIIC2U1 = 0x1C, // 3D RAGE PRO asic descrp. + GTIIIC2U2 = 0x5C, // 3D RAGE PRO asic descrp. + GTIIIC2U3 = 0x7C, // 3D RAGE PRO asic descrp. + GTBC = 0x3A, // 3D RAGE IIC asic descrp. + LTPRO = 0x9C, // 3D RAGE LT PRO +}; + +/* + * ATI Mach64(CT|ET|G*|V*|L*). + */ +typedef struct Mach64types Mach64types; +struct Mach64types { + ushort m64_id; /* Chip ID */ + int m64_vtgt; /* Is this a VT or GT chipset? */ + ulong m64_ovlclock; /* Max. overlay clock frequency */ + int m64_pro; /* Is this a PRO? */ +}; + +static ulong mach64refclock; +static Mach64types *mach64type; +static int mach64revb; /* Revision B or greater? */ +static ulong mach64overlay; /* Overlay buffer */ + +static Mach64types mach64s[] = { + ('C'<<8)|'T', 0, 1350000, /*?*/ 0, /* 4354: CT */ + ('E'<<8)|'T', 0, 1350000, /*?*/ 0, /* 4554: ET */ + ('G'<<8)|'B', 1, 1250000, 1, /* 4742: 264GT PRO */ + ('G'<<8)|'D', 1, 1250000, 1, /* 4744: 264GT PRO */ + ('G'<<8)|'I', 1, 1250000, 1, /* 4749: 264GT PRO */ + ('G'<<8)|'M', 0, 1350000, 0, /* 474D: Rage XL */ + ('G'<<8)|'P', 1, 1250000, 1, /* 4750: 264GT PRO */ + ('G'<<8)|'Q', 1, 1250000, 1, /* 4751: 264GT PRO */ + ('G'<<8)|'R', 1, 1250000, 1, /* 4752: */ + ('G'<<8)|'T', 1, 800000, 0, /* 4754: 264GT[B] */ + ('G'<<8)|'U', 1, 1000000, 0, /* 4755: 264GT DVD */ + ('G'<<8)|'V', 1, 1000000, 0, /* 4756: Rage2C */ + ('G'<<8)|'Z', 1, 1000000, 0, /* 475A: Rage2C */ + ('V'<<8)|'T', 1, 800000, 0, /* 5654: 264VT/GT/VTB */ + ('V'<<8)|'U', 1, 800000, 0, /* 5655: 264VT3 */ + ('V'<<8)|'V', 1, 1000000, 0, /* 5656: 264VT4 */ + ('L'<<8)|'B', 0, 1350000, 1, /* 4C42: Rage LTPro AGP */ + ('L'<<8)|'I', 0, 1350000, 0, /* 4C49: Rage LTPro AGP */ + ('L'<<8)|'M', 0, 1350000, 0, /* 4C4D: Rage Mobility */ + ('L'<<8)|'P', 0, 1350000, 1, /* 4C50: 264LT PRO */ +}; + + +static int hwfill(VGAscr*, Rectangle, ulong); +static int hwscroll(VGAscr*, Rectangle, Rectangle); +static void initengine(VGAscr*); + +static Pcidev* +mach64xxpci(void) +{ + Pcidev *p; + int i; + + if((p = pcimatch(nil, 0x1002, 0)) == nil) + return nil; + + for (i = 0; i != nelem(mach64s); i++) + if (mach64s[i].m64_id == p->did) { + mach64type = &mach64s[i]; + return p; + } + return nil; +} + +static void +mach64xxenable(VGAscr* scr) +{ + Pcidev *p; + + /* + * Only once, can't be disabled for now. + */ + if(scr->io) + return; + if(p = mach64xxpci()){ + scr->id = p->did; + + /* + * The CT doesn't always have the I/O base address + * in the PCI base registers. There is a way to find + * it via the vendor-specific PCI config space but + * this will do for now. + */ + scr->io = p->mem[1].bar & ~0x03; + + if(scr->io == 0) + scr->io = 0x2EC; + } +} + +static ulong +mach64xxlinear(VGAscr* scr, int* size, int* align) +{ + ulong aperture, osize, oaperture; + int i, oapsize, wasupamem; + Pcidev *p; + + osize = *size; + oaperture = scr->aperture; + oapsize = scr->apsize; + wasupamem = scr->isupamem; + + if(p = mach64xxpci()){ + for(i=0; i<nelem(p->mem); i++){ + if(p->mem[i].size >= *size + && ((p->mem[i].bar & ~0x0F) & (*align-1)) == 0) + break; + } + if(i >= nelem(p->mem)){ + print("vgamach64xx: aperture not found\n"); + return 0; + } + aperture = p->mem[i].bar & ~0x0F; + *size = p->mem[i].size; + } + else + aperture = 0; + + if(wasupamem) + upafree(oaperture, oapsize); + scr->isupamem = 0; + + aperture = upamalloc(aperture, *size, *align); + if(aperture == 0){ + if(wasupamem && upamalloc(oaperture, oapsize, 0)) + scr->isupamem = 1; + } + else + scr->isupamem = 1; + + scr->mmio = KADDR(aperture+osize-0x400); + if(oaperture && oaperture != aperture) + print("warning (BUG): redefinition of aperture does not change mach64mmio segment\n"); + addvgaseg("mach64mmio", aperture+osize-BY2PG, BY2PG); + addvgaseg("mach64screen", aperture, osize); + + return aperture; +} + +enum { + CrtcOffPitch = 0x05, + CrtcGenCtl = 0x07, + CurClr0 = 0x0B, /* I/O Select */ + CurClr1 = 0x0C, + CurOffset = 0x0D, + CurHVposn = 0x0E, + CurHVoff = 0x0F, + BusCntl = 0x13, + GenTestCntl = 0x19, + + CrtcHsyncDis = 0x04, + CrtcVsyncDis = 0x08, + + ContextMask = 0x100, /* not accessible via I/O */ + FifoStat, + GuiStat, + DpFrgdClr, + DpBkgdClr, + DpWriteMask, + DpMix, + DpPixWidth, + DpSrc, + ClrCmpCntl, + GuiTrajCntl, + ScLeftRight, + ScTopBottom, + DstOffPitch, + DstYX, + DstHeightWidth, + DstCntl, + DstHeight, + DstBresErr, + DstBresInc, + DstBresDec, + SrcCntl, + SrcHeight1Width1, + SrcHeight2Width2, + SrcYX, + SrcWidth1, + SrcYXstart, + HostCntl, + PatReg0, + PatReg1, + PatCntl, + ScBottom, + ScLeft, + ScRight, + ScTop, + ClrCmpClr, + ClrCmpMask, + DpChainMask, + SrcOffPitch, + LcdIndex, + LcdData, + ClockCntl, + OverlayScaleCntl, + ConfigChipId, + Buf0Pitch, + ScalerBuf0Pitch, + CaptureConfig, + OverlayKeyCntl, + ScalerColourCntl, + ScalerHCoef0, + ScalerHCoef1, + ScalerHCoef2, + ScalerHCoef3, + ScalerHCoef4, + VideoFormat, + Buf0Offset, + ScalerBuf0Offset, + CrtcGenCntl, + OverlayScaleInc, + OverlayYX, + OverlayYXEnd, + ScalerHeightWidth, + HTotalDisp, + VTotalDisp, +}; + +enum { + LCD_ConfigPanel = 0, + LCD_GenCtrl, + LCD_DstnCntl, + LCD_HfbPitchAddr, + LCD_HorzStretch, + LCD_VertStretch, + LCD_ExtVertStretch, + LCD_LtGio, + LCD_PowerMngmnt, + LCD_ZvgPio, + Nlcd, +}; + +#define Bank1 (-0x100) /* 1KB */ + +static int mmoffset[] = { + [HTotalDisp] 0x00, + [VTotalDisp] 0x02, + [CrtcOffPitch] 0x05, + [CrtcGenCntl] 0x07, + [CurClr0] 0x18, + [CurClr1] 0x19, + [CurOffset] 0x1A, + [CurHVposn] 0x1B, + [CurHVoff] 0x1C, + [ClockCntl] 0x24, + [BusCntl] 0x28, + [LcdIndex] 0x29, + [LcdData] 0x2A, + [GenTestCntl] 0x34, + [ConfigChipId] 0x38, + [DstOffPitch] 0x40, + [DstYX] 0x43, + [DstHeight] 0x45, + [DstHeightWidth] 0x46, + [DstBresErr] 0x49, + [DstBresInc] 0x4A, + [DstBresDec] 0x4B, + [DstCntl] 0x4C, + [SrcOffPitch] 0x60, + [SrcYX] 0x63, + [SrcWidth1] 0x64, + [SrcYXstart] 0x69, + [SrcHeight1Width1] 0x66, + [SrcHeight2Width2] 0x6C, + [SrcCntl] 0x6D, + [HostCntl] 0x90, + [PatReg0] 0xA0, + [PatReg1] 0xA1, + [PatCntl] 0xA2, + [ScLeft] 0xA8, + [ScRight] 0xA9, + [ScLeftRight] 0xAA, + [ScTop] 0xAB, + [ScBottom] 0xAC, + [ScTopBottom] 0xAD, + [DpBkgdClr] 0xB0, + [DpFrgdClr] 0xB1, + [DpWriteMask] 0xB2, + [DpChainMask] 0xB3, + [DpPixWidth] 0xB4, + [DpMix] 0xB5, + [DpSrc] 0xB6, + [ClrCmpClr] 0xC0, + [ClrCmpMask] 0xC1, + [ClrCmpCntl] 0xC2, + [FifoStat] 0xC4, + [ContextMask] 0xC8, + [GuiTrajCntl] 0xCC, + [GuiStat] 0xCE, + + /* Bank1 */ + [OverlayYX] Bank1 + 0x00, + [OverlayYXEnd] Bank1 + 0x01, + [OverlayKeyCntl] Bank1 + 0x06, + [OverlayScaleInc] Bank1 + 0x08, + [OverlayScaleCntl] Bank1 + 0x09, + [ScalerHeightWidth] Bank1 + 0x0A, + [ScalerBuf0Offset] Bank1 + 0x0D, + [ScalerBuf0Pitch] Bank1 + 0x0F, + [VideoFormat] Bank1 + 0x12, + [CaptureConfig] Bank1 + 0x14, + [Buf0Offset] Bank1 + 0x20, + [Buf0Pitch] Bank1 + 0x23, + [ScalerColourCntl] Bank1 + 0x54, + [ScalerHCoef0] Bank1 + 0x55, + [ScalerHCoef1] Bank1 + 0x56, + [ScalerHCoef2] Bank1 + 0x57, + [ScalerHCoef3] Bank1 + 0x58, + [ScalerHCoef4] Bank1 + 0x59, +}; + +static ulong +ior32(VGAscr* scr, int r) +{ + if(scr->io == 0x2EC || scr->io == 0x1C8) + return inl((r<<10)+scr->io); + if(r >= 0x100 && scr->mmio != nil) + return scr->mmio[mmoffset[r]]; + return inl((mmoffset[r]<<2)+scr->io); +} + +static void +iow32(VGAscr* scr, int r, ulong l) +{ + if(scr->io == 0x2EC || scr->io == 0x1C8) + outl(((r)<<10)+scr->io, l); + else if(r >= 0x100 && scr->mmio != nil) + scr->mmio[mmoffset[r]] = l; + else + outl((mmoffset[r]<<2)+scr->io, l); +} + +static ulong +lcdr32(VGAscr *scr, ulong r) +{ + ulong or; + + or = ior32(scr, LcdIndex); + iow32(scr, LcdIndex, (or&~0x0F) | (r&0x0F)); + return ior32(scr, LcdData); +} + +static void +lcdw32(VGAscr *scr, ulong r, ulong v) +{ + ulong or; + + or = ior32(scr, LcdIndex); + iow32(scr, LcdIndex, (or&~0x0F) | (r&0x0F)); + iow32(scr, LcdData, v); +} + +static void +mach64xxcurdisable(VGAscr* scr) +{ + ulong r; + + r = ior32(scr, GenTestCntl); + iow32(scr, GenTestCntl, r & ~0x80); +} + +static void +mach64xxcurload(VGAscr* scr, Cursor* curs) +{ + uchar *p; + int i, y; + ulong c, s, m, r; + + /* + * Disable the cursor. + */ + r = ior32(scr, GenTestCntl); + iow32(scr, GenTestCntl, r & ~0x80); + + p = KADDR(scr->aperture); + p += scr->storage; + + /* + * Initialise the 64x64 cursor RAM array. + * The cursor mode gives the following truth table: + * p1 p0 colour + * 0 0 Cursor Colour 0 + * 0 1 Cursor Colour 1 + * 1 0 Transparent + * 1 1 Complement + * Put the cursor into the top-right of the 64x64 array. + */ + for(y = 0; y < 16; y++){ + for(i = 0; i < (64-16)/8; i++){ + *p++ = 0xAA; + *p++ = 0xAA; + } + + c = (curs->clr[2*y]<<8)|curs->clr[y*2 + 1]; + s = (curs->set[2*y]<<8)|curs->set[y*2 + 1]; + + m = 0x00000000; + for(i = 0; i < 16; i++){ + if(s & (1<<(15-i))) + m |= 0x01<<(2*i); + else if(c & (1<<(15-i))){ + /* nothing to do */ + } + else + m |= 0x02<<(2*i); + } + *p++ = m; + *p++ = m>>8; + *p++ = m>>16; + *p++ = m>>24; + } + memset(p, 0xAA, (64-16)*16); + + /* + * Set the cursor hotpoint and enable the cursor. + */ + scr->offset = curs->offset; + iow32(scr, GenTestCntl, 0x80|r); +} + +static int +ptalmostinrect(Point p, Rectangle r) +{ + return p.x>=r.min.x && p.x<=r.max.x && + p.y>=r.min.y && p.y<=r.max.y; +} + +/* + * If necessary, translate the rectangle physr + * some multiple of [dx dy] so that it includes p. + * Return 1 if the rectangle changed. + */ +static int +screenpan(Point p, Rectangle *physr, int dx, int dy) +{ + int d; + + if(ptalmostinrect(p, *physr)) + return 0; + + if(p.y < physr->min.y){ + d = physr->min.y - (p.y&~(dy-1)); + physr->min.y -= d; + physr->max.y -= d; + } + if(p.y > physr->max.y){ + d = ((p.y+dy-1)&~(dy-1)) - physr->max.y; + physr->min.y += d; + physr->max.y += d; + } + + if(p.x < physr->min.x){ + d = physr->min.x - (p.x&~(dx-1)); + physr->min.x -= d; + physr->max.x -= d; + } + if(p.x > physr->max.x){ + d = ((p.x+dx-1)&~(dx-1)) - physr->max.x; + physr->min.x += d; + physr->max.x += d; + } + return 1; +} + +static int +mach64xxcurmove(VGAscr* scr, Point p) +{ + int x, xo, y, yo; + int dx; + ulong off, pitch; + + /* + * If the point we want to display is outside the current + * screen rectangle, pan the screen to display it. + * + * We have to move in 64-bit chunks. + */ + if(scr->gscreen->depth == 24) + dx = (64*3)/24; + else + dx = 64 / scr->gscreen->depth; + + if(panning && screenpan(p, &physgscreenr, dx, 1)){ + off = (physgscreenr.min.y*Dx(scr->gscreen->r)+physgscreenr.min.x)/dx; + pitch = Dx(scr->gscreen->r)/8; + iow32(scr, CrtcOffPitch, (pitch<<22)|off); + } + + p.x -= physgscreenr.min.x; + p.y -= physgscreenr.min.y; + + /* + * Mustn't position the cursor offscreen even partially, + * or it disappears. Therefore, if x or y is -ve, adjust the + * cursor presets instead. If y is negative also have to + * adjust the starting offset. + */ + if((x = p.x+scr->offset.x) < 0){ + xo = x; + x = 0; + } + else + xo = 0; + if((y = p.y+scr->offset.y) < 0){ + yo = y; + y = 0; + } + else + yo = 0; + + iow32(scr, CurHVoff, ((64-16-yo)<<16)|(64-16-xo)); + iow32(scr, CurOffset, scr->storage/8 + (-yo*2)); + iow32(scr, CurHVposn, (y<<16)|x); + + return 0; +} + +static void +mach64xxcurenable(VGAscr* scr) +{ + ulong r, storage; + + mach64xxenable(scr); + if(scr->io == 0) + return; + + r = ior32(scr, GenTestCntl); + iow32(scr, GenTestCntl, r & ~0x80); + + iow32(scr, CurClr0, (Pwhite<<24)|(Pwhite<<16)|(Pwhite<<8)|Pwhite); + iow32(scr, CurClr1, (Pblack<<24)|(Pblack<<16)|(Pblack<<8)|Pblack); + + /* + * Find a place for the cursor data in display memory. + * Must be 64-bit aligned. + */ + storage = (scr->gscreen->width*BY2WD*scr->gscreen->r.max.y+7)/8; + iow32(scr, CurOffset, storage); + scr->storage = storage*8; + + /* + * Cursor goes in the top right corner of the 64x64 array + * so the horizontal and vertical presets are 64-16. + */ + iow32(scr, CurHVposn, (0<<16)|0); + iow32(scr, CurHVoff, ((64-16)<<16)|(64-16)); + + /* + * Load, locate and enable the 64x64 cursor. + */ + mach64xxcurload(scr, &arrow); + mach64xxcurmove(scr, ZP); + iow32(scr, GenTestCntl, 0x80|r); +} + +static void +waitforfifo(VGAscr *scr, int entries) +{ + int x; + + x = 0; + while((ior32(scr, FifoStat)&0xFF) > (0x8000>>entries) && x++ < 1000000) + ; + if(x >= 1000000) + iprint("fifo %d stat %.8lux %.8lux scrio %.8lux mmio %p scr %p pc %luX\n", entries, ior32(scr, FifoStat), scr->mmio[mmoffset[FifoStat]], scr->io, scr->mmio, scr, getcallerpc(&scr)); +} + +static void +waitforidle(VGAscr *scr) +{ + int x; + + waitforfifo(scr, 16); + x = 0; + while((ior32(scr, GuiStat)&1) && x++ < 1000000) + ; + if(x >= 1000000) + iprint("idle stat %.8lux %.8lux scrio %.8lux mmio %p scr %p pc %luX\n", ior32(scr, GuiStat), scr->mmio[mmoffset[GuiStat]], scr->io, scr->mmio, scr, getcallerpc(&scr)); +} + +static void +resetengine(VGAscr *scr) +{ + ulong x; + x = ior32(scr, GenTestCntl); + iow32(scr, GenTestCntl, x&~0x100); + iow32(scr, GenTestCntl, x|0x100); + iow32(scr, BusCntl, ior32(scr, BusCntl)|0x00A00000); +} + +static void +init_overlayclock(VGAscr *scr) +{ + uchar *cc, save, pll_ref_div, pll_vclk_cntl, vclk_post_div, + vclk_fb_div, ecp_div; + int i; + ulong dotclock; + + /* Taken from GLX */ + /* Get monitor dotclock, check for Overlay Scaler clock limit */ + cc = (uchar *)&scr->mmio[mmoffset[ClockCntl]]; + save = cc[1]; i = cc[0] & 3; + cc[1] = 2<<2; pll_ref_div = cc[2]; + cc[1] = 5<<2; pll_vclk_cntl = cc[2]; + cc[1] = 6<<2; vclk_post_div = (cc[2]>>(i+i)) & 3; + cc[1] = (7+i)<<2; vclk_fb_div = cc[2]; + + dotclock = 2 * mach64refclock * vclk_fb_div / + (pll_ref_div * (1 << vclk_post_div)); + /* ecp_div: 0=dotclock, 1=dotclock/2, 2=dotclock/4 */ + ecp_div = dotclock / mach64type->m64_ovlclock; + if (ecp_div>2) ecp_div = 2; + + /* Force a scaler clock factor of 1 if refclock * + * is unknown (VCLK_SRC not PLLVCLK) */ + if ((pll_vclk_cntl & 0x03) != 0x03) + ecp_div = 0; + if ((pll_vclk_cntl & 0x30) != ecp_div<<4) { + cc[1] = (5<<2)|2; + cc[2] = (pll_vclk_cntl&0xCF) | (ecp_div<<4); + } + + /* Restore PLL Register Index */ + cc[1] = save; +} + +static void +initengine(VGAscr *scr) +{ + ulong pitch; + uchar *bios; + ushort table; + + pitch = Dx(scr->gscreen->r)/8; + if(scr->gscreen->depth == 24) + pitch *= 3; + + resetengine(scr); + waitforfifo(scr, 14); + iow32(scr, ContextMask, ~0); + iow32(scr, DstOffPitch, pitch<<22); + iow32(scr, DstYX, 0); + iow32(scr, DstHeight, 0); + iow32(scr, DstBresErr, 0); + iow32(scr, DstBresInc, 0); + iow32(scr, DstBresDec, 0); + iow32(scr, DstCntl, 0x23); + iow32(scr, SrcOffPitch, pitch<<22); + iow32(scr, SrcYX, 0); + iow32(scr, SrcHeight1Width1, 1); + iow32(scr, SrcYXstart, 0); + iow32(scr, SrcHeight2Width2, 1); + iow32(scr, SrcCntl, 0x01); + + waitforfifo(scr, 13); + iow32(scr, HostCntl, 0); + iow32(scr, PatReg0, 0); + iow32(scr, PatReg1, 0); + iow32(scr, PatCntl, 0); + iow32(scr, ScLeft, 0); + iow32(scr, ScTop, 0); + iow32(scr, ScBottom, 0xFFFF); + iow32(scr, ScRight, 0xFFFF); + iow32(scr, DpBkgdClr, 0); + iow32(scr, DpFrgdClr, ~0); + iow32(scr, DpWriteMask, ~0); + iow32(scr, DpMix, 0x70003); + iow32(scr, DpSrc, 0x00010100); + + waitforfifo(scr, 3); + iow32(scr, ClrCmpClr, 0); + iow32(scr, ClrCmpMask, ~0); + iow32(scr, ClrCmpCntl, 0); + + waitforfifo(scr, 2); + switch(scr->gscreen->depth){ + case 8: + case 24: /* [sic] */ + iow32(scr, DpPixWidth, 0x00020202); + iow32(scr, DpChainMask, 0x8080); + break; + case 16: + iow32(scr, DpPixWidth, 0x00040404); + iow32(scr, DpChainMask, 0x8410); + break; + case 32: + iow32(scr, DpPixWidth, 0x00060606); + iow32(scr, DpChainMask, 0x8080); + break; + } + + /* Get the base freq from the BIOS */ + bios = KADDR(0xC000); + table = *(ushort *)(bios + 0x48); + table = *(ushort *)(bios + table + 0x10); + switch (*(ushort *)(bios + table + 0x08)) { + case 2700: + mach64refclock = 270000; + break; + case 2863: + case 2864: + mach64refclock = 286363; + break; + case 2950: + mach64refclock = 294989; + break; + case 1432: + default: + mach64refclock = 143181; + break ; + } + + /* Figure out which revision this chip is */ + switch ((scr->mmio[mmoffset[ConfigChipId]] >> 24) & 0xFF) { + case VTGTB1S1: + case GTB1U1: + case GTB1S2: + case GTB2U1: + case GTB2U2: + case GTB2U3: + case GTBC: + case GTIIIC1U1: + case GTIIIC1U2: + case GTIIIC2U1: + case GTIIIC2U2: + case GTIIIC2U3: + case LTPRO: + mach64revb = 1; + break; + default: + mach64revb = 0; + break; + } + + waitforidle(scr); +} + +static int +mach64hwfill(VGAscr *scr, Rectangle r, ulong sval) +{ + ulong pitch; + ulong ctl; + +if(drawdebug) + iprint("hwfill %R val %lux...\n", r, sval); + + /* shouldn't happen */ + if(scr->io == 0x2EC || scr->io == 0x1C8 || scr->io == 0) + return 0; + + pitch = Dx(scr->gscreen->r)/8; + ctl = 1|2; /* left-to-right, top-to-bottom */ + if(scr->gscreen->depth == 24){ + r.min.x *= 3; + r.max.x *= 3; + pitch *= 3; + ctl |= (1<<7)|(((r.min.x/4)%6)<<8); + } + + waitforfifo(scr, 11); + iow32(scr, DpFrgdClr, sval); + iow32(scr, DpWriteMask, 0xFFFFFFFF); + iow32(scr, DpMix, 0x00070003); + iow32(scr, DpSrc, 0x00000111); + iow32(scr, ClrCmpCntl, 0x00000000); + iow32(scr, ScLeftRight, 0x1FFF0000); + iow32(scr, ScTopBottom, 0x1FFF0000); + iow32(scr, DstOffPitch, pitch<<22); + iow32(scr, DstCntl, ctl); + iow32(scr, DstYX, (r.min.x<<16)|r.min.y); + iow32(scr, DstHeightWidth, (Dx(r)<<16)|Dy(r)); + + waitforidle(scr); + return 1; +} + +static int +mach64hwscroll(VGAscr *scr, Rectangle r, Rectangle sr) +{ + ulong pitch; + Point dp, sp; + ulong ctl; + int dx, dy; + + dx = Dx(r); + dy = Dy(r); + pitch = Dx(scr->gscreen->r)/8; + if(scr->gscreen->depth == 24){ + dx *= 3; + pitch *= 3; + r.min.x *= 3; + sr.min.x *= 3; + } + + ctl = 0; + if(r.min.x <= sr.min.x){ + ctl |= 1; + dp.x = r.min.x; + sp.x = sr.min.x; + }else{ + dp.x = r.min.x+dx-1; + sp.x = sr.min.x+dx-1; + } + + if(r.min.y <= sr.min.y){ + ctl |= 2; + dp.y = r.min.y; + sp.y = sr.min.y; + }else{ + dp.y = r.min.y+dy-1; + sp.y = sr.min.y+dy-1; + } + + if(scr->gscreen->depth == 24) + ctl |= (1<<7)|(((dp.x/4)%6)<<8); + + waitforfifo(scr, 6); + iow32(scr, ScLeftRight, 0x1FFF0000); + iow32(scr, ScTopBottom, 0x1FFF0000); + iow32(scr, DpWriteMask, 0xFFFFFFFF); + iow32(scr, DpMix, 0x00070003); + iow32(scr, DpSrc, 0x00000300); + iow32(scr, ClrCmpCntl, 0x00000000); + + waitforfifo(scr, 8); + iow32(scr, SrcOffPitch, pitch<<22); + iow32(scr, SrcCntl, 0x00000000); + iow32(scr, SrcYX, (sp.x<<16)|sp.y); + iow32(scr, SrcWidth1, dx); + iow32(scr, DstOffPitch, pitch<<22); + iow32(scr, DstCntl, ctl); + + iow32(scr, DstYX, (dp.x<<16)|dp.y); + iow32(scr, DstHeightWidth, (dx<<16)|dy); + + waitforidle(scr); + + return 1; +} + +/* + * This should work, but doesn't. + * It messes up the screen timings for some reason. + */ +static void +mach64blank(VGAscr *scr, int blank) +{ + ulong ctl; + + ctl = ior32(scr, CrtcGenCtl) & ~(CrtcHsyncDis|CrtcVsyncDis); + if(blank) + ctl |= CrtcHsyncDis|CrtcVsyncDis; + iow32(scr, CrtcGenCtl, ctl); +} + +/* + * We squirrel away whether the LCD and/or CRT were + * on when we were called to blank the screen, and + * restore the old state. If we are called to blank the + * screen when it is already blank, we don't update the state. + * Such a call sequence should not happen, though. + * + * We could try forcing the chip into power management + * mode instead, but I'm not sure how that would interact + * with screen updates going on while the screen is blanked. + */ +static void +mach64lcdblank(VGAscr *scr, int blank) +{ + static int crtlcd; + ulong x; + + if(blank) { + x = lcdr32(scr, LCD_GenCtrl); + if(x & 3) { + crtlcd = x & 3; + lcdw32(scr, LCD_GenCtrl, x&~3); + } + } else { + if(crtlcd == 0) + crtlcd = 2; /* lcd only */ + x = lcdr32(scr, LCD_GenCtrl); + lcdw32(scr, LCD_GenCtrl, x | crtlcd); + } +} + +static void +mach64xxdrawinit(VGAscr *scr) +{ + if(scr->io > 0x2FF){ + initengine(scr); + scr->fill = mach64hwfill; + scr->scroll = mach64hwscroll; + } +/* scr->blank = mach64blank; */ + switch(scr->id){ + default: + break; + case ('L'<<8)|'B': /* 4C42: Rage 3D LTPro */ + case ('L'<<8)|'I': /* 4C49: Rage 3D LTPro */ + case ('L'<<8)|'M': /* 4C4D: Rage Mobility */ + case ('L'<<8)|'P': /* 4C50: Rage 3D LTPro */ + scr->blank = mach64lcdblank; + hwblank = 1; + break; + } +} + +static void +ovl_configure(VGAscr *scr, Chan *c, char **field) +{ + int w, h; + char *format; + + w = (int)strtol(field[1], nil, 0); + h = (int)strtol(field[2], nil, 0); + format = field[3]; + + if (c != ovl_chan) + error(Einuse); + if (strcmp(format, "YUYV")) + error(Eunsupportedformat); + + ovl_width = w; + ovl_height = h; + ovl_fib = w * h * sizeof(ushort); + + waitforidle(scr); + scr->mmio[mmoffset[BusCntl]] |= 0x08000000; /* Enable regblock 1 */ + scr->mmio[mmoffset[OverlayScaleCntl]] = + SCALE_ZERO_EXTEND|SCALE_RED_TEMP_6500K| + SCALE_HORZ_BLEND|SCALE_VERT_BLEND; + scr->mmio[mmoffset[!mach64revb? Buf0Pitch: ScalerBuf0Pitch]] = w; + scr->mmio[mmoffset[CaptureConfig]] = + SCALER_FRAME_READ_MODE_FULL| + SCALER_BUF_MODE_SINGLE| + SCALER_BUF_NEXT_0; + scr->mmio[mmoffset[OverlayKeyCntl]] = !mach64revb? + OVERLAY_MIX_ALWAYS_V|(OVERLAY_EXCLUSIVE_NORMAL << 28): + 0x011; + + if (mach64type->m64_pro) { + waitforfifo(scr, 6); + + /* set the scaler co-efficient registers */ + scr->mmio[mmoffset[ScalerColourCntl]] = + (0x00) | (0x10 << 8) | (0x10 << 16); + scr->mmio[mmoffset[ScalerHCoef0]] = + (0x00) | (0x20 << 8); + scr->mmio[mmoffset[ScalerHCoef1]] = + (0x0D) | (0x20 << 8) | (0x06 << 16) | (0x0D << 24); + scr->mmio[mmoffset[ScalerHCoef2]] = + (0x0D) | (0x1C << 8) | (0x0A << 16) | (0x0D << 24); + scr->mmio[mmoffset[ScalerHCoef3]] = + (0x0C) | (0x1A << 8) | (0x0E << 16) | (0x0C << 24); + scr->mmio[mmoffset[ScalerHCoef4]] = + (0x0C) | (0x14 << 8) | (0x14 << 16) | (0x0C << 24); + } + + waitforfifo(scr, 3); + scr->mmio[mmoffset[VideoFormat]] = SCALE_IN_YVYU422 | + (!mach64revb? 0xC: 0); + + if (mach64overlay == 0) + mach64overlay = scr->storage + 64 * 64 * sizeof(uchar); + scr->mmio[mmoffset[!mach64revb? Buf0Offset: ScalerBuf0Offset]] = + mach64overlay; +} + +static void +ovl_enable(VGAscr *scr, Chan *c, char **field) +{ + int x, y, w, h; + long h_inc, v_inc; + + x = (int)strtol(field[1], nil, 0); + y = (int)strtol(field[2], nil, 0); + w = (int)strtol(field[3], nil, 0); + h = (int)strtol(field[4], nil, 0); + + if (x < 0 || x + w > physgscreenr.max.x || + y < 0 || y + h > physgscreenr.max.y) + error(Ebadarg); + + if (c != ovl_chan) + error(Einuse); + if (scr->mmio[mmoffset[CrtcGenCntl]] & 1) { /* double scan enable */ + y *= 2; + h *= 2; + } + + waitforfifo(scr, 2); + scr->mmio[mmoffset[OverlayYX]] = + ((x & 0xFFFF) << 16) | (y & 0xFFFF); + scr->mmio[mmoffset[OverlayYXEnd]] = + (((x + w) & 0xFFFF) << 16) | ((y + h) & 0xFFFF); + + h_inc = (ovl_width << 12) / (w >> 1); /* ??? */ + v_inc = (ovl_height << 12) / h; + waitforfifo(scr, 2); + scr->mmio[mmoffset[OverlayScaleInc]] = + ((h_inc & 0xFFFF) << 16) | (v_inc & 0xFFFF); + scr->mmio[mmoffset[ScalerHeightWidth]] = + ((ovl_width & 0xFFFF) << 16) | (ovl_height & 0xFFFF); + waitforidle(scr); + scr->mmio[mmoffset[OverlayScaleCntl]] |= + (SCALE_ENABLE|OVERLAY_ENABLE); +} + +static void +ovl_status(VGAscr *scr, Chan *, char **field) +{ + pprint("%s: %s %.4uX, VT/GT %s, PRO %s, ovlclock %d, rev B %s, refclock %ld\n", + scr->dev->name, field[0], mach64type->m64_id, + mach64type->m64_vtgt? "yes": "no", + mach64type->m64_pro? "yes": "no", + mach64type->m64_ovlclock, + mach64revb? "yes": "no", + mach64refclock); + pprint("%s: storage @%.8luX, aperture @%8.ulX, ovl buf @%.8ulX\n", + scr->dev->name, scr->storage, scr->aperture, + mach64overlay); +} + +static void +ovl_openctl(VGAscr *, Chan *c, char **) +{ + if (ovl_chan) + error(Einuse); + ovl_chan = c; +} + +static void +ovl_closectl(VGAscr *scr, Chan *c, char **) +{ + if (c != ovl_chan) return; + + waitforidle(scr); + scr->mmio[mmoffset[OverlayScaleCntl]] &= + ~(SCALE_ENABLE|OVERLAY_ENABLE); + ovl_chan = nil; + ovl_width = ovl_height = ovl_fib = 0; +} + +enum +{ + CMclosectl, + CMconfigure, + CMenable, + CMopenctl, + CMstatus, +}; + +static void (*ovl_cmds[])(VGAscr *, Chan *, char **) = +{ + [CMclosectl] ovl_closectl, + [CMconfigure] ovl_configure, + [CMenable] ovl_enable, + [CMopenctl] ovl_openctl, + [CMstatus] ovl_status, +}; + +static Cmdtab mach64xxcmd[] = +{ + CMclosectl, "closectl", 1, + CMconfigure, "configure", 4, + CMenable, "enable", 5, + CMopenctl, "openctl", 1, + CMstatus, "status", 1, +}; + +static void +mach64xxovlctl(VGAscr *scr, Chan *c, void *a, int n) +{ + Cmdbuf *cb; + Cmdtab *ct; + + if(!mach64type->m64_vtgt) + error(Enodev); + + if(!scr->overlayinit){ + scr->overlayinit = 1; + init_overlayclock(scr); + } + cb = parsecmd(a, n); + if(waserror()){ + free(cb); + nexterror(); + } + + ct = lookupcmd(cb, mach64xxcmd, nelem(mach64xxcmd)); + + ovl_cmds[ct->index](scr, c, cb->f); + + poperror(); + free(cb); +} + +static int +mach64xxovlwrite(VGAscr *scr, void *a, int len, vlong offs) +{ + uchar *src; + int _len; + + if (ovl_chan == nil) return len; /* Acts as a /dev/null */ + + /* Calculate the destination address */ + _len = len; + src = (uchar *)a; + while (len > 0) { + ulong _offs; + int nb; + + _offs = (ulong)(offs % ovl_fib); + nb = (_offs + len > ovl_fib)? ovl_fib - _offs: len; + memmove((uchar *)KADDR(scr->aperture + mach64overlay + _offs), + src, nb); + offs += nb; + src += nb; + len -= nb; + } + return _len; +} + +VGAdev vgamach64xxdev = { + "mach64xx", + + mach64xxenable, /* enable */ + 0, /* disable */ + 0, /* page */ + mach64xxlinear, /* linear */ + mach64xxdrawinit, /* drawinit */ + 0, + mach64xxovlctl, /* overlay control */ + mach64xxovlwrite, /* write the overlay */ +}; + +VGAcur vgamach64xxcur = { + "mach64xxhwgc", + + mach64xxcurenable, /* enable */ + mach64xxcurdisable, /* disable */ + mach64xxcurload, /* load */ + mach64xxcurmove, /* move */ + + 1 /* doespanning */ +}; + diff --git a/os/pc/vgamga2164w.c b/os/pc/vgamga2164w.c new file mode 100644 index 00000000..6332236a --- /dev/null +++ b/os/pc/vgamga2164w.c @@ -0,0 +1,289 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#define Image IMAGE +#include <draw.h> +#include <memdraw.h> +#include <cursor.h> +#include "screen.h" + +/* + * Matrox Millennium and Matrox Millennium II. + * Matrox MGA-2064W, MGA-2164W 3D graphics accelerators. + * Texas Instruments Tvp3026 RAMDAC. + */ + +enum { + /* pci chip manufacturer */ + MATROX = 0x102B, + + /* pci chip device ids */ + MGA2064 = 0x0519, + MGA2164 = 0x051B, + MGA2164AGP = 0x051F +}; + +static Pcidev* +mgapcimatch(void) +{ + Pcidev *p; + + p = pcimatch(nil, MATROX, MGA2164AGP); + if(p == nil) { + p = pcimatch(nil, MATROX, MGA2164); + if(p == nil) + p = pcimatch(nil, MATROX, MGA2064); + } + return p; +} + +static ulong +mga2164wlinear(VGAscr* scr, int* size, int* align) +{ + ulong aperture, oaperture; + int oapsize, wasupamem; + Pcidev *p; + + oaperture = scr->aperture; + oapsize = scr->apsize; + wasupamem = scr->isupamem; + + if(p = mgapcimatch()){ + aperture = p->mem[p->did==MGA2064? 1 : 0].bar & ~0x0F; + *size = (p->did==MGA2064? 8 :16)*1024*1024; + } + else + aperture = 0; + + if(wasupamem) { + if(oaperture == aperture) + return oaperture; + upafree(oaperture, oapsize); + } + scr->isupamem = 0; + + aperture = upamalloc(aperture, *size, *align); + if(aperture == 0){ + if(wasupamem && upamalloc(oaperture, oapsize, 0)) { + aperture = oaperture; + scr->isupamem = 1; + } + else + scr->isupamem = 0; + } + else + scr->isupamem = 1; + + return aperture; +} + +static void +mga2164wenable(VGAscr* scr) +{ + Pcidev *p; + int size, align, immio; + ulong aperture; + + /* + * Only once, can't be disabled for now. + * scr->io holds the virtual address of + * the MMIO registers. + */ + if(scr->io) + return; + + p = mgapcimatch(); + if(p == nil) + return; + + immio = p->did==MGA2064? 0 : 1; + scr->io = upamalloc(p->mem[immio].bar & ~0x0F, p->mem[immio].size, 0); + if(scr->io == 0) + return; + addvgaseg("mga2164wmmio", scr->io, p->mem[immio].size); + + scr->io = (ulong)KADDR(scr->io); + + /* need to map frame buffer here too, so vga can find memory size */ + size = (p->did==MGA2064? 8 :16)*1024*1024; + align = 0; + aperture = mga2164wlinear(scr, &size, &align); + if(aperture) { + scr->aperture = aperture; + scr->apsize = size; + addvgaseg("mga2164wscreen", aperture, size); + } +} + +enum { + Index = 0x00, /* Index */ + Data = 0x0A, /* Data */ + + CaddrW = 0x04, /* Colour Write Address */ + Cdata = 0x05, /* Colour Data */ + + Cctl = 0x09, /* Direct Cursor Control */ + Cram = 0x0B, /* Cursor Ram Data */ + Cxlsb = 0x0C, /* Cursor X LSB */ + Cxmsb = 0x0D, /* Cursor X MSB */ + Cylsb = 0x0E, /* Cursor Y LSB */ + Cymsb = 0x0F, /* Cursor Y MSB */ + + Icctl = 0x06, /* Indirect Cursor Control */ +}; + +static void +tvp3026disable(VGAscr* scr) +{ + uchar *tvp3026; + + if(scr->io == 0) + return; + tvp3026 = KADDR(scr->io+0x3C00); + + /* + * Make sure cursor is off + * and direct control enabled. + */ + *(tvp3026+Index) = Icctl; + *(tvp3026+Data) = 0x90; + *(tvp3026+Cctl) = 0x00; +} + +static void +tvp3026load(VGAscr* scr, Cursor* curs) +{ + int x, y; + uchar *tvp3026; + + if(scr->io == 0) + return; + tvp3026 = KADDR(scr->io+0x3C00); + + /* + * Make sure cursor is off by initialising the cursor + * control to defaults. + * Write to the indirect control register to make sure + * direct register is enabled and upper 2 bits of cursor + * RAM address are 0. + * Put 0 in index register for lower 8 bits of cursor RAM address. + */ + tvp3026disable(scr); + *(tvp3026+Index) = 0; + + /* + * Initialise the 64x64 cursor RAM array. There are 2 planes, + * p0 and p1. Data is written 8 pixels per byte, with p0 in the + * first 512 bytes of the array and p1 in the second. + * The cursor is set in 3-colour mode which gives the following + * truth table: + * p1 p0 colour + * 0 0 transparent + * 0 1 cursor colour 0 + * 1 0 cursor colour 1 + * 1 1 cursor colour 2 + * Put the cursor into the top-left of the 64x64 array. + * The 0,0 cursor point is bottom-right, so positioning will + * have to take that into account. + */ + for(y = 0; y < 64; y++){ + for(x = 0; x < 64/8; x++){ + if(x < 16/8 && y < 16) + *(tvp3026+Cram) = curs->clr[x+y*2]; + else + *(tvp3026+Cram) = 0x00; + } + } + for(y = 0; y < 64; y++){ + for(x = 0; x < 64/8; x++){ + if(x < 16/8 && y < 16) + *(tvp3026+Cram) = curs->set[x+y*2]; + else + *(tvp3026+Cram) = 0x00; + } + } + + /* + * Initialise the cursor hotpoint + * and enable the cursor in 3-colour mode. + */ + scr->offset.x = 64+curs->offset.x; + scr->offset.y = 64+curs->offset.y; + *(tvp3026+Cctl) = 0x01; +} + +static int +tvp3026move(VGAscr* scr, Point p) +{ + int x, y; + uchar *tvp3026; + + if(scr->io == 0) + return 1; + tvp3026 = KADDR(scr->io+0x3C00); + + x = p.x+scr->offset.x; + y = p.y+scr->offset.y; + + *(tvp3026+Cxlsb) = x & 0xFF; + *(tvp3026+Cxmsb) = (x>>8) & 0x0F; + *(tvp3026+Cylsb) = y & 0xFF; + *(tvp3026+Cymsb) = (y>>8) & 0x0F; + + return 0; +} + +static void +tvp3026enable(VGAscr* scr) +{ + int i; + uchar *tvp3026; + + if(scr->io == 0) + return; + tvp3026 = KADDR(scr->io+0x3C00); + + tvp3026disable(scr); + + /* + * Overscan colour, + * cursor colour 1 (white), + * cursor colour 2, 3 (black). + */ + *(tvp3026+CaddrW) = 0x00; + for(i = 0; i < 6; i++) + *(tvp3026+Cdata) = Pwhite; + for(i = 0; i < 6; i++) + *(tvp3026+Cdata) = Pblack; + + /* + * Load, locate and enable the + * 64x64 cursor in 3-colour mode. + */ + tvp3026load(scr, &arrow); + tvp3026move(scr, ZP); + *(tvp3026+Cctl) = 0x01; +} + +VGAdev vgamga2164wdev = { + "mga2164w", + + mga2164wenable, /* enable */ + 0, /* disable */ + 0, /* page */ + mga2164wlinear, /* linear */ +}; + +VGAcur vgamga2164wcur = { + "mga2164whwgc", + + tvp3026enable, + tvp3026disable, + tvp3026load, + tvp3026move, +}; diff --git a/os/pc/vgamga4xx.c b/os/pc/vgamga4xx.c new file mode 100644 index 00000000..7ddeebf1 --- /dev/null +++ b/os/pc/vgamga4xx.c @@ -0,0 +1,603 @@ + +/* + * Matrox G200, G400 and G450. + * see /sys/src/cmd/aux/vga/mga4xx.c + */ + +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#define Image IMAGE +#include <draw.h> +#include <memdraw.h> +#include <cursor.h> +#include "screen.h" + +enum { + MATROX = 0x102B, + MGA4xx = 0x0525, + MGA200 = 0x0521, + + Kilo = 1024, + Meg = 1024*1024, + + FCOL = 0x1c24, + FXRIGHT = 0x1cac, + FXLEFT = 0x1ca8, + YDST = 0x1c90, + YLEN = 0x1c5c, + DWGCTL = 0x1c00, + DWG_TRAP = 0x04, + DWG_BITBLT = 0x08, + DWG_ILOAD = 0x09, + DWG_LINEAR = 0x0080, + DWG_SOLID = 0x0800, + DWG_ARZERO = 0x1000, + DWG_SGNZERO = 0x2000, + DWG_SHIFTZERO = 0x4000, + DWG_REPLACE = 0x000C0000, + DWG_REPLACE2 = (DWG_REPLACE | 0x40), + DWG_XOR = 0x00060010, + DWG_BFCOL = 0x04000000, + DWG_BMONOWF = 0x08000000, + DWG_TRANSC = 0x40000000, + SRCORG = 0x2cb4, + PITCH = 0x1c8c, + DSTORG = 0x2cb8, + PLNWRT = 0x1c1c, + ZORG = 0x1c0c, + MACCESS = 0x1c04, + STATUS = 0x1e14, + FXBNDRY = 0x1C84, + CXBNDRY = 0x1C80, + YTOP = 0x1C98, + YBOT = 0x1C9C, + YDSTLEN = 0x1C88, + AR0 = 0x1C60, + AR1 = 0x1C64, + AR2 = 0x1C68, + AR3 = 0x1C6C, + AR4 = 0x1C70, + AR5 = 0x1C74, + SGN = 0x1C58, + SGN_SCANLEFT = 1, + SGN_SCANRIGHT = 0, + SGN_SDY_POSITIVE = 0, + SGN_SDY_NEGATIVE = 4, + + GO = 0x0100, + FIFOSTATUS = 0x1E10, + CACHEFLUSH = 0x1FFF, + + CRTCEXTIDX = 0x1FDE, /* CRTC Extension Index */ + CRTCEXTDATA = 0x1FDF, /* CRTC Extension Data */ + + FILL_OPERAND = 0x800c7804, +}; + +static Pcidev* +mgapcimatch(void) +{ + Pcidev* p; + + p = pcimatch(nil, MATROX, MGA4xx); + if (p == nil) + p = pcimatch(nil, MATROX, MGA200); + return p; +} + +static ulong +mga4xxlinear(VGAscr* scr, int* size, int* align) +{ + ulong aperture, oaperture; + int oapsize, wasupamem; + Pcidev * p; + + oaperture = scr->aperture; + oapsize = scr->apsize; + wasupamem = scr->isupamem; + + if(p = mgapcimatch()){ + aperture = p->mem[0].bar & ~0x0F; + if(p->did == MGA4xx) + *size = 32*Meg; + else + *size = 8*Meg; + } + else + aperture = 0; + + if(wasupamem) { + if(oaperture == aperture) + return oaperture; + upafree(oaperture, oapsize); + } + scr->isupamem = 0; + + aperture = upamalloc(aperture, *size, *align); + if(aperture == 0){ + if(wasupamem && upamalloc(oaperture, oapsize, 0)) { + aperture = oaperture; + scr->isupamem = 1; + } + else + scr->isupamem = 0; + } + else + scr->isupamem = 1; + + return aperture; +} + +static void +mgawrite8(VGAscr* scr, int index, uchar val) +{ + ((uchar*)scr->io)[index] = val; +} + +static uchar +mgaread8(VGAscr* scr, int index) +{ + return ((uchar*)scr->io)[index]; +} + +static uchar +crtcextset(VGAscr* scr, int index, uchar set, uchar clr) +{ + uchar tmp; + + mgawrite8(scr, CRTCEXTIDX, index); + tmp = mgaread8(scr, CRTCEXTDATA); + mgawrite8(scr, CRTCEXTIDX, index); + mgawrite8(scr, CRTCEXTDATA, (tmp & ~clr) | set); + + return tmp; +} + +static void +mga4xxenable(VGAscr* scr) +{ + Pcidev * pci; + int size, align; + ulong aperture; + int i, n, k; + uchar * p; + uchar x[16]; + uchar crtcext3; + + /* + * Only once, can't be disabled for now. + * scr->io holds the virtual address of + * the MMIO registers. + */ + if(scr->io) + return; + + pci = mgapcimatch(); + if(pci == nil) + return; + + scr->io = upamalloc(pci->mem[1].bar & ~0x0F, 16*1024, 0); + if(scr->io == 0) + return; + + addvgaseg("mga4xxmmio", scr->io, pci->mem[1].size); + + scr->io = (ulong)KADDR(scr->io); + + /* need to map frame buffer here too, so vga can find memory size */ + size = 8*Meg; + align = 0; + aperture = mga4xxlinear(scr, &size, &align); + if(aperture) { + scr->aperture = aperture; + addvgaseg("mga4xxscreen", aperture, size); + + /* Find out how much memory is here, some multiple of 2 Meg */ + + /* First Set MGA Mode ... */ + crtcext3 = crtcextset(scr, 3, 0x80, 0x00); + + p = (uchar*)aperture; + n = (size / Meg) / 2; + for (i = 0; i < n; i++) { + k = (2*i+1)*Meg; + p[k] = 0; + p[k] = i+1; + *((uchar*)(scr->io + CACHEFLUSH)) = 0; + x[i] = p[k]; + } + for(i = 1; i < n; i++) + if(x[i] != i+1) + break; + scr->apsize = 2*i*Meg; + + crtcextset(scr, 3, crtcext3, 0xff); + } +} + +enum { + Index = 0x00, /* Index */ + Data = 0x0A, /* Data */ + + Cxlsb = 0x0C, /* Cursor X LSB */ + Cxmsb = 0x0D, /* Cursor X MSB */ + Cylsb = 0x0E, /* Cursor Y LSB */ + Cymsb = 0x0F, /* Cursor Y MSB */ + + Icuradrl = 0x04, /* Cursor Base Address Low */ + Icuradrh = 0x05, /* Cursor Base Address High */ + Icctl = 0x06, /* Indirect Cursor Control */ +}; + +static void +dac4xxdisable(VGAscr* scr) +{ + uchar * dac4xx; + + if(scr->io == 0) + return; + + dac4xx = KADDR(scr->io+0x3C00); + + *(dac4xx+Index) = Icctl; + *(dac4xx+Data) = 0x00; +} + +static void +dac4xxload(VGAscr* scr, Cursor* curs) +{ + int y; + uchar * p; + uchar * dac4xx; + + if(scr->io == 0) + return; + + dac4xx = KADDR(scr->io+0x3C00); + + dac4xxdisable(scr); + + p = KADDR(scr->storage); + for(y = 0; y < 64; y++){ + *p++ = 0; *p++ = 0; *p++ = 0; + *p++ = 0; *p++ = 0; *p++ = 0; + if(y <16){ + *p++ = curs->set[1+y*2]|curs->clr[1+2*y]; + *p++ = curs->set[y*2]|curs->clr[2*y]; + } else{ + *p++ = 0; *p++ = 0; + } + + *p++ = 0; *p++ = 0; *p++ = 0; + *p++ = 0; *p++ = 0; *p++ = 0; + if(y <16){ + *p++ = curs->set[1+y*2]; + *p++ = curs->set[y*2]; + } else{ + *p++ = 0; *p++ = 0; + } + } + scr->offset.x = 64 + curs->offset.x; + scr->offset.y = 64 + curs->offset.y; + + *(dac4xx+Index) = Icctl; + *(dac4xx+Data) = 0x03; +} + +static int +dac4xxmove(VGAscr* scr, Point p) +{ + int x, y; + uchar * dac4xx; + + if(scr->io == 0) + return 1; + + dac4xx = KADDR(scr->io + 0x3C00); + + x = p.x + scr->offset.x; + y = p.y + scr->offset.y; + + *(dac4xx+Cxlsb) = x & 0xFF; + *(dac4xx+Cxmsb) = (x>>8) & 0x0F; + + *(dac4xx+Cylsb) = y & 0xFF; + *(dac4xx+Cymsb) = (y>>8) & 0x0F; + + return 0; +} + +static void +dac4xxenable(VGAscr* scr) +{ + uchar * dac4xx; + ulong storage; + + if(scr->io == 0) + return; + dac4xx = KADDR(scr->io+0x3C00); + + dac4xxdisable(scr); + + storage = (scr->apsize - 4096) & ~0x3ff; + + *(dac4xx+Index) = Icuradrl; + *(dac4xx+Data) = 0xff & (storage >> 10); + *(dac4xx+Index) = Icuradrh; + *(dac4xx+Data) = 0xff & (storage >> 18); + + scr->storage = (ulong) KADDR((ulong)scr->aperture + (ulong)storage); + + /* Show X11-Like Cursor */ + *(dac4xx+Index) = Icctl; + *(dac4xx+Data) = 0x03; + + /* Cursor Color 0 : White */ + *(dac4xx+Index) = 0x08; + *(dac4xx+Data) = 0xff; + *(dac4xx+Index) = 0x09; + *(dac4xx+Data) = 0xff; + *(dac4xx+Index) = 0x0a; + *(dac4xx+Data) = 0xff; + + /* Cursor Color 1 : Black */ + *(dac4xx+Index) = 0x0c; + *(dac4xx+Data) = 0x00; + *(dac4xx+Index) = 0x0d; + *(dac4xx+Data) = 0x00; + *(dac4xx+Index) = 0x0e; + *(dac4xx+Data) = 0x00; + + /* Cursor Color 2 : Red */ + *(dac4xx+Index) = 0x10; + *(dac4xx+Data) = 0xff; + *(dac4xx+Index) = 0x11; + *(dac4xx+Data) = 0x00; + *(dac4xx+Index) = 0x12; + *(dac4xx+Data) = 0x00; + + /* + * Load, locate and enable the + * 64x64 cursor in X11 mode. + */ + dac4xxload(scr, &arrow); + dac4xxmove(scr, ZP); +} + +static void +mga4xxblank(VGAscr* scr, int blank) +{ + char * cp; + uchar * mga; + uchar seq1, crtcext1; + + /* blank = 0 -> turn screen on */ + /* blank = 1 -> turn screen off */ + + if(scr->io == 0) + return; + mga = KADDR(scr->io); + + if (blank == 0) { + seq1 = 0x00; + crtcext1 = 0x00; + } else { + seq1 = 0x20; + crtcext1 = 0x10; /* Default value ... : standby */ + cp = getconf("*dpms"); + if (cp) { + if (cistrcmp(cp, "standby") == 0) { + crtcext1 = 0x10; + } else if (cistrcmp(cp, "suspend") == 0) { + crtcext1 = 0x20; + } else if (cistrcmp(cp, "off") == 0) { + crtcext1 = 0x30; + } + } + } + + *(mga + 0x1fc4) = 1; + seq1 |= *(mga + 0x1fc5) & ~0x20; + *(mga + 0x1fc5) = seq1; + + *(mga + 0x1fde) = 1; + crtcext1 |= *(mga + 0x1fdf) & ~0x30; + *(mga + 0x1fdf) = crtcext1; +} + +static void +mgawrite32(uchar * mga, ulong reg, ulong val) +{ + ulong * l; + + l = (ulong *)(&mga[reg]); + l[0] = val; +} + +static ulong +mgaread32(uchar * mga, ulong reg) +{ + return *((ulong *)(&mga[reg])); +} + +static int +mga4xxfill(VGAscr* scr, Rectangle r, ulong color) +{ + uchar * mga; + + /* Constant Shaded Trapezoids / Rectangle Fills */ + if(scr->io == 0) + return 0; + mga = KADDR(scr->io); + + mgawrite32(mga, DWGCTL, 0); + mgawrite32(mga, FCOL, color); + mgawrite32(mga, FXRIGHT, r.max.x); + mgawrite32(mga, FXLEFT, r.min.x); + mgawrite32(mga, YDST, r.min.y); + mgawrite32(mga, YLEN, Dy(r)); + mgawrite32(mga, DWGCTL + GO, FILL_OPERAND); + + while (mgaread32(mga, STATUS) & 0x00010000) + ; + return 1; +} + +#define mga_fifo(n) do {} while ((mgaread32(mga, FIFOSTATUS) & 0xFF) < (n)) + +static int +mga4xxscroll(VGAscr* scr, Rectangle r_dst, Rectangle r_src) +{ + uchar * mga; + ulong pitch, y; + ulong width, height, start, end, scandir; + int ydir; + + /* Two-operand Bitblts */ + if(scr->io == 0) + return 0; + + mga = KADDR(scr->io); + + pitch = Dx(scr->gscreen->r); + + mgawrite32(mga, DWGCTL, 0); + + scandir = 0; + + height = abs(Dy(r_src)); + width = abs(Dx(r_src)); + + assert(height == abs(Dy(r_dst))); + assert(width == abs(Dx(r_dst))); + + if ((r_src.min.y == r_dst.min.y) && (r_src.min.x == r_dst.min.x)) + { + if (0) + print("move x,y to x,y !\n"); + return 1; + } + + ydir = 1; + if (r_dst.min.y > r_src.min.y) + { + if (0) + print("ydir = -1\n"); + ydir = -1; + scandir |= 4; // Blit UP + } + + if (r_dst.min.x > r_src.min.x) + { + if (0) + print("xdir = -1\n"); + scandir |= 1; // Blit Left + } + + mga_fifo(4); + if (scandir) + { + mgawrite32(mga, DWGCTL, DWG_BITBLT | DWG_SHIFTZERO | + DWG_SGNZERO | DWG_BFCOL | DWG_REPLACE); + mgawrite32(mga, SGN, scandir); + } else + { + mgawrite32(mga, DWGCTL, DWG_BITBLT | DWG_SHIFTZERO | + DWG_BFCOL | DWG_REPLACE); + } + mgawrite32(mga, AR5, ydir * pitch); + + width--; + start = end = r_src.min.x + (r_src.min.y * pitch); + if ((scandir & 1) == 1) + { + start += width; + } else + { + end += width; + } + + y = r_dst.min.y; + if ((scandir & 4) == 4) + { + start += (height - 1) * pitch; + end += (height - 1) * pitch; + y += (height - 1); + } + + mga_fifo(4); + mgawrite32(mga, AR0, end); + mgawrite32(mga, AR3, start); + mgawrite32(mga, FXBNDRY, ((r_dst.min.x+width)<<16) | r_dst.min.x); + mgawrite32(mga, YDSTLEN + GO, (y << 16) | height); + + if (1) + { + while (mgaread32(mga, STATUS) & 0x00010000) + ; + } + + return 1; +} + +static void +mga4xxdrawinit(VGAscr* scr) +{ + uchar * mga; + Pcidev* p; + + p = pcimatch(nil, MATROX, MGA4xx); + if (p == nil) + return ; + + if(scr->io == 0) + return; + mga = KADDR(scr->io); + + mgawrite32(mga, SRCORG, 0); + mgawrite32(mga, DSTORG, 0); + mgawrite32(mga, ZORG, 0); + mgawrite32(mga, PLNWRT, ~0); + mgawrite32(mga, FCOL, 0xffff0000); + mgawrite32(mga, CXBNDRY, 0xFFFF0000); + mgawrite32(mga, YTOP, 0); + mgawrite32(mga, YBOT, 0x01FFFFFF); + mgawrite32(mga, PITCH, Dx(scr->gscreen->r) & ((1 << 13) - 1)); + switch(scr->gscreen->depth) { + case 8: + mgawrite32(mga, MACCESS, 0); + break; + case 32: + mgawrite32(mga, MACCESS, 2); + break; + default: + return; /* depth not supported ! */ + } + scr->fill = mga4xxfill; + scr->scroll = mga4xxscroll; + scr->blank = mga4xxblank; +} + +VGAdev vgamga4xxdev = { + "mga4xx", + + mga4xxenable, /* enable */ + 0, /* disable */ + 0, /* page */ + mga4xxlinear, /* linear */ + mga4xxdrawinit, +}; + +VGAcur vgamga4xxcur = { + "mga4xxhwgc", + dac4xxenable, + dac4xxdisable, + dac4xxload, + dac4xxmove, +}; diff --git a/os/pc/vganeomagic.c b/os/pc/vganeomagic.c new file mode 100644 index 00000000..ba25a581 --- /dev/null +++ b/os/pc/vganeomagic.c @@ -0,0 +1,541 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#define Image IMAGE +#include <draw.h> +#include <memdraw.h> +#include <cursor.h> +#include "screen.h" + +typedef struct CursorNM CursorNM; +struct CursorNM { + int enable; + int x; + int y; + int colour1; + int colour2; + int addr; +}; + +static ulong +neomagiclinear(VGAscr* scr, int* size, int* align) +{ + ulong aperture, oaperture; + int oapsize, wasupamem; + Pcidev *p; + + oaperture = scr->aperture; + oapsize = scr->apsize; + wasupamem = scr->isupamem; + + aperture = 0; + if(p = pcimatch(nil, 0x10C8, 0)){ + switch(p->did){ + case 0x0004: /* MagicGraph 128XD */ + case 0x0005: /* MagicMedia 256AV */ + case 0x0006: /* MagicMedia 256ZX */ + aperture = p->mem[0].bar & ~0x0F; + *size = p->mem[0].size; + break; + default: + break; + } + } + + if(wasupamem){ + if(oaperture == aperture) + return oaperture; + upafree(oaperture, oapsize); + } + scr->isupamem = 0; + + aperture = upamalloc(aperture, *size, *align); + if(aperture == 0){ + if(wasupamem && upamalloc(oaperture, oapsize, 0)){ + aperture = oaperture; + scr->isupamem = 1; + } + else + scr->isupamem = 0; + } + else + scr->isupamem = 1; + + return aperture; +} + +static void +neomagicenable(VGAscr* scr) +{ + Pcidev *p; + int align, curoff, size, vmsize; + ulong aperture; + + /* + * Only once, can't be disabled for now. + * scr->io holds the physical address of the cursor registers + * in the MMIO space. This may need to change for older chips + * which have the MMIO space offset in the framebuffer region. + */ + if(scr->io) + return; + if(p = pcimatch(nil, 0x10C8, 0)){ + switch(p->did){ + case 0x0004: /* MagicGraph 128XD */ + curoff = 0x100; + vmsize = 2048*1024; + break; + case 0x0005: /* MagicMedia 256AV */ + curoff = 0x1000; + vmsize = 2560*1024; + break; + case 0x0006: /* MagicMedia 256ZX */ + curoff = 0x1000; + vmsize = 4096*1024; + break; + default: + return; + } + } + else + return; + scr->io = upamalloc(p->mem[1].bar & ~0x0F, p->mem[1].size, 0); + if(scr->io == 0) + return; + addvgaseg("neomagicmmio", scr->io, p->mem[1].size); + scr->mmio = KADDR(scr->io); + + /* + * Find a place for the cursor data in display memory. + * 2 cursor images might be needed, 1KB each so use the + * last 2KB of the framebuffer. + */ + scr->storage = vmsize-2*1024; + scr->io += curoff; + + size = p->mem[0].size; + align = 0; + aperture = neomagiclinear(scr, &size, &align); + if(aperture) { + scr->aperture = aperture; + scr->apsize = size; + addvgaseg("neomagicscreen", aperture, size); + } +} + +static void +neomagiccurdisable(VGAscr* scr) +{ + CursorNM *cursornm; + + if(scr->io == 0) + return; + cursornm = KADDR(scr->io); + cursornm->enable = 0; +} + +static void +neomagicinitcursor(VGAscr* scr, int xo, int yo, int index) +{ + uchar *p; + uint p0, p1; + int x, y; + + p = KADDR(scr->aperture); + p += scr->storage + index*1024; + + for(y = yo; y < 16; y++){ + p0 = scr->set[2*y]; + p1 = scr->set[2*y+1]; + if(xo){ + p0 = (p0<<xo)|(p1>>(8-xo)); + p1 <<= xo; + } + *p++ = p0; + *p++ = p1; + + for(x = 16; x < 64; x += 8) + *p++ = 0x00; + + p0 = scr->clr[2*y]|scr->set[2*y]; + p1 = scr->clr[2*y+1]|scr->set[2*y+1]; + if(xo){ + p0 = (p0<<xo)|(p1>>(8-xo)); + p1 <<= xo; + } + *p++ = p0; + *p++ = p1; + + for(x = 16; x < 64; x += 8) + *p++ = 0x00; + } + while(y < 64+yo){ + for(x = 0; x < 64; x += 8){ + *p++ = 0x00; + *p++ = 0x00; + } + y++; + } +} + +static void +neomagiccurload(VGAscr* scr, Cursor* curs) +{ + CursorNM *cursornm; + + if(scr->io == 0) + return; + cursornm = KADDR(scr->io); + + cursornm->enable = 0; + memmove(&scr->Cursor, curs, sizeof(Cursor)); + neomagicinitcursor(scr, 0, 0, 0); + cursornm->enable = 1; +} + +static int +neomagiccurmove(VGAscr* scr, Point p) +{ + CursorNM *cursornm; + int addr, index, x, xo, y, yo; + + if(scr->io == 0) + return 1; + cursornm = KADDR(scr->io); + + index = 0; + if((x = p.x+scr->offset.x) < 0){ + xo = -x; + x = 0; + } + else + xo = 0; + if((y = p.y+scr->offset.y) < 0){ + yo = -y; + y = 0; + } + else + yo = 0; + + if(xo || yo){ + index = 1; + neomagicinitcursor(scr, xo, yo, index); + } + addr = ((scr->storage+(1024*index))>>10) & 0xFFF; + addr = ((addr & 0x00F)<<8)|((addr>>4) & 0xFF); + if(cursornm->addr != addr) + cursornm->addr = addr; + + cursornm->x = x; + cursornm->y = y; + + return 0; +} + +static void +neomagiccurenable(VGAscr* scr) +{ + CursorNM *cursornm; + + neomagicenable(scr); + if(scr->io == 0) + return; + cursornm = KADDR(scr->io); + cursornm->enable = 0; + + /* + * Cursor colours. + */ + cursornm->colour1 = (Pblack<<16)|(Pblack<<8)|Pblack; + cursornm->colour2 = (Pwhite<<16)|(Pwhite<<8)|Pwhite; + + /* + * Load, locate and enable the 64x64 cursor. + */ + neomagiccurload(scr, &arrow); + neomagiccurmove(scr, ZP); + cursornm->enable = 1; +} + +static int neomagicbltflags; + +/* registers */ +enum { + BltStat = 0, + BltCntl = 1, + XPColor = 2, + FGColor = 3, + BGColor = 4, + Pitch = 5, + ClipLT = 6, + ClipRB = 7, + SrcBitOff = 8, + SrcStartOff = 9, + + DstStartOff = 11, + XYExt = 12, + + PageCntl = 20, + PageBase, + PostBase, + PostPtr, + DataPtr, +}; + +/* flags */ +enum { + NEO_BS0_BLT_BUSY = 0x00000001, + NEO_BS0_FIFO_AVAIL = 0x00000002, + NEO_BS0_FIFO_PEND = 0x00000004, + + NEO_BC0_DST_Y_DEC = 0x00000001, + NEO_BC0_X_DEC = 0x00000002, + NEO_BC0_SRC_TRANS = 0x00000004, + NEO_BC0_SRC_IS_FG = 0x00000008, + NEO_BC0_SRC_Y_DEC = 0x00000010, + NEO_BC0_FILL_PAT = 0x00000020, + NEO_BC0_SRC_MONO = 0x00000040, + NEO_BC0_SYS_TO_VID = 0x00000080, + + NEO_BC1_DEPTH8 = 0x00000100, + NEO_BC1_DEPTH16 = 0x00000200, + NEO_BC1_DEPTH24 = 0x00000300, + NEO_BC1_X_320 = 0x00000400, + NEO_BC1_X_640 = 0x00000800, + NEO_BC1_X_800 = 0x00000c00, + NEO_BC1_X_1024 = 0x00001000, + NEO_BC1_X_1152 = 0x00001400, + NEO_BC1_X_1280 = 0x00001800, + NEO_BC1_X_1600 = 0x00001c00, + NEO_BC1_DST_TRANS = 0x00002000, + NEO_BC1_MSTR_BLT = 0x00004000, + NEO_BC1_FILTER_Z = 0x00008000, + + NEO_BC2_WR_TR_DST = 0x00800000, + + NEO_BC3_SRC_XY_ADDR = 0x01000000, + NEO_BC3_DST_XY_ADDR = 0x02000000, + NEO_BC3_CLIP_ON = 0x04000000, + NEO_BC3_FIFO_EN = 0x08000000, + NEO_BC3_BLT_ON_ADDR = 0x10000000, + NEO_BC3_SKIP_MAPPING = 0x80000000, + + NEO_MODE1_DEPTH8 = 0x0100, + NEO_MODE1_DEPTH16 = 0x0200, + NEO_MODE1_DEPTH24 = 0x0300, + NEO_MODE1_X_320 = 0x0400, + NEO_MODE1_X_640 = 0x0800, + NEO_MODE1_X_800 = 0x0c00, + NEO_MODE1_X_1024 = 0x1000, + NEO_MODE1_X_1152 = 0x1400, + NEO_MODE1_X_1280 = 0x1800, + NEO_MODE1_X_1600 = 0x1c00, + NEO_MODE1_BLT_ON_ADDR = 0x2000, +}; + +/* Raster Operations */ +enum { + GXclear = 0x000000, /* 0x0000 */ + GXand = 0x080000, /* 0x1000 */ + GXandReverse = 0x040000, /* 0x0100 */ + GXcopy = 0x0c0000, /* 0x1100 */ + GXandInvert = 0x020000, /* 0x0010 */ + GXnoop = 0x0a0000, /* 0x1010 */ + GXxor = 0x060000, /* 0x0110 */ + GXor = 0x0e0000, /* 0x1110 */ + GXnor = 0x010000, /* 0x0001 */ + GXequiv = 0x090000, /* 0x1001 */ + GXinvert = 0x050000, /* 0x0101 */ + GXorReverse = 0x0d0000, /* 0x1101 */ + GXcopyInvert = 0x030000, /* 0x0011 */ + GXorInverted = 0x0b0000, /* 0x1011 */ + GXnand = 0x070000, /* 0x0111 */ + GXset = 0x0f0000, /* 0x1111 */ +}; + +static void +waitforidle(VGAscr *scr) +{ + ulong *mmio; + long x; + + mmio = scr->mmio; + x = 0; + while((mmio[BltStat] & NEO_BS0_BLT_BUSY) && x++ < 1000000) + ; + //if(x >= 1000000) + // iprint("idle stat %lud scrmmio %.8lux scr %p pc %luX\n", mmio[BltStat], scr->mmio, scr, getcallerpc(&scr)); +} + +static void +waitforfifo(VGAscr *scr, int entries) +{ + ulong *mmio; + long x; + + mmio = scr->mmio; + x = 0; + while(((mmio[BltStat]>>8) < entries) && x++ < 1000000) + ; + //if(x >= 1000000) + // iprint("fifo stat %d scrmmio %.8lux scr %p pc %luX\n", mmio[BltStat]>>8, scr->mmio, scr, getcallerpc(&scr)); + /* DirectFB says the above doesn't work. if so... */ + /* waitforidle(scr); */ +} + +static int +neomagichwfill(VGAscr *scr, Rectangle r, ulong sval) +{ + ulong *mmio; + + mmio = scr->mmio; + + waitforfifo(scr, 1); + mmio[FGColor] = sval; + waitforfifo(scr, 3); + mmio[BltCntl] = neomagicbltflags + | NEO_BC3_FIFO_EN + | NEO_BC0_SRC_IS_FG + | NEO_BC3_SKIP_MAPPING + | GXcopy; + mmio[DstStartOff] = scr->aperture + + r.min.y*scr->gscreen->width*BY2WD + + r.min.x*scr->gscreen->depth/BI2BY; + mmio[XYExt] = (Dy(r) << 16) | (Dx(r) & 0xffff); + waitforidle(scr); + return 1; +} + +static int +neomagichwscroll(VGAscr *scr, Rectangle r, Rectangle sr) +{ + ulong *mmio; + int pitch, pixel; + + mmio = scr->mmio; + + pitch = scr->gscreen->width*BY2WD; + pixel = scr->gscreen->depth/BI2BY; + + waitforfifo(scr, 4); + if (r.min.y < sr.min.y || (r.min.y == sr.min.y && r.min.x < sr.min.x)) { + /* start from upper-left */ + mmio[BltCntl] = neomagicbltflags + | NEO_BC3_FIFO_EN + | NEO_BC3_SKIP_MAPPING + | GXcopy; + mmio[SrcStartOff] = scr->aperture + + sr.min.y*pitch + sr.min.x*pixel; + mmio[DstStartOff] = scr->aperture + + r.min.y*pitch + r.min.x*pixel; + } else { + /* start from lower-right */ + mmio[BltCntl] = neomagicbltflags + | NEO_BC0_X_DEC + | NEO_BC0_DST_Y_DEC + | NEO_BC0_SRC_Y_DEC + | NEO_BC3_FIFO_EN + | NEO_BC3_SKIP_MAPPING + | GXcopy; + mmio[SrcStartOff] = scr->aperture + + (sr.max.y-1)*pitch + (sr.max.x-1)*pixel; + mmio[DstStartOff] = scr->aperture + + (r.max.y-1)*pitch + (r.max.x-1)*pixel; + } + mmio[XYExt] = (Dy(r) << 16) | (Dx(r) & 0xffff); + waitforidle(scr); + return 1; +} + +static void +neomagicdrawinit(VGAscr *scr) +{ + ulong *mmio; + uint bltmode, pitch; + + mmio = scr->mmio; + + pitch = scr->gscreen->width*BY2WD; + + neomagicbltflags = bltmode = 0; + + switch(scr->gscreen->depth) { + case 8: + bltmode |= NEO_MODE1_DEPTH8; + neomagicbltflags |= NEO_BC1_DEPTH8; + break; + case 16: + bltmode |= NEO_MODE1_DEPTH16; + neomagicbltflags |= NEO_BC1_DEPTH16; + break; + case 24: /* I can't get it to work, and XFree86 doesn't either. */ + default: /* give up */ + return; + } + + switch(Dx(scr->gscreen->r)) { + case 320: + bltmode |= NEO_MODE1_X_320; + neomagicbltflags |= NEO_BC1_X_320; + break; + case 640: + bltmode |= NEO_MODE1_X_640; + neomagicbltflags |= NEO_BC1_X_640; + break; + case 800: + bltmode |= NEO_MODE1_X_800; + neomagicbltflags |= NEO_BC1_X_800; + break; + case 1024: + bltmode |= NEO_MODE1_X_1024; + neomagicbltflags |= NEO_BC1_X_1024; + break; + case 1152: + bltmode |= NEO_MODE1_X_1152; + neomagicbltflags |= NEO_BC1_X_1152; + break; + case 1280: + bltmode |= NEO_MODE1_X_1280; + neomagicbltflags |= NEO_BC1_X_1280; + break; + case 1600: + bltmode |= NEO_MODE1_X_1600; + neomagicbltflags |= NEO_BC1_X_1600; + break; + default: + /* don't worry about it */ + break; + } + + waitforidle(scr); + mmio[BltStat] = bltmode << 16; + mmio[Pitch] = (pitch << 16) | (pitch & 0xffff); + + scr->fill = neomagichwfill; + scr->scroll = neomagichwscroll; +} + +VGAdev vganeomagicdev = { + "neomagic", + + neomagicenable, + nil, + nil, + neomagiclinear, + neomagicdrawinit, +}; + +VGAcur vganeomagiccur = { + "neomagichwgc", + + neomagiccurenable, + neomagiccurdisable, + neomagiccurload, + neomagiccurmove, +}; + diff --git a/os/pc/vganvidia.c b/os/pc/vganvidia.c new file mode 100644 index 00000000..e057ef1f --- /dev/null +++ b/os/pc/vganvidia.c @@ -0,0 +1,373 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#define Image IMAGE +#include <draw.h> +#include <memdraw.h> +#include <cursor.h> +#include "screen.h" + +enum { + Pramin = 0x00710000, + Pramdac = 0x00680000, + Fifo = 0x00800000, + Pgraph = 0x00400000 +}; + +enum { + hwCurPos = Pramdac + 0x0300, + hwCurImage = Pramin + (0x00010000 - 0x0800), +}; + +/* Nvidia is good about backwards compatibility -- any did >= 0x20 is fine */ +static Pcidev* +nvidiapci(void) +{ + Pcidev *p; + + p = nil; + while((p = pcimatch(p, 0x10DE, 0)) != nil){ + if(p->did >= 0x20 && p->ccrb == 3) /* video card */ + return p; + } + return nil; +} + +static ulong +nvidialinear(VGAscr* scr, int* size, int* align) +{ + Pcidev *p; + int oapsize, wasupamem; + ulong aperture, oaperture; + + oaperture = scr->aperture; + oapsize = scr->apsize; + wasupamem = scr->isupamem; + + aperture = 0; + if(p = nvidiapci()){ + aperture = p->mem[1].bar & ~0x0F; + *size = p->mem[1].size; + } + + if(wasupamem){ + if(oaperture == aperture) + return oaperture; + upafree(oaperture, oapsize); + } + scr->isupamem = 0; + + aperture = upamalloc(aperture, *size, *align); + if(aperture == 0){ + if(wasupamem && upamalloc(oaperture, oapsize, 0)){ + aperture = oaperture; + scr->isupamem = 1; + } + else + scr->isupamem = 0; + } + else + scr->isupamem = 1; + + return aperture; +} + +static void +nvidiaenable(VGAscr* scr) +{ + Pcidev *p; + ulong aperture; + int align, size; + + /* + * Only once, can't be disabled for now. + * scr->io holds the physical address of + * the MMIO registers. + */ + if(scr->io) + return; + p = nvidiapci(); + if(p == nil) + return; + scr->id = p->did; + + scr->io = upamalloc(p->mem[0].bar & ~0x0F, p->mem[0].size, 0); + if(scr->io == 0) + return; + addvgaseg("nvidiammio", scr->io, p->mem[0].size); + + size = p->mem[1].size; + align = 0; + aperture = nvidialinear(scr, &size, &align); + if(aperture){ + scr->aperture = aperture; + scr->apsize = size; + addvgaseg("nvidiascreen", aperture, size); + } +} + +static void +nvidiacurdisable(VGAscr* scr) +{ + if(scr->io == 0) + return; + + vgaxo(Crtx, 0x31, vgaxi(Crtx, 0x31) & ~0x01); +} + +static void +nvidiacurload(VGAscr* scr, Cursor* curs) +{ + ulong* p; + int i,j; + ushort c,s; + ulong tmp; + + if(scr->io == 0) + return; + + vgaxo(Crtx, 0x31, vgaxi(Crtx, 0x31) & ~0x01); + + p = KADDR(scr->io + hwCurImage); + + for(i=0; i<16; i++) { + switch(scr->id){ + default: + c = (curs->clr[2 * i] << 8) | curs->clr[2 * i+1]; + s = (curs->set[2 * i] << 8) | curs->set[2 * i+1]; + break; + case 0x171: /* for Geforece4 MX bug, K.Okamoto */ + case 0x181: + c = (curs->clr[2 * i+1] << 8) | curs->clr[2 * i]; + s = (curs->set[2 * i+1] << 8) | curs->set[2 * i]; + break; + } + tmp = 0; + for (j=0; j<16; j++){ + if(s&0x8000) + tmp |= 0x80000000; + else if(c&0x8000) + tmp |= 0xFFFF0000; + if (j&0x1){ + *p++ = tmp; + tmp = 0; + } else { + tmp>>=16; + } + c<<=1; + s<<=1; + } + for (j=0; j<8; j++) + *p++ = 0; + } + for (i=0; i<256; i++) + *p++ = 0; + + scr->offset = curs->offset; + vgaxo(Crtx, 0x31, vgaxi(Crtx, 0x31) | 0x01); + + return; +} + +static int +nvidiacurmove(VGAscr* scr, Point p) +{ + ulong* cursorpos; + + if(scr->io == 0) + return 1; + + cursorpos = KADDR(scr->io + hwCurPos); + *cursorpos = ((p.y+scr->offset.y)<<16)|((p.x+scr->offset.x) & 0xFFFF); + + return 0; +} + +static void +nvidiacurenable(VGAscr* scr) +{ + nvidiaenable(scr); + if(scr->io == 0) + return; + + vgaxo(Crtx, 0x1F, 0x57); + + nvidiacurload(scr, &arrow); + nvidiacurmove(scr, ZP); + + vgaxo(Crtx, 0x31, vgaxi(Crtx, 0x31) | 0x01); +} + +enum { + RopFifo = 0x00000000, + ClipFifo = 0x00002000, + PattFifo = 0x00004000, + BltFifo = 0x00008000, + BitmapFifo = 0x0000A000, +}; + +enum { + RopRop3 = RopFifo + 0x300, + + ClipTopLeft = ClipFifo + 0x300, + ClipWidthHeight = ClipFifo + 0x304, + + PattShape = PattFifo + 0x0308, + PattColor0 = PattFifo + 0x0310, + PattColor1 = PattFifo + 0x0314, + PattMonochrome0 = PattFifo + 0x0318, + PattMonochrome1 = PattFifo + 0x031C, + + BltTopLeftSrc = BltFifo + 0x0300, + BltTopLeftDst = BltFifo + 0x0304, + BltWidthHeight = BltFifo + 0x0308, + + BitmapColor1A = BitmapFifo + 0x03FC, + BitmapURect0TopLeft = BitmapFifo + 0x0400, + BitmapURect0WidthHeight = BitmapFifo + 0x0404, +}; + +static void +waitforidle(VGAscr *scr) +{ + ulong* pgraph; + int x; + + pgraph = KADDR(scr->io + Pgraph); + + x = 0; + while(pgraph[0x00000700/4] & 0x01 && x++ < 1000000) + ; + + if(x >= 1000000) + iprint("idle stat %lud scrio %.8lux scr %p pc %luX\n", *pgraph, scr->io, scr, getcallerpc(&scr)); +} + +static void +waitforfifo(VGAscr *scr, int fifo, int entries) +{ + ushort* fifofree; + int x; + + x = 0; + fifofree = KADDR(scr->io + Fifo + fifo + 0x10); + + while(((*fifofree >> 2) < entries) && x++ < 1000000) + ; + + if(x >= 1000000) + iprint("fifo stat %d scrio %.8lux scr %p pc %luX\n", *fifofree, scr->io, scr, getcallerpc(&scr)); +} + +static int +nvidiahwfill(VGAscr *scr, Rectangle r, ulong sval) +{ + ulong* fifo; + + fifo = KADDR(scr->io + Fifo); + + waitforfifo(scr, BitmapFifo, 1); + + fifo[BitmapColor1A/4] = sval; + + waitforfifo(scr, BitmapFifo, 2); + + fifo[BitmapURect0TopLeft/4] = (r.min.x << 16) | r.min.y; + fifo[BitmapURect0WidthHeight/4] = (Dx(r) << 16) | Dy(r); + + waitforidle(scr); + + return 1; +} + +static int +nvidiahwscroll(VGAscr *scr, Rectangle r, Rectangle sr) +{ + ulong* fifo; + + fifo = KADDR(scr->io + Fifo); + + waitforfifo(scr, BltFifo, 3); + + fifo[BltTopLeftSrc/4] = (sr.min.y << 16) | sr.min.x; + fifo[BltTopLeftDst/4] = (r.min.y << 16) | r.min.x; + fifo[BltWidthHeight/4] = (Dy(r) << 16) | Dx(r); + + waitforidle(scr); + + return 1; +} + +void +nvidiablank(VGAscr*, int blank) +{ + uchar seq1, crtc1A; + + seq1 = vgaxi(Seqx, 1) & ~0x20; + crtc1A = vgaxi(Crtx, 0x1A) & ~0xC0; + + if(blank){ + seq1 |= 0x20; +// crtc1A |= 0xC0; + crtc1A |= 0x80; + } + + vgaxo(Seqx, 1, seq1); + vgaxo(Crtx, 0x1A, crtc1A); +} + +static void +nvidiadrawinit(VGAscr *scr) +{ + ulong* fifo; + + fifo = KADDR(scr->io + Fifo); + + waitforfifo(scr, ClipFifo, 2); + + fifo[ClipTopLeft/4] = 0x0; + fifo[ClipWidthHeight/4] = 0x80008000; + + waitforfifo(scr, PattFifo, 5); + + fifo[PattShape/4] = 0; + fifo[PattColor0/4] = 0xffffffff; + fifo[PattColor1/4] = 0xffffffff; + fifo[PattMonochrome0/4] = 0xffffffff; + fifo[PattMonochrome1/4] = 0xffffffff; + + waitforfifo(scr, RopFifo, 1); + + fifo[RopRop3/4] = 0xCC; + + waitforidle(scr); + + scr->blank = nvidiablank; + hwblank = 1; + scr->fill = nvidiahwfill; + scr->scroll = nvidiahwscroll; +} + +VGAdev vganvidiadev = { + "nvidia", + + nvidiaenable, + nil, + nil, + nvidialinear, + nvidiadrawinit, +}; + +VGAcur vganvidiacur = { + "nvidiahwgc", + + nvidiacurenable, + nvidiacurdisable, + nvidiacurload, + nvidiacurmove, +}; diff --git a/os/pc/vgargb524.c b/os/pc/vgargb524.c new file mode 100644 index 00000000..89c2129d --- /dev/null +++ b/os/pc/vgargb524.c @@ -0,0 +1,236 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#define Image IMAGE +#include <draw.h> +#include <memdraw.h> +#include <cursor.h> +#include "screen.h" + +/* + * IBM RGB524. + * 170/220MHz High Performance Palette DAC. + * + * Assumes hooked up to an S3 Vision96[48]. + */ +enum { + IndexLo = 0x00, + IndexHi = 0x01, + Data = 0x02, + IndexCtl = 0x03, +}; + +enum { /* index registers */ + CursorCtl = 0x30, + CursorXLo = 0x31, + CursorXHi = 0x32, + CursorYLo = 0x33, + CursorYHi = 0x34, + CursorHotX = 0x35, + CursorHotY = 0x36, + + CursorR1 = 0x40, + CursorG1 = 0x41, + CursorB1 = 0x42, + CursorR2 = 0x43, + CursorG2 = 0x44, + CursorB2 = 0x45, + CursorR3 = 0x46, + CursorG3 = 0x47, + CursorB3 = 0x48, + + CursorArray = 0x100, +}; + +/* + * Lower 2-bits of indirect DAC register + * addressing. + */ +static ushort dacxreg[4] = { + PaddrW, Pdata, Pixmask, PaddrR +}; + +static uchar +rgb524setrs2(void) +{ + uchar rs2; + + rs2 = vgaxi(Crtx, 0x55); + vgaxo(Crtx, 0x55, (rs2 & 0xFC)|0x01); + + return rs2; +} + +static void +rgb524xo(int index, uchar data) +{ + vgao(dacxreg[IndexLo], index & 0xFF); + vgao(dacxreg[IndexHi], (index>>8) & 0xFF); + vgao(dacxreg[Data], data); +} + +static void +rgb524disable(VGAscr*) +{ + uchar rs2; + + rs2 = rgb524setrs2(); + rgb524xo(CursorCtl, 0x00); + vgaxo(Crtx, 0x55, rs2); +} + +static void +rgb524enable(VGAscr*) +{ + uchar rs2; + + rs2 = rgb524setrs2(); + + /* + * Make sure cursor is off by initialising the cursor + * control to defaults. + */ + rgb524xo(CursorCtl, 0x00); + + /* + * Cursor colour 1 (white), + * cursor colour 2 (black). + */ + rgb524xo(CursorR1, Pwhite); rgb524xo(CursorG1, Pwhite); rgb524xo(CursorB1, Pwhite); + rgb524xo(CursorR2, Pblack); rgb524xo(CursorG2, Pblack); rgb524xo(CursorB2, Pblack); + + /* + * Enable the cursor, 32x32, mode 2. + */ + rgb524xo(CursorCtl, 0x23); + + vgaxo(Crtx, 0x55, rs2); +} + +static void +rgb524load(VGAscr*, Cursor* curs) +{ + uchar p, p0, p1, rs2; + int x, y; + + rs2 = rgb524setrs2(); + + /* + * Make sure cursor is off by initialising the cursor + * control to defaults. + */ + rgb524xo(CursorCtl, 0x00); + + /* + * Set auto-increment mode for index-register addressing + * and initialise the cursor array index. + */ + vgao(dacxreg[IndexCtl], 0x01); + vgao(dacxreg[IndexLo], CursorArray & 0xFF); + vgao(dacxreg[IndexHi], (CursorArray>>8) & 0xFF); + + /* + * Initialise the 32x32 cursor RAM array. There are 2 planes, + * p0 and p1. Data is written 4 pixels per byte, with p1 the + * MS bit of each pixel. + * The cursor is set in X-Windows mode which gives the following + * truth table: + * p1 p0 colour + * 0 0 underlying pixel colour + * 0 1 underlying pixel colour + * 1 0 cursor colour 1 + * 1 1 cursor colour 2 + * Put the cursor into the top-left of the 32x32 array. + */ + for(y = 0; y < 32; y++){ + for(x = 0; x < 32/8; x++){ + if(x < 16/8 && y < 16){ + p0 = curs->clr[x+y*2]; + p1 = curs->set[x+y*2]; + + p = 0x00; + if(p1 & 0x80) + p |= 0xC0; + else if(p0 & 0x80) + p |= 0x80; + if(p1 & 0x40) + p |= 0x30; + else if(p0 & 0x40) + p |= 0x20; + if(p1 & 0x20) + p |= 0x0C; + else if(p0 & 0x20) + p |= 0x08; + if(p1 & 0x10) + p |= 0x03; + else if(p0 & 0x10) + p |= 0x02; + vgao(dacxreg[Data], p); + + p = 0x00; + if(p1 & 0x08) + p |= 0xC0; + else if(p0 & 0x08) + p |= 0x80; + if(p1 & 0x04) + p |= 0x30; + else if(p0 & 0x04) + p |= 0x20; + if(p1 & 0x02) + p |= 0x0C; + else if(p0 & 0x02) + p |= 0x08; + if(p1 & 0x01) + p |= 0x03; + else if(p0 & 0x01) + p |= 0x02; + vgao(dacxreg[Data], p); + } + else{ + vgao(dacxreg[Data], 0x00); + vgao(dacxreg[Data], 0x00); + } + } + } + + /* + * Initialise the cursor hotpoint, + * enable the cursor and restore state. + */ + rgb524xo(CursorHotX, -curs->offset.x); + rgb524xo(CursorHotY, -curs->offset.y); + + rgb524xo(CursorCtl, 0x23); + + vgaxo(Crtx, 0x55, rs2); +} + +static int +rgb524move(VGAscr*, Point p) +{ + uchar rs2; + + rs2 = rgb524setrs2(); + + rgb524xo(CursorXLo, p.x & 0xFF); + rgb524xo(CursorXHi, (p.x>>8) & 0x0F); + rgb524xo(CursorYLo, p.y & 0xFF); + rgb524xo(CursorYHi, (p.y>>8) & 0x0F); + + vgaxo(Crtx, 0x55, rs2); + + return 0; +} + +VGAcur vgargb524cur = { + "rgb524hwgc", + + rgb524enable, + rgb524disable, + rgb524load, + rgb524move, +}; diff --git a/os/pc/vgas3.c b/os/pc/vgas3.c new file mode 100644 index 00000000..b23657e6 --- /dev/null +++ b/os/pc/vgas3.c @@ -0,0 +1,620 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#define Image IMAGE +#include <draw.h> +#include <memdraw.h> +#include <cursor.h> +#include "screen.h" + +enum { + PCIS3 = 0x5333, /* PCI VID */ + + SAVAGE3D = 0x8A20, /* PCI DID */ + SAVAGE3DMV = 0x8A21, + SAVAGE4 = 0x8A22, + PROSAVAGEP = 0x8A25, + PROSAVAGEK = 0x8A26, + PROSAVAGE8 = 0x8D04, + SAVAGEMXMV = 0x8C10, + SAVAGEMX = 0x8C11, + SAVAGEIXMV = 0x8C12, + SAVAGEIX = 0x8C13, + SUPERSAVAGEIXC16 = 0x8C2E, + SAVAGE2000 = 0x9102, + + VIRGE = 0x5631, + VIRGEGX2 = 0x8A10, + VIRGEDXGX = 0x8A01, + VIRGEVX = 0x883D, + VIRGEMX = 0x8C01, + VIRGEMXP = 0x8C03, + + VIRTUALPC2004 = 0x8810, + AURORA64VPLUS = 0x8812, +}; + +static int +s3pageset(VGAscr* scr, int page) +{ + uchar crt35, crt51; + int opage; + + crt35 = vgaxi(Crtx, 0x35); + if(scr->gscreen->depth >= 8){ + /* + * The S3 registers need to be unlocked for this. + * Let's hope they are already: + * vgaxo(Crtx, 0x38, 0x48); + * vgaxo(Crtx, 0x39, 0xA0); + * + * The page is 6 bits, the lower 4 bits in Crt35<3:0>, + * the upper 2 in Crt51<3:2>. + */ + vgaxo(Crtx, 0x35, page & 0x0F); + crt51 = vgaxi(Crtx, 0x51); + vgaxo(Crtx, 0x51, (crt51 & ~0x0C)|((page & 0x30)>>2)); + opage = ((crt51 & 0x0C)<<2)|(crt35 & 0x0F); + } + else{ + vgaxo(Crtx, 0x35, (page<<2) & 0x0C); + opage = (crt35>>2) & 0x03; + } + + return opage; +} + +static void +s3page(VGAscr* scr, int page) +{ + int id; + + id = (vgaxi(Crtx, 0x2D)<<8)|vgaxi(Crtx, 0x2E); + switch(id){ + + case VIRGEGX2: + break; + + default: + lock(&scr->devlock); + s3pageset(scr, page); + unlock(&scr->devlock); + break; + } +} + +static ulong +s3linear(VGAscr* scr, int* size, int* align) +{ + char *mmioname; + ulong aperture, oaperture, mmiobase, mmiosize; + int i, id, j, osize, oapsize, wasupamem; + Pcidev *p; + + osize = *size; + oaperture = scr->aperture; + oapsize = scr->apsize; + wasupamem = scr->isupamem; + + mmiosize = 0; + mmiobase = 0; + mmioname = nil; + + /* + * S3 makes cards other than display controllers, so + * look for the first S3 display controller (device class 3) + * and not one of their sound cards. + */ + p = nil; + while(p = pcimatch(p, PCIS3, 0)){ + if(p->ccrb == 0x03) + break; + } + if(p != nil){ + for(i=0; i<nelem(p->mem); i++){ + if(p->mem[i].size >= *size + && ((p->mem[i].bar & ~0x0F) & (*align-1)) == 0) + break; + } + if(i >= nelem(p->mem)){ + print("vgas3: aperture not found\n"); + return 0; + } + aperture = p->mem[i].bar & ~0x0F; + *size = p->mem[i].size; + + id = (vgaxi(Crtx, 0x2D)<<8)|vgaxi(Crtx, 0x2E); + switch(id){ /* find mmio */ + case SAVAGE4: + case PROSAVAGEP: + case PROSAVAGEK: + case PROSAVAGE8: + case SUPERSAVAGEIXC16: + /* + * We could assume that the MMIO registers + * will be in the screen segment and just use + * that, but PCI software is allowed to move them + * if it feels like it, so we look for an aperture of + * the right size; only the first 512k actually means + * anything. The S3 engineers overestimated how + * much space they would need in the first design. + */ + for(j=0; j<nelem(p->mem); j++){ + if(i == j) + continue; + if(p->mem[j].size==512*1024 || p->mem[j].size==16*1024*1024){ + mmiobase = p->mem[j].bar & ~0x0F; + mmiosize = 512*1024; + scr->mmio = (ulong*)upamalloc(mmiobase, mmiosize, 0); + mmioname = "savagemmio"; + break; + } + } + if(mmiosize == 0){ + print("savage4: mmio not found\n"); + return 0; + } + } + }else + aperture = 0; + + if(wasupamem) + upafree(oaperture, oapsize); + scr->isupamem = 0; + + aperture = upamalloc(aperture, *size, *align); + if(aperture == 0){ + if(wasupamem && upamalloc(oaperture, oapsize, 0)) + scr->isupamem = 1; + } + else + scr->isupamem = 1; + + if(oaperture && oaperture != aperture) + print("warning (BUG): redefinition of aperture does not change s3screen segment\n"); + addvgaseg("s3screen", aperture, osize); + + if(mmiosize) + addvgaseg(mmioname, mmiobase, mmiosize); + + return aperture; +} + +static void +s3vsyncactive(void) +{ + /* + * Hardware cursor information is fetched from display memory + * during the horizontal blank active time. The 80x chips may hang + * if the cursor is turned on or off during this period. + */ + while((vgai(Status1) & 0x08) == 0) + ; +} + +static void +s3disable(VGAscr*) +{ + uchar crt45; + + /* + * Turn cursor off. + */ + crt45 = vgaxi(Crtx, 0x45) & 0xFE; + s3vsyncactive(); + vgaxo(Crtx, 0x45, crt45); +} + +static void +s3load(VGAscr* scr, Cursor* curs) +{ + uchar *p; + int id, dolock, opage, x, y; + + /* + * Disable the cursor and + * set the pointer to the two planes. + */ + s3disable(scr); + + opage = 0; + p = KADDR(scr->aperture); + id = (vgaxi(Crtx, 0x2D)<<8)|vgaxi(Crtx, 0x2E); + switch(id){ + + case VIRTUALPC2004: + case VIRGE: + case VIRGEDXGX: + case VIRGEGX2: + case VIRGEVX: + case SAVAGEMXMV: + case SAVAGEIXMV: + case SAVAGE4: + case PROSAVAGEP: + case PROSAVAGEK: + case PROSAVAGE8: + case SUPERSAVAGEIXC16: + dolock = 0; + p += scr->storage; + break; + + default: + dolock = 1; + lock(&scr->devlock); + opage = s3pageset(scr, scr->storage>>16); + p += (scr->storage & 0xFFFF); + break; + } + + /* + * The cursor is set in Microsoft Windows format (the ViRGE/GX2 doesn't + * support the X11 format) which gives the following truth table: + * and xor colour + * 0 0 background colour + * 0 1 foreground colour + * 1 0 current screen pixel + * 1 1 NOT current screen pixel + * Put the cursor into the top-left of the 64x64 array. + * + * The cursor pattern in memory is interleaved words of + * AND and XOR patterns. + */ + for(y = 0; y < 64; y++){ + for(x = 0; x < 64/8; x += 2){ + if(x < 16/8 && y < 16){ + *p++ = ~(curs->clr[2*y + x]|curs->set[2*y + x]); + *p++ = ~(curs->clr[2*y + x+1]|curs->set[2*y + x+1]); + *p++ = curs->set[2*y + x]; + *p++ = curs->set[2*y + x+1]; + } + else { + *p++ = 0xFF; + *p++ = 0xFF; + *p++ = 0x00; + *p++ = 0x00; + } + } + } + + if(dolock){ + s3pageset(scr, opage); + unlock(&scr->devlock); + } + + /* + * Save the cursor hotpoint and enable the cursor. + */ + scr->offset = curs->offset; + s3vsyncactive(); + vgaxo(Crtx, 0x45, 0x01); +} + +static int +s3move(VGAscr* scr, Point p) +{ + int x, xo, y, yo; + + /* + * Mustn't position the cursor offscreen even partially, + * or it disappears. Therefore, if x or y is -ve, adjust the + * cursor offset instead. + * There seems to be a bug in that if the offset is 1, the + * cursor doesn't disappear off the left edge properly, so + * round it up to be even. + */ + if((x = p.x+scr->offset.x) < 0){ + xo = -x; + xo = ((xo+1)/2)*2; + x = 0; + } + else + xo = 0; + if((y = p.y+scr->offset.y) < 0){ + yo = -y; + y = 0; + } + else + yo = 0; + + vgaxo(Crtx, 0x46, (x>>8) & 0x07); + vgaxo(Crtx, 0x47, x & 0xFF); + vgaxo(Crtx, 0x49, y & 0xFF); + vgaxo(Crtx, 0x4E, xo); + vgaxo(Crtx, 0x4F, yo); + vgaxo(Crtx, 0x48, (y>>8) & 0x07); + + return 0; +} + +static void +s3enable(VGAscr* scr) +{ + int i; + ulong storage; + + s3disable(scr); + + /* + * Cursor colours. Set both the CR0[EF] and the colour + * stack in case we are using a 16-bit RAMDAC. + */ + vgaxo(Crtx, 0x0E, Pwhite); + vgaxo(Crtx, 0x0F, Pblack); + vgaxi(Crtx, 0x45); + + for(i = 0; i < 3; i++) + vgaxo(Crtx, 0x4A, Pblack); + vgaxi(Crtx, 0x45); + for(i = 0; i < 3; i++) + vgaxo(Crtx, 0x4B, Pwhite); + + /* + * Find a place for the cursor data in display memory. + * Must be on a 1024-byte boundary. + */ + storage = (scr->gscreen->width*BY2WD*scr->gscreen->r.max.y+1023)/1024; + vgaxo(Crtx, 0x4C, storage>>8); + vgaxo(Crtx, 0x4D, storage & 0xFF); + storage *= 1024; + scr->storage = storage; + + /* + * Load, locate and enable the cursor + * in Microsoft Windows format. + */ + s3load(scr, &arrow); + s3move(scr, ZP); + vgaxo(Crtx, 0x55, vgaxi(Crtx, 0x55) & ~0x10); + s3vsyncactive(); + vgaxo(Crtx, 0x45, 0x01); +} + +/* + * The manual gives byte offsets, but we want ulong offsets, hence /4. + */ +enum { + SrcBase = 0xA4D4/4, + DstBase = 0xA4D8/4, + Stride = 0xA4E4/4, + FgrdData = 0xA4F4/4, + WidthHeight = 0xA504/4, + SrcXY = 0xA508/4, + DestXY = 0xA50C/4, + Command = 0xA500/4, + SubStat = 0x8504/4, + FifoStat = 0x850C/4, +}; + +/* + * Wait for writes to VGA memory via linear aperture to flush. + */ +enum {Maxloop = 1<<24}; +struct { + ulong linear; + ulong fifo; + ulong idle; + ulong lineartimeout; + ulong fifotimeout; + ulong idletimeout; +} waitcount; + +static void +waitforlinearfifo(VGAscr *scr) +{ + ulong *mmio; + long x; + static ulong nwaitforlinearfifo; + ulong mask, val; + + switch(scr->id){ + default: + panic("unknown scr->id in s3 waitforlinearfifo"); + case 0x8A01: /* ViRGE/[DG]X. XFree86 says no waiting necessary */ + return; + case 0x5631: /* ViRGE */ + case 0x883D: /* ViRGE/VX */ + mask = 0x0F<<6; + val = 0x08<<6; + break; + case 0x8A10: /* ViRGE/GX2 */ + mask = 0x1F<<6; + val = 0x10<<6; + break; + } + mmio = scr->mmio; + x = 0; + while((mmio[FifoStat]&mask) != val && x++ < Maxloop) + waitcount.linear++; + if(x >= Maxloop) + waitcount.lineartimeout++; +} + +static void +waitforfifo(VGAscr *scr, int entries) +{ + ulong *mmio; + long x; + static ulong nwaitforfifo; + + mmio = scr->mmio; + x = 0; + while((mmio[SubStat]&0x1F00) < ((entries+2)<<8) && x++ < Maxloop) + waitcount.fifo++; + if(x >= Maxloop) + waitcount.fifotimeout++; +} + +static void +waitforidle(VGAscr *scr) +{ + ulong *mmio; + long x; + + mmio = scr->mmio; + x = 0; + while((mmio[SubStat]&0x3F00) != 0x3000 && x++ < Maxloop) + waitcount.idle++; + if(x >= Maxloop) + waitcount.idletimeout++; +} + +static int +hwscroll(VGAscr *scr, Rectangle r, Rectangle sr) +{ + enum { Bitbltop = 0xCC }; /* copy source */ + ulong *mmio; + ulong cmd, stride; + Point dp, sp; + int did, d; + + d = scr->gscreen->depth; + did = (d-8)/8; + cmd = 0x00000020|(Bitbltop<<17)|(did<<2); + stride = Dx(scr->gscreen->r)*d/8; + + if(r.min.x <= sr.min.x){ + cmd |= 1<<25; + dp.x = r.min.x; + sp.x = sr.min.x; + }else{ + dp.x = r.max.x-1; + sp.x = sr.max.x-1; + } + + if(r.min.y <= sr.min.y){ + cmd |= 1<<26; + dp.y = r.min.y; + sp.y = sr.min.y; + }else{ + dp.y = r.max.y-1; + sp.y = sr.max.y-1; + } + + mmio = scr->mmio; + waitforlinearfifo(scr); + waitforfifo(scr, 7); + mmio[SrcBase] = scr->aperture; + mmio[DstBase] = scr->aperture; + mmio[Stride] = (stride<<16)|stride; + mmio[WidthHeight] = ((Dx(r)-1)<<16)|Dy(r); + mmio[SrcXY] = (sp.x<<16)|sp.y; + mmio[DestXY] = (dp.x<<16)|dp.y; + mmio[Command] = cmd; + waitforidle(scr); + return 1; +} + +static int +hwfill(VGAscr *scr, Rectangle r, ulong sval) +{ + enum { Bitbltop = 0xCC }; /* copy source */ + ulong *mmio; + ulong cmd, stride; + int did, d; + + d = scr->gscreen->depth; + did = (d-8)/8; + cmd = 0x16000120|(Bitbltop<<17)|(did<<2); + stride = Dx(scr->gscreen->r)*d/8; + mmio = scr->mmio; + waitforlinearfifo(scr); + waitforfifo(scr, 8); + mmio[SrcBase] = scr->aperture; + mmio[DstBase] = scr->aperture; + mmio[DstBase] = scr->aperture; + mmio[Stride] = (stride<<16)|stride; + mmio[FgrdData] = sval; + mmio[WidthHeight] = ((Dx(r)-1)<<16)|Dy(r); + mmio[DestXY] = (r.min.x<<16)|r.min.y; + mmio[Command] = cmd; + waitforidle(scr); + return 1; +} + +enum { + CursorSyncCtl = 0x0D, /* in Seqx */ + VsyncHi = 0x80, + VsyncLo = 0x40, + HsyncHi = 0x20, + HsyncLo = 0x10, +}; + +static void +s3blank(VGAscr*, int blank) +{ + uchar x; + + x = vgaxi(Seqx, CursorSyncCtl); + x &= ~0xF0; + if(blank) + x |= VsyncLo | HsyncLo; + vgaxo(Seqx, CursorSyncCtl, x); +} + +static void +s3drawinit(VGAscr *scr) +{ + extern void savageinit(VGAscr*); /* vgasavage.c */ + ulong id; + + id = (vgaxi(Crtx, 0x2D)<<8)|vgaxi(Crtx, 0x2E); + scr->id = id; + + /* + * It's highly likely that other ViRGEs will work without + * change to the driver, with the exception of the size of + * the linear aperture memory write FIFO. Since we don't + * know that size, I'm not turning them on. See waitforlinearfifo + * above. + */ + scr->blank = s3blank; + /* hwblank = 1; not known to work well */ + + switch(id){ + case VIRGE: + case VIRGEVX: + case VIRGEGX2: + scr->mmio = (ulong*)(scr->aperture+0x1000000); + scr->fill = hwfill; + scr->scroll = hwscroll; + break; + case SAVAGEMXMV: + case SAVAGEIXMV: + scr->mmio = (ulong*)(scr->aperture+0x1000000); + savageinit(scr); + break; + case SUPERSAVAGEIXC16: + case SAVAGE4: + case PROSAVAGEP: + case PROSAVAGE8: + case PROSAVAGEK: + /* scr->mmio is set by s3linear */ + savageinit(scr); + break; + } +} + +VGAdev vgas3dev = { + "s3", + + 0, + 0, + s3page, + s3linear, + s3drawinit, +}; + +VGAcur vgas3cur = { + "s3hwgc", + + s3enable, + s3disable, + s3load, + s3move, +}; + diff --git a/os/pc/vgasavage.c b/os/pc/vgasavage.c new file mode 100644 index 00000000..263b688b --- /dev/null +++ b/os/pc/vgasavage.c @@ -0,0 +1,571 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#define Image IMAGE +#include <draw.h> +#include <memdraw.h> +#include <cursor.h> +#include "screen.h" + +enum { + PCIS3 = 0x5333, /* PCI VID */ + + SAVAGE3D = 0x8A20, /* PCI DID */ + SAVAGE3DMV = 0x8A21, + SAVAGE4 = 0x8A22, + PROSAVAGEP = 0x8A25, + PROSAVAGEK = 0x8A26, + PROSAVAGE8 = 0x8D04, + SAVAGEMXMV = 0x8C10, + SAVAGEMX = 0x8C11, + SAVAGEIXMV = 0x8C12, + SAVAGEIX = 0x8C13, + SUPERSAVAGEIXC16 = 0x8C2E, + SAVAGE2000 = 0x9102, + + VIRGE = 0x5631, + VIRGEGX2 = 0x8A10, + VIRGEDXGX = 0x8A01, + VIRGEVX = 0x883D, + VIRGEMX = 0x8C01, + VIRGEMXP = 0x8C03, + + AURORA64VPLUS = 0x8812, +}; + +/* + * Savage4 et al. acceleration. + * + * This is based only on the Savage4 documentation. + * It is expected to work on other Savage cards as well, + * but has not been tried. + * + * There are five ways to access the 2D graphics engine registers: + * - Old MMIO non-packed format + * - Old MMIO packed format + * - New MMIO non-packed format + * - New MMIO packed format + * - Burst Command Interface (BCI) + * + * Of these, the manual hints that the first three are deprecated, + * and it does not document any of those three well enough to use. + * + * I have tried for many hours with no success to understand the BCI + * interface well enough to use it. It is not well documented, and the + * XFree86 driver seems to completely contradict what little documentation + * there is. + * + * This leaves the packed new MMIO. + * The manual contradicts itself here, claming that the registers + * start at 0x2008100 as well as at 0x0008100 from the base of the + * mmio segment. Since the segment is only 512k, we assume that + * the latter is the correct offset. + * + * According to the manual, only 16-bit reads of the 2D registers + * are supported: 32-bit reads will return garbage in the upper word. + * 32-bit writes must be enabled explicitly. + * + * 32-bit reads of the status registers seem just fine. + */ + +/* 2D graphics engine registers for Savage4; others appear to be mostly the same */ +enum { + SubsystemStatus = 0x8504, /* Subsystem Status: read only */ + /* read only: whether we get interrupts on various events */ + VsyncInt = 1<<0, /* vertical sync */ + GeBusyInt = 1<<1, /* 2D graphics engine busy */ + BfifoFullInt = 1<<2, /* BIU FIFO full */ + BfifoEmptyInt = 1<<3, /* BIU FIFO empty */ + CfifoFullInt = 1<<4, /* command FIFO full */ + CfifoEmptyInt = 1<<5, /* command FIFO empty */ + BciInt = 1<<6, /* BCI */ + LpbInt = 1<<7, /* LPB */ + CbHiInt = 1<<16, /* COB upper threshold */ + CbLoInt = 1<<17, /* COB lower threshold */ + + SubsystemCtl = 0x8504, /* Subsystem Control: write only */ + /* clear interrupts for various events */ + VsyncClr = 1<<0, + GeBusyClr = 1<<1, + BfifoFullClr = 1<<2, + BfifoEmptyClr = 1<<3, + CfifoFullClr = 1<<4, + CfifoEmptyClr = 1<<5, + BciClr = 1<<6, + LpbClr = 1<<7, + CbHiClr = 1<<16, + CbLoClr = 1<<17, + + /* enable interrupts for various events */ + VsyncEna = 1<<8, + Busy2DEna = 1<<9, + BfifoFullEna = 1<<10, + BfifoEmptyEna = 1<<11, + CfifoFullEna = 1<<12, + CfifoEmptyEna = 1<<13, + SubsysBciEna = 1<<14, + CbHiEna = 1<<24, + CbLoEna = 1<<25, + + /* 2D graphics engine software reset */ + GeSoftReset = 1<<15, + + FifoStatus = 0x8508, /* FIFO status: read only */ + CwbEmpty = 1<<0, /* command write buffer empty */ + CrbEmpty = 1<<1, /* command read buffer empty */ + CobEmpty = 1<<2, /* command overflow buffer empty */ + CfifoEmpty = 1<<3, /* command FIFO empty */ + CwbFull = 1<<8, /* command write buffer full */ + CrbFull = 1<<9, /* command read buffer full */ + CobFull = 1<<10, /* command overflow buffer full */ + CfifoFull = 1<<11, /* command FIFO full */ + + AdvFunCtl = 0x850C, /* Advanced Function Control: read/write */ + GeEna = 1<<0, /* enable 2D/3D engine */ + /* + * according to the manual, BigPixel should be + * set when bpp >= 8 (bpp != 4), and then CR50_5-4 are + * used to figure out bpp example. however, it does bad things + * to the screen in 8bpp mode. + */ + BigPixel = 1<<2, /* 8 or more bpp enhanced mode */ + LaEna = 1<<3, /* linear addressing ena: or'ed with CR58_4 */ + Mclk_2 = 0<<8, /* 2D engine clock divide: MCLK/2 */ + Mclk_4 = 1<<8, /* " MCLK/4 */ + Mclk = 2<<8, /* " MCLK */ + /* Mclk = 3<<8, /* " MCLK */ + Ic33mhz = 1<<16, /* Internal clock 33 MHz (instead of 66) */ + + WakeupReg = 0x8510, /* Wakeup: read/write */ + WakeupBit = 1<<0, /* wake up: or'ed with 3C3_0 */ + + SourceY = 0x8100, /* UL corner of bitblt source */ + SourceX = 0x8102, /* " */ + RectY = 0x8100, /* UL corner of rectangle fill */ + RectX = 0x8102, /* " */ + DestY = 0x8108, /* UL corner of bitblt dest */ + DestX = 0x810A, /* " */ + Height = 0x8148, /* bitblt, image xfer rectangle height */ + Width = 0x814A, /* bitblt, image xfer rectangle width */ + + StartY = 0x8100, /* Line draw: first point*/ + StartX = 0x8102, /* " */ + /* + * For line draws, the following must be programmed: + * axial step constant = 2*min(|dx|,|dy|) + * diagonal step constant = 2*[min(|dx|,|dy|) - max(|dx|,|dy|)] + * error term = 2*min(|dx|,|dy|) - max(|dx|,|dy| - 1 + * [sic] when start X < end X + * error term = 2*min(|dx|,|dy|) - max(|dx|,|dy| + * [sic] when start X >= end X + */ + AxialStep = 0x8108, + DiagonalStep = 0x810A, + LineError = 0x8110, + MinorLength = 0x8148, /* pixel count along minor axis */ + MajorLength = 0x814A, /* pixel count along major axis */ + + DrawCmd = 0x8118, /* Drawing Command: write only */ + CmdMagic = 0<<1, + AcrossPlane = 1<<1, /* across the plane mode */ + LastPixelOff = 1<<2, /* last pixel of line or vector draw not drawn */ + Radial = 1<<3, /* enable radial direction (else axial) */ + DoDraw = 1<<4, /* draw pixels (else only move current pos) */ + + DrawRight = 1<<5, /* axial drawing direction: left to right */ + /* DrawLeft = 0<<5, */ + MajorY = 1<<6, + /* MajorX = 0<<6, */ + DrawDown = 1<<7, + /* DrawUp = 0<<7, */ + Degree0 = 0<<5, /* drawing direction when Radial */ + Degree45 = 1<<5, + /* ... */ + Degree315 = 7<<5, + + UseCPUData = 1<<8, + + /* image write bus transfer width */ + Bus8 = 0<<9, + Bus16 = 1<<9, + /* + * in Bus32 mode, doubleword bits beyond the image rect width are + * discarded. each line starts on a new doubleword. + * Bus32AP is intended for across-the-plane mode and + * rounds to byte boundaries instead. + */ + Bus32 = 2<<9, + Bus32AP = 3<<9, + + CmdNop = 0<<13, /* nop */ + CmdLine = 1<<13, /* draw line */ + CmdFill = 2<<13, /* fill rectangle */ + CmdBitblt = 6<<13, /* bitblt */ + CmdPatblt = 7<<13, /* 8x8 pattern blt */ + + SrcGBD = 0<<16, + SrcPBD = 1<<16, + SrcSBD = 2<<16, + + DstGBD = 0<<18, + DstPBD = 1<<18, + DstSBD = 2<<18, + + /* color sources, controls */ + BgColor = 0x8120, /* Background Color: read/write */ + FgColor = 0x8124, /* Foreground Color: read/write */ + BitplaneWmask = 0x8128, /* Bitplane Write Mask: read/write */ + BitplaneRmask = 0x812C, /* Bitplane Read Mask: read/write */ + CmpColor = 0x8130, /* Color Compare: read/write */ + BgMix = 0x8134, + FgMix = 0x8136, + MixNew = 7, + SrcBg = 0<<5, + SrcFg = 1<<5, + SrcCPU = 2<<5, + SrcDisp = 3<<5, + + /* clipping rectangle */ + TopScissors = 0x8138, /* Top Scissors: write only */ + LeftScissors = 0x813A, /* Left Scissors: write only */ + BottomScissors = 0x813C, /* Bottom Scissors: write only */ + RightScissors = 0x813E, /* Right Scissors: write only */ + + /* + * Registers with Magic were indirectly accessed in older modes. + * It is not clear whether the Magic is necessary. + * In the older modes, writes to these registers were pipelined, + * so that you had to issue an engine command and wait for engine + * idle before reading a write back. It is not clear if this is + * still the case either. + */ + PixCtl = 0x8140, /* Pixel Control: write only */ + PixMagic = 0xA<<12, + PixMixFg = 0<<6, /* foreground mix register always */ + PixMixCPU = 2<<6, /* CPU data determines mix register */ + PixMixDisp = 3<<6, /* display data determines mix register */ + + MfMisc2Ctl = 0x8142, /* Multifunction Control Misc. 2: write only */ + MfMisc2Magic = 0xD<<12, + DstShift = 0, /* 3 bits: destination base address in MB */ + SrcShift = 4, /* 3 bits: source base address in MB */ + WaitFifoEmpty = 2<<8, /* wait for write FIFO empty between draws */ + + MfMiscCtl = 0x8144, /* Multifunction Control Misc: write only */ + MfMiscMagic = 0xE<<12, + UseHighBits = 1<<4, /* select upper 16 bits for 32-bit reg access */ + ClipInvert = 1<<5, /* only touch pixels outside clip rectangle */ + SkipSame = 0<<6, /* ignore pixels with color CmpColor */ + SkipDifferent = 1<<7, /* ignore pixels not color CmpColor */ + CmpEna = 1<<8, /* enable color compare */ + W32Ena = 1<<9, /* enable 32-bit register write */ + ClipDis = 1<<11, /* disable clipping */ + + /* + * The bitmap descriptor 1 registers contain the starting + * address of the bitmap (in bytes). + * The bitmap descriptor 2 registesr contain stride (in pixels) + * in the lower 16 bits, depth (in bits) in the next 8 bits, + * and whether block write is disabled. + */ + GBD1 = 0x8168, /* Global Bitmap Descriptor 1: read/write */ + GBD2 = 0x816C, /* Global Bitmap Descriptor 2: read/write */ + /* GBD2-only bits */ + BDS64 = 1<<0, /* bitmap descriptor size 64 bits */ + GBDBciEna = 1<<3, /* BCI enable */ + /* generic BD2 bits */ + BlockWriteDis = 1<<28, + StrideShift = 0, + DepthShift = 16, + + PBD1 = 0x8170, /* Primary Bitmap Descriptor: read/write */ + PBD2 = 0x8174, + SBD1 = 0x8178, /* Secondary Bitmap Descriptor: read/write */ + SBD2 = 0x817C, +}; + +/* mastered data transfer registers */ + +/* configuration/status registers */ +enum { + XStatus0 = 0x48C00, /* Status Word 0: read only */ + /* rev. A silicon differs from rev. B; use AltStatus0 */ + CBEMaskA = 0x1FFFF, /* filled command buffer entries */ + CBEShiftA = 0, + BciIdleA = 1<<17, /* BCI idle */ + Ge3IdleA = 1<<18, /* 3D engine idle */ + Ge2IdleA = 1<<19, /* 2D engine idle */ + McpIdleA = 1<<20, /* motion compensation processor idle */ + MeIdleA = 1<<22, /* master engine idle */ + PfPendA = 1<<23, /* page flip pending */ + + CBEMaskB = 0x1FFFFF, + CBEShiftB = 0, + BciIdleB = 1<<25, + Ge3IdleB = 1<<26, + Ge2IdleB = 1<<27, + McpIdleB = 1<<28, + MeIdleB = 1<<30, + PfPendB = 1<<31, + + AltStatus0 = 0x48C60, /* Alternate Status Word 0: read only */ + CBEMask = 0x1FFFF, + CBEShift = 0, + /* the Savage4 manual says bits 17..23 for these, like Status0 */ + /* empirically, they are bits 21..26 */ + BciIdle = 1<<21, + Ge3Idle = 1<<22, + Ge2Idle = 1<<23, + McpIdle = 1<<24, + MeIdle = 1<<25, + PfPend = 1<<26, + + XStatus1 = 0x48C04, /* Status Word 1: read only */ + /* contains event tag 1, event tag 0, both 16 bits */ + + XStatus2 = 0x48C08, /* Status Word 2: read only */ + ScanMask = 0x3FF, /* current scan line */ + ScanShift = 0, + VRTMask = 0x7F100, /* vert retrace count */ + VRTShift = 11, + + CbThresh = 0x48C10, /* Command Buffer Thresholds: read/write */ + CobOff = 0x48C14, /* Command Overflow Buffer: read/write */ + + CobPtr = 0x48C18, /* Command Overflow Buffer Pointers: read/write */ + CobEna = 1<<2, /* command overflow buffer enable */ + CobBciEna = 1<<3, /* BCI function enable */ + CbeMask = 0xFFFF8000, /* no. of entries in command buffer */ + CbeShift = 15, + + AltStatus1 = 0x48C64, /* Alternate Status Word 1: read onnly */ + /* contains current texture surface tag, vertex buffer tag */ + +}; + +struct { + ulong idletimeout; + ulong tostatw[16]; +} savagestats; + +enum { + Maxloop = 1<<20 +}; + +static void +savagewaitidle(VGAscr *scr) +{ + long x; + ulong *statw, mask, goal; + + switch(scr->id){ + case SAVAGE4: + case PROSAVAGEP: + case PROSAVAGEK: + case PROSAVAGE8: + /* wait for engine idle and FIFO empty */ + statw = (ulong*)((uchar*)scr->mmio+AltStatus0); + mask = CBEMask | Ge2Idle; + goal = Ge2Idle; + break; + /* case SAVAGEMXMV: ? */ + /* case SAVAGEMX: ? */ + /* case SAVAGEIX: ? */ + case SUPERSAVAGEIXC16: + case SAVAGEIXMV: + case SAVAGEMXMV: + /* wait for engine idle and FIFO empty */ + statw = (ulong*)((uchar*)scr->mmio+XStatus0); + mask = CBEMaskA | Ge2IdleA; + goal = Ge2IdleA; + break; + default: + /* + * best we can do: can't print or we'll call ourselves. + * savageinit is supposed to not let this happen. + */ + return; + } + + for(x=0; x<Maxloop; x++) + if((*statw & mask) == goal) + return; + + savagestats.tostatw[savagestats.idletimeout++&15] = *statw; + savagestats.tostatw[savagestats.idletimeout++&15] = (ulong)statw; +} + +static int +savagefill(VGAscr *scr, Rectangle r, ulong sval) +{ + uchar *mmio; + + mmio = (uchar*)scr->mmio; + + *(ulong*)(mmio+FgColor) = sval; + *(ulong*)(mmio+BgColor) = sval; + *(ulong*)(mmio+BgMix) = SrcFg|MixNew; + *(ulong*)(mmio+FgMix) = SrcFg|MixNew; + *(ushort*)(mmio+RectY) = r.min.y; + *(ushort*)(mmio+RectX) = r.min.x; + *(ushort*)(mmio+Width) = Dx(r)-1; + *(ushort*)(mmio+Height) = Dy(r)-1; + *(ulong*)(mmio+DrawCmd) = CmdMagic | DoDraw | CmdFill | DrawRight | DrawDown; + savagewaitidle(scr); + return 1; +} + +static int +savagescroll(VGAscr *scr, Rectangle r, Rectangle sr) +{ + uchar *mmio; + ulong cmd; + Point dp, sp; + + cmd = CmdMagic | DoDraw | CmdBitblt | SrcPBD | DstGBD; + + if(r.min.x <= sr.min.x){ + cmd |= DrawRight; + dp.x = r.min.x; + sp.x = sr.min.x; + }else{ + dp.x = r.max.x-1; + sp.x = sr.max.x-1; + } + + if(r.min.y <= sr.min.y){ + cmd |= DrawDown; + dp.y = r.min.y; + sp.y = sr.min.y; + }else{ + dp.y = r.max.y-1; + sp.y = sr.max.y-1; + } + + mmio = (uchar*)scr->mmio; + + *(ushort*)(mmio+SourceX) = sp.x; + *(ushort*)(mmio+SourceY) = sp.y; + *(ushort*)(mmio+DestX) = dp.x; + *(ushort*)(mmio+DestY) = dp.y; + *(ushort*)(mmio+Width) = Dx(r)-1; + *(ushort*)(mmio+Height) = Dy(r)-1; + *(ulong*)(mmio+BgMix) = SrcDisp|MixNew; + *(ulong*)(mmio+FgMix) = SrcDisp|MixNew; + *(ulong*)(mmio+DrawCmd) = cmd; + savagewaitidle(scr); + return 1; +} + +static void +savageblank(VGAscr*, int blank) +{ + uchar seqD; + + /* + * Will handle DPMS to monitor + */ + vgaxo(Seqx, 8, vgaxi(Seqx,8)|0x06); + seqD = vgaxi(Seqx, 0xD); + seqD &= 0x03; + if(blank) + seqD |= 0x50; + vgaxo(Seqx, 0xD, seqD); + + /* + * Will handle LCD + */ + if(blank) + vgaxo(Seqx, 0x31, vgaxi(Seqx, 0x31) & ~0x10); + else + vgaxo(Seqx, 0x31, vgaxi(Seqx, 0x31) | 0x10); +} + + +void +savageinit(VGAscr *scr) +{ + uchar *mmio; + ulong bd; + + /* if you add chip IDs here be sure to update savagewaitidle */ + switch(scr->id){ + case SAVAGE4: + case PROSAVAGEP: + case PROSAVAGEK: + case PROSAVAGE8: + case SAVAGEIXMV: + case SUPERSAVAGEIXC16: + case SAVAGEMXMV: + break; + default: + print("unknown savage %.4lux\n", scr->id); + return; + } + + mmio = (uchar*)scr->mmio; + if(mmio == nil) { + print("savageinit: no mmio\n"); + return; + } + + /* 2D graphics engine software reset */ + *(ushort*)(mmio+SubsystemCtl) = GeSoftReset; + delay(2); + *(ushort*)(mmio+SubsystemCtl) = 0; + savagewaitidle(scr); + + /* disable BCI as much as possible */ + *(ushort*)(mmio+CobPtr) &= ~CobBciEna; + *(ushort*)(mmio+GBD2) &= ~GBDBciEna; + savagewaitidle(scr); + + /* enable 32-bit writes, disable clipping */ + *(ushort*)(mmio+MfMiscCtl) = MfMiscMagic|W32Ena|ClipDis; + savagewaitidle(scr); + + /* enable all read, write planes */ + *(ulong*)(mmio+BitplaneRmask) = ~0; + *(ulong*)(mmio+BitplaneWmask) = ~0; + savagewaitidle(scr); + + /* turn on linear access, 2D engine */ + *(ulong*)(mmio+AdvFunCtl) |= GeEna|LaEna; + savagewaitidle(scr); + + /* set bitmap descriptors */ + bd = (scr->gscreen->depth<<DepthShift) | + (Dx(scr->gscreen->r)<<StrideShift) | BlockWriteDis + | BDS64; + + *(ulong*)(mmio+GBD1) = 0; + *(ulong*)(mmio+GBD2) = bd; + + *(ulong*)(mmio+PBD1) = 0; + *(ulong*)(mmio+PBD2) = bd; + + *(ulong*)(mmio+SBD1) = 0; + *(ulong*)(mmio+SBD2) = bd; + + /* + * For some reason, the GBD needs to get programmed twice, + * once before the PBD, SBD, and once after. + * This empirically makes it get set right. + * I would like to better understand the ugliness + * going on here. + */ + *(ulong*)(mmio+GBD1) = 0; + *(ulong*)(mmio+GBD2) = bd; + *(ushort*)(mmio+GBD2+2) = bd>>16; + savagewaitidle(scr); + + scr->fill = savagefill; + scr->scroll = savagescroll; + scr->blank = savageblank; + hwblank = 0; +} diff --git a/os/pc/vgat2r4.c b/os/pc/vgat2r4.c new file mode 100644 index 00000000..b8003b87 --- /dev/null +++ b/os/pc/vgat2r4.c @@ -0,0 +1,586 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#define Image IMAGE +#include <draw.h> +#include <memdraw.h> +#include <cursor.h> +#include "screen.h" + +/* + * #9 Ticket to Ride IV. + */ +enum { + IndexLo = 0x10/4, + IndexHi = 0x14/4, + Data = 0x18/4, + IndexCtl = 0x1C/4, + + Zoom = 0x54/4, +}; + +enum { /* index registers */ + CursorSyncCtl = 0x03, + HsyncHi = 0x01, + HsyncLo = 0x02, + VsyncHi = 0x04, + VsyncLo = 0x08, + + CursorCtl = 0x30, + CursorXLo = 0x31, + CursorXHi = 0x32, + CursorYLo = 0x33, + CursorYHi = 0x34, + CursorHotX = 0x35, + CursorHotY = 0x36, + + CursorR1 = 0x40, + CursorG1 = 0x41, + CursorB1 = 0x42, + CursorR2 = 0x43, + CursorG2 = 0x44, + CursorB2 = 0x45, + CursorR3 = 0x46, + CursorG3 = 0x47, + CursorB3 = 0x48, + + CursorArray = 0x100, + + CursorMode32x32 = 0x23, + CursorMode64x64 = 0x27, + CursorMode = CursorMode32x32, +}; + +static ulong +t2r4linear(VGAscr* scr, int* size, int* align) +{ + ulong aperture, oaperture; + int oapsize, wasupamem; + Pcidev *p; + + oaperture = scr->aperture; + oapsize = scr->apsize; + wasupamem = scr->isupamem; + + aperture = 0; + if(p = pcimatch(nil, 0x105D, 0)){ + switch(p->did){ + case 0x5348: + aperture = p->mem[0].bar & ~0x0F; + *size = p->mem[0].size; + break; + default: + break; + } + } + + if(wasupamem){ + if(oaperture == aperture) + return oaperture; + upafree(oaperture, oapsize); + } + scr->isupamem = 0; + + aperture = upamalloc(aperture, *size, *align); + if(aperture == 0){ + if(wasupamem && upamalloc(oaperture, oapsize, 0)){ + aperture = oaperture; + scr->isupamem = 1; + } + else + scr->isupamem = 0; + } + else + scr->isupamem = 1; + + return aperture; +} + +static void +t2r4enable(VGAscr* scr) +{ + Pcidev *p; + int size, align; + ulong aperture, mmio; + + /* + * Only once, can't be disabled for now. + * scr->mmio holds the virtual address of + * the MMIO registers. + */ + if(scr->mmio) + return; + if(p = pcimatch(nil, 0x105D, 0)){ + switch(p->did){ + case 0x5348: + break; + default: + return; + } + } + else + return; + mmio = upamalloc(p->mem[4].bar & ~0x0F, p->mem[4].size, 0); + if(mmio == 0) + return; + addvgaseg("t2r4mmio", mmio, p->mem[4].size); + + scr->mmio = KADDR(mmio); + + size = p->mem[0].size; + align = 0; + aperture = t2r4linear(scr, &size, &align); + if(aperture){ + scr->aperture = aperture; + scr->apsize = size; + addvgaseg("t2r4screen", aperture, size); + } +} + +static uchar +t2r4xi(VGAscr* scr, int index) +{ + ulong *mmio; + + mmio = scr->mmio; + mmio[IndexLo] = index & 0xFF; + mmio[IndexHi] = (index>>8) & 0xFF; + + return mmio[Data]; +} + +static void +t2r4xo(VGAscr* scr, int index, uchar data) +{ + ulong *mmio; + + mmio = scr->mmio; + mmio[IndexLo] = index & 0xFF; + mmio[IndexHi] = (index>>8) & 0xFF; + + mmio[Data] = data; +} + +static void +t2r4curdisable(VGAscr* scr) +{ + if(scr->mmio == 0) + return; + t2r4xo(scr, CursorCtl, 0x00); +} + +static void +t2r4curload(VGAscr* scr, Cursor* curs) +{ + uchar *data; + int size, x, y, zoom; + ulong clr, *mmio, pixels, set; + + mmio = scr->mmio; + if(mmio == 0) + return; + + /* + * Make sure cursor is off by initialising the cursor + * control to defaults. + */ + t2r4xo(scr, CursorCtl, 0x00); + + /* + * Set auto-increment mode for index-register addressing + * and initialise the cursor array index. + */ + mmio[IndexCtl] = 0x01; + mmio[IndexLo] = CursorArray & 0xFF; + mmio[IndexHi] = (CursorArray>>8) & 0xFF; + + /* + * Initialise the cursor RAM array. There are 2 planes, + * p0 and p1. Data is written 4 pixels per byte, with p1 the + * MS bit of each pixel. + * The cursor is set in X-Windows mode which gives the following + * truth table: + * p1 p0 colour + * 0 0 underlying pixel colour + * 0 1 underlying pixel colour + * 1 0 cursor colour 1 + * 1 1 cursor colour 2 + * Put the cursor into the top-left of the array. + * + * Although this looks a lot like the IBM RGB524 cursor, the + * scanlines appear to be twice as long as they should be and + * some of the other features are missing. + */ + if(mmio[Zoom] & 0x0F) + zoom = 32; + else + zoom = 16; + data = (uchar*)&mmio[Data]; + for(y = 0; y < zoom; y++){ + clr = (curs->clr[2*y]<<8)|curs->clr[y*2 + 1]; + set = (curs->set[2*y]<<8)|curs->set[y*2 + 1]; + pixels = 0; + for(x = 0; x < 16; x++){ + if(set & (1<<x)) + pixels |= 0x03<<(x*2); + else if(clr & (1<<x)) + pixels |= 0x02<<(x*2); + } + + *data = pixels>>24; + *data = pixels>>16; + *data = pixels>>8; + *data = pixels; + + *data = 0x00; + *data = 0x00; + *data = 0x00; + *data = 0x00; + + if(CursorMode == CursorMode32x32 && zoom == 16) + continue; + *data = pixels>>24; + *data = pixels>>16; + *data = pixels>>8; + *data = pixels; + + *data = 0x00; + *data = 0x00; + *data = 0x00; + *data = 0x00; + } + if(CursorMode == CursorMode32x32) + size = 32; + else + size = 64; + while(y < size){ + for(x = 0; x < size/8; x++){ + *data = 0x00; + *data = 0x00; + } + y++; + } + mmio[IndexCtl] = 0x00; + + /* + * Initialise the hotpoint and enable the cursor. + */ + t2r4xo(scr, CursorHotX, -curs->offset.x); + zoom = (scr->mmio[Zoom] & 0x0F)+1; + t2r4xo(scr, CursorHotY, -curs->offset.y*zoom); + + t2r4xo(scr, CursorCtl, CursorMode); +} + +static int +t2r4curmove(VGAscr* scr, Point p) +{ + int y, zoom; + + if(scr->mmio == 0) + return 1; + + t2r4xo(scr, CursorXLo, p.x & 0xFF); + t2r4xo(scr, CursorXHi, (p.x>>8) & 0x0F); + + zoom = (scr->mmio[Zoom] & 0x0F)+1; + y = p.y*zoom; + t2r4xo(scr, CursorYLo, y & 0xFF); + t2r4xo(scr, CursorYHi, (y>>8) & 0x0F); + + return 0; +} + +static void +t2r4curenable(VGAscr* scr) +{ + t2r4enable(scr); + if(scr->mmio == 0) + return; + + /* + * Make sure cursor is off by initialising the cursor + * control to defaults. + */ + t2r4xo(scr, CursorCtl, 0x00); + + /* + * Cursor colour 1 (white), + * cursor colour 2 (black). + */ + t2r4xo(scr, CursorR1, Pwhite); + t2r4xo(scr, CursorG1, Pwhite); + t2r4xo(scr, CursorB1, Pwhite); + + t2r4xo(scr, CursorR2, Pblack); + t2r4xo(scr, CursorG2, Pblack); + t2r4xo(scr, CursorB2, Pblack); + + /* + * Load, locate and enable the cursor, 64x64, mode 2. + */ + t2r4curload(scr, &arrow); + t2r4curmove(scr, ZP); + t2r4xo(scr, CursorCtl, CursorMode); +} + +enum { + Flow = 0x08/4, + Busy = 0x0C/4, + BufCtl = 0x20/4, + DeSorg = 0x28/4, + DeDorg = 0x2C/4, + DeSptch = 0x40/4, + DeDptch = 0x44/4, + CmdOpc = 0x50/4, + CmdRop = 0x54/4, + CmdStyle = 0x58/4, + CmdPatrn = 0x5C/4, + CmdClp = 0x60/4, + CmdPf = 0x64/4, + Fore = 0x68/4, + Back = 0x6C/4, + Mask = 0x70/4, + DeKey = 0x74/4, + Lpat = 0x78/4, + Pctrl = 0x7C/4, + Clptl = 0x80/4, + Clpbr = 0x84/4, + XY0 = 0x88/4, + XY1 = 0x8C/4, + XY2 = 0x90/4, + XY3 = 0x94/4, + XY4 = 0x98/4, + Alpha = 0x128/4, + ACtl = 0x16C/4, + + RBaseD = 0x4000/4, +}; + +/* wait until pipeline ready for new command */ +static void +waitforfifo(VGAscr *scr) +{ + int x; + ulong *d; + x = 0; + + d = scr->mmio + RBaseD; + while((d[Busy]&1) && x++ < 1000000) + ; + if(x >= 1000000) /* shouldn't happen */ + iprint("busy %8lux\n", d[Busy]); +} + +/* wait until command has finished executing */ +static void +waitforcmd(VGAscr *scr) +{ + int x; + ulong *d; + x = 0; + + d = scr->mmio + RBaseD; + while((d[Flow]&0x1B) && x++ < 1000000) + ; + if(x >= 1000000) /* shouldn't happen */ + iprint("flow %8lux\n", d[Flow]); +} + +/* wait until memory controller not busy (i.e. wait for writes to flush) */ +static void +waitformem(VGAscr *scr) +{ + int x; + ulong *d; + x = 0; + + d = scr->mmio + RBaseD; + while((d[Flow]&2)&& x++ < 1000000) + ; + if(x >= 1000000) /* shouldn't happen */ + iprint("mem %8lux\n", d[Busy]); +} + +static int +t2r4hwscroll(VGAscr *scr, Rectangle r, Rectangle sr) +{ + int ctl; + Point dp, sp; + ulong *d; + int depth; + + if(r.min.y == sr.min.y){ /* a purely horizontal scroll */ + depth = scr->gscreen->depth; + switch(depth){ + case 32: + /* + * Using the SGI flat panels with the Ticket-to-Ride IV, horizontal + * 32-bit scrolls don't work perfectly on rectangles of width <= 24. + * we bail on a bigger bound for padding. + */ + if(Dx(r) < 32) + return 0; + break; + case 16: + /* + * Using the SGI flat panels with the Ticket-to-Ride IV, horizontal + * 16-bit scrolls don't work perfectly on rectangles of width <= 96. + * we bail on a bigger bound for padding. + */ + if(Dx(r) < 104) + return 0; + break; + } + } + waitformem(scr); + waitforfifo(scr); + d = scr->mmio + RBaseD; + ctl = 0; + if(r.min.x <= sr.min.x){ + dp.x = r.min.x; + sp.x = sr.min.x; + }else{ + ctl |= 2; + dp.x = r.max.x-1; + sp.x = sr.max.x-1; + } + + if(r.min.y < sr.min.y){ + dp.y = r.min.y; + sp.y = sr.min.y; + }else{ + ctl |= 1; + dp.y = r.max.y-1; + sp.y = sr.max.y-1; + } + + d[CmdOpc] = 0x1; /* bitblt */ + d[CmdRop] = 0xC; /* copy source */ + d[CmdStyle] = 0; + d[CmdPatrn] = 0; + d[Fore] = 0; + d[Back] = 0; + + /* writing XY1 executes cmd */ + d[XY3] = ctl; + d[XY0] = (sp.x<<16)|sp.y; + d[XY2] = (Dx(r)<<16)|Dy(r); + d[XY4] = 0; + d[XY1] = (dp.x<<16)|dp.y; + waitforcmd(scr); + + return 1; +} + +static int +t2r4hwfill(VGAscr *scr, Rectangle r, ulong sval) +{ + ulong *d; + + d = scr->mmio + RBaseD; + + waitformem(scr); + waitforfifo(scr); + d[CmdOpc] = 0x1; /* bitblt */ + d[CmdRop] = 0xC; /* copy source */ + d[CmdStyle] = 1; /* use source from Fore register */ + d[CmdPatrn] = 0; /* no stipple */ + d[Fore] = sval; + d[Back] = sval; + + /* writing XY1 executes cmd */ + d[XY3] = 0; + d[XY0] = (r.min.x<<16)|r.min.y; + d[XY2] = (Dx(r)<<16)|Dy(r); + d[XY4] = 0; + d[XY1] = (r.min.x<<16)|r.min.y; + waitforcmd(scr); + + return 1; +} + +static void +t2r4blank(VGAscr *scr, int blank) +{ + uchar x; + + x = t2r4xi(scr, CursorSyncCtl); + x &= ~0x0F; + if(blank) + x |= HsyncLo | VsyncLo; + t2r4xo(scr, CursorSyncCtl, x); +} + +static void +t2r4drawinit(VGAscr *scr) +{ + ulong pitch; + int depth; + int fmt; + ulong *d; + + pitch = Dx(scr->gscreen->r); + depth = scr->gscreen->depth; + + switch(scr->gscreen->chan){ + case RGB16: + fmt = 3; + break; + case XRGB32: + fmt = 2; + break; + case RGB15: + fmt = 1; + break; + default: + scr->fill = nil; + scr->scroll = nil; + return; + } + + d = scr->mmio + RBaseD; + + d[BufCtl] = fmt<<24; + d[DeSorg] = 0; + d[DeDorg] = 0; + d[DeSptch] = (pitch*depth)/8; + d[DeDptch] = (pitch*depth)/8; + d[CmdClp] = 0; /* 2 = inside rectangle */ + d[Mask] = ~0; + d[DeKey] = 0; + d[Clptl] = 0; + d[Clpbr] = 0xFFF0FFF0; + d[Alpha] = 0; + d[ACtl] = 0; + + scr->fill = t2r4hwfill; + scr->scroll = t2r4hwscroll; + scr->blank = t2r4blank; + hwblank = 1; +} + +VGAdev vgat2r4dev = { + "t2r4", + + t2r4enable, + nil, + nil, + t2r4linear, + t2r4drawinit, +}; + +VGAcur vgat2r4cur = { + "t2r4hwgc", + + t2r4curenable, + t2r4curdisable, + t2r4curload, + t2r4curmove, +}; + diff --git a/os/pc/vgatvp3020.c b/os/pc/vgatvp3020.c new file mode 100644 index 00000000..08871d4a --- /dev/null +++ b/os/pc/vgatvp3020.c @@ -0,0 +1,216 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#define Image IMAGE +#include <draw.h> +#include <memdraw.h> +#include <cursor.h> +#include "screen.h" + +/* + * TVP3020 Viewpoint Video Interface Pallette. + * Assumes hooked up to an S3 86C928. + */ +enum { + Index = 0x06, /* Index register */ + Data = 0x07, /* Data register */ +}; + +/* + * Lower 2-bits of indirect DAC register + * addressing. + */ +static ushort dacxreg[4] = { + PaddrW, Pdata, Pixmask, PaddrR +}; + +static uchar +tvp3020io(uchar reg, uchar data) +{ + uchar crt55; + + crt55 = vgaxi(Crtx, 0x55) & 0xFC; + vgaxo(Crtx, 0x55, crt55|((reg>>2) & 0x03)); + vgao(dacxreg[reg & 0x03], data); + + return crt55; +} + +static void +tvp3020xo(uchar index, uchar data) +{ + uchar crt55; + + crt55 = tvp3020io(Index, index); + vgao(dacxreg[Data & 0x03], data); + vgaxo(Crtx, 0x55, crt55); +} + +static void +tvp3020disable(VGAscr*) +{ + uchar r; + + /* + * Disable + * cursor; + * cursor control enable for Bt485 DAC (!); + * the hardware cursor external operation mode. + */ + tvp3020xo(0x06, 0x10); /* Cursor Control Register */ + + r = vgaxi(Crtx, 0x45) & ~0x20; + vgaxo(Crtx, 0x45, r); + + r = vgaxi(Crtx, 0x55) & ~0x20; + vgaxo(Crtx, 0x55, r); +} + +static void +tvp3020enable(VGAscr*) +{ + uchar r; + + /* + * Make sure cursor is off by initialising the cursor + * control to defaults + X-Windows cursor mode. + */ + tvp3020xo(0x06, 0x10); /* Cursor Control Register */ + + /* + * Overscan colour, + * cursor colour 1 (white), + * cursor colour 2 (black). + */ + tvp3020xo(0x20, Pwhite); tvp3020xo(0x21, Pwhite); tvp3020xo(0x22, Pwhite); + tvp3020xo(0x23, Pwhite); tvp3020xo(0x24, Pwhite); tvp3020xo(0x25, Pwhite); + tvp3020xo(0x26, Pblack); tvp3020xo(0x27, Pblack); tvp3020xo(0x28, Pblack); + + /* + * Finally, enable + * the hardware cursor external operation mode; + * cursor control enable for Bt485 DAC (!). + */ + r = vgaxi(Crtx, 0x55)|0x20; + vgaxo(Crtx, 0x55, r); + + r = vgaxi(Crtx, 0x45)|0x20; + vgaxo(Crtx, 0x45, r); +} + +static void +tvp3020load(VGAscr*, Cursor* curs) +{ + uchar p, p0, p1; + int x, y; + + /* + * Make sure cursor is off by initialising the cursor + * control to defaults + X-Windows cursor mode. + */ + tvp3020xo(0x06, 0x10); /* Cursor Control Register */ + + /* + * Initialise the cursor RAM LS and MS address + * (LS must be first). + */ + tvp3020xo(0x08, 0x00); /* Cursor RAM LS Address */ + tvp3020xo(0x09, 0x00); /* Cursor RAM MS Address */ + + /* + * Initialise the 64x64 cursor RAM array. There are 2 planes, + * p0 and p1. Data is written 4 pixels per byte, with p1 the + * MS bit of each pixel. + * The cursor is set in X-Windows mode which gives the following + * truth table: + * p1 p0 colour + * 0 0 underlying pixel colour + * 0 1 underlying pixel colour + * 1 0 cursor colour 1 + * 1 1 cursor colour 2 + * Put the cursor into the top-left of the 64x64 array. + */ + for(y = 0; y < 64; y++){ + for(x = 0; x < 64/8; x++){ + if(x < 16/8 && y < 16){ + p0 = curs->clr[x+y*2]; + p1 = curs->set[x+y*2]; + + p = 0x00; + if(p1 & 0x10) + p |= 0x03; + else if(p0 & 0x10) + p |= 0x02; + if(p1 & 0x20) + p |= 0x0C; + else if(p0 & 0x20) + p |= 0x08; + if(p1 & 0x40) + p |= 0x30; + else if(p0 & 0x40) + p |= 0x20; + if(p1 & 0x80) + p |= 0xC0; + else if(p0 & 0x80) + p |= 0x80; + tvp3020xo(0x0A, p); /* Cursor RAM Data */ + + p = 0x00; + if(p1 & 0x01) + p |= 0x03; + else if(p0 & 0x01) + p |= 0x02; + if(p1 & 0x02) + p |= 0x0C; + else if(p0 & 0x02) + p |= 0x08; + if(p1 & 0x04) + p |= 0x30; + else if(p0 & 0x04) + p |= 0x20; + if(p1 & 0x08) + p |= 0xC0; + else if(p0 & 0x08) + p |= 0x80; + tvp3020xo(0x0A, p); /* Cursor RAM Data */ + } + else{ + tvp3020xo(0x0A, 0x00); /* Cursor RAM Data */ + tvp3020xo(0x0A, 0x00); + } + } + } + + /* + * Initialise the cursor hotpoint + * and enable the cursor. + */ + tvp3020xo(0x04, -curs->offset.x); /* Sprite Origin X */ + tvp3020xo(0x05, -curs->offset.y); /* Sprite Origin Y */ + + tvp3020xo(0x06, 0x40|0x10); /* Cursor Control Register */ +} + +static int +tvp3020move(VGAscr*, Point p) +{ + tvp3020xo(0x00, p.x & 0xFF); /* Cursor Position X LSB */ + tvp3020xo(0x01, (p.x>>8) & 0x0F); /* Cursor Position X MSB */ + tvp3020xo(0x02, p.y & 0xFF); /* Cursor Position Y LSB */ + tvp3020xo(0x03, (p.y>>8) & 0x0F); /* Cursor Position Y MSB */ + + return 0; +} + +VGAcur vgatvp3020cur = { + "tvp3020hwgc", + + tvp3020enable, + tvp3020disable, + tvp3020load, + tvp3020move, +}; diff --git a/os/pc/vgatvp3026.c b/os/pc/vgatvp3026.c new file mode 100644 index 00000000..1b97d1bf --- /dev/null +++ b/os/pc/vgatvp3026.c @@ -0,0 +1,189 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#define Image IMAGE +#include <draw.h> +#include <memdraw.h> +#include <cursor.h> +#include "screen.h" + +/* + * TVP3026 Viewpoint Video Interface Pallette. + * Assumes hooked up to an S3 Vision968. + */ +enum { + Index = 0x00, /* Index */ + Data = 0x0A, /* Data */ + + CaddrW = 0x04, /* Colour Write Address */ + Cdata = 0x05, /* Colour Data */ + + Cctl = 0x09, /* Direct Cursor Control */ + Cram = 0x0B, /* Cursor Ram Data */ + Cxlsb = 0x0C, /* Cursor X LSB */ + Cxmsb = 0x0D, /* Cursor X MSB */ + Cylsb = 0x0E, /* Cursor Y LSB */ + Cymsb = 0x0F, /* Cursor Y MSB */ + + Icctl = 0x06, /* Indirect Cursor Control */ +}; + +/* + * Lower 2-bits of indirect DAC register + * addressing. + */ +static ushort dacxreg[4] = { + PaddrW, Pdata, Pixmask, PaddrR +}; + +static uchar +tvp3026io(uchar reg, uchar data) +{ + uchar crt55; + + crt55 = vgaxi(Crtx, 0x55) & 0xFC; + vgaxo(Crtx, 0x55, crt55|((reg>>2) & 0x03)); + vgao(dacxreg[reg & 0x03], data); + + return crt55; +} + +static void +tvp3026o(uchar reg, uchar data) +{ + uchar crt55; + + crt55 = tvp3026io(reg, data); + vgaxo(Crtx, 0x55, crt55); +} + +void +tvp3026xo(uchar index, uchar data) +{ + uchar crt55; + + crt55 = tvp3026io(Index, index); + vgaxo(Crtx, 0x55, crt55|((Data>>2) & 0x03)); + vgao(dacxreg[Data & 0x03], data); + vgaxo(Crtx, 0x55, crt55); +} + +static void +tvp3026disable(VGAscr*) +{ + tvp3026xo(Icctl, 0x90); + tvp3026o(Cctl, 0x00); +} + +static void +tvp3026enable(VGAscr*) +{ + /* + * Make sure cursor is off and direct control enabled. + */ + tvp3026xo(Icctl, 0x90); + tvp3026o(Cctl, 0x00); + + /* + * Overscan colour, + * cursor colour 1 (white), + * cursor colour 2, 3 (black). + */ + tvp3026o(CaddrW, 0x00); + tvp3026o(Cdata, Pwhite); tvp3026o(Cdata, Pwhite); tvp3026o(Cdata, Pwhite); + tvp3026o(Cdata, Pwhite); tvp3026o(Cdata, Pwhite); tvp3026o(Cdata, Pwhite); + tvp3026o(Cdata, Pblack); tvp3026o(Cdata, Pblack); tvp3026o(Cdata, Pblack); + tvp3026o(Cdata, Pblack); tvp3026o(Cdata, Pblack); tvp3026o(Cdata, Pblack); + + /* + * Enable the cursor in 3-colour mode. + */ + tvp3026o(Cctl, 0x01); +} + +static void +tvp3026load(VGAscr* scr, Cursor* curs) +{ + int x, y; + + /* + * Make sure cursor is off by initialising the cursor + * control to defaults. + * Write to the indirect control register to make sure + * direct register is enabled and upper 2 bits of cursor + * RAM address are 0. + * The LSBs of the cursor RAM address are in PaddrW. + */ + tvp3026xo(Icctl, 0x90); + tvp3026o(Cctl, 0x00); + vgao(PaddrW, 0x00); + + /* + * Initialise the 64x64 cursor RAM array. There are 2 planes, + * p0 and p1. Data is written 8 pixels per byte, with p0 in the + * first 512 bytes of the array and p1 in the second. + * The cursor is set in 3-colour mode which gives the following + * truth table: + * p1 p0 colour + * 0 0 transparent + * 0 1 cursor colour 0 + * 1 0 cursor colour 1 + * 1 1 cursor colour 2 + * Put the cursor into the top-left of the 64x64 array. + * The 0,0 cursor point is bottom-right, so positioning will + * have to take that into account. + */ + for(y = 0; y < 64; y++){ + for(x = 0; x < 64/8; x++){ + if(x < 16/8 && y < 16) + tvp3026o(Cram, curs->clr[x+y*2]); + else + tvp3026o(Cram, 0x00); + } + } + for(y = 0; y < 64; y++){ + for(x = 0; x < 64/8; x++){ + if(x < 16/8 && y < 16) + tvp3026o(Cram, curs->set[x+y*2]); + else + tvp3026o(Cram, 0x00); + } + } + + /* + * Initialise the cursor hotpoint + * and enable the cursor in 3-colour mode. + */ + scr->offset.x = 64+curs->offset.x; + scr->offset.y = 64+curs->offset.y; + tvp3026o(Cctl, 0x01); +} + +static int +tvp3026move(VGAscr* scr, Point p) +{ + int x, y; + + x = p.x+scr->offset.x; + y = p.y+scr->offset.y; + + tvp3026o(Cxlsb, x & 0xFF); + tvp3026o(Cxmsb, (x>>8) & 0x0F); + tvp3026o(Cylsb, y & 0xFF); + tvp3026o(Cymsb, (y>>8) & 0x0F); + + return 0; +} + +VGAcur vgatvp3026cur = { + "tvp3026hwgc", + + tvp3026enable, + tvp3026disable, + tvp3026load, + tvp3026move, +}; diff --git a/os/pc/vgavmware.c b/os/pc/vgavmware.c new file mode 100644 index 00000000..4cf09e48 --- /dev/null +++ b/os/pc/vgavmware.c @@ -0,0 +1,386 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#define Image IMAGE +#include <draw.h> +#include <memdraw.h> +#include <cursor.h> +#include "screen.h" + +enum { + PCIVMWARE = 0x15AD, /* PCI VID */ + + VMWARE1 = 0x0710, /* PCI DID */ + VMWARE2 = 0x0405, +}; + +enum { + Rid = 0, + Renable, + Rwidth, + Rheight, + Rmaxwidth, + + Rmaxheight, + Rdepth, + Rbpp, + Rpseudocolor, + Rrmask, + + Rgmask, + Rbmask, + Rbpl, + Rfbstart, + Rfboffset, + + Rfbmaxsize, + Rfbsize, + Rcap, + Rmemstart, + Rmemsize, + + Rconfigdone, + Rsync, + Rbusy, + Rguestid, + Rcursorid, + + Rcursorx, + Rcursory, + Rcursoron, + Nreg, + + Crectfill = 1<<0, + Crectcopy = 1<<1, + Crectpatfill = 1<<2, + Coffscreen = 1<<3, + Crasterop = 1<<4, + Ccursor = 1<<5, + Ccursorbypass = 1<<6, + Ccursorbypass2 = 1<<7, + C8bitemulation = 1<<8, + Calphacursor = 1<<9, + + FifoMin = 0, + FifoMax = 1, + FifoNextCmd = 2, + FifoStop = 3, + FifoUser = 4, + + Xupdate = 1, + Xrectfill = 2, + Xrectcopy = 3, + Xdefinebitmap = 4, + Xdefinebitmapscanline = 5, + Xdefinepixmap = 6, + Xdefinepixmapscanline = 7, + Xrectbitmapfill = 8, + Xrectpixmapfill = 9, + Xrectbitmapcopy = 10, + Xrectpixmapcopy = 11, + Xfreeobject = 12, + Xrectropfill = 13, + Xrectropcopy = 14, + Xrectropbitmapfill = 15, + Xrectroppixmapfill = 16, + Xrectropbitmapcopy = 17, + Xrectroppixmapcopy = 18, + Xdefinecursor = 19, + Xdisplaycursor = 20, + Xmovecursor = 21, + Xdefinealphacursor = 22, + Xcmdmax = 23, + + CursorOnHide = 0, + CursorOnShow = 1, + CursorOnRemoveFromFb = 2, + CursorOnRestoreToFb = 3, + + Rpalette = 1024, +}; + +typedef struct Vmware Vmware; +struct Vmware { + ulong fb; + + ulong ra; + ulong rd; + + ulong r[Nreg]; + ulong *mmio; + ulong mmiosize; + + char chan[32]; + int depth; +}; + +Vmware xvm; +Vmware *vm=&xvm; + +static ulong +vmrd(Vmware *vm, int i) +{ + outl(vm->ra, i); + return inl(vm->rd); +} + +static void +vmwr(Vmware *vm, int i, ulong v) +{ + outl(vm->ra, i); + outl(vm->rd, v); +} + +static void +vmwait(Vmware *vm) +{ + vmwr(vm, Rsync, 1); + while(vmrd(vm, Rbusy)) + ; +} + +static ulong +vmwarelinear(VGAscr* scr, int* size, int* align) +{ + char err[64]; + ulong aperture, oaperture; + int osize, oapsize, wasupamem; + Pcidev *p; + + osize = *size; + oaperture = scr->aperture; + oapsize = scr->apsize; + wasupamem = scr->isupamem; + + p = pcimatch(nil, PCIVMWARE, 0); + if(p == nil) + error("no vmware card found"); + + switch(p->did){ + default: + snprint(err, sizeof err, "unknown vmware id %.4ux", p->did); + error(err); + + case VMWARE1: + vm->ra = 0x4560; + vm->rd = 0x4560+4; + break; + + case VMWARE2: + vm->ra = p->mem[0].bar&~3; + vm->rd = vm->ra + 1; + } + + aperture = (ulong)(vmrd(vm, Rfbstart)); + *size = vmrd(vm, Rfbsize); + + if(wasupamem) + upafree(oaperture, oapsize); + scr->isupamem = 0; + + aperture = upamalloc(aperture, *size, *align); + if(aperture == 0){ + if(wasupamem && upamalloc(oaperture, oapsize, 0)) + scr->isupamem = 1; + }else + scr->isupamem = 1; + + if(oaperture && aperture != oaperture) + print("warning (BUG): redefinition of aperture does not change vmwarescreen segment\n"); + addvgaseg("vmwarescreen", aperture, osize); + + return aperture; +} + +static void +vmfifowr(Vmware *vm, ulong v) +{ + ulong *mm; + + mm = vm->mmio; + if(mm == nil){ + iprint("!"); + return; + } + + if(mm[FifoNextCmd]+sizeof(ulong) == mm[FifoStop] + || (mm[FifoNextCmd]+sizeof(ulong) == mm[FifoMax] + && mm[FifoStop] == mm[FifoMin])) + vmwait(vm); + + mm[mm[FifoNextCmd]/sizeof(ulong)] = v; + + /* must do this way so mm[FifoNextCmd] is never mm[FifoMax] */ + v = mm[FifoNextCmd] + sizeof(ulong); + if(v == mm[FifoMax]) + v = mm[FifoMin]; + mm[FifoNextCmd] = v; +} + +static void +vmwareflush(VGAscr*, Rectangle r) +{ + if(vm->mmio == nil) + return; + + vmfifowr(vm, Xupdate); + vmfifowr(vm, r.min.x); + vmfifowr(vm, r.min.y); + vmfifowr(vm, r.max.x-r.min.x); + vmfifowr(vm, r.max.y-r.min.y); + vmwait(vm); +} + +static void +vmwareload(VGAscr*, Cursor *c) +{ + int i; + ulong clr, set; + ulong and[16]; + ulong xor[16]; + + if(vm->mmio == nil) + return; + vmfifowr(vm, Xdefinecursor); + vmfifowr(vm, 1); /* cursor id */ + vmfifowr(vm, -c->offset.x); + vmfifowr(vm, -c->offset.y); + + vmfifowr(vm, 16); /* width */ + vmfifowr(vm, 16); /* height */ + vmfifowr(vm, 1); /* depth for and mask */ + vmfifowr(vm, 1); /* depth for xor mask */ + + for(i=0; i<16; i++){ + clr = (c->clr[i*2+1]<<8) | c->clr[i*2]; + set = (c->set[i*2+1]<<8) | c->set[i*2]; + and[i] = ~(clr|set); /* clr and set pixels => black */ + xor[i] = clr&~set; /* clr pixels => white */ + } + for(i=0; i<16; i++) + vmfifowr(vm, and[i]); + for(i=0; i<16; i++) + vmfifowr(vm, xor[i]); + + vmwait(vm); +} + +static int +vmwaremove(VGAscr*, Point p) +{ + vmwr(vm, Rcursorid, 1); + vmwr(vm, Rcursorx, p.x); + vmwr(vm, Rcursory, p.y); + vmwr(vm, Rcursoron, CursorOnShow); + return 0; +} + +static void +vmwaredisable(VGAscr*) +{ + vmwr(vm, Rcursorid, 1); + vmwr(vm, Rcursoron, CursorOnHide); +} + +static void +vmwareenable(VGAscr*) +{ + vmwr(vm, Rcursorid, 1); + vmwr(vm, Rcursoron, CursorOnShow); +} + +static void +vmwareblank(int) +{ +} + +static int +vmwarescroll(VGAscr*, Rectangle r, Rectangle sr) +{ + if(vm->mmio == nil) + return 0; + vmfifowr(vm, Xrectcopy); + vmfifowr(vm, sr.min.x); + vmfifowr(vm, sr.min.y); + vmfifowr(vm, r.min.x); + vmfifowr(vm, r.min.y); + vmfifowr(vm, Dx(r)); + vmfifowr(vm, Dy(r)); + vmwait(vm); + return 1; +} + +static int +vmwarefill(VGAscr*, Rectangle r, ulong sval) +{ + if(vm->mmio == nil) + return 0; + vmfifowr(vm, Xrectfill); + vmfifowr(vm, sval); + vmfifowr(vm, r.min.x); + vmfifowr(vm, r.min.y); + vmfifowr(vm, r.max.x-r.min.x); + vmfifowr(vm, r.max.y-r.min.y); + vmwait(vm); + return 1; +} + +static void +vmwaredrawinit(VGAscr *scr) +{ + ulong offset; + ulong mmiobase, mmiosize; + + if(scr->mmio==nil){ + mmiobase = vmrd(vm, Rmemstart); + if(mmiobase == 0) + return; + mmiosize = vmrd(vm, Rmemsize); + scr->mmio = KADDR(upamalloc(mmiobase, mmiosize, 0)); + vm->mmio = scr->mmio; + vm->mmiosize = mmiosize; + if(scr->mmio == nil) + return; + addvgaseg("vmwaremmio", mmiobase, mmiosize); + } + + scr->mmio[FifoMin] = 4*sizeof(ulong); + scr->mmio[FifoMax] = vm->mmiosize; + scr->mmio[FifoNextCmd] = 4*sizeof(ulong); + scr->mmio[FifoStop] = 4*sizeof(ulong); + vmwr(vm, Rconfigdone, 1); + + scr->scroll = vmwarescroll; + scr->fill = vmwarefill; + + offset = vmrd(vm, Rfboffset); + scr->gscreendata->bdata += offset; +} + +VGAdev vgavmwaredev = { + "vmware", + + 0, + 0, + 0, + vmwarelinear, + vmwaredrawinit, + 0, + 0, + 0, + vmwareflush, +}; + +VGAcur vgavmwarecur = { + "vmwarehwgc", + + vmwareenable, + vmwaredisable, + vmwareload, + vmwaremove, +}; diff --git a/os/pc/vgax.c b/os/pc/vgax.c new file mode 100644 index 00000000..c765508e --- /dev/null +++ b/os/pc/vgax.c @@ -0,0 +1,102 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#define Image IMAGE +#include <draw.h> +#include <memdraw.h> +#include <cursor.h> +#include "screen.h" + +static Lock vgaxlock; /* access to index registers */ + +int +vgaxi(long port, uchar index) +{ + uchar data; + + ilock(&vgaxlock); + switch(port){ + + case Seqx: + case Crtx: + case Grx: + outb(port, index); + data = inb(port+1); + break; + + case Attrx: + /* + * Allow processor access to the colour + * palette registers. Writes to Attrx must + * be preceded by a read from Status1 to + * initialise the register to point to the + * index register and not the data register. + * Processor access is allowed by turning + * off bit 0x20. + */ + inb(Status1); + if(index < 0x10){ + outb(Attrx, index); + data = inb(Attrx+1); + inb(Status1); + outb(Attrx, 0x20|index); + } + else{ + outb(Attrx, 0x20|index); + data = inb(Attrx+1); + } + break; + + default: + iunlock(&vgaxlock); + return -1; + } + iunlock(&vgaxlock); + + return data & 0xFF; +} + +int +vgaxo(long port, uchar index, uchar data) +{ + ilock(&vgaxlock); + switch(port){ + + case Seqx: + case Crtx: + case Grx: + /* + * We could use an outport here, but some chips + * (e.g. 86C928) have trouble with that for some + * registers. + */ + outb(port, index); + outb(port+1, data); + break; + + case Attrx: + inb(Status1); + if(index < 0x10){ + outb(Attrx, index); + outb(Attrx, data); + inb(Status1); + outb(Attrx, 0x20|index); + } + else{ + outb(Attrx, 0x20|index); + outb(Attrx, data); + } + break; + + default: + iunlock(&vgaxlock); + return -1; + } + iunlock(&vgaxlock); + + return 0; +} diff --git a/os/pc/wavelan.c b/os/pc/wavelan.c new file mode 100644 index 00000000..0916f460 --- /dev/null +++ b/os/pc/wavelan.c @@ -0,0 +1,1268 @@ +/* + Lucent Wavelan IEEE 802.11 pcmcia. + There is almost no documentation for the card. + the driver is done using both the FreeBSD, Linux and + original Plan 9 drivers as `documentation'. + + Has been used with the card plugged in during all up time. + no cards removals/insertions yet. + + For known BUGS see the comments below. Besides, + the driver keeps interrupts disabled for just too + long. When it gets robust, locks should be revisited. + + BUGS: check endian, alignment and mem/io issues; + multicast; + receive watchdog interrupts. + TODO: automatic power management; + improve locking. + */ +#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/netif.h" +#include "etherif.h" +#include "wavelan.h" + +enum +{ + MSperTick= 50, /* ms between ticks of kproc */ +}; + +/* + * When we're using a PCI device and memory-mapped I/O, + * the registers are spaced out as though each takes 32 bits, + * even though they are only 16-bit registers. Thus, + * ctlr->mmb[reg] is the right way to access register reg, + * even though a priori you'd expect to use ctlr->mmb[reg/2]. + */ +void +csr_outs(Ctlr *ctlr, int reg, ushort arg) +{ + if(ctlr->mmb) + ctlr->mmb[reg] = arg; + else + outs(ctlr->iob+reg, arg); +} + +ushort +csr_ins(Ctlr *ctlr, int reg) +{ + if(ctlr->mmb) + return ctlr->mmb[reg]; + else + return ins(ctlr->iob+reg); +} + +static void +csr_ack(Ctlr *ctlr, int ev) +{ + csr_outs(ctlr, WR_EvAck, ev); +} + +static void +csr_inss(Ctlr *ctlr, int reg, void *dat, int ndat) +{ + ushort *rp, *wp; + + if(ctlr->mmb){ + rp = &ctlr->mmb[reg]; + wp = dat; + while(ndat-- > 0) + *wp++ = *rp; + }else + inss(ctlr->iob+reg, dat, ndat); +} + +static void +csr_outss(Ctlr *ctlr, int reg, void *dat, int ndat) +{ + ushort *rp, *wp; + + if(ctlr->mmb){ + rp = dat; + wp = &ctlr->mmb[reg]; + while(ndat-- > 0) + *wp = *rp++; + }else + outss(ctlr->iob+reg, dat, ndat); +} + +// w_... routines do not ilock the Ctlr and should +// be called locked. + +void +w_intdis(Ctlr* ctlr) +{ + csr_outs(ctlr, WR_IntEna, 0); + csr_ack(ctlr, 0xffff); +} + +static void +w_intena(Ctlr* ctlr) +{ + csr_outs(ctlr, WR_IntEna, WEvs); +} + +int +w_cmd(Ctlr *ctlr, ushort cmd, ushort arg) +{ + int i, rc; + + for(i=0; i<WTmOut; i++) + if((csr_ins(ctlr, WR_Cmd)&WCmdBusy) == 0) + break; + if(i==WTmOut){ + print("#l%d: issuing cmd %.4ux: %.4ux\n", ctlr->ctlrno, cmd, csr_ins(ctlr, WR_Cmd)); + return -1; + } + + csr_outs(ctlr, WR_Parm0, arg); + csr_outs(ctlr, WR_Cmd, cmd); + + for(i=0; i<WTmOut; i++) + if(csr_ins(ctlr, WR_EvSts)&WCmdEv) + break; + if(i==WTmOut){ + /* + * WCmdIni can take a really long time. + */ + enum { IniTmOut = 2000 }; + for(i=0; i<IniTmOut; i++){ + if(csr_ins(ctlr, WR_EvSts)&WCmdEv) + break; + microdelay(100); + } + if(i < IniTmOut) + if(0) print("#l%d: long cmd %.4ux %d\n", ctlr->ctlrno, cmd, i); + if(i == IniTmOut){ + print("#l%d: execing cmd %.4ux: %.4ux\n", ctlr->ctlrno, cmd, csr_ins(ctlr, WR_EvSts)); + return -1; + } + } + rc = csr_ins(ctlr, WR_Sts); + csr_ack(ctlr, WCmdEv); + + if((rc&WCmdMsk) != (cmd&WCmdMsk)){ + print("#l%d: cmd %.4ux: status %.4ux\n", ctlr->ctlrno, cmd, rc); + return -1; + } + if(rc&WResSts){ + /* + * Don't print; this happens on every WCmdAccWr for some reason. + */ + if(0) print("#l%d: cmd %.4ux: status %.4ux\n", ctlr->ctlrno, cmd, rc); + return -1; + } + return 0; +} + +static int +w_seek(Ctlr* ctlr, ushort id, ushort offset, int chan) +{ + int i, rc; + static ushort sel[] = { WR_Sel0, WR_Sel1 }; + static ushort off[] = { WR_Off0, WR_Off1 }; + + if(chan != 0 && chan != 1) + panic("wavelan: bad chan\n"); + csr_outs(ctlr, sel[chan], id); + csr_outs(ctlr, off[chan], offset); + for (i=0; i<WTmOut; i++){ + rc = csr_ins(ctlr, off[chan]); + if((rc & (WBusyOff|WErrOff)) == 0) + return 0; + } + return -1; +} + +int +w_inltv(Ctlr* ctlr, Wltv* ltv) +{ + int len; + ushort code; + + if(w_cmd(ctlr, WCmdAccRd, ltv->type)){ + DEBUG("wavelan: access read failed\n"); + return -1; + } + if(w_seek(ctlr,ltv->type,0,1)){ + DEBUG("wavelan: seek failed\n"); + return -1; + } + len = csr_ins(ctlr, WR_Data1); + if(len > ltv->len) + return -1; + ltv->len = len; + if((code=csr_ins(ctlr, WR_Data1)) != ltv->type){ + USED(code); + DEBUG("wavelan: type %x != code %x\n",ltv->type,code); + return -1; + } + if(ltv->len > 0) + csr_inss(ctlr, WR_Data1, <v->val, ltv->len-1); + + return 0; +} + +static void +w_outltv(Ctlr* ctlr, Wltv* ltv) +{ + if(w_seek(ctlr,ltv->type, 0, 1)) + return; + csr_outss(ctlr, WR_Data1, ltv, ltv->len+1); + w_cmd(ctlr, WCmdAccWr, ltv->type); +} + +void +ltv_outs(Ctlr* ctlr, int type, ushort val) +{ + Wltv ltv; + + ltv.len = 2; + ltv.type = type; + ltv.val = val; + w_outltv(ctlr, <v); +} + +int +ltv_ins(Ctlr* ctlr, int type) +{ + Wltv ltv; + + ltv.len = 2; + ltv.type = type; + ltv.val = 0; + if(w_inltv(ctlr, <v)) + return -1; + return ltv.val; +} + +static void +ltv_outstr(Ctlr* ctlr, int type, char* val) +{ + Wltv ltv; + int len; + + len = strlen(val); + if(len > sizeof(ltv.s)) + len = sizeof(ltv.s); + memset(<v, 0, sizeof(ltv)); + ltv.len = (sizeof(ltv.type)+sizeof(ltv.slen)+sizeof(ltv.s))/2; + ltv.type = type; + +// This should be ltv.slen = len; according to Axel Belinfante + ltv.slen = len; + + strncpy(ltv.s, val, len); + w_outltv(ctlr, <v); +} + +static char Unkname[] = "who knows"; +static char Nilname[] = "card does not tell"; + +static char* +ltv_inname(Ctlr* ctlr, int type) +{ + static Wltv ltv; + int len; + + memset(<v,0,sizeof(ltv)); + ltv.len = WNameLen/2+2; + ltv.type = type; + if(w_inltv(ctlr, <v)) + return Unkname; + len = ltv.slen; + if(len == 0 || ltv.s[0] == 0) + return Nilname; + if(len >= sizeof ltv.s) + len = sizeof ltv.s - 1; + ltv.s[len] = '\0'; + return ltv.s; +} + +static int +w_read(Ctlr* ctlr, int type, int off, void* buf, ulong len) +{ + if(w_seek(ctlr, type, off, 1)){ + DEBUG("wavelan: w_read: seek failed"); + return 0; + } + csr_inss(ctlr, WR_Data1, buf, len/2); + + return len; +} + +static int +w_write(Ctlr* ctlr, int type, int off, void* buf, ulong len) +{ + int tries; + + for (tries=0; tries < WTmOut; tries++){ + if(w_seek(ctlr, type, off, 0)){ + DEBUG("wavelan: w_write: seek failed\n"); + return 0; + } + + csr_outss(ctlr, WR_Data0, buf, len/2); + + csr_outs(ctlr, WR_Data0, 0xdead); + csr_outs(ctlr, WR_Data0, 0xbeef); + if(w_seek(ctlr, type, off + len, 0)){ + DEBUG("wavelan: write seek failed\n"); + return 0; + } + if(csr_ins(ctlr, WR_Data0) == 0xdead) + if(csr_ins(ctlr, WR_Data0) == 0xbeef) + return len; + DEBUG("wavelan: Hermes bug byte.\n"); + return 0; + } + DEBUG("wavelan: tx timeout\n"); + return 0; +} + +static int +w_alloc(Ctlr* ctlr, int len) +{ + int rc; + int i,j; + + if(w_cmd(ctlr, WCmdMalloc, len)==0) + for (i = 0; i<WTmOut; i++) + if(csr_ins(ctlr, WR_EvSts) & WAllocEv){ + csr_ack(ctlr, WAllocEv); + rc=csr_ins(ctlr, WR_Alloc); + if(w_seek(ctlr, rc, 0, 0)) + return -1; + len = len/2; + for (j=0; j<len; j++) + csr_outs(ctlr, WR_Data0, 0); + return rc; + } + return -1; +} + +static int +w_enable(Ether* ether) +{ + Wltv ltv; + Ctlr* ctlr = (Ctlr*) ether->ctlr; + + if(!ctlr) + return -1; + + w_intdis(ctlr); + w_cmd(ctlr, WCmdDis, 0); + w_intdis(ctlr); + if(w_cmd(ctlr, WCmdIni, 0)) + return -1; + w_intdis(ctlr); + + ltv_outs(ctlr, WTyp_Tick, 8); + ltv_outs(ctlr, WTyp_MaxLen, ctlr->maxlen); + ltv_outs(ctlr, WTyp_Ptype, ctlr->ptype); + ltv_outs(ctlr, WTyp_CreateIBSS, ctlr->createibss); + ltv_outs(ctlr, WTyp_RtsThres, ctlr->rtsthres); + ltv_outs(ctlr, WTyp_TxRate, ctlr->txrate); + ltv_outs(ctlr, WTyp_ApDens, ctlr->apdensity); + ltv_outs(ctlr, WTyp_PMWait, ctlr->pmwait); + ltv_outs(ctlr, WTyp_PM, ctlr->pmena); + if(*ctlr->netname) + ltv_outstr(ctlr, WTyp_NetName, ctlr->netname); + if(*ctlr->wantname) + ltv_outstr(ctlr, WTyp_WantName, ctlr->wantname); + ltv_outs(ctlr, WTyp_Chan, ctlr->chan); + if(*ctlr->nodename) + ltv_outstr(ctlr, WTyp_NodeName, ctlr->nodename); + ltv.len = 4; + ltv.type = WTyp_Mac; + memmove(ltv.addr, ether->ea, Eaddrlen); + w_outltv(ctlr, <v); + + ltv_outs(ctlr, WTyp_Prom, (ether->prom?1:0)); + + if(ctlr->hascrypt && ctlr->crypt){ + ltv_outs(ctlr, WTyp_Crypt, ctlr->crypt); + ltv_outs(ctlr, WTyp_TxKey, ctlr->txkey); + w_outltv(ctlr, &ctlr->keys); + ltv_outs(ctlr, WTyp_XClear, ctlr->xclear); + } + + // BUG: set multicast addresses + + if(w_cmd(ctlr, WCmdEna, 0)){ + DEBUG("wavelan: Enable failed"); + return -1; + } + ctlr->txdid = w_alloc(ctlr, 1518 + sizeof(WFrame) + 8); + ctlr->txmid = w_alloc(ctlr, 1518 + sizeof(WFrame) + 8); + if(ctlr->txdid == -1 || ctlr->txmid == -1) + DEBUG("wavelan: alloc failed"); + ctlr->txbusy = 0; + w_intena(ctlr); + return 0; +} + +static void +w_rxdone(Ether* ether) +{ + Ctlr* ctlr = (Ctlr*) ether->ctlr; + int len, sp; + WFrame f; + Block* bp=0; + Etherpkt* ep; + + sp = csr_ins(ctlr, WR_RXId); + len = w_read(ctlr, sp, 0, &f, sizeof(f)); + if(len == 0){ + DEBUG("wavelan: read frame error\n"); + goto rxerror; + } + if(f.sts&WF_Err){ + goto rxerror; + } + switch(f.sts){ + case WF_1042: + case WF_Tunnel: + case WF_WMP: + len = f.dlen + WSnapHdrLen; + bp = iallocb(ETHERHDRSIZE + len + 2); + if(!bp) + goto rxerror; + ep = (Etherpkt*) bp->wp; + memmove(ep->d, f.addr1, Eaddrlen); + memmove(ep->s, f.addr2, Eaddrlen); + memmove(ep->type,&f.type,2); + bp->wp += ETHERHDRSIZE; + if(w_read(ctlr, sp, WF_802_11_Off, bp->wp, len+2) == 0){ + DEBUG("wavelan: read 802.11 error\n"); + goto rxerror; + } + bp->wp = bp->rp+(ETHERHDRSIZE+f.dlen); + break; + default: + len = ETHERHDRSIZE + f.dlen + 2; + bp = iallocb(len); + if(!bp) + goto rxerror; + if(w_read(ctlr, sp, WF_802_3_Off, bp->wp, len) == 0){ + DEBUG("wavelan: read 800.3 error\n"); + goto rxerror; + } + bp->wp += len; + } + + ctlr->nrx++; + etheriq(ether,bp,1); + ctlr->signal = ((ctlr->signal*15)+((f.qinfo>>8) & 0xFF))/16; + ctlr->noise = ((ctlr->noise*15)+(f.qinfo & 0xFF))/16; + return; + +rxerror: + freeb(bp); + ctlr->nrxerr++; +} + +static void +w_txstart(Ether* ether) +{ + Etherpkt *pkt; + Ctlr *ctlr; + Block *bp; + int len, off; + + if((ctlr = ether->ctlr) == nil || (ctlr->state & (Attached|Power)) != (Attached|Power) || ctlr->txbusy) + return; + + if((bp = qget(ether->oq)) == nil) + return; + pkt = (Etherpkt*)bp->rp; + + // + // If the packet header type field is > 1500 it is an IP or + // ARP datagram, otherwise it is an 802.3 packet. See RFC1042. + // + memset(&ctlr->txf, 0, sizeof(ctlr->txf)); + if(((pkt->type[0]<<8)|pkt->type[1]) > 1500){ + ctlr->txf.framectl = WF_Data; + memmove(ctlr->txf.addr1, pkt->d, Eaddrlen); + memmove(ctlr->txf.addr2, pkt->s, Eaddrlen); + memmove(ctlr->txf.dstaddr, pkt->d, Eaddrlen); + memmove(ctlr->txf.srcaddr, pkt->s, Eaddrlen); + memmove(&ctlr->txf.type, pkt->type, 2); + bp->rp += ETHERHDRSIZE; + len = BLEN(bp); + off = WF_802_11_Off; + ctlr->txf.dlen = len+ETHERHDRSIZE-WSnapHdrLen; + hnputs((uchar*)&ctlr->txf.dat[0], WSnap0); + hnputs((uchar*)&ctlr->txf.dat[1], WSnap1); + hnputs((uchar*)&ctlr->txf.len, len+ETHERHDRSIZE-WSnapHdrLen); + } + else{ + len = BLEN(bp); + off = WF_802_3_Off; + ctlr->txf.dlen = len; + } + w_write(ctlr, ctlr->txdid, 0, &ctlr->txf, sizeof(ctlr->txf)); + w_write(ctlr, ctlr->txdid, off, bp->rp, len+2); + + if(w_cmd(ctlr, WCmdReclaim|WCmdTx, ctlr->txdid)){ + DEBUG("wavelan: transmit failed\n"); + ctlr->ntxerr++; + } + else{ + ctlr->txbusy = 1; + ctlr->txtmout = 2; + } + freeb(bp); +} + +static void +w_txdone(Ctlr* ctlr, int sts) +{ + ctlr->txbusy = 0; + ctlr->txtmout = 0; + if(sts & WTxErrEv) + ctlr->ntxerr++; + else + ctlr->ntx++; +} + +/* save the stats info in the ctlr struct */ +static void +w_stats(Ctlr* ctlr, int len) +{ + int i, rc; + ulong* p = (ulong*)&ctlr->WStats; + ulong* pend = (ulong*)&ctlr->end; + + for (i = 0; i < len && p < pend; i++){ + rc = csr_ins(ctlr, WR_Data1); + if(rc > 0xf000) + rc = ~rc & 0xffff; + p[i] += rc; + } +} + +/* send the base station scan info to any readers */ +static void +w_scaninfo(Ether* ether, Ctlr *ctlr, int len) +{ + int i, j; + Netfile **ep, *f, **fp; + Block *bp; + WScan *wsp; + ushort *scanbuf; + + scanbuf = malloc(len*2); + if(scanbuf == nil) + return; + + for (i = 0; i < len ; i++) + scanbuf[i] = csr_ins(ctlr, WR_Data1); + + /* calculate number of samples */ + len /= 25; + if(len == 0) + goto out; + + i = ether->scan; + ep = ðer->f[Ntypes]; + for(fp = ether->f; fp < ep && i > 0; fp++){ + f = *fp; + if(f == nil || f->scan == 0) + continue; + + bp = iallocb(100*len); + if(bp == nil) + break; + for(j = 0; j < len; j++){ + wsp = (WScan*)(&scanbuf[j*25]); + if(wsp->ssid_len > 32) + wsp->ssid_len = 32; + bp->wp = (uchar*)seprint((char*)bp->wp, (char*)bp->lim, + "ssid=%.*s;bssid=%E;signal=%d;noise=%d;chan=%d%s\n", + wsp->ssid_len, wsp->ssid, wsp->bssid, wsp->signal, + wsp->noise, wsp->chan, (wsp->capinfo&(1<<4))?";wep":""); + } + qpass(f->in, bp); + i--; + } +out: + free(scanbuf); +} + +static int +w_info(Ether *ether, Ctlr* ctlr) +{ + int sp; + Wltv ltv; + + sp = csr_ins(ctlr, WR_InfoId); + ltv.len = ltv.type = 0; + w_read(ctlr, sp, 0, <v, 4); + ltv.len--; + switch(ltv.type){ + case WTyp_Stats: + w_stats(ctlr, ltv.len); + return 0; + case WTyp_Scan: + w_scaninfo(ether, ctlr, ltv.len); + return 0; + } + return -1; +} + +/* set scanning interval */ +static void +w_scanbs(void *a, uint secs) +{ + Ether *ether = a; + Ctlr* ctlr = (Ctlr*) ether->ctlr; + + ctlr->scanticks = secs*(1000/MSperTick); +} + +static void +w_intr(Ether *ether) +{ + int rc, txid; + Ctlr* ctlr = (Ctlr*) ether->ctlr; + + if((ctlr->state & Power) == 0) + return; + + if((ctlr->state & Attached) == 0){ + csr_ack(ctlr, 0xffff); + csr_outs(ctlr, WR_IntEna, 0); + return; + } + + rc = csr_ins(ctlr, WR_EvSts); + csr_ack(ctlr, ~WEvs); // Not interested in them + if(rc & WRXEv){ + w_rxdone(ether); + csr_ack(ctlr, WRXEv); + } + if(rc & WTXEv){ + w_txdone(ctlr, rc); + csr_ack(ctlr, WTXEv); + } + if(rc & WAllocEv){ + ctlr->nalloc++; + txid = csr_ins(ctlr, WR_Alloc); + csr_ack(ctlr, WAllocEv); + if(txid == ctlr->txdid){ + if((rc & WTXEv) == 0) + w_txdone(ctlr, rc); + } + } + if(rc & WInfoEv){ + ctlr->ninfo++; + w_info(ether, ctlr); + csr_ack(ctlr, WInfoEv); + } + if(rc & WTxErrEv){ + w_txdone(ctlr, rc); + csr_ack(ctlr, WTxErrEv); + } + if(rc & WIDropEv){ + ctlr->nidrop++; + csr_ack(ctlr, WIDropEv); + } + w_txstart(ether); +} + +// Watcher to ensure that the card still works properly and +// to request WStats updates once a minute. +// BUG: it runs much more often, see the comment below. + +static void +w_timer(void* arg) +{ + Ether* ether = (Ether*) arg; + Ctlr* ctlr = (Ctlr*)ether->ctlr; + + ctlr->timerproc = up; + for(;;){ + tsleep(&up->sleep, return0, 0, MSperTick); + ctlr = (Ctlr*)ether->ctlr; + if(ctlr == 0) + break; + if((ctlr->state & (Attached|Power)) != (Attached|Power)) + continue; + ctlr->ticks++; + + ilock(ctlr); + + // Seems that the card gets frames BUT does + // not send the interrupt; this is a problem because + // I suspect it runs out of receive buffers and + // stops receiving until a transmit watchdog + // reenables the card. + // The problem is serious because it leads to + // poor rtts. + // This can be seen clearly by commenting out + // the next if and doing a ping: it will stop + // receiving (although the icmp replies are being + // issued from the remote) after a few seconds. + // Of course this `bug' could be because I'm reading + // the card frames in the wrong way; due to the + // lack of documentation I cannot know. + + if(csr_ins(ctlr, WR_EvSts)&WEvs){ + ctlr->tickintr++; + w_intr(ether); + } + + if((ctlr->ticks % 10) == 0) { + if(ctlr->txtmout && --ctlr->txtmout == 0){ + ctlr->nwatchdogs++; + w_txdone(ctlr, WTxErrEv); + if(w_enable(ether)){ + DEBUG("wavelan: wdog enable failed\n"); + } + w_txstart(ether); + } + if((ctlr->ticks % 120) == 0) + if(ctlr->txbusy == 0) + w_cmd(ctlr, WCmdEnquire, WTyp_Stats); + if(ctlr->scanticks > 0) + if((ctlr->ticks % ctlr->scanticks) == 0) + if(ctlr->txbusy == 0) + w_cmd(ctlr, WCmdEnquire, WTyp_Scan); + } + iunlock(ctlr); + } + pexit("terminated", 0); +} + +void +w_multicast(void*, uchar*, int) +{ + // BUG: to be added. +} + +void +w_attach(Ether* ether) +{ + Ctlr* ctlr; + char name[64]; + int rc; + + if(ether->ctlr == 0) + return; + + snprint(name, sizeof(name), "#l%dtimer", ether->ctlrno); + ctlr = (Ctlr*) ether->ctlr; + if((ctlr->state & Attached) == 0){ + ilock(ctlr); + rc = w_enable(ether); + iunlock(ctlr); + if(rc == 0){ + ctlr->state |= Attached; + kproc(name, w_timer, ether, 0); + } else + print("#l%d: enable failed\n",ether->ctlrno); + } +} + +void +w_detach(Ether* ether) +{ + Ctlr* ctlr; + char name[64]; + + if(ether->ctlr == nil) + return; + + snprint(name, sizeof(name), "#l%dtimer", ether->ctlrno); + ctlr = (Ctlr*) ether->ctlr; + if(ctlr->state & Attached){ + ilock(ctlr); + w_intdis(ctlr); + if(ctlr->timerproc){ + if(!postnote(ctlr->timerproc, 1, "kill", 0)) + print("timerproc note not posted\n"); + print("w_detach, killing 0x%p\n", ctlr->timerproc); + } + ctlr->state &= ~Attached; + iunlock(ctlr); + } + ether->ctlr = nil; +} + +void +w_power(Ether* ether, int on) +{ + Ctlr *ctlr; + + ctlr = (Ctlr*) ether->ctlr; + ilock(ctlr); +iprint("w_power %d\n", on); + if(on){ + if((ctlr->state & Power) == 0){ + if (wavelanreset(ether, ctlr) < 0){ + iprint("w_power: reset failed\n"); + iunlock(ctlr); + w_detach(ether); + free(ctlr); + return; + } + if(ctlr->state & Attached) + w_enable(ether); + ctlr->state |= Power; + } + }else{ + if(ctlr->state & Power){ + if(ctlr->state & Attached) + w_intdis(ctlr); + ctlr->state &= ~Power; + } + } + iunlock(ctlr); +} + +#define PRINTSTAT(fmt,val) l += snprint(p+l, READSTR-l, (fmt), (val)) +#define PRINTSTR(fmt) l += snprint(p+l, READSTR-l, (fmt)) + +long +w_ifstat(Ether* ether, void* a, long n, ulong offset) +{ + Ctlr *ctlr = (Ctlr*) ether->ctlr; + char *k, *p; + int i, l, txid; + + ether->oerrs = ctlr->ntxerr; + ether->crcs = ctlr->nrxfcserr; + ether->frames = 0; + ether->buffs = ctlr->nrxdropnobuf; + ether->overflows = 0; + + // + // Offset must be zero or there's a possibility the + // new data won't match the previous read. + // + if(n == 0 || offset != 0) + return 0; + + p = malloc(READSTR); + l = 0; + + PRINTSTAT("Signal: %d\n", ctlr->signal-149); + PRINTSTAT("Noise: %d\n", ctlr->noise-149); + PRINTSTAT("SNR: %ud\n", ctlr->signal-ctlr->noise); + PRINTSTAT("Interrupts: %lud\n", ctlr->nints); + PRINTSTAT("Double Interrupts: %lud\n", ctlr->ndoubleint); + PRINTSTAT("TxPackets: %lud\n", ctlr->ntx); + PRINTSTAT("RxPackets: %lud\n", ctlr->nrx); + PRINTSTAT("TxErrors: %lud\n", ctlr->ntxerr); + PRINTSTAT("RxErrors: %lud\n", ctlr->nrxerr); + PRINTSTAT("TxRequests: %lud\n", ctlr->ntxrq); + PRINTSTAT("AllocEvs: %lud\n", ctlr->nalloc); + PRINTSTAT("InfoEvs: %lud\n", ctlr->ninfo); + PRINTSTAT("InfoDrop: %lud\n", ctlr->nidrop); + PRINTSTAT("WatchDogs: %lud\n", ctlr->nwatchdogs); + PRINTSTAT("Ticks: %ud\n", ctlr->ticks); + PRINTSTAT("TickIntr: %ud\n", ctlr->tickintr); + k = ((ctlr->state & Attached) ? "attached" : "not attached"); + PRINTSTAT("Card %s", k); + k = ((ctlr->state & Power) ? "on" : "off"); + PRINTSTAT(", power %s", k); + k = ((ctlr->txbusy)? ", txbusy" : ""); + PRINTSTAT("%s\n", k); + + if(ctlr->hascrypt){ + PRINTSTR("Keys: "); + for (i = 0; i < WNKeys; i++){ + if(ctlr->keys.keys[i].len == 0) + PRINTSTR("none "); + else if(SEEKEYS == 0) + PRINTSTR("set "); + else + PRINTSTAT("%s ", ctlr->keys.keys[i].dat); + } + PRINTSTR("\n"); + } + + // real card stats + ilock(ctlr); + PRINTSTR("\nCard stats: \n"); + PRINTSTAT("Status: %ux\n", csr_ins(ctlr, WR_Sts)); + PRINTSTAT("Event status: %ux\n", csr_ins(ctlr, WR_EvSts)); + i = ltv_ins(ctlr, WTyp_Ptype); + PRINTSTAT("Port type: %d\n", i); + PRINTSTAT("Transmit rate: %d\n", ltv_ins(ctlr, WTyp_TxRate)); + PRINTSTAT("Current Transmit rate: %d\n", + ltv_ins(ctlr, WTyp_CurTxRate)); + PRINTSTAT("Channel: %d\n", ltv_ins(ctlr, WTyp_Chan)); + PRINTSTAT("AP density: %d\n", ltv_ins(ctlr, WTyp_ApDens)); + PRINTSTAT("Promiscuous mode: %d\n", ltv_ins(ctlr, WTyp_Prom)); + if(i == WPTypeAdHoc) + PRINTSTAT("SSID name: %s\n", ltv_inname(ctlr, WTyp_NetName)); + else { + Wltv ltv; + PRINTSTAT("Current name: %s\n", ltv_inname(ctlr, WTyp_CurName)); + ltv.type = WTyp_BaseID; + ltv.len = 4; + if(w_inltv(ctlr, <v)) + print("#l%d: unable to read base station mac addr\n", ether->ctlrno); + l += snprint(p+l, READSTR-l, "Base station: %2.2x%2.2x%2.2x%2.2x%2.2x%2.2x\n", + ltv.addr[0], ltv.addr[1], ltv.addr[2], ltv.addr[3], ltv.addr[4], ltv.addr[5]); + } + PRINTSTAT("Net name: %s\n", ltv_inname(ctlr, WTyp_WantName)); + PRINTSTAT("Node name: %s\n", ltv_inname(ctlr, WTyp_NodeName)); + if(ltv_ins(ctlr, WTyp_HasCrypt) == 0) + PRINTSTR("WEP: not supported\n"); + else { + if(ltv_ins(ctlr, WTyp_Crypt) == 0) + PRINTSTR("WEP: disabled\n"); + else{ + PRINTSTR("WEP: enabled\n"); + k = ((ctlr->xclear)? "excluded": "included"); + PRINTSTAT("Clear packets: %s\n", k); + txid = ltv_ins(ctlr, WTyp_TxKey); + PRINTSTAT("Transmit key id: %d\n", txid); + } + } + iunlock(ctlr); + + PRINTSTAT("ntxuframes: %lud\n", ctlr->ntxuframes); + PRINTSTAT("ntxmframes: %lud\n", ctlr->ntxmframes); + PRINTSTAT("ntxfrags: %lud\n", ctlr->ntxfrags); + PRINTSTAT("ntxubytes: %lud\n", ctlr->ntxubytes); + PRINTSTAT("ntxmbytes: %lud\n", ctlr->ntxmbytes); + PRINTSTAT("ntxdeferred: %lud\n", ctlr->ntxdeferred); + PRINTSTAT("ntxsretries: %lud\n", ctlr->ntxsretries); + PRINTSTAT("ntxmultiretries: %lud\n", ctlr->ntxmultiretries); + PRINTSTAT("ntxretrylimit: %lud\n", ctlr->ntxretrylimit); + PRINTSTAT("ntxdiscards: %lud\n", ctlr->ntxdiscards); + PRINTSTAT("nrxuframes: %lud\n", ctlr->nrxuframes); + PRINTSTAT("nrxmframes: %lud\n", ctlr->nrxmframes); + PRINTSTAT("nrxfrags: %lud\n", ctlr->nrxfrags); + PRINTSTAT("nrxubytes: %lud\n", ctlr->nrxubytes); + PRINTSTAT("nrxmbytes: %lud\n", ctlr->nrxmbytes); + PRINTSTAT("nrxfcserr: %lud\n", ctlr->nrxfcserr); + PRINTSTAT("nrxdropnobuf: %lud\n", ctlr->nrxdropnobuf); + PRINTSTAT("nrxdropnosa: %lud\n", ctlr->nrxdropnosa); + PRINTSTAT("nrxcantdecrypt: %lud\n", ctlr->nrxcantdecrypt); + PRINTSTAT("nrxmsgfrag: %lud\n", ctlr->nrxmsgfrag); + PRINTSTAT("nrxmsgbadfrag: %lud\n", ctlr->nrxmsgbadfrag); + USED(l); + n = readstr(offset, a, n, p); + free(p); + return n; +} +#undef PRINTSTR +#undef PRINTSTAT + +static int +parsekey(WKey* key, char* a) +{ + int i, k, len, n; + char buf[WMaxKeyLen]; + + len = strlen(a); + if(len == WMinKeyLen || len == WMaxKeyLen){ + memset(key->dat, 0, sizeof(key->dat)); + memmove(key->dat, a, len); + key->len = len; + + return 0; + } + else if(len == WMinKeyLen*2 || len == WMaxKeyLen*2){ + k = 0; + for(i = 0; i < len; i++){ + if(*a >= '0' && *a <= '9') + n = *a++ - '0'; + else if(*a >= 'a' && *a <= 'f') + n = *a++ - 'a' + 10; + else if(*a >= 'A' && *a <= 'F') + n = *a++ - 'A' + 10; + else + return -1; + + if(i & 1){ + buf[k] |= n; + k++; + } + else + buf[k] = n<<4; + } + + memset(key->dat, 0, sizeof(key->dat)); + memmove(key->dat, buf, k); + key->len = k; + + return 0; + } + + return -1; +} + +int +w_option(Ctlr* ctlr, char* buf, long n) +{ + char *p; + int i, r; + Cmdbuf *cb; + + r = 0; + + cb = parsecmd(buf, n); + if(cb->nf < 2) + r = -1; + else if(cistrcmp(cb->f[0], "essid") == 0){ + if(cistrcmp(cb->f[1],"default") == 0) + p = ""; + else + p = cb->f[1]; + if(ctlr->ptype == WPTypeAdHoc){ + memset(ctlr->netname, 0, sizeof(ctlr->netname)); + strncpy(ctlr->netname, p, WNameLen); + } + else{ + memset(ctlr->wantname, 0, sizeof(ctlr->wantname)); + strncpy(ctlr->wantname, p, WNameLen); + } + } + else if(cistrcmp(cb->f[0], "station") == 0){ + memset(ctlr->nodename, 0, sizeof(ctlr->nodename)); + strncpy(ctlr->nodename, cb->f[1], WNameLen); + } + else if(cistrcmp(cb->f[0], "channel") == 0){ + if((i = atoi(cb->f[1])) >= 1 && i <= 16) + ctlr->chan = i; + else + r = -1; + } + else if(cistrcmp(cb->f[0], "mode") == 0){ + if(cistrcmp(cb->f[1], "managed") == 0) + ctlr->ptype = WPTypeManaged; + else if(cistrcmp(cb->f[1], "wds") == 0) + ctlr->ptype = WPTypeWDS; + else if(cistrcmp(cb->f[1], "adhoc") == 0) + ctlr->ptype = WPTypeAdHoc; + else if((i = atoi(cb->f[1])) >= 0 && i <= 3) + ctlr->ptype = i; + else + r = -1; + } + else if(cistrcmp(cb->f[0], "ibss") == 0){ + if(cistrcmp(cb->f[1], "on") == 0) + ctlr->createibss = 1; + else + ctlr->createibss = 0; + } + else if(cistrcmp(cb->f[0], "crypt") == 0){ + if(cistrcmp(cb->f[1], "off") == 0) + ctlr->crypt = 0; + else if(cistrcmp(cb->f[1], "on") == 0 && ctlr->hascrypt) + ctlr->crypt = 1; + else + r = -1; + } + else if(cistrcmp(cb->f[0], "clear") == 0){ + if(cistrcmp(cb->f[1], "on") == 0) + ctlr->xclear = 0; + else if(cistrcmp(cb->f[1], "off") == 0 && ctlr->hascrypt) + ctlr->xclear = 1; + else + r = -1; + } + else if(cistrncmp(cb->f[0], "key", 3) == 0){ + if((i = atoi(cb->f[0]+3)) >= 1 && i <= WNKeys){ + ctlr->txkey = i-1; + if(parsekey(&ctlr->keys.keys[ctlr->txkey], cb->f[1])) + r = -1; + } + else + r = -1; + } + else if(cistrcmp(cb->f[0], "txkey") == 0){ + if((i = atoi(cb->f[1])) >= 1 && i <= WNKeys) + ctlr->txkey = i-1; + else + r = -1; + } + else if(cistrcmp(cb->f[0], "pm") == 0){ + if(cistrcmp(cb->f[1], "off") == 0) + ctlr->pmena = 0; + else if(cistrcmp(cb->f[1], "on") == 0){ + ctlr->pmena = 1; + if(cb->nf == 3){ + i = atoi(cb->f[2]); + // check range here? what are the units? + ctlr->pmwait = i; + } + } + else + r = -1; + } + else + r = -2; + free(cb); + + return r; +} + +long +w_ctl(Ether* ether, void* buf, long n) +{ + Ctlr *ctlr; + + if((ctlr = ether->ctlr) == nil) + error(Enonexist); + if((ctlr->state & Attached) == 0) + error(Eshutdown); + + ilock(ctlr); + if(w_option(ctlr, buf, n)){ + iunlock(ctlr); + error(Ebadctl); + } + if(ctlr->txbusy) + w_txdone(ctlr, WTxErrEv); + w_enable(ether); + w_txstart(ether); + iunlock(ctlr); + + return n; +} + +void +w_transmit(Ether* ether) +{ + Ctlr* ctlr = ether->ctlr; + + if(ctlr == 0) + return; + + ilock(ctlr); + ctlr->ntxrq++; + w_txstart(ether); + iunlock(ctlr); +} + +void +w_promiscuous(void* arg, int on) +{ + Ether* ether = (Ether*)arg; + Ctlr* ctlr = ether->ctlr; + + if(ctlr == nil) + error("card not found"); + if((ctlr->state & Attached) == 0) + error("card not attached"); + ilock(ctlr); + ltv_outs(ctlr, WTyp_Prom, (on?1:0)); + iunlock(ctlr); +} + +void +w_interrupt(Ureg* ,void* arg) +{ + Ether* ether = (Ether*) arg; + Ctlr* ctlr = (Ctlr*) ether->ctlr; + + if(ctlr == 0) + return; + ilock(ctlr); + ctlr->nints++; + w_intr(ether); + iunlock(ctlr); +} + +int +wavelanreset(Ether* ether, Ctlr *ctlr) +{ + Wltv ltv; + + iprint("wavelanreset, iob 0x%ux\n", ctlr->iob); + w_intdis(ctlr); + if(w_cmd(ctlr,WCmdIni,0)){ + iprint("#l%d: init failed\n", ether->ctlrno); + return -1; + } + w_intdis(ctlr); + ltv_outs(ctlr, WTyp_Tick, 8); + + ctlr->chan = 0; + ctlr->ptype = WDfltPType; + ctlr->txkey = 0; + ctlr->createibss = 0; + ctlr->keys.len = sizeof(WKey)*WNKeys/2 + 1; + ctlr->keys.type = WTyp_Keys; + if(ctlr->hascrypt = ltv_ins(ctlr, WTyp_HasCrypt)) + ctlr->crypt = 1; + *ctlr->netname = *ctlr->wantname = 0; + strcpy(ctlr->nodename, "Plan 9 STA"); + + ctlr->netname[WNameLen-1] = 0; + ctlr->wantname[WNameLen-1] = 0; + ctlr->nodename[WNameLen-1] =0; + + ltv.type = WTyp_Mac; + ltv.len = 4; + if(w_inltv(ctlr, <v)){ + iprint("#l%d: unable to read mac addr\n", + ether->ctlrno); + return -1; + } + memmove(ether->ea, ltv.addr, Eaddrlen); + + if(ctlr->chan == 0) + ctlr->chan = ltv_ins(ctlr, WTyp_Chan); + ctlr->apdensity = WDfltApDens; + ctlr->rtsthres = WDfltRtsThres; + ctlr->txrate = WDfltTxRate; + ctlr->maxlen = WMaxLen; + ctlr->pmena = 0; + ctlr->pmwait = 100; + ctlr->signal = 1; + ctlr->noise = 1; + ctlr->state |= Power; + + // free old Ctlr struct if resetting after suspend + if(ether->ctlr && ether->ctlr != ctlr) + free(ether->ctlr); + + // link to ether + ether->ctlr = ctlr; + ether->mbps = 10; + ether->attach = w_attach; + ether->detach = w_detach; + ether->interrupt = w_interrupt; + ether->transmit = w_transmit; + ether->ifstat = w_ifstat; + ether->ctl = w_ctl; + ether->power = w_power; + ether->promiscuous = w_promiscuous; + ether->multicast = w_multicast; + ether->scanbs = w_scanbs; + ether->arg = ether; + + DEBUG("#l%d: irq %d port %lx type %s", + ether->ctlrno, ether->irq, ether->port, ether->type); + DEBUG(" %2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux\n", + ether->ea[0], ether->ea[1], ether->ea[2], + ether->ea[3], ether->ea[4], ether->ea[5]); + + return 0; +} + +char* wavenames[] = { + "WaveLAN/IEEE", + "TrueMobile 1150", + "Instant Wireless ; Network PC CARD", + "Instant Wireless Network PC Card", + "Avaya Wireless PC Card", + "AirLancer MC-11", + "INTERSIL;HFA384x/IEEE;Version 01.02;", + nil, +}; diff --git a/os/pc/wavelan.h b/os/pc/wavelan.h new file mode 100644 index 00000000..7f76149e --- /dev/null +++ b/os/pc/wavelan.h @@ -0,0 +1,327 @@ +#define DEBUG if(1){}else print + +#define SEEKEYS 0 + +// Lucent's Length-Type-Value records to talk to the wavelan. +// most operational parameters are read/set using this. +enum +{ + WTyp_Stats = 0xf100, + WTyp_Scan = 0xf101, + WTyp_Link = 0xf200, + WTyp_Ptype = 0xfc00, + WTyp_Mac = 0xfc01, + WTyp_WantName = 0xfc02, + WTyp_Chan = 0xfc03, + WTyp_NetName = 0xfc04, + WTyp_ApDens = 0xfc06, + WTyp_MaxLen = 0xfc07, + WTyp_PM = 0xfc09, + WTyp_PMWait = 0xfc0c, + WTyp_NodeName = 0xfc0e, + WTyp_Crypt = 0xfc20, + WTyp_XClear = 0xfc22, + WTyp_CreateIBSS = 0xfc81, + WTyp_RtsThres = 0xfc83, + WTyp_TxRate = 0xfc84, + WTx1Mbps = 0x0, + WTx2Mbps = 0x1, + WTxAuto = 0x3, + WTyp_Prom = 0xfc85, + WTyp_Keys = 0xfcb0, + WTyp_TxKey = 0xfcb1, + WTyp_StationID = 0xfd20, + WTyp_CurName = 0xfd41, + WTyp_BaseID = 0xfd42, // ID of the currently connected-to base station + WTyp_CurTxRate = 0xfd44, // Current TX rate + WTyp_HasCrypt = 0xfd4f, + WTyp_Tick = 0xfce0, +}; + +// Controller +enum +{ + WDfltIRQ = 3, // default irq + WDfltIOB = 0x180, // default IO base + + WIOLen = 0x40, // Hermes IO length + + WTmOut = 65536, // Cmd time out + + WPTypeManaged = 1, + WPTypeWDS = 2, + WPTypeAdHoc = 3, + WDfltPType = WPTypeManaged, + + WDfltApDens = 1, + WDfltRtsThres = 2347, // == disabled + WDfltTxRate = WTxAuto, // 2Mbps + + WMaxLen = 2304, + WNameLen = 32, + + WNKeys = 4, + WKeyLen = 14, + WMinKeyLen = 5, + WMaxKeyLen = 13, + + // Wavelan hermes registers + WR_Cmd = 0x00, + WCmdIni = 0x0000, + WCmdEna = 0x0001, + WCmdDis = 0x0002, + WCmdTx = 0x000b, + WCmdMalloc = 0x000a, + WCmdEnquire = 0x0011, + WCmdMsk = 0x003f, + WCmdAccRd = 0x0021, + WCmdReclaim = 0x0100, + WCmdAccWr = 0x0121, + WCmdBusy = 0x8000, + WR_Parm0 = 0x02, + WR_Parm1 = 0x04, + WR_Parm2 = 0x06, + WR_Sts = 0x08, + WR_InfoId = 0x10, + WR_Sel0 = 0x18, + WR_Sel1 = 0x1a, + WR_Off0 = 0x1c, + WR_Off1 = 0x1e, + WBusyOff = 0x8000, + WErrOff = 0x4000, + WResSts = 0x7f00, + WR_RXId = 0x20, + WR_Alloc = 0x22, + WR_EvSts = 0x30, + WR_IntEna = 0x32, + WCmdEv = 0x0010, + WRXEv = 0x0001, + WTXEv = 0x0002, + WTxErrEv = 0x0004, + WAllocEv = 0x0008, + WInfoEv = 0x0080, + WIDropEv = 0x2000, + WTickEv = 0x8000, + WEvs = WRXEv|WTXEv|WAllocEv|WInfoEv|WIDropEv, + + WR_EvAck = 0x34, + WR_Data0 = 0x36, + WR_Data1 = 0x38, + + WR_PciCor = 0x26, + WR_PciHcr = 0x2E, + + // Frame stuff + + WF_Err = 0x0003, + WF_1042 = 0x2000, + WF_Tunnel = 0x4000, + WF_WMP = 0x6000, + + WF_Data = 0x0008, + + WSnapK1 = 0xaa, + WSnapK2 = 0x00, + WSnapCtlr = 0x03, + WSnap0 = (WSnapK1|(WSnapK1<<8)), + WSnap1 = (WSnapK2|(WSnapCtlr<<8)), + WSnapHdrLen = 6, + + WF_802_11_Off = 0x44, + WF_802_3_Off = 0x2e, + +}; + +typedef struct Ctlr Ctlr; +typedef struct Wltv Wltv; +typedef struct WFrame WFrame; +typedef struct Stats Stats; +typedef struct WStats WStats; +typedef struct WScan WScan; +typedef struct WKey WKey; + +struct WStats +{ + ulong ntxuframes; // unicast frames + ulong ntxmframes; // multicast frames + ulong ntxfrags; // fragments + ulong ntxubytes; // unicast bytes + ulong ntxmbytes; // multicast bytes + ulong ntxdeferred; // deferred transmits + ulong ntxsretries; // single retries + ulong ntxmultiretries; // multiple retries + ulong ntxretrylimit; + ulong ntxdiscards; + ulong nrxuframes; // unicast frames + ulong nrxmframes; // multicast frames + ulong nrxfrags; // fragments + ulong nrxubytes; // unicast bytes + ulong nrxmbytes; // multicast bytes + ulong nrxfcserr; + ulong nrxdropnobuf; + ulong nrxdropnosa; + ulong nrxcantdecrypt; + ulong nrxmsgfrag; + ulong nrxmsgbadfrag; + ulong end; +}; + +struct WScan +{ + ushort chan; /* dss channel */ + ushort noise; /* average noise in the air */ + ushort signal; /* signal strength */ + uchar bssid[Eaddrlen]; /* MAC address of the ap */ + ushort interval; /* beacon transmit interval */ + ushort capinfo; /* capability bits (0-ess, 1-ibss, 4-privacy [wep]) */ + ushort ssid_len; /* ssid length */ + char ssid[WNameLen]; /* ssid (ap name) */ +}; + +struct WFrame +{ + ushort sts; + ushort rsvd0; + ushort rsvd1; + ushort qinfo; + ushort rsvd2; + ushort rsvd3; + ushort txctl; + ushort framectl; + ushort id; + uchar addr1[Eaddrlen]; + uchar addr2[Eaddrlen]; + uchar addr3[Eaddrlen]; + ushort seqctl; + uchar addr4[Eaddrlen]; + ushort dlen; + uchar dstaddr[Eaddrlen]; + uchar srcaddr[Eaddrlen]; + ushort len; + ushort dat[3]; + ushort type; +}; + +struct WKey +{ + ushort len; + char dat[WKeyLen]; +}; + +struct Wltv +{ + ushort len; + ushort type; + union + { + struct { + ushort val; + ushort pad; + }; + struct { + uchar addr[8]; + }; + struct { + ushort slen; + char s[WNameLen]; + }; + struct { + char name[WNameLen]; + }; + struct { + WKey keys[WNKeys]; + }; + }; +}; + +// What the driver thinks. Not what the card thinks. +struct Stats +{ + ulong nints; + ulong ndoubleint; + ulong nrx; + ulong ntx; + ulong ntxrq; + ulong nrxerr; + ulong ntxerr; + ulong nalloc; // allocation (reclaim) events + ulong ninfo; + ulong nidrop; + ulong nwatchdogs; // transmit time outs, actually + int ticks; + int tickintr; + int signal; + int noise; +}; + +enum { + Attached = 0x01, + Power = 0x02, +}; + +struct Ctlr +{ + Lock; + + int state; // Attached | Power + int slot; + int iob; + int createibss; + int ptype; + int apdensity; + int rtsthres; + int txbusy; + int txrate; + int txdid; + int txmid; + int txtmout; + int maxlen; + int chan; + int pmena; + int pmwait; + + Proc *timerproc; + int scanticks; + + char netname[WNameLen]; + char wantname[WNameLen]; + char nodename[WNameLen]; + WFrame txf; + uchar txbuf[1536]; + + int hascrypt; // card has encryption + int crypt; // encryption off/on + int txkey; // transmit key + Wltv keys; // default keys + int xclear; // exclude clear packets off/on + + int ctlrno; + + ushort *mmb; + /* for PCI-based devices */ + Ctlr *next; + int active; + Pcidev *pcidev; + + Stats; + WStats; +}; + +extern char* wavenames[]; + +void csr_outs(Ctlr*, int, ushort); +ushort csr_ins(Ctlr*, int); +void w_intdis(Ctlr*); +int w_cmd(Ctlr *, ushort, ushort); +void ltv_outs(Ctlr*, int, ushort); +int ltv_ins(Ctlr*, int); +int w_option(Ctlr*, char*, long); +int w_inltv(Ctlr*, Wltv*); +void w_attach(Ether*); +void w_interrupt(Ureg*,void*); +void w_transmit(Ether*); +long w_ifstat(Ether*, void*, long, ulong); +long w_ctl(Ether*, void*, long); +void w_promiscuous(void*, int); +void w_multicast(void*, uchar*, int); +int wavelanreset(Ether*, Ctlr*); diff --git a/os/pc/x86break.c b/os/pc/x86break.c new file mode 100644 index 00000000..db31fe4f --- /dev/null +++ b/os/pc/x86break.c @@ -0,0 +1,138 @@ +#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" + +// +// from trap.c +// + +uchar BREAK = 0xcc; +static ulong skipflags; +extern int (*breakhandler)(Ureg *ur, Proc*); +static Bkpt *skip; +int breakmatch(BkptCond *cond, Ureg *ur, Proc *p); +void breaknotify(Bkpt *b, Proc *p); +void breakrestore(Bkpt *b); +Bkpt* breakclear(int id); + +void +skiphandler(Ureg *ur, void*) +{ + if (skip == 0) + panic("single step outside of skip"); + + breakrestore( skip ); + skip = 0; + ur->flags = skipflags; + if (up != 0) + up->state = Running; +} + +void +machbreakinit(void) +{ + breakhandler = breakhit; + trapenable(VectorDBG, skiphandler, nil, "bkpt.skip"); +} + +Instr +machinstr(ulong addr) +{ + if (addr < KTZERO) + error(Ebadarg); + return *(uchar*)addr; +} + +void +machbreakset(ulong addr) +{ + if (addr < KTZERO) + error(Ebadarg); + *(uchar*)addr = BREAK; +} + +void +machbreakclear(ulong addr, Instr i) +{ + if (addr < KTZERO) + error(Ebadarg); + *(uchar*)addr = i; +} + +// +// Called from the exception handler when a breakpoint instruction has been +// hit. This cannot not be called unless at least one breakpoint with this +// address is in the list of breakpoints. (All breakpoint notifications must +// previously have been set via setbreak()) +// +// foreach breakpoint in list +// if breakpoint matches conditions +// notify the break handler +// if no breakpoints matched the conditions +// pick a random breakpoint set to this address +// +// set a breakpoint at the next instruction to be executed, +// and pass the current breakpoint to the "skiphandler" +// +// clear the current breakpoint +// +// Tell the scheduler to stop scheduling, so the caller is +// guaranteed to execute the instruction, followed by the +// added breakpoint. +// +// + +extern Bkpt *breakpoints; + + +int +breakhit(Ureg *ur, Proc *p) +{ + Bkpt *b; + int nmatched; + + ur->pc--; + + nmatched = 0; + for(b = breakpoints; b != nil; b = b->next) { + if(breakmatch(b->conditions, ur, p)) { + breaknotify(b, p); + ++nmatched; + } + } + + if (nmatched) + return 1; + + if (skip != nil) + panic("x86break: non-nil skip in breakhit\n"); + + for(b = breakpoints; b != (Bkpt*) nil; b = b->next) { + if(b->addr == ur->pc) { + if(breakclear(b->id) == 0) + panic("breakhit: breakclear() failed"); + + skip = b; + skipflags = ur->flags; + if (p != 0) + p->state = Stopped; /* this should disable scheduling */ + + if (ur->flags & (1 << 9)) { /* mask all interrupts */ + ur->flags &= ~(1<<9); + } + ur->flags |= (1 << 8); + } + } + return 1; +} + +int +isvalid_va(void*) +{ + return 1; +} diff --git a/os/pc/zoran.h b/os/pc/zoran.h new file mode 100644 index 00000000..b54305ef --- /dev/null +++ b/os/pc/zoran.h @@ -0,0 +1,907 @@ +static uchar +zrmpeg1[] = { +0x00, 0x08, 0x80, 0x00, 0x00, 0x08, 0x66, 0x05, +0x00, 0x08, 0x00, 0x80, 0x00, 0x08, 0x5a, 0x00, +0x00, 0x08, 0x58, 0x01, 0x00, 0x08, 0x6e, 0x00, +0x00, 0x08, 0x6c, 0x00, 0x00, 0x03, 0x81, 0x03, +0x00, 0x08, 0x9a, 0x00, 0x00, 0x08, 0xfc, 0x3f, +0x00, 0x0d, 0x02, 0x97, 0x00, 0x06, 0x01, 0xfa, +0x00, 0x0c, 0xa2, 0xd1, 0x00, 0x08, 0x6a, 0x00, +0x00, 0x06, 0x01, 0x00, 0x00, 0x0c, 0xa0, 0x6b, +0x00, 0x06, 0x01, 0xb3, 0x00, 0x0c, 0xa8, 0x09, +0x00, 0x0c, 0xd8, 0x12, 0x00, 0x08, 0x5c, 0x01, +0x00, 0x08, 0x02, 0xf0, 0x00, 0x08, 0xe4, 0x33, +0x00, 0x08, 0x0c, 0x0c, 0x00, 0x08, 0x04, 0x08, +0x00, 0x00, 0xe3, 0x80, 0x00, 0x04, 0x85, 0x90, +0x00, 0x08, 0x0c, 0x0c, 0x00, 0x00, 0xe3, 0x80, +0x00, 0x04, 0x85, 0xb1, 0x00, 0x08, 0x0c, 0x08, +0x00, 0x04, 0x8d, 0x08, 0x00, 0x06, 0x81, 0x08, +0x00, 0x0c, 0xa8, 0x22, 0x00, 0x08, 0x64, 0xff, +0x00, 0x01, 0x01, 0x07, 0x00, 0x08, 0x02, 0x00, +0x00, 0x06, 0x01, 0x02, 0x00, 0x0c, 0x90, 0x27, +0x00, 0x08, 0x02, 0x01, 0x00, 0x06, 0x6b, 0x00, +0x00, 0x08, 0xde, 0x01, 0x00, 0x0c, 0xa8, 0x09, +0x00, 0x08, 0x0c, 0x0f, 0x00, 0x08, 0x80, 0x00, +0x00, 0x08, 0x0c, 0x10, 0x00, 0x06, 0x8d, 0x01, +0x00, 0x0c, 0xa0, 0x3a, 0x00, 0x08, 0x0a, 0x00, +0x00, 0x08, 0x60, 0x00, 0x00, 0x08, 0x0c, 0x08, +0x00, 0x04, 0x9d, 0x08, 0x00, 0x08, 0x8b, 0x41, +0x00, 0x02, 0x50, 0x05, 0x00, 0x06, 0x0b, 0x40, +0x00, 0x0c, 0xa8, 0x31, 0x00, 0x06, 0x6b, 0x00, +0x00, 0x0c, 0xa8, 0x3f, 0x00, 0x0d, 0x00, 0x3b, +0x00, 0x08, 0x60, 0x01, 0x00, 0x08, 0x0a, 0x40, +0x00, 0x08, 0x0c, 0x01, 0x00, 0x06, 0x0d, 0x00, +0x00, 0x0c, 0x98, 0x49, 0x00, 0x08, 0x0a, 0x40, +0x00, 0x08, 0x0c, 0x08, 0x00, 0x04, 0x9d, 0x08, +0x00, 0x08, 0x8b, 0x41, 0x00, 0x02, 0x50, 0x05, +0x00, 0x06, 0x0b, 0x80, 0x00, 0x0c, 0xa8, 0x40, +0x00, 0x06, 0x6b, 0x00, 0x00, 0x0c, 0xa8, 0x16, +0x00, 0x0d, 0x00, 0x4e, 0x00, 0x08, 0x02, 0x10, +0x00, 0x08, 0x8a, 0x41, 0x00, 0x02, 0x50, 0x05, +0x00, 0x06, 0x0b, 0x80, 0x00, 0x0c, 0xa8, 0x4a, +0x00, 0x08, 0xfc, 0x3f, 0x00, 0x0d, 0x02, 0x97, +0x00, 0x08, 0xfc, 0x3f, 0x00, 0x0d, 0x02, 0x95, +0x00, 0x06, 0x01, 0xb8, 0x00, 0x0c, 0xa9, 0xff, +0x00, 0x06, 0x6f, 0x00, 0x00, 0x0c, 0xa0, 0x57, +0x00, 0x08, 0x58, 0x00, 0x00, 0x08, 0x6e, 0x01, +0x00, 0x08, 0x0c, 0x10, 0x00, 0x08, 0x0c, 0x09, +0x00, 0x08, 0x0c, 0x01, 0x00, 0x08, 0x80, 0x06, +0x00, 0x08, 0x0c, 0x01, 0x00, 0x04, 0x9d, 0x0f, +0x00, 0x03, 0x12, 0x6c, 0x00, 0x08, 0xd8, 0x01, +0x00, 0x08, 0x6c, 0x00, 0x00, 0x06, 0x01, 0x00, +0x00, 0x0c, 0x98, 0x64, 0x00, 0x08, 0x58, 0x00, +0x00, 0x08, 0xfc, 0x3f, 0x00, 0x0d, 0x02, 0x97, +0x00, 0x08, 0xfc, 0x3f, 0x00, 0x0d, 0x02, 0x95, +0x00, 0x0d, 0x01, 0xf7, 0x00, 0x06, 0x5d, 0x01, +0x00, 0x0c, 0xa0, 0x6c, 0x00, 0x0c, 0xd8, 0x6b, +0x00, 0x09, 0x20, 0xe5, 0x00, 0x09, 0x62, 0xe6, +0x00, 0x09, 0x5e, 0xe3, 0x00, 0x09, 0x60, 0xc2, +0x00, 0x08, 0x5c, 0x00, 0x00, 0x08, 0x80, 0x00, +0x00, 0x02, 0x00, 0x2d, 0x00, 0x0c, 0xa0, 0x75, +0x00, 0x08, 0xda, 0x00, 0x00, 0x0c, 0xd0, 0x76, +0x00, 0x08, 0x50, 0x00, 0x00, 0x08, 0x23, 0xff, +0x00, 0x08, 0x24, 0x00, 0x00, 0x08, 0x0c, 0x0a, +0x00, 0x08, 0xe6, 0x32, 0x00, 0x04, 0x8d, 0x06, +0x00, 0x08, 0xe8, 0x00, 0x00, 0x08, 0x0c, 0x03, +0x00, 0x04, 0x8d, 0x0d, 0x00, 0x08, 0xaa, 0x00, +0x00, 0x08, 0x0c, 0x10, 0x00, 0x06, 0x2b, 0x01, +0x00, 0x0c, 0xa8, 0x8c, 0x00, 0x06, 0x6d, 0x00, +0x00, 0x0c, 0xa8, 0x8a, 0x00, 0x08, 0x80, 0x2c, +0x00, 0x01, 0x01, 0x01, 0x00, 0x08, 0xd8, 0x00, +0x00, 0x08, 0x6c, 0x01, 0x00, 0x0d, 0x00, 0xab, +0x00, 0x08, 0x58, 0x00, 0x00, 0x0d, 0x00, 0xab, +0x00, 0x06, 0x2b, 0x02, 0x00, 0x0c, 0xa8, 0x97, +0x00, 0x06, 0x6d, 0x00, 0x00, 0x0c, 0xa0, 0x99, +0x00, 0x08, 0x58, 0x00, 0x00, 0x0d, 0x00, 0x9a, +0x00, 0x08, 0x02, 0x03, 0x00, 0x06, 0x5b, 0x01, +0x00, 0x0c, 0xa8, 0xb0, 0x00, 0x08, 0x02, 0x01, +0x00, 0x0d, 0x00, 0xb0, 0x00, 0x06, 0xd9, 0x01, +0x00, 0x0c, 0xa0, 0x9a, 0x00, 0x08, 0x58, 0x03, +0x00, 0x08, 0xfc, 0x3f, 0x00, 0x0d, 0x02, 0xab, +0x00, 0x08, 0xd4, 0x02, 0x00, 0x08, 0xae, 0x00, +0x00, 0x08, 0xac, 0x03, 0x00, 0x01, 0xc1, 0x9e, +0x00, 0x02, 0xc1, 0x9f, 0x00, 0x08, 0x80, 0x00, +0x00, 0x06, 0x2b, 0x03, 0x00, 0x0c, 0xa8, 0xab, +0x00, 0x08, 0xfc, 0x3f, 0x00, 0x0d, 0x02, 0xab, +0x00, 0x08, 0xd6, 0x02, 0x00, 0x08, 0xb2, 0x00, +0x00, 0x08, 0xb0, 0x03, 0x00, 0x01, 0xc1, 0xa0, +0x00, 0x02, 0xc1, 0xa1, 0x00, 0x08, 0xfc, 0x3f, +0x00, 0x0d, 0x02, 0x97, 0x00, 0x08, 0x82, 0x15, +0x00, 0x06, 0xd9, 0x02, 0x00, 0x0c, 0xa8, 0x92, +0x00, 0x09, 0x02, 0xe7, 0x00, 0x08, 0xfc, 0x3f, +0x00, 0x0d, 0x02, 0x95, 0x00, 0x08, 0x88, 0x0d, +0x00, 0x08, 0x90, 0x04, 0x00, 0x08, 0x92, 0x04, +0x00, 0x08, 0x94, 0x04, 0x00, 0x08, 0x45, 0xfe, +0x00, 0x08, 0x34, 0x00, 0x00, 0x08, 0x36, 0x00, +0x00, 0x08, 0x38, 0x00, 0x00, 0x08, 0x3a, 0x00, +0x00, 0x08, 0x0c, 0x05, 0x00, 0x04, 0x8d, 0x0b, +0x00, 0x09, 0x00, 0xc0, 0x00, 0x08, 0x46, 0x01, +0x00, 0x08, 0x0c, 0x01, 0x00, 0x08, 0x80, 0x28, +0x00, 0x08, 0x0c, 0x1c, 0x00, 0x08, 0x0e, 0x01, +0x00, 0x08, 0xce, 0x00, 0x00, 0x06, 0x0f, 0x22, +0x00, 0x0c, 0xa0, 0xc2, 0x00, 0x0c, 0x82, 0x7c, +0x00, 0x00, 0x80, 0xa2, 0x00, 0x06, 0x47, 0x00, +0x00, 0x08, 0xd0, 0x00, 0x00, 0x0c, 0xaa, 0x77, +0x00, 0x08, 0x4c, 0x01, 0x00, 0x0d, 0x02, 0x85, +0x00, 0x06, 0x5b, 0x00, 0x00, 0x0c, 0xa2, 0xbe, +0x00, 0x08, 0x52, 0x00, 0x00, 0x08, 0x4c, 0x00, +0x00, 0x08, 0xfc, 0x3f, 0x00, 0x0d, 0x02, 0xd5, +0x00, 0x09, 0x52, 0xe0, 0x00, 0x06, 0x2b, 0x01, +0x00, 0x0c, 0xa0, 0xf0, 0x00, 0x08, 0x0c, 0x1c, +0x00, 0x06, 0x2b, 0x03, 0x00, 0x0c, 0xa1, 0x01, +0x00, 0x0c, 0x91, 0xff, 0x00, 0x08, 0x0e, 0x02, +0x00, 0x08, 0x80, 0x00, 0x00, 0x06, 0x8f, 0x01, +0x00, 0x0c, 0xa8, 0xf8, 0x00, 0x08, 0xa6, 0x07, +0x00, 0x08, 0x52, 0x20, 0x00, 0x08, 0x80, 0x00, +0x00, 0x06, 0xa7, 0x08, 0x00, 0x0c, 0xa9, 0x0c, +0x00, 0x08, 0x34, 0x00, 0x00, 0x08, 0x36, 0x00, +0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x02, 0x00, +0x00, 0x08, 0x8a, 0x2a, 0x00, 0x08, 0x48, 0x03, +0x00, 0x08, 0x46, 0x06, 0x00, 0x08, 0xfc, 0x3f, +0x00, 0x0d, 0x02, 0x23, 0x00, 0x06, 0x4d, 0x00, +0x00, 0x0c, 0xa9, 0x66, 0x00, 0x0d, 0x01, 0x0c, +0x00, 0x08, 0x0c, 0x01, 0x00, 0x06, 0x0d, 0x00, +0x00, 0x0c, 0x80, 0xf6, 0x00, 0x08, 0x0c, 0x01, +0x00, 0x08, 0x26, 0x11, 0x00, 0x0d, 0x01, 0x0e, +0x00, 0x08, 0x26, 0x01, 0x00, 0x0d, 0x01, 0x0e, +0x00, 0x06, 0xd9, 0x02, 0x00, 0x0c, 0xa1, 0x0c, +0x00, 0x08, 0xa6, 0x07, 0x00, 0x08, 0x52, 0x00, +0x00, 0x08, 0x46, 0x06, 0x00, 0x08, 0x48, 0x03, +0x00, 0x08, 0xfc, 0x3f, 0x00, 0x0d, 0x02, 0x23, +0x00, 0x0d, 0x01, 0x0c, 0x00, 0x08, 0x0e, 0x03, +0x00, 0x08, 0x00, 0x00, 0x00, 0x06, 0x8f, 0x01, +0x00, 0x0c, 0xa8, 0xf8, 0x00, 0x06, 0x8f, 0x04, +0x00, 0x0c, 0xa1, 0x08, 0x00, 0x03, 0x01, 0x04, +0x00, 0x06, 0x8f, 0x08, 0x00, 0x0c, 0xa1, 0x0b, +0x00, 0x03, 0x01, 0x20, 0x00, 0x08, 0xd2, 0x00, +0x00, 0x08, 0xa6, 0x07, 0x00, 0x08, 0x80, 0x00, +0x00, 0x06, 0xa7, 0x10, 0x00, 0x0c, 0xa1, 0x13, +0x00, 0x08, 0x0c, 0x05, 0x00, 0x04, 0x8d, 0x0b, +0x00, 0x09, 0x00, 0xc0, 0x00, 0x06, 0xa7, 0x08, +0x00, 0x0c, 0xa1, 0x2f, 0x00, 0x08, 0x88, 0x17, +0x00, 0x08, 0x8a, 0x1e, 0x00, 0x08, 0x84, 0x1f, +0x00, 0x08, 0x80, 0x16, 0x00, 0x08, 0x86, 0x1a, +0x00, 0x08, 0xc8, 0x02, 0x00, 0x08, 0xc6, 0x03, +0x00, 0x08, 0xfc, 0x3f, 0x00, 0x0d, 0x02, 0x00, +0x00, 0x08, 0x84, 0x1b, 0x00, 0x08, 0x80, 0x16, +0x00, 0x08, 0xb4, 0x01, 0x00, 0x08, 0xc6, 0x02, +0x00, 0x08, 0xfc, 0x3f, 0x00, 0x0d, 0x02, 0x00, +0x00, 0x08, 0xb6, 0x01, 0x00, 0x08, 0x80, 0x00, +0x00, 0x06, 0xa7, 0x08, 0x00, 0x0c, 0xa1, 0x2f, +0x00, 0x08, 0x82, 0x1b, 0x00, 0x08, 0x80, 0x1a, +0x00, 0x08, 0x8a, 0x2a, 0x00, 0x08, 0x46, 0x06, +0x00, 0x08, 0x48, 0x03, 0x00, 0x08, 0xfc, 0x3f, +0x00, 0x0d, 0x02, 0x23, 0x00, 0x06, 0xa7, 0x04, +0x00, 0x0c, 0xa1, 0x4e, 0x00, 0x06, 0x4d, 0x00, +0x00, 0x0c, 0xa9, 0x44, 0x00, 0x08, 0x80, 0x18, +0x00, 0x08, 0x88, 0x19, 0x00, 0x08, 0x8a, 0x20, +0x00, 0x08, 0x84, 0x21, 0x00, 0x08, 0x86, 0x1c, +0x00, 0x08, 0xc8, 0x02, 0x00, 0x08, 0xc6, 0x03, +0x00, 0x08, 0xfc, 0x3f, 0x00, 0x0d, 0x02, 0x00, +0x00, 0x08, 0xb8, 0x01, 0x00, 0x08, 0x80, 0x00, +0x00, 0x08, 0x84, 0x1d, 0x00, 0x08, 0x80, 0x18, +0x00, 0x08, 0xc6, 0x02, 0x00, 0x08, 0xfc, 0x3f, +0x00, 0x0d, 0x02, 0x00, 0x00, 0x08, 0xba, 0x01, +0x00, 0x0c, 0xc9, 0x44, 0x00, 0x08, 0x82, 0x1d, +0x00, 0x08, 0x80, 0x1c, 0x00, 0x08, 0x8a, 0x2b, +0x00, 0x08, 0x46, 0x06, 0x00, 0x08, 0x48, 0x00, +0x00, 0x08, 0xfc, 0x3f, 0x00, 0x0d, 0x02, 0x23, +0x00, 0x06, 0x4d, 0x00, 0x00, 0x0c, 0xa9, 0x66, +0x00, 0x06, 0x4d, 0x00, 0x00, 0x0c, 0xa9, 0x58, +0x00, 0x06, 0xa7, 0x02, 0x00, 0x08, 0x28, 0x00, +0x00, 0x0c, 0xa1, 0x58, 0x00, 0x08, 0x0c, 0x1c, +0x00, 0x08, 0x0e, 0x04, 0x00, 0x08, 0x80, 0x00, +0x00, 0x08, 0xa8, 0x07, 0x00, 0x08, 0x80, 0x00, +0x00, 0x06, 0xd9, 0x02, 0x00, 0x0c, 0xa1, 0x5c, +0x00, 0x08, 0x00, 0x00, 0x00, 0x0d, 0x01, 0x5e, +0x00, 0x08, 0x80, 0x13, 0x00, 0x01, 0x01, 0x01, +0x00, 0x09, 0x00, 0xc1, 0x00, 0x06, 0xa7, 0x01, +0x00, 0x0c, 0xa1, 0x66, 0x00, 0x08, 0x28, 0x3f, +0x00, 0x08, 0x34, 0x00, 0x00, 0x08, 0x36, 0x00, +0x00, 0x08, 0x38, 0x00, 0x00, 0x08, 0x3a, 0x00, +0x00, 0x08, 0x46, 0x06, 0x00, 0x0c, 0xd9, 0x69, +0x00, 0x0d, 0x01, 0xff, 0x00, 0x0c, 0xd1, 0x67, +0x00, 0x0c, 0xc9, 0x6b, 0x00, 0x08, 0x00, 0x01, +0x00, 0x01, 0x80, 0x23, 0x00, 0x06, 0xa7, 0x01, +0x00, 0x08, 0xc6, 0x00, 0x00, 0x0c, 0xa1, 0xa5, +0x00, 0x08, 0x0c, 0x1c, 0x00, 0x06, 0x47, 0x02, +0x00, 0x0c, 0x81, 0x75, 0x00, 0x08, 0x0e, 0x06, +0x00, 0x0d, 0x01, 0x76, 0x00, 0x08, 0x0e, 0x07, +0x00, 0x08, 0x08, 0x00, 0x00, 0x06, 0x0f, 0x00, +0x00, 0x0c, 0xa1, 0x85, 0x00, 0x08, 0xfa, 0x07, +0x00, 0x08, 0x8c, 0x3d, 0x00, 0x05, 0x4f, 0x10, +0x00, 0x04, 0xe9, 0x84, 0x00, 0x01, 0x90, 0x07, +0x00, 0x03, 0xa2, 0x81, 0x00, 0x06, 0x88, 0x02, +0x00, 0x0c, 0xa9, 0x85, 0x00, 0x08, 0x05, 0xff, +0x00, 0x03, 0xaf, 0x82, 0x00, 0x02, 0x10, 0x04, +0x00, 0x03, 0x15, 0x84, 0x00, 0x03, 0xc9, 0x03, +0x00, 0x06, 0x47, 0x04, 0x00, 0x0c, 0x91, 0x96, +0x00, 0x06, 0x47, 0x02, 0x00, 0x0c, 0x81, 0x96, +0x00, 0x00, 0xc8, 0x48, 0x00, 0x0d, 0x01, 0x9f, +0x00, 0x06, 0x47, 0x04, 0x00, 0x0c, 0x91, 0x8a, +0x00, 0x06, 0x47, 0x01, 0x00, 0x0c, 0xa1, 0x93, +0x00, 0x00, 0xc8, 0x4a, 0x00, 0x08, 0x94, 0x04, +0x00, 0x0d, 0x01, 0xa0, 0x00, 0x00, 0xc8, 0x49, +0x00, 0x08, 0x92, 0x04, 0x00, 0x0d, 0x01, 0xa0, +0x00, 0x08, 0x84, 0x28, 0x00, 0x05, 0x14, 0x22, +0x00, 0x06, 0x03, 0x01, 0x00, 0x0c, 0xa1, 0x8c, +0x00, 0x00, 0xc8, 0x4d, 0x00, 0x06, 0x47, 0x01, +0x00, 0x0c, 0xa1, 0x94, 0x00, 0x06, 0x47, 0x00, +0x00, 0x0c, 0xa1, 0x91, 0x00, 0x08, 0x90, 0x04, +0x00, 0x08, 0x0a, 0x80, 0x00, 0x06, 0xd9, 0x02, +0x00, 0x0c, 0xa9, 0xcc, 0x00, 0x08, 0x8b, 0x44, +0x00, 0x0d, 0x01, 0xbc, 0x00, 0x06, 0x47, 0x00, +0x00, 0x0c, 0xa1, 0xaf, 0x00, 0x06, 0xd3, 0x20, +0x00, 0x0c, 0xa1, 0xaf, 0x00, 0x08, 0x80, 0x1a, +0x00, 0x08, 0x82, 0x1b, 0x00, 0x08, 0x8a, 0x2a, +0x00, 0x08, 0x48, 0x03, 0x00, 0x08, 0xfc, 0x3f, +0x00, 0x0d, 0x02, 0x23, 0x00, 0x06, 0x4d, 0x00, +0x00, 0x0c, 0xa9, 0xd5, 0x00, 0x08, 0x80, 0x23, +0x00, 0x03, 0x80, 0x81, 0x00, 0x06, 0x80, 0x14, +0x00, 0x0c, 0xa1, 0xd5, 0x00, 0x08, 0x0c, 0x1c, +0x00, 0x08, 0x0e, 0x08, 0x00, 0x08, 0x0a, 0x80, +0x00, 0x00, 0xda, 0x3c, 0x00, 0x06, 0xd9, 0x02, +0x00, 0x0c, 0xa9, 0xd1, 0x00, 0x08, 0x8b, 0x47, +0x00, 0x08, 0x7a, 0x1c, 0x00, 0x08, 0x8c, 0x3d, +0x00, 0x08, 0x0e, 0x00, 0x00, 0x08, 0x80, 0x00, +0x00, 0x0c, 0xb1, 0xd5, 0x00, 0x00, 0xda, 0x3c, +0x00, 0x02, 0x50, 0x05, 0x00, 0x08, 0x8b, 0xc7, +0x00, 0x0c, 0xc1, 0xc0, 0x00, 0x08, 0x52, 0x00, +0x00, 0x08, 0x80, 0x00, 0x00, 0x09, 0x52, 0xe0, +0x00, 0x0c, 0xb1, 0xd5, 0x00, 0x08, 0x8c, 0x3d, +0x00, 0x0c, 0xc1, 0xc8, 0x00, 0x0d, 0x01, 0xc8, +0x00, 0x06, 0x47, 0x00, 0x00, 0x0c, 0xa1, 0xd1, +0x00, 0x08, 0x48, 0x03, 0x00, 0x08, 0xfc, 0x3f, +0x00, 0x0d, 0x02, 0x23, 0x00, 0x08, 0x7a, 0x1c, +0x00, 0x08, 0x8c, 0x3d, 0x00, 0x08, 0x0e, 0x00, +0x00, 0x0d, 0x01, 0xc8, 0x00, 0x06, 0x47, 0x00, +0x00, 0x0c, 0xa1, 0xdf, 0x00, 0x06, 0xd3, 0x04, +0x00, 0x0c, 0xa1, 0xdf, 0x00, 0x08, 0x80, 0x1c, +0x00, 0x08, 0x82, 0x1d, 0x00, 0x08, 0x8a, 0x2b, +0x00, 0x08, 0x48, 0x00, 0x00, 0x08, 0xfc, 0x3f, +0x00, 0x0d, 0x02, 0x23, 0x00, 0x08, 0x82, 0x29, +0x00, 0x01, 0x13, 0x24, 0x00, 0x06, 0x47, 0x00, +0x00, 0x08, 0xd2, 0x01, 0x00, 0x0c, 0xa9, 0x67, +0x00, 0x06, 0x4d, 0x00, 0x00, 0x0c, 0xaa, 0x8d, +0x00, 0x06, 0xa7, 0x01, 0x00, 0x0c, 0xa1, 0xea, +0x00, 0x08, 0x80, 0x28, 0x00, 0x08, 0xc4, 0x00, +0x00, 0x08, 0x0c, 0x1c, 0x00, 0x08, 0x0e, 0x01, +0x00, 0x08, 0x80, 0x28, 0x00, 0x06, 0x0f, 0x24, +0x00, 0x0c, 0xa1, 0xf1, 0x00, 0x08, 0xce, 0x00, +0x00, 0x0d, 0x00, 0xc5, 0x00, 0x08, 0xfc, 0x3f, +0x00, 0x0d, 0x02, 0x97, 0x00, 0x06, 0x01, 0x01, +0x00, 0x0c, 0x81, 0xf7, 0x00, 0x06, 0x01, 0xaf, +0x00, 0x0c, 0x88, 0xb3, 0x00, 0x06, 0x01, 0x00, +0x00, 0x0c, 0xa0, 0x69, 0x00, 0x06, 0x01, 0xb8, +0x00, 0x0c, 0xa0, 0x54, 0x00, 0x06, 0x01, 0xb3, +0x00, 0x0c, 0xa0, 0x12, 0x00, 0x06, 0x01, 0xb7, +0x00, 0x0c, 0xa2, 0xbe, 0x00, 0x0d, 0x00, 0x09, +0x00, 0x08, 0x0c, 0x1c, 0x00, 0x08, 0x0e, 0x05, +0x00, 0x08, 0x02, 0x00, 0x00, 0x08, 0x86, 0x07, +0x00, 0x03, 0x88, 0x81, 0x00, 0x06, 0x01, 0x01, +0x00, 0x0c, 0xa2, 0x0f, 0x00, 0x06, 0x07, 0x00, +0x00, 0x0c, 0xa2, 0x0f, 0x00, 0x08, 0xfa, 0x04, +0x00, 0x08, 0x8c, 0x3d, 0x00, 0x05, 0x29, 0x10, +0x00, 0x04, 0xe5, 0x82, 0x00, 0x05, 0x10, 0x81, +0x00, 0x05, 0x23, 0x81, 0x00, 0x08, 0x04, 0x00, +0x00, 0x03, 0xb9, 0x83, 0x00, 0x06, 0x07, 0x00, +0x00, 0x0c, 0xa2, 0x1b, 0x00, 0x03, 0xa1, 0x05, +0x00, 0x06, 0x07, 0x00, 0x00, 0x0c, 0x92, 0x19, +0x00, 0x00, 0x97, 0x83, 0x00, 0x00, 0xa7, 0x82, +0x00, 0x0d, 0x02, 0x1b, 0x00, 0x05, 0x17, 0x83, +0x00, 0x05, 0x27, 0x82, 0x00, 0x00, 0x96, 0x23, +0x00, 0x06, 0x02, 0x05, 0x00, 0x0c, 0x92, 0x21, +0x00, 0x06, 0x48, 0x01, 0x00, 0x0c, 0x92, 0x21, +0x00, 0x08, 0xfe, 0x3e, 0x00, 0x00, 0x94, 0x23, +0x00, 0x08, 0xfe, 0x3e, 0x00, 0x06, 0xd9, 0x02, +0x00, 0x0c, 0xa2, 0x27, 0x00, 0x08, 0x00, 0x00, +0x00, 0x08, 0x02, 0x00, 0x00, 0x06, 0x0b, 0x01, +0x00, 0x0c, 0xaa, 0x2b, 0x00, 0x03, 0x81, 0x01, +0x00, 0x03, 0x93, 0x01, 0x00, 0x06, 0x47, 0x02, +0x00, 0x0c, 0x92, 0x41, 0x00, 0x06, 0x01, 0x00, +0x00, 0x0c, 0x9a, 0x30, 0x00, 0x00, 0x81, 0x01, +0x00, 0x04, 0x21, 0x01, 0x00, 0x04, 0x05, 0x01, +0x00, 0x03, 0xc1, 0x01, 0x00, 0x05, 0x45, 0x82, +0x00, 0x06, 0x03, 0x00, 0x00, 0x0c, 0x9a, 0x37, +0x00, 0x00, 0x93, 0x01, 0x00, 0x04, 0x33, 0x01, +0x00, 0x04, 0x17, 0x01, 0x00, 0x03, 0xc3, 0x01, +0x00, 0x05, 0x47, 0x83, 0x00, 0x08, 0x0a, 0x03, +0x00, 0x03, 0xca, 0x11, 0x00, 0x00, 0xc1, 0x80, +0x00, 0x03, 0xca, 0x12, 0x00, 0x00, 0xc3, 0x81, +0x00, 0x0d, 0x02, 0x4a, 0x00, 0x01, 0x21, 0x01, +0x00, 0x04, 0x01, 0x01, 0x00, 0x01, 0x33, 0x01, +0x00, 0x04, 0x13, 0x01, 0x00, 0x08, 0x0a, 0x04, +0x00, 0x03, 0xca, 0x11, 0x00, 0x00, 0xc1, 0x80, +0x00, 0x03, 0xca, 0x12, 0x00, 0x00, 0xc3, 0x81, +0x00, 0x08, 0x88, 0x29, 0x00, 0x03, 0xa4, 0x64, +0x00, 0x03, 0x45, 0x84, 0x00, 0x02, 0x50, 0x24, +0x00, 0x03, 0xbb, 0x83, 0x00, 0x03, 0x47, 0x84, +0x00, 0x01, 0x28, 0xa4, 0x00, 0x06, 0x05, 0x24, +0x00, 0x0c, 0xaa, 0x58, 0x00, 0x06, 0x49, 0x00, +0x00, 0x0c, 0xaa, 0x58, 0x00, 0x0c, 0xda, 0x57, +0x00, 0x0d, 0x01, 0xff, 0x00, 0x0c, 0xca, 0x55, +0x00, 0x06, 0xd9, 0x02, 0x00, 0x0c, 0xa2, 0x5f, +0x00, 0x06, 0x6d, 0x00, 0x00, 0x0c, 0xa2, 0x5e, +0x00, 0x08, 0x08, 0x04, 0x00, 0x0d, 0x02, 0x5f, +0x00, 0x08, 0x08, 0x20, 0x00, 0x08, 0xd2, 0x04, +0x00, 0x09, 0x08, 0xe0, 0x00, 0x01, 0xc0, 0x23, +0x00, 0x03, 0xc9, 0x01, 0x00, 0x08, 0x0a, 0x26, +0x00, 0x03, 0xdb, 0x04, 0x00, 0x03, 0x5b, 0x08, +0x00, 0x00, 0xd9, 0x84, 0x00, 0x08, 0xfe, 0x04, +0x00, 0x0d, 0x02, 0x72, 0x00, 0x08, 0x80, 0x00, +0x00, 0x0d, 0x02, 0x72, 0x00, 0x08, 0x80, 0x00, +0x00, 0x00, 0x81, 0x08, 0x00, 0x0d, 0x02, 0x75, +0x00, 0x00, 0x93, 0x08, 0x00, 0x0d, 0x02, 0x72, +0x00, 0x00, 0x81, 0x08, 0x00, 0x08, 0x80, 0x00, +0x00, 0x09, 0x00, 0xe1, 0x00, 0x09, 0x02, 0xe2, +0x00, 0x08, 0xfe, 0x3e, 0x00, 0x00, 0x93, 0x08, +0x00, 0x0d, 0x02, 0x72, 0x00, 0x08, 0x0c, 0x1c, +0x00, 0x08, 0x0e, 0x01, 0x00, 0x08, 0x80, 0x00, +0x00, 0x06, 0x0f, 0x23, 0x00, 0x0c, 0xa0, 0xc8, +0x00, 0x08, 0x82, 0x28, 0x00, 0x08, 0xce, 0x01, +0x00, 0x00, 0xf3, 0x80, 0x00, 0x08, 0xd0, 0x00, +0x00, 0x06, 0x0f, 0x01, 0x00, 0x0c, 0xa0, 0xce, +0x00, 0x06, 0x47, 0x00, 0x00, 0x0c, 0xa8, 0xce, +0x00, 0x08, 0x4c, 0x02, 0x00, 0x08, 0x00, 0x00, +0x00, 0x09, 0x00, 0xc1, 0x00, 0x08, 0xfc, 0x3f, +0x00, 0x0d, 0x02, 0xd5, 0x00, 0x06, 0x2b, 0x02, +0x00, 0x0c, 0xa9, 0x26, 0x00, 0x08, 0x26, 0x00, +0x00, 0x0d, 0x00, 0xe0, 0x00, 0x02, 0x00, 0x27, +0x00, 0x02, 0x10, 0x00, 0x00, 0x06, 0x50, 0x01, +0x00, 0x08, 0xce, 0x00, 0x00, 0x0c, 0x92, 0x85, +0x00, 0x06, 0x4d, 0x01, 0x00, 0x0c, 0xa2, 0x77, +0x00, 0x0d, 0x00, 0xce, 0x00, 0x06, 0x01, 0xb2, +0x00, 0x0c, 0xaa, 0xa7, 0x00, 0x08, 0x0d, 0x08, +0x00, 0x06, 0x0d, 0x00, 0x00, 0x0c, 0xaa, 0x97, +0x00, 0x08, 0x0c, 0x08, 0x00, 0x04, 0x8d, 0x08, +0x00, 0x0c, 0xaa, 0x97, 0x00, 0x08, 0x0c, 0x10, +0x00, 0x06, 0x0d, 0x00, 0x00, 0x0c, 0xa2, 0x9d, +0x00, 0x06, 0x0d, 0x01, 0x00, 0x0c, 0xa2, 0xa8, +0x00, 0x04, 0x8d, 0x08, 0x00, 0x06, 0x01, 0x01, +0x00, 0x0c, 0xaa, 0x97, 0x00, 0x03, 0x8d, 0x08, +0x00, 0x04, 0x81, 0x08, 0x00, 0x08, 0xfe, 0x3e, +0x00, 0x08, 0x0c, 0x08, 0x00, 0x04, 0x8d, 0x08, +0x00, 0x08, 0xfe, 0x3e, 0x00, 0x08, 0x0c, 0x01, +0x00, 0x04, 0xad, 0x0f, 0x00, 0x08, 0x0c, 0x03, +0x00, 0x04, 0x8d, 0x0d, 0x00, 0x06, 0x67, 0xff, +0x00, 0x0c, 0xa2, 0xba, 0x00, 0x08, 0x82, 0x33, +0x00, 0x05, 0x92, 0x34, 0x00, 0x01, 0x13, 0x1f, +0x00, 0x02, 0x10, 0x01, 0x00, 0x00, 0x83, 0x80, +0x00, 0x06, 0x01, 0x07, 0x00, 0x0c, 0x8a, 0xba, +0x00, 0x05, 0x00, 0x87, 0x00, 0x0d, 0x02, 0xb6, +0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0xb0, 0x81, +0x00, 0x03, 0xc7, 0x04, 0x00, 0x08, 0xfe, 0x3e, +0x00, 0x06, 0x5b, 0x01, 0x00, 0x0c, 0xa2, 0xc2, +0x00, 0x09, 0x00, 0xe8, 0x00, 0x0d, 0x00, 0x01, +0x00, 0x0c, 0xda, 0xc2, 0x00, 0x08, 0x5a, 0x00, +0x00, 0x08, 0x23, 0xff, 0x00, 0x08, 0x24, 0x00, +0x00, 0x08, 0x4e, 0x00, 0x00, 0x08, 0x2a, 0x02, +0x00, 0x08, 0x00, 0x00, 0x00, 0x09, 0x2a, 0xe7, +0x00, 0x08, 0x82, 0x10, 0x00, 0x00, 0x80, 0x71, +0x00, 0x01, 0x90, 0x01, 0x00, 0x0c, 0xaa, 0xcb, +0x00, 0x02, 0x01, 0xa8, 0x00, 0x08, 0x4c, 0x02, +0x00, 0x0d, 0x02, 0x85, 0x00, 0x08, 0xe4, 0x33, +0x00, 0x08, 0x60, 0x00, 0x00, 0x08, 0x6a, 0x01, +0x00, 0x0d, 0x00, 0x2f, 0x00, 0x02, 0x00, 0x11, +0x00, 0x06, 0x20, 0x00, 0x00, 0x08, 0xa2, 0x00, +0x00, 0x0c, 0x92, 0xdd, 0x00, 0x02, 0x00, 0x12, +0x00, 0x08, 0x22, 0x00, 0x00, 0x08, 0xa4, 0x00, +0x00, 0x08, 0x80, 0x00, 0x00, 0x08, 0xfe, 0x3e, +0x00, 0x08, 0x80, 0x00, 0x00, 0x08, 0x80, 0x00, +0x00, 0x08, 0x80, 0x00, 0x00, 0x08, 0x80, 0x00, +0x00, 0x08, 0x80, 0x00, 0x00, 0x08, 0x80, 0x00, +0x00, 0x08, 0x80, 0x00, 0x00, 0x08, 0x80, 0x00, +0x00, 0x08, 0x80, 0x00, 0x00, 0x08, 0x80, 0x00, +0x00, 0x08, 0x80, 0x00, 0x00, 0x08, 0x80, 0x00, +0x00, 0x08, 0x80, 0x00, 0x00, 0x08, 0x80, 0x00, +0x00, 0x08, 0x80, 0x00, 0x00, 0x08, 0x80, 0x00, +0x00, 0x08, 0x80, 0x00, 0x00, 0x08, 0x80, 0x00, +0x00, 0x08, 0x80, 0x00, 0x00, 0x08, 0x80, 0x00, +0x00, 0x08, 0x80, 0x00, 0x00, 0x08, 0x80, 0x00, +0x00, 0x08, 0x80, 0x00, 0x00, 0x08, 0x80, 0x00, +0x00, 0x08, 0x80, 0x00, 0x00, 0x08, 0x80, 0x00, +0x00, 0x08, 0x80, 0x00, 0x00, 0x08, 0x80, 0x00, +0x00, 0x08, 0x80, 0x00, 0x00, 0x08, 0x80, 0x00, +0x00, 0x08, 0x80, 0x00, 0x00, 0x08, 0x80, 0x00, +0x0a, 0x0d, +}; +static uchar +zrmpeg2[] = { +0x00, 0x02, 0xef, 0x00, 0x00, 0x03, 0xc1, 0x81, +0x00, 0x02, 0xee, 0x00, 0x00, 0x03, 0x2e, 0x01, +0x00, 0x02, 0xee, 0x00, 0x00, 0x03, 0x47, 0x17, +0x00, 0x03, 0x2e, 0x02, 0x00, 0x02, 0xee, 0x00, +0x00, 0x03, 0x74, 0x17, 0x00, 0x02, 0x9c, 0x02, +0x00, 0x03, 0x88, 0x0c, 0x00, 0x03, 0x46, 0x81, +0x00, 0x03, 0x50, 0x07, 0x00, 0x02, 0x80, 0x80, +0x00, 0x03, 0x88, 0x13, 0x00, 0x03, 0x43, 0x81, +0x00, 0x02, 0x9c, 0x01, 0x00, 0x03, 0x88, 0x14, +0x00, 0x03, 0x48, 0x80, 0x00, 0x03, 0x40, 0x80, +0x00, 0x02, 0x9c, 0x80, 0x00, 0x03, 0x88, 0x17, +0x00, 0x03, 0x44, 0x81, 0x00, 0x02, 0x9c, 0x04, +0x00, 0x03, 0x88, 0x1a, 0x00, 0x03, 0x45, 0x81, +0x00, 0x02, 0xee, 0x00, 0x00, 0x03, 0x50, 0x17, +0x00, 0x00, 0x5c, 0x8f, 0x00, 0x02, 0x84, 0x01, +0x00, 0x03, 0x88, 0x20, 0x00, 0x03, 0x49, 0x81, +0x00, 0x02, 0xee, 0x00, 0x00, 0x00, 0x5d, 0x0f, +0x00, 0x00, 0xc9, 0x04, 0x00, 0x03, 0x4d, 0x12, +0x00, 0x00, 0xa4, 0x2d, 0x00, 0x02, 0xe8, 0x00, +0x00, 0x02, 0x80, 0x01, 0x00, 0x03, 0x8a, 0x29, +0x00, 0x02, 0xe8, 0x01, 0x00, 0x03, 0x4d, 0x14, +0x00, 0x00, 0x41, 0x10, 0x00, 0x01, 0x49, 0x02, +0x00, 0x00, 0xab, 0x6d, 0x00, 0x00, 0x41, 0x20, +0x00, 0x01, 0x49, 0x02, 0x00, 0x00, 0xaa, 0x0d, +0x00, 0x02, 0x9c, 0x01, 0x00, 0x03, 0x8a, 0x33, +0x00, 0x00, 0x92, 0x10, 0x00, 0x03, 0x4d, 0x14, +0x00, 0x00, 0x5d, 0x10, 0x00, 0x00, 0xc9, 0x02, +0x00, 0x00, 0xab, 0x6d, 0x00, 0x00, 0x5d, 0x20, +0x00, 0x00, 0xc9, 0x02, 0x00, 0x00, 0xaa, 0x0d, +0x00, 0x02, 0xee, 0x00, 0x00, 0x03, 0x28, 0x5f, +0x00, 0x02, 0xef, 0x00, 0x00, 0x03, 0x42, 0x17, +0x00, 0x03, 0x2e, 0x20, 0x00, 0x01, 0xdc, 0x01, +0x00, 0x03, 0xa6, 0x43, 0x00, 0x03, 0x4d, 0x17, +0x00, 0x00, 0x20, 0x0d, 0x00, 0x03, 0x20, 0x17, +0x00, 0x02, 0xef, 0x00, 0x00, 0x03, 0x2e, 0x21, +0x00, 0x00, 0x3c, 0x02, 0x00, 0x03, 0x20, 0x24, +0x00, 0x02, 0xef, 0x00, 0x00, 0x03, 0x75, 0x17, +0x00, 0x03, 0x2e, 0x68, 0x00, 0x02, 0xef, 0x00, +0x00, 0x03, 0x43, 0x17, 0x00, 0x02, 0xef, 0x00, +0x00, 0x03, 0x44, 0x17, 0x00, 0x02, 0xef, 0x00, +0x00, 0x03, 0x45, 0x17, 0x00, 0x00, 0x1c, 0x01, +0x00, 0x03, 0x46, 0x10, 0x00, 0x00, 0x20, 0x02, +0x00, 0x03, 0x47, 0x10, 0x00, 0x00, 0x22, 0x23, +0x00, 0x00, 0x20, 0x02, 0x00, 0x03, 0x49, 0x10, +0x00, 0x00, 0x23, 0xa3, 0x00, 0x03, 0x50, 0x35, +0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0xe0, 0x04, +0x00, 0x03, 0xa6, 0x5e, 0x00, 0x01, 0xe0, 0x02, +0x00, 0x03, 0x4f, 0x10, 0x00, 0x00, 0x20, 0x02, +0x00, 0x03, 0x58, 0x10, 0x00, 0x03, 0x50, 0x35, +0x00, 0x01, 0x40, 0x01, 0x00, 0x00, 0x00, 0x01, +0x00, 0x00, 0x20, 0x83, 0x00, 0x03, 0x59, 0x11, +0x00, 0x01, 0xe0, 0x84, 0x00, 0x03, 0x5a, 0x11, +0x00, 0x02, 0xe2, 0x01, 0x00, 0x00, 0x26, 0x83, +0x00, 0x00, 0x36, 0xa5, 0x00, 0x03, 0x14, 0x69, +0x00, 0x03, 0x10, 0x6a, 0x00, 0x03, 0xa6, 0x6f, +0x00, 0x03, 0x1c, 0x6a, 0x00, 0x03, 0x1e, 0x6b, +0x00, 0x03, 0xa6, 0x72, 0x00, 0x03, 0x30, 0x6b, +0x00, 0x03, 0x2a, 0x6c, 0x00, 0x03, 0xa8, 0x77, +0x00, 0x03, 0x0e, 0x6c, 0x00, 0x03, 0xa6, 0x77, +0x00, 0x03, 0x12, 0x6c, 0x00, 0x03, 0x32, 0x6d, +0x00, 0x03, 0x34, 0x6e, 0x00, 0x03, 0xa8, 0x7c, +0x00, 0x03, 0x0c, 0x6d, 0x00, 0x03, 0x2a, 0x6e, +0x00, 0x02, 0xef, 0x00, 0x00, 0x03, 0x42, 0x17, +0x00, 0x03, 0x2e, 0x22, 0x00, 0x00, 0x1c, 0x01, +0x00, 0x01, 0x40, 0x01, 0x00, 0x00, 0xc0, 0x01, +0x00, 0x03, 0x20, 0x18, 0x00, 0x02, 0xef, 0x00, +0x00, 0x03, 0x73, 0x17, 0x00, 0x03, 0x2e, 0x23, +0x00, 0x02, 0xef, 0x00, 0x00, 0x03, 0x76, 0x17, +0x00, 0x03, 0x2e, 0x60, 0x00, 0x01, 0x5f, 0x01, +0x00, 0x02, 0x9c, 0x01, 0x00, 0x03, 0x88, 0x8d, +0x00, 0x03, 0x41, 0x81, 0x00, 0x02, 0xef, 0x00, +0x00, 0x03, 0x43, 0x17, 0x00, 0x03, 0x2e, 0x61, +0x00, 0x02, 0x9c, 0x01, 0x00, 0x03, 0x88, 0x94, +0x00, 0x03, 0x42, 0x81, 0x00, 0x03, 0x47, 0x80, +0x00, 0x02, 0xef, 0x00, 0x00, 0x03, 0x44, 0x17, +0x00, 0x02, 0xef, 0x00, 0x00, 0x03, 0x45, 0x17, +0x00, 0x03, 0x4d, 0x33, 0x00, 0x01, 0xff, 0x2d, +0x00, 0x03, 0x73, 0x0c, 0x00, 0x03, 0x5b, 0x1f, +0x00, 0x03, 0x8f, 0x94, 0x00, 0x03, 0x4d, 0x36, +0x00, 0x03, 0x50, 0x05, 0x00, 0x00, 0x20, 0x02, +0x00, 0x03, 0x51, 0x03, 0x00, 0x00, 0x24, 0x84, +0x00, 0x03, 0xa6, 0xa7, 0x00, 0x00, 0xc0, 0x01, +0x00, 0x01, 0xa3, 0x6d, 0x00, 0x01, 0xa7, 0x6d, +0x00, 0x03, 0x8e, 0xab, 0x00, 0x01, 0xa0, 0x0d, +0x00, 0x00, 0xc0, 0x01, 0x00, 0x03, 0x4d, 0x11, +0x00, 0x01, 0xe3, 0x6d, 0x00, 0x03, 0x72, 0x0d, +0x00, 0x00, 0x1c, 0x81, 0x00, 0x03, 0x41, 0x11, +0x00, 0x00, 0x3d, 0xa2, 0x00, 0x02, 0xee, 0x00, +0x00, 0x00, 0x1f, 0x81, 0x00, 0x03, 0x2e, 0x6f, +0x00, 0x02, 0xee, 0x00, 0x00, 0x03, 0x4c, 0x17, +0x00, 0x03, 0x51, 0x03, 0x00, 0x01, 0x45, 0x01, +0x00, 0x00, 0x28, 0x86, 0x00, 0x03, 0x47, 0x11, +0x00, 0x00, 0x06, 0x01, 0x00, 0x03, 0x51, 0x06, +0x00, 0x00, 0xc4, 0x81, 0x00, 0x03, 0x48, 0x11, +0x00, 0x00, 0x26, 0x63, 0x00, 0x00, 0x08, 0x81, +0x00, 0x03, 0x4e, 0x11, 0x00, 0x00, 0x28, 0x05, +0x00, 0x03, 0x4f, 0x10, 0x00, 0x00, 0x00, 0x01, +0x00, 0x03, 0x71, 0x10, 0x00, 0x03, 0x50, 0x05, +0x00, 0x00, 0xc1, 0x81, 0x00, 0x03, 0x4b, 0x13, +0x00, 0x00, 0x2c, 0x03, 0x00, 0x03, 0x58, 0x10, +0x00, 0x03, 0x50, 0x36, 0x00, 0x01, 0x41, 0x81, +0x00, 0x03, 0x4d, 0x13, 0x00, 0x03, 0x52, 0x04, +0x00, 0x01, 0x48, 0x81, 0x00, 0x01, 0xa5, 0x0d, +0x00, 0x01, 0xca, 0x81, 0x00, 0x03, 0x53, 0x32, +0x00, 0x00, 0x2d, 0x08, 0x00, 0x00, 0x29, 0x03, +0x00, 0x01, 0xc9, 0x01, 0x00, 0x03, 0x56, 0x12, +0x00, 0x03, 0x52, 0x31, 0x00, 0x00, 0x2b, 0x62, +0x00, 0x01, 0x4d, 0x81, 0x00, 0x00, 0x2d, 0x8d, +0x00, 0x03, 0x5a, 0x13, 0x00, 0x01, 0xcd, 0x81, +0x00, 0x03, 0x59, 0x13, 0x00, 0x01, 0xc0, 0x01, +0x00, 0x03, 0x4d, 0x11, 0x00, 0x01, 0xe2, 0xad, +0x00, 0x03, 0x18, 0x62, 0x00, 0x03, 0x1c, 0x63, +0x00, 0x03, 0x1e, 0x64, 0x00, 0x03, 0x0e, 0x65, +0x00, 0x03, 0x32, 0x66, 0x00, 0x03, 0xa6, 0xed, +0x00, 0x03, 0xa8, 0xf2, 0x00, 0x03, 0xa3, 0x03, +0x00, 0x03, 0xab, 0x09, 0x00, 0x03, 0x0a, 0x62, +0x00, 0x03, 0x50, 0x31, 0x00, 0x03, 0x20, 0x64, +0x00, 0x03, 0x28, 0x65, 0x00, 0x03, 0x34, 0x66, +0x00, 0x03, 0x8f, 0x0c, 0x00, 0x03, 0xa9, 0x0c, +0x00, 0x03, 0xaa, 0xfb, 0x00, 0x03, 0xa2, 0xfe, +0x00, 0x03, 0x02, 0x62, 0x00, 0x03, 0x8f, 0x0c, +0x00, 0x03, 0xa2, 0xf6, 0x00, 0x03, 0xa5, 0x0c, +0x00, 0x03, 0x2a, 0x66, 0x00, 0x03, 0x8f, 0x0c, +0x00, 0x03, 0x30, 0x63, 0x00, 0x03, 0x12, 0x64, +0x00, 0x03, 0x2c, 0x65, 0x00, 0x03, 0x2a, 0x66, +0x00, 0x03, 0x8f, 0x0c, 0x00, 0x03, 0x02, 0x62, +0x00, 0x03, 0x0c, 0x65, 0x00, 0x03, 0x8f, 0x0c, +0x00, 0x03, 0x0a, 0x62, 0x00, 0x03, 0x30, 0x64, +0x00, 0x03, 0x12, 0x65, 0x00, 0x03, 0x2c, 0x66, +0x00, 0x03, 0x8f, 0x0c, 0x00, 0x03, 0x0a, 0x62, +0x00, 0x03, 0x30, 0x63, 0x00, 0x03, 0x12, 0x64, +0x00, 0x03, 0x2c, 0x65, 0x00, 0x03, 0x16, 0x66, +0x00, 0x03, 0x8f, 0x0c, 0x00, 0x03, 0x02, 0x62, +0x00, 0x03, 0x0c, 0x65, 0x00, 0x03, 0x34, 0x66, +0x00, 0x03, 0x14, 0x67, 0x00, 0x03, 0xa9, 0x0f, +0x00, 0x03, 0x10, 0x67, 0x00, 0x03, 0x4c, 0x1f, +0x00, 0x03, 0x8f, 0x86, 0x00, 0x02, 0xda, 0x44, +0x00, 0x02, 0xe2, 0x01, 0x00, 0x02, 0xe0, 0x04, +0x00, 0x02, 0xee, 0x00, 0x00, 0x03, 0x4d, 0x57, +0x00, 0x00, 0x27, 0x6d, 0x00, 0x01, 0xc0, 0x01, +0x00, 0x03, 0x8b, 0x14, 0x00, 0x02, 0xe0, 0x04, +0x00, 0x02, 0xef, 0x00, 0x00, 0x01, 0x5d, 0x0a, +0x00, 0x03, 0x4d, 0x52, 0x00, 0x00, 0x27, 0x6d, +0x00, 0x03, 0x4d, 0x57, 0x00, 0x00, 0x27, 0x6d, +0x00, 0x01, 0xc0, 0x01, 0x00, 0x03, 0x8b, 0x1a, +0x00, 0x02, 0xe0, 0x06, 0x00, 0x02, 0xee, 0x00, +0x00, 0x03, 0x4d, 0x57, 0x00, 0x00, 0x27, 0x6d, +0x00, 0x01, 0xc0, 0x01, 0x00, 0x03, 0x8b, 0x23, +0x00, 0x02, 0xef, 0x00, 0x00, 0x03, 0x2e, 0x56, +0x00, 0x02, 0xef, 0x00, 0x00, 0x03, 0x2e, 0x57, +0x00, 0x02, 0xef, 0x00, 0x00, 0x03, 0x71, 0x17, +0x00, 0x02, 0xef, 0x00, 0x00, 0x03, 0x72, 0x17, +0x00, 0x02, 0xef, 0x00, 0x00, 0x03, 0x77, 0x17, +0x00, 0x02, 0xef, 0x00, 0x00, 0x03, 0x41, 0x17, +0x00, 0x03, 0xa1, 0x36, 0x00, 0x01, 0x5f, 0x82, +0x00, 0x02, 0xf1, 0x75, 0x00, 0x03, 0x59, 0x17, +0x00, 0x03, 0x5b, 0x1f, 0x00, 0x03, 0x8f, 0xc5, +0x00, 0x00, 0xc4, 0x89, 0x00, 0x01, 0x40, 0x07, +0x00, 0x03, 0x4d, 0x10, 0x00, 0x00, 0xa4, 0x0d, +0x00, 0x03, 0x73, 0x10, 0x00, 0x02, 0xee, 0x00, +0x00, 0x03, 0x50, 0x34, 0x00, 0x03, 0x20, 0x03, +0x00, 0x02, 0xe4, 0x00, 0x00, 0x03, 0x53, 0x00, +0x00, 0x03, 0xb3, 0x48, 0x00, 0x01, 0x4d, 0x84, +0x00, 0x02, 0x8c, 0x01, 0x00, 0x03, 0x89, 0x50, +0x00, 0x00, 0x4c, 0x0e, 0x00, 0x01, 0x40, 0x01, +0x00, 0x02, 0x40, 0x05, 0x00, 0x03, 0x81, 0x50, +0x00, 0x02, 0xe4, 0x01, 0x00, 0x02, 0x40, 0x03, +0x00, 0x03, 0x81, 0x50, 0x00, 0x02, 0xe4, 0x02, +0x00, 0x03, 0x75, 0x12, 0x00, 0x02, 0xe0, 0x00, +0x00, 0x03, 0x76, 0x10, 0x00, 0x02, 0x48, 0x02, +0x00, 0x03, 0x85, 0x70, 0x00, 0x03, 0x59, 0x37, +0x00, 0x03, 0x50, 0x33, 0x00, 0x01, 0x40, 0x02, +0x00, 0x03, 0x58, 0x10, 0x00, 0x03, 0x5b, 0x1f, +0x00, 0x03, 0x8f, 0xc5, 0x00, 0x00, 0xc4, 0x81, +0x00, 0x01, 0x40, 0x0f, 0x00, 0x03, 0x4d, 0x10, +0x00, 0x00, 0xa4, 0x0d, 0x00, 0x00, 0x23, 0xaf, +0x00, 0x03, 0x59, 0x31, 0x00, 0x03, 0xb3, 0x63, +0x00, 0x03, 0x59, 0x32, 0x00, 0x03, 0x4c, 0x1f, +0x00, 0x03, 0x8f, 0x7c, 0x00, 0x03, 0x72, 0x10, +0x00, 0x03, 0x4b, 0x0e, 0x00, 0x02, 0x60, 0x0e, +0x00, 0x03, 0x85, 0x6a, 0x00, 0x03, 0x4b, 0x10, +0x00, 0x03, 0x50, 0x0e, 0x00, 0x01, 0xa0, 0x0b, +0x00, 0x03, 0x76, 0x10, 0x00, 0x03, 0x51, 0x32, +0x00, 0x01, 0xa4, 0x8b, 0x00, 0x03, 0x72, 0x11, +0x00, 0x02, 0xef, 0x00, 0x00, 0x03, 0x50, 0x10, +0x00, 0x02, 0xef, 0x00, 0x00, 0x03, 0x4a, 0x17, +0x00, 0x02, 0xef, 0x00, 0x00, 0x03, 0x4b, 0x17, +0x00, 0x02, 0xef, 0x00, 0x00, 0x03, 0x4b, 0x17, +0x00, 0x02, 0xee, 0x00, 0x00, 0x03, 0x45, 0x17, +0x00, 0x02, 0xee, 0x00, 0x00, 0x03, 0x8f, 0x7a, +0x00, 0x03, 0x50, 0x33, 0x00, 0x01, 0x40, 0x02, +0x00, 0x03, 0x58, 0x10, 0x00, 0x03, 0x5b, 0x1f, +0x00, 0x03, 0x8f, 0xc5, 0x00, 0x00, 0xc4, 0x81, +0x00, 0x01, 0x40, 0x0f, 0x00, 0x03, 0x4d, 0x10, +0x00, 0x03, 0x5f, 0x0c, 0x00, 0x00, 0xa4, 0x0d, +0x00, 0x03, 0x58, 0x35, 0x00, 0x03, 0x59, 0x36, +0x00, 0x03, 0x5b, 0x1f, 0x00, 0x03, 0x8f, 0xc5, +0x00, 0x00, 0xc4, 0x89, 0x00, 0x01, 0x40, 0x07, +0x00, 0x03, 0x4d, 0x10, 0x00, 0x00, 0xa4, 0x0d, +0x00, 0x03, 0xa1, 0x90, 0x00, 0x01, 0x40, 0x02, +0x00, 0x03, 0xb1, 0x92, 0x00, 0x00, 0xc0, 0x02, +0x00, 0x03, 0x5f, 0x0c, 0x00, 0x03, 0x4f, 0x10, +0x00, 0x03, 0xa7, 0x96, 0x00, 0x02, 0xec, 0x0f, +0x00, 0x03, 0x4c, 0x33, 0x00, 0x00, 0x3b, 0x0c, +0x00, 0x03, 0x87, 0x9a, 0x00, 0x02, 0xec, 0x00, +0x00, 0x03, 0x4d, 0x16, 0x00, 0x00, 0xd8, 0x01, +0x00, 0x00, 0x22, 0x03, 0x00, 0x03, 0x52, 0x03, +0x00, 0x01, 0x49, 0x01, 0x00, 0x00, 0x2a, 0x8d, +0x00, 0x03, 0x50, 0x15, 0x00, 0x03, 0xa9, 0xbf, +0x00, 0x03, 0xa7, 0xb5, 0x00, 0x03, 0xab, 0xb0, +0x00, 0x03, 0xad, 0xab, 0x00, 0x03, 0xa3, 0xa8, +0x00, 0x03, 0xa5, 0xc2, 0x00, 0x03, 0x8f, 0xc3, +0x00, 0x03, 0x50, 0x14, 0x00, 0x03, 0xa5, 0xc3, +0x00, 0x03, 0x8f, 0xc2, 0x00, 0x03, 0xa3, 0xae, +0x00, 0x03, 0xaf, 0xc3, 0x00, 0x03, 0x8f, 0xc2, +0x00, 0x03, 0x50, 0x14, 0x00, 0x03, 0x8f, 0xc2, +0x00, 0x03, 0x50, 0x16, 0x00, 0x03, 0xa3, 0xb3, +0x00, 0x03, 0x8f, 0xc3, 0x00, 0x00, 0xc0, 0x01, +0x00, 0x03, 0x8f, 0xc2, 0x00, 0x03, 0xab, 0xbc, +0x00, 0x03, 0xa3, 0xb9, 0x00, 0x03, 0xaf, 0xc3, +0x00, 0x03, 0x8f, 0xc2, 0x00, 0x03, 0x50, 0x14, +0x00, 0x03, 0xaf, 0xc3, 0x00, 0x03, 0x8f, 0xc2, +0x00, 0x03, 0x50, 0x16, 0x00, 0x01, 0xc0, 0x01, +0x00, 0x03, 0x8f, 0xc3, 0x00, 0x03, 0xa7, 0xc1, +0x00, 0x03, 0x8f, 0xa4, 0x00, 0x03, 0xaf, 0xc3, +0x00, 0x00, 0x00, 0x01, 0x00, 0x03, 0x5f, 0x1b, +0x00, 0x03, 0x20, 0x70, 0x00, 0x02, 0xe4, 0x01, +0x00, 0x02, 0xe0, 0x01, 0x00, 0x00, 0xc0, 0x0f, +0x00, 0x03, 0x5a, 0x10, 0x00, 0x02, 0xe0, 0x00, +0x00, 0x02, 0xe2, 0x00, 0x00, 0x02, 0xa8, 0x19, +0x00, 0x03, 0x89, 0xce, 0x00, 0x00, 0x24, 0x98, +0x00, 0x01, 0x40, 0x01, 0x00, 0x02, 0x84, 0x01, +0x00, 0x03, 0x89, 0xd2, 0x00, 0x00, 0xa0, 0x1a, +0x00, 0x01, 0x44, 0x81, 0x00, 0x00, 0xc9, 0x01, +0x00, 0x03, 0x8b, 0xcb, 0x00, 0x03, 0x5f, 0x1b, +0x00, 0x03, 0x50, 0x10, 0x00, 0x03, 0x50, 0x10, +0x00, 0x03, 0x50, 0x10, 0x00, 0x03, 0x50, 0x10, +0x00, 0x03, 0x50, 0x10, 0x00, 0x03, 0x50, 0x10, +0x00, 0x03, 0x50, 0x10, 0x00, 0x03, 0x50, 0x10, +0x00, 0x03, 0x50, 0x10, 0x00, 0x03, 0x50, 0x10, +0x00, 0x03, 0x50, 0x10, 0x00, 0x03, 0x50, 0x10, +0x00, 0x03, 0x50, 0x10, 0x00, 0x03, 0x50, 0x10, +0x00, 0x03, 0x50, 0x10, 0x00, 0x03, 0x50, 0x10, +0x00, 0x03, 0x50, 0x10, 0x00, 0x03, 0x50, 0x10, +0x00, 0x03, 0x50, 0x10, 0x00, 0x03, 0x50, 0x10, +0x00, 0x03, 0x50, 0x10, 0x00, 0x03, 0x50, 0x10, +0x00, 0x03, 0x50, 0x10, 0x00, 0x03, 0x50, 0x10, +0x00, 0x03, 0x50, 0x10, 0x00, 0x03, 0x50, 0x10, +0x00, 0x03, 0x50, 0x10, 0x00, 0x03, 0x50, 0x10, +0x00, 0x03, 0x50, 0x10, 0x00, 0x03, 0x50, 0x10, +0x00, 0x03, 0x50, 0x10, 0x00, 0x03, 0x50, 0x10, +0x00, 0x03, 0x50, 0x10, 0x00, 0x03, 0x50, 0x10, +0x00, 0x03, 0x50, 0x10, 0x00, 0x03, 0x50, 0x10, +0x00, 0x03, 0x50, 0x10, 0x00, 0x03, 0x50, 0x10, +0x00, 0x03, 0x50, 0x10, 0x00, 0x03, 0x50, 0x10, +0x00, 0x03, 0x50, 0x10, 0x00, 0x03, 0x50, 0x10, +0x0a, 0x0d, +}; +static uchar +zrmpeg3s[] = { +0x00, 0x03, 0x8e, 0x00, 0x00, 0x03, 0xc0, 0x0a, +0x00, 0x03, 0x50, 0x0b, 0x00, 0x01, 0x40, 0x08, +0x00, 0x02, 0x80, 0x02, 0x00, 0x03, 0x8a, 0x07, +0x00, 0x02, 0xca, 0x00, 0x00, 0x02, 0x80, 0x01, +0x00, 0x03, 0x8a, 0x0a, 0x00, 0x02, 0xd6, 0xe0, +0x00, 0x02, 0xce, 0x00, 0x00, 0x03, 0x4c, 0x80, +0x00, 0x02, 0xde, 0x04, 0x00, 0x02, 0xc2, 0x00, +0x00, 0x02, 0xd2, 0x00, 0x00, 0x02, 0xf8, 0x00, +0x00, 0x03, 0x5d, 0x10, 0x00, 0x03, 0x5b, 0x1f, +0x00, 0x03, 0x8f, 0x8c, 0x00, 0x03, 0x88, 0x1d, +0x00, 0x02, 0x5c, 0xfa, 0x00, 0x03, 0x8a, 0x11, +0x00, 0x02, 0xe8, 0x46, 0x00, 0x02, 0xf8, 0x00, +0x00, 0x02, 0xef, 0x00, 0x00, 0x03, 0x5d, 0x17, +0x00, 0x01, 0xd2, 0x01, 0x00, 0x03, 0x8a, 0x18, +0x00, 0x03, 0x8e, 0x11, 0x00, 0x02, 0xee, 0x00, +0x00, 0x03, 0x5b, 0x1f, 0x00, 0x03, 0x8f, 0xd2, +0x00, 0x03, 0xa0, 0x22, 0x00, 0x03, 0x46, 0x17, +0x00, 0x03, 0x5b, 0x1f, 0x00, 0x03, 0x8f, 0x8c, +0x00, 0x02, 0x5c, 0xbb, 0x00, 0x03, 0x8a, 0x29, +0x00, 0x03, 0x5b, 0x1f, 0x00, 0x03, 0x8f, 0x8c, +0x00, 0x03, 0x88, 0x1d, 0x00, 0x02, 0x5c, 0xb9, +0x00, 0x03, 0x89, 0x00, 0x00, 0x02, 0x5c, 0xbc, +0x00, 0x03, 0x84, 0x26, 0x00, 0x03, 0x50, 0x17, +0x00, 0x02, 0xef, 0x00, 0x00, 0x03, 0x54, 0x17, +0x00, 0x02, 0xee, 0x00, 0x00, 0x01, 0xd2, 0x01, +0x00, 0x02, 0x5c, 0xff, 0x00, 0x03, 0x88, 0x30, +0x00, 0x01, 0x5d, 0x06, 0x00, 0x02, 0x48, 0x01, +0x00, 0x03, 0x8a, 0x3a, 0x00, 0x02, 0xee, 0x00, +0x00, 0x01, 0xd2, 0x02, 0x00, 0x02, 0xee, 0x00, +0x00, 0x01, 0x5d, 0x04, 0x00, 0x03, 0x88, 0x46, +0x00, 0x03, 0x5b, 0x1f, 0x00, 0x03, 0x8f, 0xd2, +0x00, 0x03, 0x44, 0x17, 0x00, 0x01, 0xd2, 0x04, +0x00, 0x02, 0x48, 0x03, 0x00, 0x03, 0x8a, 0x46, +0x00, 0x02, 0xee, 0x00, 0x00, 0x03, 0x5b, 0x1f, +0x00, 0x03, 0x8f, 0xd2, 0x00, 0x01, 0xd2, 0x05, +0x00, 0x02, 0x50, 0x05, 0x00, 0x03, 0x86, 0x26, +0x00, 0x02, 0xe2, 0xff, 0x00, 0x00, 0x67, 0x6b, +0x00, 0x02, 0x60, 0x0d, 0x00, 0x03, 0x85, 0xfa, +0x00, 0x03, 0xb2, 0xab, 0x00, 0x02, 0x88, 0x02, +0x00, 0x03, 0x89, 0x16, 0x00, 0x03, 0x42, 0x17, +0x00, 0x03, 0x8e, 0xa0, 0x00, 0x02, 0x88, 0x02, +0x00, 0x03, 0x89, 0x16, 0x00, 0x03, 0x55, 0x00, +0x00, 0x02, 0x94, 0x01, 0x00, 0x03, 0x88, 0x5a, +0x00, 0x01, 0x57, 0x01, 0x00, 0x00, 0x5b, 0x07, +0x00, 0x03, 0xb5, 0x16, 0x00, 0x03, 0x8e, 0x5f, +0x00, 0x02, 0x94, 0x10, 0x00, 0x03, 0x89, 0x16, +0x00, 0x01, 0x57, 0x05, 0x00, 0x00, 0x5b, 0x07, +0x00, 0x03, 0xb7, 0x16, 0x00, 0x01, 0x41, 0x05, +0x00, 0x02, 0x48, 0x06, 0x00, 0x03, 0x8b, 0x16, +0x00, 0x02, 0x58, 0x00, 0x00, 0x03, 0x8b, 0x16, +0x00, 0x03, 0x55, 0x00, 0x00, 0x02, 0x94, 0x01, +0x00, 0x03, 0x8a, 0x69, 0x00, 0x03, 0x4b, 0x81, +0x00, 0x03, 0x8e, 0x6a, 0x00, 0x03, 0x4a, 0x81, +0x00, 0x03, 0x45, 0x10, 0x00, 0x03, 0xa0, 0x6e, +0x00, 0x03, 0x40, 0x81, 0x00, 0x03, 0x22, 0x16, +0x00, 0x03, 0x50, 0x35, 0x00, 0x02, 0x40, 0x01, +0x00, 0x03, 0x80, 0x75, 0x00, 0x03, 0x84, 0x7d, +0x00, 0x02, 0xe0, 0x00, 0x00, 0x02, 0xe2, 0x00, +0x00, 0x03, 0x8e, 0x79, 0x00, 0x03, 0x4d, 0x32, +0x00, 0x03, 0x50, 0x04, 0x00, 0x03, 0x4c, 0x1f, +0x00, 0x03, 0x8f, 0x83, 0x00, 0x03, 0x20, 0x10, +0x00, 0x03, 0x22, 0x11, 0x00, 0x03, 0x20, 0x12, +0x00, 0x03, 0x22, 0x13, 0x00, 0x03, 0xb4, 0x80, +0x00, 0x03, 0xb6, 0x80, 0x00, 0x03, 0x8e, 0x51, +0x00, 0x03, 0x55, 0x1c, 0x00, 0x02, 0xf8, 0x01, +0x00, 0x03, 0xb4, 0x84, 0x00, 0x02, 0xf8, 0x02, +0x00, 0x03, 0x51, 0x07, 0x00, 0x03, 0x5d, 0x11, +0x00, 0x01, 0xd2, 0x02, 0x00, 0x03, 0xa6, 0x90, +0x00, 0x03, 0x43, 0x81, 0x00, 0x02, 0xe2, 0xff, +0x00, 0x00, 0xc4, 0x88, 0x00, 0x00, 0x67, 0x65, +0x00, 0x02, 0xee, 0x00, 0x00, 0x00, 0xbc, 0x8d, +0x00, 0x03, 0x5d, 0x11, 0x00, 0x01, 0xd2, 0x01, +0x00, 0x02, 0xef, 0x00, 0x00, 0x03, 0x5d, 0x17, +0x00, 0x01, 0xd2, 0x02, 0x00, 0x02, 0x50, 0x01, +0x00, 0x03, 0x80, 0x90, 0x00, 0x02, 0xef, 0x00, +0x00, 0x03, 0x47, 0x17, 0x00, 0x02, 0x90, 0x01, +0x00, 0x03, 0x88, 0x26, 0x00, 0x02, 0xee, 0x00, +0x00, 0x00, 0xdd, 0x08, 0x00, 0x02, 0xe0, 0xff, +0x00, 0x00, 0x61, 0x65, 0x00, 0x00, 0xa9, 0x65, +0x00, 0x03, 0x43, 0x80, 0x00, 0x03, 0x8e, 0x26, +0x00, 0x03, 0x48, 0x81, 0x00, 0x03, 0x49, 0x81, +0x00, 0x03, 0xa0, 0xa5, 0x00, 0x03, 0x40, 0x81, +0x00, 0x03, 0x22, 0x16, 0x00, 0x03, 0x4d, 0x36, +0x00, 0x03, 0x50, 0x02, 0x00, 0x03, 0x4c, 0x1f, +0x00, 0x03, 0x8f, 0x83, 0x00, 0x03, 0x20, 0x14, +0x00, 0x03, 0x22, 0x15, 0x00, 0x03, 0x50, 0x0e, +0x00, 0x02, 0xf8, 0x00, 0x00, 0x03, 0x51, 0x07, +0x00, 0x03, 0x5d, 0x11, 0x00, 0x03, 0x45, 0x80, +0x00, 0x03, 0xa5, 0x1d, 0x00, 0x01, 0xd2, 0x02, +0x00, 0x03, 0xa2, 0xbc, 0x00, 0x03, 0x41, 0x81, +0x00, 0x02, 0xe2, 0xff, 0x00, 0x00, 0xc4, 0x88, +0x00, 0x00, 0x67, 0x60, 0x00, 0x02, 0xee, 0x00, +0x00, 0x00, 0xbf, 0x8d, 0x00, 0x00, 0x12, 0x01, +0x00, 0x03, 0x8e, 0xbd, 0x00, 0x03, 0x50, 0x17, +0x00, 0x02, 0xef, 0x00, 0x00, 0x03, 0xa8, 0xc7, +0x00, 0x02, 0x5d, 0xe0, 0x00, 0x03, 0x89, 0x98, +0x00, 0x02, 0x5c, 0x00, 0x00, 0x03, 0x8a, 0xc7, +0x00, 0x02, 0xec, 0x01, 0x00, 0x01, 0xbb, 0xef, +0x00, 0x03, 0x8a, 0xcd, 0x00, 0x02, 0xde, 0x01, +0x00, 0x03, 0x8e, 0xcf, 0x00, 0x02, 0xde, 0x04, +0x00, 0x02, 0x5c, 0x01, 0x00, 0x03, 0x89, 0x1d, +0x00, 0x01, 0x5f, 0x08, 0x00, 0x02, 0x58, 0x01, +0x00, 0x03, 0x89, 0x70, 0x00, 0x03, 0xa8, 0xcf, +0x00, 0x03, 0x5d, 0x17, 0x00, 0x01, 0xd2, 0x02, +0x00, 0x02, 0x50, 0x01, 0x00, 0x03, 0x80, 0xbb, +0x00, 0x03, 0x51, 0x17, 0x00, 0x02, 0xef, 0x00, +0x00, 0x03, 0x45, 0x81, 0x00, 0x03, 0x42, 0x80, +0x00, 0x02, 0xce, 0x00, 0x00, 0x03, 0x4e, 0x17, +0x00, 0x03, 0xa8, 0xda, 0x00, 0x03, 0x47, 0x17, +0x00, 0x02, 0xd8, 0x26, 0x00, 0x02, 0x90, 0x01, +0x00, 0x03, 0x88, 0xde, 0x00, 0x02, 0xd8, 0xf9, +0x00, 0x03, 0x4f, 0x81, 0x00, 0x02, 0x5c, 0x01, +0x00, 0x03, 0x88, 0xea, 0x00, 0x01, 0x5e, 0x88, +0x00, 0x02, 0x54, 0x01, 0x00, 0x03, 0x88, 0xe5, +0x00, 0x03, 0x5f, 0x0c, 0x00, 0x02, 0xda, 0xff, +0x00, 0x00, 0x7f, 0x0d, 0x00, 0x02, 0x44, 0x00, +0x00, 0x03, 0x89, 0x2d, 0x00, 0x03, 0x5f, 0x0c, +0x00, 0x02, 0x84, 0xff, 0x00, 0x03, 0x8a, 0xf1, +0x00, 0x02, 0x90, 0x01, 0x00, 0x03, 0x8a, 0xf4, +0x00, 0x03, 0x42, 0x81, 0x00, 0x02, 0xce, 0x00, +0x00, 0x02, 0xdc, 0x00, 0x00, 0x03, 0x50, 0x10, +0x00, 0x03, 0x5f, 0x0c, 0x00, 0x03, 0x50, 0x10, +0x00, 0x02, 0xee, 0x00, 0x00, 0x00, 0xdd, 0x08, +0x00, 0x03, 0x56, 0x17, 0x00, 0x02, 0xd8, 0xfb, +0x00, 0x03, 0x8f, 0x2d, 0x00, 0x02, 0xee, 0x00, +0x00, 0x00, 0xdd, 0x08, 0x00, 0x02, 0xe0, 0xff, +0x00, 0x00, 0x60, 0x20, 0x00, 0x00, 0xa8, 0x20, +0x00, 0x03, 0x41, 0x80, 0x00, 0x03, 0x8e, 0x26, +0x00, 0x03, 0xb9, 0x0a, 0x00, 0x03, 0x50, 0x1e, +0x00, 0x02, 0x40, 0x00, 0x00, 0x03, 0x89, 0x06, +0x00, 0x02, 0x40, 0x90, 0x00, 0x03, 0x8b, 0x08, +0x00, 0x02, 0xe0, 0x10, 0x00, 0x03, 0x20, 0xc0, +0x00, 0x02, 0xe0, 0x80, 0x00, 0x03, 0x20, 0xc0, +0x00, 0x03, 0x50, 0x07, 0x00, 0x03, 0xa7, 0x0e, +0x00, 0x03, 0x50, 0x05, 0x00, 0x03, 0x8f, 0x0f, +0x00, 0x03, 0x50, 0x05, 0x00, 0x02, 0xf8, 0x00, +0x00, 0x03, 0x5d, 0x10, 0x00, 0x03, 0x4c, 0x1f, +0x00, 0x03, 0x8f, 0x7c, 0x00, 0x02, 0xe0, 0xf0, +0x00, 0x03, 0x20, 0xc0, 0x00, 0x03, 0x8e, 0x00, +0x00, 0x02, 0x50, 0x00, 0x00, 0x03, 0x88, 0x26, +0x00, 0x01, 0xd2, 0x02, 0x00, 0x02, 0xef, 0x00, +0x00, 0x01, 0xd2, 0x02, 0x00, 0x03, 0x85, 0x19, +0x00, 0x03, 0x8e, 0x26, 0x00, 0x03, 0x4f, 0x80, +0x00, 0x03, 0x56, 0x10, 0x00, 0x03, 0xbf, 0x21, +0x00, 0x00, 0xdb, 0x08, 0x00, 0x02, 0x58, 0x00, +0x00, 0x03, 0x8a, 0xcd, 0x00, 0x02, 0xd8, 0xcd, +0x00, 0x03, 0xbf, 0x2b, 0x00, 0x01, 0xd2, 0x02, +0x00, 0x02, 0x50, 0x01, 0x00, 0x03, 0x87, 0x72, +0x00, 0x02, 0xef, 0x00, 0x00, 0x01, 0x5f, 0x08, +0x00, 0x03, 0x8f, 0x2d, 0x00, 0x00, 0xdf, 0x08, +0x00, 0x01, 0x5b, 0x08, 0x00, 0x02, 0x58, 0xb7, +0x00, 0x03, 0x89, 0x6b, 0x00, 0x02, 0x58, 0xb8, +0x00, 0x03, 0x89, 0x5e, 0x00, 0x02, 0xea, 0x00, +0x00, 0x02, 0x74, 0x0a, 0x00, 0x03, 0x89, 0x63, +0x00, 0x02, 0x58, 0xb3, 0x00, 0x03, 0x89, 0x5f, +0x00, 0x02, 0x58, 0xb8, 0x00, 0x03, 0x89, 0x5f, +0x00, 0x02, 0x58, 0x00, 0x00, 0x03, 0x8b, 0x63, +0x00, 0x02, 0xec, 0x01, 0x00, 0x00, 0x3a, 0x69, +0x00, 0x03, 0xa9, 0x5f, 0x00, 0x02, 0x50, 0x01, +0x00, 0x03, 0x87, 0x63, 0x00, 0x03, 0x56, 0x0a, +0x00, 0x02, 0x78, 0x09, 0x00, 0x03, 0x83, 0x63, +0x00, 0x03, 0xbf, 0x49, 0x00, 0x00, 0xdf, 0x88, +0x00, 0x03, 0x4d, 0x17, 0x00, 0x02, 0xee, 0x00, +0x00, 0x00, 0xbf, 0x8d, 0x00, 0x01, 0xd2, 0x01, +0x00, 0x03, 0x8f, 0x4b, 0x00, 0x02, 0xef, 0x00, +0x00, 0x01, 0xd2, 0x02, 0x00, 0x00, 0x5f, 0x38, +0x00, 0x02, 0x58, 0x18, 0x00, 0x03, 0x8b, 0x59, +0x00, 0x03, 0x44, 0x81, 0x00, 0x03, 0x56, 0x0a, +0x00, 0x01, 0xba, 0x69, 0x00, 0x00, 0x38, 0x61, +0x00, 0x03, 0x56, 0x01, 0x00, 0x01, 0xbb, 0x0b, +0x00, 0x02, 0x78, 0x0a, 0x00, 0x03, 0x81, 0x58, +0x00, 0x02, 0xc2, 0x00, 0x00, 0x01, 0xba, 0x69, +0x00, 0x03, 0x8f, 0x68, 0x00, 0x02, 0xec, 0x00, +0x00, 0x03, 0x5d, 0x16, 0x00, 0x02, 0xed, 0x00, +0x00, 0x03, 0x5d, 0x16, 0x00, 0x03, 0x8f, 0x68, +0x00, 0x03, 0xaf, 0x75, 0x00, 0x03, 0x44, 0x80, +0x00, 0x02, 0xec, 0x00, 0x00, 0x03, 0x5d, 0x16, +0x00, 0x03, 0x47, 0x0e, 0x00, 0x03, 0xbf, 0x68, +0x00, 0x03, 0xa9, 0x68, 0x00, 0x03, 0xab, 0x68, +0x00, 0x02, 0xec, 0x01, 0x00, 0x03, 0x5d, 0x16, +0x00, 0x03, 0x50, 0x10, 0x00, 0x03, 0x5f, 0x0c, +0x00, 0x03, 0x50, 0x10, 0x00, 0x02, 0xec, 0xcd, +0x00, 0x02, 0x78, 0x0c, 0x00, 0x03, 0x8b, 0x76, +0x00, 0x02, 0xd8, 0xcf, 0x00, 0x03, 0x8f, 0x76, +0x00, 0x03, 0x4f, 0x81, 0x00, 0x03, 0x8f, 0x1e, +0x00, 0x02, 0xe2, 0x01, 0x00, 0x02, 0xd8, 0xd4, +0x00, 0x03, 0x8f, 0x28, 0x00, 0x02, 0xd9, 0x00, +0x00, 0x03, 0x4c, 0x81, 0x00, 0x02, 0xce, 0x00, +0x00, 0x02, 0xe6, 0x00, 0x00, 0x03, 0x5d, 0x13, +0x00, 0x02, 0xe7, 0xb7, 0x00, 0x03, 0x5d, 0x13, +0x00, 0x02, 0xec, 0x0a, 0x00, 0x02, 0xe6, 0x00, +0x00, 0x03, 0x5d, 0x13, 0x00, 0x01, 0xdb, 0x01, +0x00, 0x03, 0x8b, 0x7e, 0x00, 0x03, 0x5f, 0x0c, +0x00, 0x03, 0x50, 0x10, 0x00, 0x01, 0xe0, 0x06, +0x00, 0x01, 0x40, 0x01, 0x00, 0x03, 0x58, 0x10, +0x00, 0x03, 0x59, 0x33, 0x00, 0x03, 0x5b, 0x1f, +0x00, 0x03, 0x8f, 0xda, 0x00, 0x00, 0x24, 0x0d, +0x00, 0x03, 0x5f, 0x0c, 0x00, 0x01, 0x40, 0x88, +0x00, 0x02, 0xe0, 0x00, 0x00, 0x02, 0xee, 0x00, +0x00, 0x02, 0x5c, 0x01, 0x00, 0x03, 0x89, 0x93, +0x00, 0x03, 0x81, 0x8c, 0x00, 0x00, 0x00, 0x01, +0x00, 0x03, 0x8f, 0x8d, 0x00, 0x02, 0x40, 0x02, +0x00, 0x03, 0x85, 0x8c, 0x00, 0x02, 0xee, 0x00, +0x00, 0x03, 0x5f, 0x1b, 0x00, 0x02, 0x5c, 0xba, +0x00, 0x02, 0x50, 0x0d, 0x00, 0x03, 0x84, 0xc0, +0x00, 0x02, 0xea, 0x02, 0x00, 0x02, 0xe2, 0x3c, +0x00, 0x02, 0x50, 0x0d, 0x00, 0x03, 0x83, 0xa0, +0x00, 0x02, 0xef, 0x00, 0x00, 0x03, 0x8e, 0xc0, +0x00, 0x00, 0x16, 0x81, 0x00, 0x02, 0xee, 0x00, +0x00, 0x02, 0xc4, 0xff, 0x00, 0x00, 0x64, 0xa2, +0x00, 0x02, 0x7c, 0x02, 0x00, 0x03, 0x8b, 0xb0, +0x00, 0x00, 0x45, 0x07, 0x00, 0x01, 0x44, 0x83, +0x00, 0x00, 0xc9, 0x08, 0x00, 0x03, 0x42, 0x12, +0x00, 0x00, 0xa4, 0x82, 0x00, 0x02, 0x54, 0x0b, +0x00, 0x03, 0x85, 0xa0, 0x00, 0x02, 0xea, 0x00, +0x00, 0x01, 0xd2, 0x0b, 0x00, 0x03, 0x8f, 0x9c, +0x00, 0x03, 0x43, 0x15, 0x00, 0x01, 0xf2, 0x03, +0x00, 0x02, 0xe2, 0x07, 0x00, 0x00, 0xc4, 0x88, +0x00, 0x00, 0x04, 0x81, 0x00, 0x00, 0xc5, 0x08, +0x00, 0x02, 0x54, 0x02, 0x00, 0x03, 0x81, 0xc3, +0x00, 0x03, 0x85, 0xbd, 0x00, 0x03, 0x42, 0x12, +0x00, 0x00, 0xbf, 0x82, 0x00, 0x00, 0x12, 0x02, +0x00, 0x03, 0x8e, 0xc0, 0x00, 0x00, 0xdd, 0x08, +0x00, 0x03, 0x43, 0x12, 0x00, 0x02, 0xee, 0x00, +0x00, 0x00, 0xbf, 0x83, 0x00, 0x00, 0x12, 0x01, +0x00, 0x03, 0x8e, 0xc0, 0x00, 0x03, 0x43, 0x12, +0x00, 0x03, 0x4f, 0x80, 0x00, 0x00, 0x45, 0x07, +0x00, 0x01, 0x44, 0x83, 0x00, 0x00, 0xc9, 0x08, +0x00, 0x03, 0x42, 0x12, 0x00, 0x00, 0xa4, 0x82, +0x00, 0x03, 0xbf, 0xb5, 0x00, 0x00, 0xc5, 0x08, +0x00, 0x01, 0x49, 0x08, 0x00, 0x00, 0xa9, 0x03, +0x00, 0x03, 0x5d, 0x12, 0x00, 0x01, 0xd6, 0x82, +0x00, 0x03, 0x4f, 0x81, 0x00, 0x03, 0x8f, 0xc5, +0x00, 0x02, 0xef, 0x00, 0x00, 0x00, 0x5f, 0x82, +0x00, 0x00, 0xdf, 0x8e, 0x00, 0x03, 0x5a, 0x17, +0x00, 0x02, 0xef, 0x00, 0x00, 0x01, 0x5f, 0x81, +0x00, 0x03, 0x5f, 0x1b, 0x00, 0x00, 0xbf, 0x9a, +0x00, 0x02, 0xe4, 0x01, 0x00, 0x02, 0xe0, 0x01, +0x00, 0x00, 0xc0, 0x0f, 0x00, 0x03, 0x5a, 0x10, +0x00, 0x02, 0xe0, 0x00, 0x00, 0x02, 0xe2, 0x00, +0x00, 0x02, 0xa8, 0x19, 0x00, 0x03, 0x89, 0xe3, +0x00, 0x00, 0x24, 0x98, 0x00, 0x01, 0x40, 0x01, +0x00, 0x02, 0x84, 0x01, 0x00, 0x03, 0x89, 0xe7, +0x00, 0x00, 0xa0, 0x1a, 0x00, 0x01, 0x44, 0x81, +0x00, 0x00, 0xc9, 0x01, 0x00, 0x03, 0x8b, 0xe0, +0x00, 0x03, 0x5f, 0x1b, 0x00, 0x03, 0x50, 0x10, +0x00, 0x03, 0x50, 0x10, 0x00, 0x03, 0x50, 0x10, +0x00, 0x03, 0x50, 0x10, 0x00, 0x03, 0x50, 0x10, +0x00, 0x03, 0x3c, 0xc0, 0x00, 0x03, 0x70, 0x10, +0x00, 0x03, 0x50, 0x1e, 0x00, 0x02, 0x40, 0xe0, +0x00, 0x03, 0x8b, 0xf6, 0x00, 0x03, 0x47, 0x81, +0x00, 0x01, 0x40, 0x04, 0x00, 0x03, 0x88, 0x01, +0x00, 0x03, 0x60, 0x80, 0x00, 0x03, 0x50, 0x30, +0x00, 0x02, 0xea, 0xff, 0x00, 0x00, 0x77, 0x65, +0x00, 0x03, 0x88, 0x51, 0x00, 0x02, 0x60, 0x0d, +0x00, 0x03, 0x88, 0x7d, 0x00, 0x03, 0x8f, 0x16, +0x0a, 0x0d, +}; |
