From 9fans-admin@cse.psu.edu Fri Sep 07 15:21:30 2001 Return-Path: <9fans-admin@cse.psu.edu> Delivered-To: dpx@acl.lanl.gov Received: (qmail 304752 invoked from network); 7 Sep 2001 09:21:30 -0600 Received: from mailrelay1.lanl.gov (128.165.3.6) by acl.lanl.gov with SMTP; 7 Sep 2001 09:21:30 -0600 Received: from mailproxy1.lanl.gov (localhost.localdomain [127.0.0.1]) by mailrelay1.lanl.gov (8.11.4/8.11.4/(ccn-5)) with ESMTP id f87FLTE22503; Fri, 7 Sep 2001 09:21:29 -0600 Received: from mail.cse.psu.edu (psuvax1.cse.psu.edu [130.203.4.6]) by mailproxy1.lanl.gov (8.11.6/8.11.6/(ccn-5)) with ESMTP id f87FLTQ30417; Fri, 7 Sep 2001 09:21:29 -0600 Received: from psuvax1.cse.psu.edu (psuvax1.cse.psu.edu [130.203.30.6]) by mail.cse.psu.edu (CSE Mail Server) with ESMTP id 2883E19A0C; Fri, 7 Sep 2001 11:21:15 -0400 (EDT) Delivered-To: 9fans@cse.psu.edu Received: from nautilus.escet.urjc.es (nautilus.escet.urjc.es [212.128.4.207]) by mail.cse.psu.edu (CSE Mail Server) with SMTP id 31D17199E8 for <9fans@cse.psu.edu>; Fri, 7 Sep 2001 11:20:30 -0400 (EDT) To: 9fans@cse.psu.edu From: "Fco.J.Ballesteros" MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="upas-cefrnfuwhwfqcaxqtnsykuzaqu" Message-Id: <20010907152030.31D17199E8@mail.cse.psu.edu> Subject: [9fans] sd on the bitsy Sender: 9fans-admin@cse.psu.edu Errors-To: 9fans-admin@cse.psu.edu X-BeenThere: 9fans@cse.psu.edu X-Mailman-Version: 2.0.6 Precedence: bulk Reply-To: 9fans@cse.psu.edu List-Id: Fans of the OS Plan 9 from Bell Labs <9fans.cse.psu.edu> List-Archive: Date: Fri, 7 Sep 2001 17:42:47 +0200 Status: RO X-Status: This is a multi-part message in MIME format. --upas-cefrnfuwhwfqcaxqtnsykuzaqu Content-Disposition: inline Content-Type: text/plain; charset="US-ASCII" Content-Transfer-Encoding: 7bit Hi, I'm now using kfs on the bitsy using a 2G pcmcia disk. This is just the pc sdata driver adapted a little bit to work on the bitsy, together with some fixes for cis that jmk kindly sent to me. I only have used it with a MK2001MPL 2G HDD, so it might fail with other drives. The files are bitsy/dat.h bitsy/sdata.c port/cis.c port/devsd.c port/sd.h Since I don't trust my network and the files are not too big, I attach them here. The change in port/sd is to allow it to be configured in the cpurc like this: # start sd if(grep -s MK2001MPL /dev/pcm0ctl){ echo -n 'configure #S0 sd 1'>/dev/pcm0ctl bind -a '#S' /dev } The bitsy is a cool plan9 mpeg player, not to talk about using it as an emergency file&cpu server... so thanks you all --upas-cefrnfuwhwfqcaxqtnsykuzaqu Content-Disposition: attachment; filename=dat.h Content-Type: text/plain; charset="US-ASCII" Content-Transfer-Encoding: 7bit typedef struct Cisdat Cisdat; typedef struct Conf Conf; typedef struct Cycintr Cycintr; typedef struct FPU FPU; typedef struct FPenv FPenv; typedef struct FPsave FPsave; typedef struct DevConf DevConf; typedef struct Label Label; typedef struct Lock Lock; typedef struct MMU MMU; typedef struct Mach Mach; typedef struct Notsave Notsave; typedef struct Page Page; typedef struct PCMmap PCMmap; typedef struct PCMslot PCMslot; typedef struct PCMconftab PCMconftab; typedef struct PhysUart PhysUart; typedef struct PMMU PMMU; typedef struct Proc Proc; typedef struct Uart Uart; typedef struct Ureg Ureg; typedef struct Vctl Vctl; typedef struct Uart Uart; typedef void IntrHandler(Ureg*, void*); /* * parameters for sysproc.c */ #define AOUT_MAGIC (E_MAGIC) struct Lock { ulong key; ulong sr; ulong pc; Proc *p; ushort isilock; }; struct Label { ulong sp; ulong pc; }; /* * FPsave.status */ enum { FPinit, FPactive, FPinactive, }; struct FPsave { ulong status; ulong control; ulong regs[8][3]; /* emulated fp */ }; struct Conf { ulong nmach; /* processors */ ulong nproc; /* processes */ ulong npage0; /* total physical pages of memory */ ulong npage1; /* total physical pages of memory */ ulong npage; /* total physical pages of memory */ ulong upages; /* user page pool */ ulong nimage; /* number of page cache image headers */ ulong nswap; /* number of swap pages */ int nswppo; /* max # of pageouts per segment pass */ ulong base0; /* base of bank 0 */ ulong base1; /* base of bank 1 */ ulong copymode; /* 0 is copy on write, 1 is copy on reference */ int monitor; ulong ialloc; /* bytes available for interrupt time allocation */ ulong pipeqsize; /* size in bytes of pipe queues */ ulong hz; /* processor cycle freq */ ulong mhz; }; /* * MMU stuff in proc */ enum { NCOLOR= 1, /* 1 level cache, don't worry about VCE's */ Nmeg= 32, /* maximum size of user space */ }; struct PMMU { Page *l1page[Nmeg]; /* this's process' level 1 entries */ ulong l1table[Nmeg]; /* ... */ Page *mmufree; /* free mmu pages */ }; /* * things saved in the Proc structure during a notify */ struct Notsave { int dummy; }; #include "../port/portdat.h" struct Mach { int machno; /* physical id of processor */ ulong splpc; /* pc of last caller to splhi */ Proc *proc; /* current process */ ulong mmupid; /* process id currently in mmu & cache */ ulong ticks; /* of the clock since boot time */ Label sched; /* scheduler wakeup */ Lock alarmlock; /* access to alarm list */ void* alarm; /* alarms bound to this clock */ int inclockintr; ulong fairness; /* for runproc */ /* stats */ int tlbfault; int tlbpurge; int pfault; int cs; int syscall; int load; int intr; vlong fastclock; /* last sampled value */ vlong intrts; /* time stamp of last interrupt */ ulong spuriousintr; int lastintr; int flushmmu; /* make current proc flush it's mmu state */ Proc *pid2proc[31]; /* what proc holds what pid */ int lastpid; /* highest assigned pid slot */ int cpumhz; /* speed of cpu */ int cpuhz; /* ... * /* save areas for exceptions */ ulong sfiq[5]; ulong sirq[5]; ulong sund[5]; ulong sabt[5]; int stack[1]; }; /* * fasttick timer interrupts */ struct Cycintr { vlong when; /* fastticks when f should be called */ void (*f)(Ureg*, Cycintr*); void *a; Cycintr *next; }; /* * Fake kmap since we direct map dram */ typedef void KMap; #define VA(k) ((ulong)(k)) #define kmap(p) (KMap*)((p)->pa) #define kunmap(k) struct { Lock; int machs; /* bitmap of active CPUs */ int exiting; /* shutdown */ int ispanic; /* shutdown in response to a panic */ }active; #define MACHP(n) ((Mach *)(MACHADDR+(n)*BY2PG)) extern Mach *m; extern Proc *up; enum { OneMeg= 1024*1024, }; /* * routines to access UART hardware */ struct PhysUart { void (*enable)(Uart*, int); void (*disable)(Uart*); void (*kick)(Uart*); void (*intr)(Ureg*, void*); void (*dobreak)(Uart*, int); void (*baud)(Uart*, int); void (*bits)(Uart*, int); void (*stop)(Uart*, int); void (*parity)(Uart*, int); void (*modemctl)(Uart*, int); void (*rts)(Uart*, int); void (*dtr)(Uart*, int); long (*status)(Uart*, void*, long, long); }; enum { Stagesize= 1024 }; /* * software UART */ struct Uart { QLock; int type; int dev; int opens; void *regs; PhysUart *phys; int enabled; Uart *elist; /* next enabled interface */ char name[NAMELEN]; uchar sticky[4]; /* sticky write register values */ ulong freq; /* clock frequency */ uchar mask; /* bits/char */ int baud; /* baud rate */ int parity; /* parity errors */ int frame; /* framing errors */ int overrun; /* rcvr overruns */ /* buffers */ int (*putc)(Queue*, int); Queue *iq; Queue *oq; uchar istage[Stagesize]; uchar *iw; uchar *ir; uchar *ie; Lock tlock; /* transmit */ uchar ostage[Stagesize]; uchar *op; uchar *oe; int modem; /* hardware flow control on */ int xonoff; /* software flow control on */ int blocked; int cts, dsr, dcd, dcdts; /* keep track of modem status */ int ctsbackoff; int hup_dsr, hup_dcd; /* send hangup upstream? */ int dohup; int kinuse; /* device in use by kernel */ Rendez r; }; /* * PCMCIA structures known by both port/cis.c and the pcmcia driver */ /* * Map between ISA memory space and PCMCIA card memory space. */ struct PCMmap { ulong ca; /* card address */ ulong cea; /* card end address */ ulong isa; /* local virtual address */ int len; /* length of the ISA area */ int attr; /* attribute memory */ }; /* * a PCMCIA configuration 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; }; /* * PCMCIA card slot */ struct PCMslot { RWlock; Ref ref; long memlen; /* memory length */ uchar slotno; /* slot number */ void *regs; /* i/o registers */ void *mem; /* memory */ void *attr; /* attribute memory */ /* status */ uchar occupied; /* card in the slot */ uchar configed; /* card configured */ /* cis info */ int cisread; /* set when the cis has been read */ char verstr[512]; /* version string */ uchar cpresent; /* config registers present */ ulong caddr; /* relative address of config registers */ int nctab; /* number of config table entries */ PCMconftab ctab[8]; PCMconftab *def; /* default conftab */ /* maps are fixed */ PCMmap memmap; PCMmap attrmap; }; /* * hardware info about a device */ struct DevConf { ulong mem; /* mapped memory address */ ulong port; /* mapped i/o regs */ int size; /* access size */ int itype; /* type of interrupt */ ulong interrupt; /* interrupt number */ char type[NAMELEN]; /* card type */ }; --upas-cefrnfuwhwfqcaxqtnsykuzaqu Content-Disposition: attachment; filename=sdata.c Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: 8bit #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; //BUG? #define PCIWADDR(x) ((ulong)(x)) enum { DbgCONFIG = 0x01, /* detected drive config info */ DbgIDENTIFY = 0x02, /* detected drive identify info */ DbgSTATE = 0x04, /* dump state on panic */ DbgPROBE = 0x08, /* trace device probing */ DbgDEBUG = 0x80, /* the current problem... */ }; #define DEBUG (DbgDEBUG|DbgSTATE|DbgCONFIG) enum { /* I/O ports */ Data = 0, Error = 1, /* (read) */ Features = 1, /* (write) */ Count = 2, /* sector count */ Ir = 2, /* interrupt reason (PACKET) */ Sector = 3, /* sector number, LBA<7-0> */ Cyllo = 4, /* cylinder low, LBA<15-8> */ Bytelo = 4, /* byte count low (PACKET) */ Cylhi = 5, /* cylinder high, LBA<23-16> */ Bytehi = 5, /* byte count hi (PACKET) */ Dh = 6, /* Device/Head, LBA<32-14> */ Status = 7, /* (read) */ Command = 7, /* (write) */ As = 2, /* Alternate Status (read) */ Dc = 2, /* Device Control (write) */ }; enum { /* Error */ Med = 0x01, /* Media error */ Ili = 0x01, /* command set specific (PACKET) */ Nm = 0x02, /* No Media */ Eom = 0x02, /* command set specific (PACKET) */ Abrt = 0x04, /* Aborted command */ Mcr = 0x08, /* Media Change Request */ Idnf = 0x10, /* no user-accessible address */ Mc = 0x20, /* Media Change */ Unc = 0x40, /* Uncorrectable data error */ Wp = 0x40, /* Write Protect */ Icrc = 0x80, /* Interface CRC error */ }; enum { /* Features */ Dma = 0x01, /* data transfer via DMA (PACKET) */ Ovl = 0x02, /* command overlapped (PACKET) */ }; enum { /* Interrupt Reason */ Cd = 0x01, /* Command/Data */ Io = 0x02, /* I/O direction */ Rel = 0x04, /* Bus Release */ }; enum { /* Device/Head */ Dev0 = 0xA0, /* Master */ Dev1 = 0xB0, /* Slave */ Lba = 0x40, /* LBA mode */ }; enum { /* Status, Alternate Status */ Err = 0x01, /* Error */ Chk = 0x01, /* Check error (PACKET) */ Drq = 0x08, /* Data Request */ Dsc = 0x10, /* Device Seek Complete */ Serv = 0x10, /* Service */ Df = 0x20, /* Device Fault */ Dmrd = 0x20, /* DMA ready (PACKET) */ Drdy = 0x40, /* Device Ready */ Bsy = 0x80, /* Busy */ }; enum { /* Command */ Cnop = 0x00, /* NOP */ Cdr = 0x08, /* Device Reset */ Crs = 0x20, /* Read Sectors */ Cws = 0x30, /* Write Sectors */ Cedd = 0x90, /* Execute Device Diagnostics */ Cpkt = 0xA0, /* Packet */ Cidpkt = 0xA1, /* Identify Packet Device */ Crsm = 0xC4, /* Read Multiple */ Cwsm = 0xC5, /* Write Multiple */ 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 */ }; enum { /* PCI Configuration Registers */ Bmiba = 0x20, /* Bus Master Interface Base Address */ Idetim = 0x40, /* IE Timing */ Sidetim = 0x44, /* Slave IE Timing */ Udmactl = 0x48, /* Ultra DMA/33 Control */ Udmatim = 0x4A, /* Ultra DMA/33 Timing */ }; enum { /* Bus Master IDE I/O Ports */ Bmicx = 0, /* Command */ Bmisx = 2, /* Status */ Bmidtpx = 4, /* Descriptor Table Pointer */ }; enum { /* Bmicx */ Ssbm = 0x01, /* Start/Stop Bus Master */ Rwcon = 0x08, /* Read/Write Control */ }; enum { /* Bmisx */ Bmidea = 0x01, /* Bus Master IDE Active */ Idedmae = 0x02, /* IDE DMA Error (R/WC) */ Ideints = 0x04, /* IDE Interrupt Status (R/WC) */ Dma0cap = 0x20, /* Drive 0 DMA Capable */ Dma1cap = 0x40, /* Drive 0 DMA Capable */ }; enum { /* Physical Region Descriptor */ PrdEOT = 0x80000000, /* Bus Master IDE Active */ }; enum { /* offsets into the identify info. */ Iconfig = 0, /* general configuration */ Ilcyl = 1, /* logical cylinders */ Ilhead = 3, /* logical heads */ Ilsec = 6, /* logical sectors per logical track */ Iserial = 10, /* serial number */ Ifirmware = 23, /* firmware revision */ Imodel = 27, /* model number */ Imaxrwm = 47, /* max. read/write multiple sectors */ Icapabilities = 49, /* capabilities */ Istandby = 50, /* device specific standby timer */ Ipiomode = 51, /* PIO data transfer mode number */ Ivalid = 53, Iccyl = 54, /* cylinders if (valid&0x01) */ Ichead = 55, /* heads if (valid&0x01) */ Icsec = 56, /* sectors if (valid&0x01) */ Iccap = 57, /* capacity if (valid&0x01) */ Irwm = 59, /* read/write multiple */ Ilba0 = 60, /* LBA size */ Ilba1 = 61, /* LBA size */ Imwdma = 63, /* multiword DMA mode */ Iapiomode = 64, /* advanced PIO modes supported */ Iminmwdma = 65, /* min. multiword DMA cycle time */ Irecmwdma = 66, /* rec. multiword DMA cycle time */ Iminpio = 67, /* min. PIO cycle w/o flow control */ Iminiordy = 68, /* min. PIO cycle with IORDY */ Ipcktbr = 71, /* time from PACKET to bus release */ Iserbsy = 72, /* time from SERVICE to !Bsy */ Iqdepth = 75, /* max. queue depth */ Imajor = 80, /* major version number */ Iminor = 81, /* minor version number */ 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 */ Irmsn = 127, /* removable status notification */ Istatus = 128, /* security status */ }; typedef struct Ctlr Ctlr; typedef struct Drive Drive; typedef struct Prd { ulong pa; /* Physical Base Address */ int count; } Prd; enum { Nprd = SDmaxio/(64*1024)+2, }; typedef struct Ctlr { int cmdport; int ctlport; int irq; int tbdf; int bmiba; /* bus master interface base address */ void (*ienable)(Ctlr*); SDev* sdev; Drive* drive[2]; Prd* prdt; /* physical region descriptor table */ QLock; /* current command */ Drive* curdrive; int command; /* last command issued (debugging) */ Rendez; int done; Lock; /* register access */ } Ctlr; typedef struct Drive { Ctlr* ctlr; int dev; ushort info[256]; int c; /* cylinder */ int h; /* head */ int s; /* sector */ int 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; } Drive; char* getconf(char*) { return nil; } static void atadumpstate(Drive* drive, uchar* cmd, int lba, int count) { Prd *prd; 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 -> %d, 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++; } } } 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 = doprint(buf, buf+sizeof(buf), fmt, arg) - buf; va_end(arg); if(cmdport){ if(buf[n-1] == '\n') n--; n += snprint(buf+n, PRINTSIZE-n, " ataregs 0x%uX:", cmdport); for(i = 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) ; 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; } µdelay(4); } 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); µdelay(4); 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] & 0x1F1F; 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); µdelay(4); 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 < 32; 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) { ushort *sp; Drive *drive; int as, i, pkt; uchar buf[512], *p; 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; if(drive->info[Iconfig] != 0x848A && (drive->info[Iconfig] & 0xC000) == 0x8000){ if(drive->info[Iconfig] & 0x01) drive->pkt = 16; else drive->pkt = 12; } else{ if(drive->info[Ivalid] & 0x0001){ drive->c = drive->info[Ilcyl]; drive->h = drive->info[Ilhead]; drive->s = drive->info[Ilsec]; } else{ drive->c = drive->info[Iccyl]; drive->h = drive->info[Ichead]; drive->s = drive->info[Icsec]; } if(drive->info[Icapabilities] & 0x0200){ drive->sectors = (drive->info[Ilba1]<<16) |drive->info[Ilba0]; drive->dev |= Lba; } else drive->sectors = drive->c*drive->h*drive->s; atarwmmode(drive, cmdport, ctlport, dev); } atadmamode(drive); if(DEBUG & DbgCONFIG){ print("dev %2.2uX port %uX config %4.4uX capabilities %4.4uX", dev, cmdport, drive->info[Iconfig], drive->info[Icapabilities]); print(" mwdma %4.4uX", drive->info[Imwdma]); if(drive->info[Ivalid] & 0x04) print(" udma %4.4uX", drive->info[Iudma]); print(" dma %8.8uX rwm %ud\n", drive->dma, drive->rwm); } 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. */ µdelay(20); outb(ctlport+Dc, Srst); µdelay(20); outb(ctlport+Dc, 0); µdelay(4*1000); } static SDev* ataprobe(int cmdport, int ctlport, int irq) { Ctlr* ctlr; SDev *sdev; Drive *drive; int dev, error, rhi, rlo; /* * 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); µdelay(5); 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: 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(5); 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; } if((sdev = malloc(sizeof(SDev))) == nil){ free(ctlr); free(drive); goto release; } drive->ctlr = ctlr; if(dev == Dev0){ ctlr->drive[0] = drive; #ifdef notdef 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); µdelay(1); } } #endif } else ctlr->drive[1] = drive; ctlr->cmdport = cmdport; ctlr->ctlport = ctlport; ctlr->irq = irq; ctlr->tbdf = -1; ctlr->command = Cedd; /* debugging */ sdev->ifc = &sdataifc; sdev->ctlr = ctlr; sdev->nunit = 1; ctlr->sdev = sdev; return sdev; } static int atasetsense(Drive* drive, int status, int key, int asc, int ascq) { drive->sense[2] = key; drive->sense[12] = asc; drive->sense[13] = ascq; return status; } static int 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); µdelay(1); ctlport = ctlr->ctlport; for(timeo = 0; timeo < 1000; timeo++){ as = inb(ctlport+As); if(!(as & Bsy)) break; µdelay(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(atacsf(drive, 0x0000000000004000LL, 0)) atanop(drive, 0); else{ atasrst(drive->ctlr->ctlport); drive->error |= Abrt; } if(dolock) iunlock(drive->ctlr); } static int atadmasetup(Drive* drive, int ) { drive->dmactl = 0; return -1; #ifdef notdef 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; 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; #endif } 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] & 0x0060) != 0x0020){ µdelay(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 int atageniostart(Drive* drive, int lba) { Ctlr *ctlr; int as, c, cmdport, ctlport, h, len, s; if(drive->dev & Lba){ c = (lba>>8) & 0xFFFF; h = (lba>>24) & 0x0F; s = lba & 0xFF; } else{ c = lba/(drive->s*drive->h); h = ((lba/drive->s) % drive->h); s = (lba % drive->s) + 1; } ctlr = drive->ctlr; cmdport = ctlr->cmdport; ctlport = ctlr->ctlport; if(ataready(cmdport, ctlport, drive->dev, Bsy|Drq, 0, 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; outb(cmdport+Count, drive->count); outb(cmdport+Sector, s); outb(cmdport+Dh, drive->dev|h); outb(cmdport+Cyllo, c); outb(cmdport+Cylhi, c>>8); ctlr->done = 0; ctlr->curdrive = drive; ctlr->command = drive->command; /* debugging */ outb(cmdport+Command, drive->command); switch(drive->command){ case Cws: case Cwsm: µdelay(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; 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, lba, len; /* * Map SCSI commands into ATA commands for discs. * Fail any command with a LUN except INQUIRY which * will return 'logical unit not supported'. */ if((cmd[1]>>5) && cmd[0] != 0x12) return atasetsense(drive, SDcheck, 0x05, 0x25, 0); switch(cmd[0]){ default: return atasetsense(drive, SDcheck, 0x05, 0x20, 0); case 0x00: /* test unit ready */ return SDok; case 0x03: /* request sense */ if(cmd[4] < sizeof(drive->sense)) len = cmd[4]; else len = sizeof(drive->sense); if(drive->data && drive->dlen >= len){ memmove(drive->data, drive->sense, len); drive->data += len; } return SDok; case 0x12: /* inquiry */ if(cmd[4] < sizeof(drive->inquiry)) len = cmd[4]; else len = sizeof(drive->inquiry); if(drive->data && drive->dlen >= len){ memmove(drive->data, drive->inquiry, len); drive->data += len; } return SDok; case 0x1B: /* start/stop unit */ /* * NOP for now, can use the power management feature * set later. */ return SDok; case 0x25: /* read capacity */ if((cmd[1] & 0x01) || cmd[2] || cmd[3]) return atasetsense(drive, SDcheck, 0x05, 0x24, 0); if(drive->data == nil || drive->dlen < 8) return atasetsense(drive, SDcheck, 0x05, 0x20, 1); /* * Read capacity returns the LBA of the last sector. */ len = drive->sectors-1; p = drive->data; *p++ = len>>24; *p++ = len>>16; *p++ = len>>8; *p++ = len; len = drive->secsize; *p++ = len>>24; *p++ = len>>16; *p++ = len>>8; *p = len; drive->data += 8; return SDok; case 0x28: /* read */ case 0x2A: /* write */ break; case 0x5A: return atamodesense(drive, cmd); } ctlr = drive->ctlr; lba = (cmd[2]<<24)|(cmd[3]<<16)|(cmd[4]<<8)|cmd[5]; count = (cmd[7]<<8)|cmd[8]; if(drive->data == nil) return SDok; if(drive->dlen < count*drive->secsize) count = drive->dlen/drive->secsize; qlock(ctlr); while(count){ if(count > 256) drive->count = 256; else drive->count = count; 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 & DbgDEBUG) print("IBsy+"); return; } cmdport = ctlr->cmdport; status = inb(cmdport+Status); if((drive = ctlr->curdrive) == nil){ iunlock(ctlr); if((DEBUG & DbgDEBUG) && ctlr->command != Cedd) print("Inil%2.2uX/%2.2uX+", ctlr->command, status); 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); } } #ifdef notdef static SDev* atapnp(void) { int cmdport; int ctlport; int irq; cmdport = 0x200; ctlport = cmdport + 0x0C; irq = 10; return ataprobe(cmdport, ctlport, irq); } #endif static SDev* atalegacy(int port, int irq) { return ataprobe(port, port+0x204, irq); } static SDev* ataid(SDev* sdev) { int i; Ctlr *ctlr; if(sdev == nil) return nil; ctlr = sdev->ctlr; i = 0; while(sdev){ if(sdev->ifc == &sdataifc){ ctlr = sdev->ctlr; sdev->idno = 'C'+i; i++; snprint(sdev->name, NAMELEN, "sd%c", sdev->idno); } sdev = sdev->next; } return nil; } static int ataitype; static int atairq; static int ataenable(SDev* sdev) { Ctlr *ctlr; char name[NAMELEN]; ctlr = sdev->ctlr; if(ctlr->bmiba){ ctlr->prdt = xspanalloc(Nprd*sizeof(Prd), 4, 4*1024); } snprint(name, NAMELEN, "%s (%s)", sdev->name, sdev->ifc->name); // intrenable(ctlr->irq, atainterrupt, ctlr, ctlr->tbdf, name); outb(ctlr->ctlport+Dc, 0); intrenable(ataitype, atairq, atainterrupt, ctlr, name); if(ctlr->ienable) ctlr->ienable(ctlr); return 1; } static int atarctl(SDunit* unit, char* p, int l) { int n; Ctlr *ctlr; Drive *drive; if((ctlr = unit->dev->ctlr) == nil || ctlr->drive[unit->subno] == nil) return 0; drive = ctlr->drive[unit->subno]; 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); n += snprint(p+n, l-n, "\n"); if(unit->sectors){ n += snprint(p+n, l-n, "geometry %ld %ld", unit->sectors, unit->secsize); if(drive->pkt == 0) n += snprint(p+n, l-n, " %d %d %d", drive->c, drive->h, drive->s); n += snprint(p+n, l-n, "\n"); } 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 error(Ebadctl); qunlock(drive); poperror(); return 0; } 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); } static int scsirio(SDreq* r) { /* * Perform an I/O request, returning * -1 failure * 0 ok * 1 no medium present * 2 retry * The contents of r may be altered so the * caller should re-initialise if necesary. */ r->status = ~0; switch(r->unit->dev->ifc->rio(r)){ default: return -1; case SDcheck: if(!(r->flags & SDvalidsense)) return -1; switch(r->sense[2] & 0x0F){ case 0x00: /* no sense */ case 0x01: /* recovered error */ return 2; case 0x06: /* check condition */ /* * 0x28 - not ready to ready transition, * medium may have changed. * 0x29 - power on or some type of reset. */ if(r->sense[12] == 0x28 && r->sense[13] == 0) return 2; if(r->sense[12] == 0x29) return 2; return -1; case 0x02: /* not ready */ /* * If no medium present, bail out. * If unit is becoming ready, rather than not * not ready, wait a little then poke it again. */ if(r->sense[12] == 0x3A) return 1; if(r->sense[12] != 0x04 || r->sense[13] != 0x01) return -1; while(waserror()) ; tsleep(&r->unit->rendez, return0, 0, 500); poperror(); scsitest(r); return 2; default: return -1; } return -1; case SDok: return 0; } return -1; } static int ataverify(SDunit* unit) { SDreq *r; int i, status; uchar *inquiry; if((r = malloc(sizeof(SDreq))) == nil) return 0; if((inquiry = sdmalloc(sizeof(unit->inquiry))) == nil){ free(r); return 0; } r->unit = unit; r->lun = 0; /* ??? */ memset(unit->inquiry, 0, sizeof(unit->inquiry)); r->write = 0; r->cmd[0] = 0x12; r->cmd[1] = r->lun<<5; r->cmd[4] = sizeof(unit->inquiry)-1; r->clen = 6; r->data = inquiry; r->dlen = sizeof(unit->inquiry)-1; r->flags = 0; r->status = ~0; if(unit->dev->ifc->rio(r) != SDok){ free(r); return 0; } memmove(unit->inquiry, inquiry, r->dlen); free(inquiry); SET(status); for(i = 0; i < 3; i++){ while((status = scsitest(r)) == SDbusy) ; if(status == SDok || status != SDcheck) break; if(!(r->flags & SDvalidsense)) break; if((r->sense[2] & 0x0F) != 0x02) continue; /* * Unit is 'not ready'. * If it needs an initialising command, set status * so it will be spun-up below. * If there's no medium, that's OK too, but don't * try to spin it up. */ if(r->sense[12] == 0x04 && r->sense[13] == 0x02){ status = SDok; break; } if(r->sense[12] == 0x3A) break; } if(status == SDok){ /* * Try to ensure a direct-access device is spinning. * Don't wait for completion, ignore the result. */ if((unit->inquiry[0] & 0x1F) == 0){ memset(r->cmd, 0, sizeof(r->cmd)); r->write = 0; r->cmd[0] = 0x1B; r->cmd[1] = (r->lun<<5)|0x01; r->cmd[4] = 1; r->clen = 6; r->data = nil; r->dlen = 0; r->flags = 0; r->status = ~0; unit->dev->ifc->rio(r); } } free(r); if(status == SDok || status == SDcheck) return 1; return 0; } static int ataonline(SDunit* unit) { SDreq *r; uchar *p; int ok, retries; if((r = malloc(sizeof(SDreq))) == nil) return 0; if((p = sdmalloc(8)) == nil){ free(r); return 0; } ok = 0; r->unit = unit; r->lun = 0; /* ??? */ for(retries = 0; retries < 10; retries++){ /* * Read-capacity is mandatory for DA, WORM, CD-ROM and * MO. It may return 'not ready' if type DA is not * spun up, type MO or type CD-ROM are not loaded or just * plain slow getting their act together after a reset. */ r->write = 0; memset(r->cmd, 0, sizeof(r->cmd)); r->cmd[0] = 0x25; r->cmd[1] = r->lun<<5; r->clen = 10; r->data = p; r->dlen = 8; r->flags = 0; r->status = ~0; switch(scsirio(r)){ default: break; case 0: unit->sectors = (p[0]<<24)|(p[1]<<16)|(p[2]<<8)|p[3]; /* * Read-capacity returns the LBA of the last sector, * therefore the number of sectors must be incremented. */ unit->sectors++; unit->secsize = (p[4]<<24)|(p[5]<<16)|(p[6]<<8)|p[7]; /* * 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; } static long atabio(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; } struct Try { int p; int c; } tries[] = { { 0, 0x0c }, { 0, 0 }, }; static SDev* ataconfig(int on, char *, void *pf) { DevConf* cf = pf; int cmdport; int ctlport; int irq; SDev* rc; struct Try *try; if(on == 0) return nil; rc = nil; for (try = &tries[0]; try->p != 0 || try->c != 0; try++){ ataitype = cf->itype; atairq = cf->interrupt; cmdport = cf->port + try->p; ctlport = cmdport + try->c; irq = cf->interrupt; rc = ataprobe(cmdport, ctlport, irq); if (rc) break; } return rc; } SDifc sdataifc = { "ata", /* name */ nil, /* pnp */ atalegacy, /* legacy */ ataid, /* id */ ataenable, /* enable */ nil, /* disable */ ataverify, /* verify */ ataonline, /* online */ atario, /* rio */ atarctl, /* rctl */ atawctl, /* wctl */ atabio, /* bio */ ataconfig, /* config */ }; --upas-cefrnfuwhwfqcaxqtnsykuzaqu Content-Disposition: attachment; filename=cis.c Content-Type: text/plain; charset="US-ASCII" Content-Transfer-Encoding: 7bit #include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "../port/error.h" #include "io.h" /* * read and crack the card information structure enough to set * important parameters like power */ /* cis memory walking */ typedef struct Cisdat { uchar *cisbase; int cispos; int cisskip; int cislen; } Cisdat; static void tcfig(PCMslot*, Cisdat*, int); static void tentry(PCMslot*, Cisdat*, int); static void tvers1(PCMslot*, Cisdat*, int); 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 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; m = pcmmap(slotno, 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= 0) return n; return xcistuple(slotno, tuple, subtuple, v, nv, 0); } void pcmcisread(PCMslot *pp) { int this; Cisdat cis; PCMmap *m; uchar type, link; memset(pp->ctab, 0, sizeof(pp->ctab)); pp->caddr = 0; pp->cpresent = 0; pp->configed = 0; pp->nctab = 0; pp->verstr[0] = 0; /* * Read all tuples in attribute space. */ m = pcmmap(pp->slotno, 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(pp, &cis, type); break; case 0x1A: tcfig(pp, &cis, type); break; case 0x1B: tentry(pp, &cis, type); break; } if(link == 0xFF) break; cis.cispos = this + (2+link); } pcmunmap(pp->slotno, m); } 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(PCMslot *pp, Cisdat *cis, int ) { uchar size, rasize, rmsize; uchar last; if(readc(cis, &size) != 1) return; rasize = (size&0x3) + 1; rmsize = ((size>>2)&0xf) + 1; if(readc(cis, &last) != 1) return; pp->caddr = getlong(cis, rasize); pp->cpresent = getlong(cis, rmsize); } static ulong vexp[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 microvolt(Cisdat *cis) { uchar c; ulong microvolts; ulong exp; if(readc(cis, &c) != 1) return 0; exp = vexp[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 = vexp[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 mantissa[16] = { 0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80, }; static ulong exponent[8] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, }; 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 * vexp[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(PCMslot *pp, Cisdat *cis, int ) { uchar c, i, feature; PCMconftab *ct; if(pp->nctab >= nelem(pp->ctab)) return; if(readc(cis, &c) != 1) return; ct = &pp->ctab[pp->nctab++]; /* copy from last default config */ if(pp->def) *ct = *pp->def; ct->index = c & 0x3f; /* is this the new default? */ if(c & 0x40) pp->def = 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; } pp->configed++; } static void tvers1(PCMslot *pp, Cisdat *cis, int ) { uchar c, major, minor, last; int i; if(readc(cis, &major) != 1) return; if(readc(cis, &minor) != 1) return; last = 0; for(i = 0; i < sizeof(pp->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; pp->verstr[i] = c; last = c; } pp->verstr[i] = 0; } --upas-cefrnfuwhwfqcaxqtnsykuzaqu Content-Disposition: attachment; filename=devsd.c Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: 8bit /* * Storage Device. */ #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 Dev sddevtab; extern SDifc* sdifc[]; static QLock sdqlock; static SDev* sdlist; static SDunit** sdunit; static int* sdunitflg; static int sdnunit; enum { Rawcmd, Rawdata, Rawstatus, }; enum { Qtopdir = 1, /* top level directory */ Qtopbase, Qunitdir, /* directory per unit */ Qunitbase, Qctl = Qunitbase, Qlog, Qraw, Qpart, }; #define TYPE(q) ((q).path & 0x0F) #define PART(q) (((q).path>>4) & 0x0F) #define UNIT(q) (((q).path>>8) & 0xFF) #define QID(u, p, t) (((u)<<8)|((p)<<4)|(t)) static void sdaddpart(SDunit* unit, char* name, ulong start, ulong end) { SDpart *pp; int i, partno; /* * Check name not already used * and look for a free slot. */ if(unit->part != nil){ partno = -1; for(i = 0; i < unit->npart; i++){ pp = &unit->part[i]; if(!pp->valid){ if(partno == -1) partno = i; break; } if(strncmp(name, pp->name, NAMELEN) == 0){ if(pp->start == start && pp->end == end) return; error(Ebadctl); } } } else{ if((unit->part = malloc(sizeof(SDpart)*SDnpart)) == nil) error(Enomem); unit->npart = SDnpart; partno = 0; } /* * If no free slot found then increase the * array size (can't get here with unit->part == nil). */ if(partno == -1){ if((pp = malloc(sizeof(SDpart)*(unit->npart+SDnpart))) == nil) error(Enomem); memmove(pp, unit->part, sizeof(SDpart)*unit->npart); free(unit->part); unit->part = pp; partno = unit->npart; unit->npart += SDnpart; } /* * Check size and extent are valid. */ if(start > end || end > unit->sectors) error(Eio); pp = &unit->part[partno]; pp->start = start; pp->end = end; strncpy(pp->name, name, NAMELEN); strncpy(pp->user, eve, NAMELEN); pp->perm = 0640; pp->valid = 1; } static void sddelpart(SDunit* unit, char* name) { int i; SDpart *pp; /* * Look for the partition to delete. * Can't delete if someone still has it open. */ pp = unit->part; for(i = 0; i < unit->npart; i++){ if(strncmp(name, pp->name, NAMELEN) == 0) break; pp++; } if(i >= unit->npart) error(Ebadctl); pp->valid = 0; pp->vers++; } static int sdinitpart(SDunit* unit) { int i, nf; ulong start, end; char *f[4], *p, *q, buf[10]; unit->vers++; unit->sectors = unit->secsize = 0; if(unit->part){ for(i = 0; i < unit->npart; i++){ unit->part[i].valid = 0; unit->part[i].vers++; } } if(unit->inquiry[0] & 0xC0) return 0; switch(unit->inquiry[0] & 0x1F){ case 0x00: /* DA */ case 0x04: /* WORM */ case 0x05: /* CD-ROM */ case 0x07: /* MO */ break; default: return 0; } if(unit->dev->ifc->online) unit->dev->ifc->online(unit); if(unit->sectors){ sdaddpart(unit, "data", 0, unit->sectors); /* * Use partitions passed from boot program, * e.g. * sdC0part=dos 63 123123/plan9 123123 456456 * This happens before /boot sets hostname so the * partitions will have the null-string for user. * The gen functions patch it up. */ snprint(buf, sizeof buf, "%spart", unit->name); for(p = getconf(buf); p != nil; p = q){ if(q = strchr(p, '/')) *q++ = '\0'; nf = getfields(p, f, nelem(f), 1, " \t\r"); if(nf < 3) continue; start = strtoul(f[1], 0, 0); end = strtoul(f[2], 0, 0); if(!waserror()){ sdaddpart(unit, f[0], start, end); poperror(); } } } return 1; } static SDunit* sdgetunit(SDev* sdev, int subno) { int index; SDunit *unit; /* * Associate a unit with a given device and sub-unit * number on that device. * The device will be probed if it has not already been * successfully accessed. */ qlock(&sdqlock); index = sdev->index+subno; unit = sdunit[index]; if(unit == nil){ /* * Probe the unit only once. This decision * may be a little severe and reviewed later. */ if(sdunitflg[index]){ qunlock(&sdqlock); return nil; } if((unit = malloc(sizeof(SDunit))) == nil){ qunlock(&sdqlock); return nil; } sdunitflg[index] = 1; if(sdev->enabled == 0 && sdev->ifc->enable) sdev->ifc->enable(sdev); sdev->enabled = 1; µdelay(5); snprint(unit->name, NAMELEN, "%s%d", sdev->name, subno); strncpy(unit->user, eve, NAMELEN); unit->perm = 0555; unit->subno = subno; unit->dev = sdev; unit->log.logmask = ~0; unit->log.nlog = 16*1024; unit->log.minread = 4*1024; /* * No need to lock anything here as this is only * called before the unit is made available in the * sdunit[] array. */ if(unit->dev->ifc->verify(unit) == 0){ qunlock(&sdqlock); free(unit); return nil; } sdunit[index] = unit; } qunlock(&sdqlock); return unit; } static SDunit* sdindex2unit(int index) { SDev *sdev; /* * Associate a unit with a given index into the top-level * device directory. * The device will be probed if it has not already been * successfully accessed. */ for(sdev = sdlist; sdev != nil; sdev = sdev->next){ if(index >= sdev->index && index < sdev->index+sdev->nunit) return sdgetunit(sdev, index-sdev->index); } return nil; } static void sdreset(void) { int i; SDev *sdev, *tail; /* * Probe all configured controllers and make a list * of devices found, accumulating a possible maximum number * of units attached and marking each device with an index * into the linear top-level directory array of units. */ tail = nil; for(i = 0; sdifc[i] != nil; i++){ if(sdifc[i]->pnp == nil || (sdev = sdifc[i]->pnp()) == nil) continue; if(sdlist != nil) tail->next = sdev; else sdlist = sdev; for(tail = sdev; tail->next != nil; tail = tail->next){ sdev->index = sdnunit; sdnunit += tail->nunit; } tail->index = sdnunit; sdnunit += tail->nunit; } /* * Legacy and option code goes here. This will be hard... */ /* * The maximum number of possible units is known, allocate * placeholders for their datastructures; the units will be * probed and structures allocated when attached. * Allocate controller names for the different types. */ if(sdnunit == 0) return; if((sdunit = malloc(sdnunit*sizeof(SDunit*))) == nil) return; if((sdunitflg = malloc(sdnunit*sizeof(int))) == nil){ free(sdunit); sdunit = nil; return; } for(i = 0; sdifc[i] != nil; i++){ /* * BUG: no check is made here or later when a * unit is attached that the id and name are set. */ if(sdifc[i]->id) sdifc[i]->id(sdlist); } } static int sdconfig(int on, char *spec, DevConf *cf) { int i; SDev *sdev, *tail; /* can't unconfigure */ if(on == 0) return -1; /* * Config all configured controllers and make a list * of devices found, accumulating a possible maximum number * of units attached and marking each device with an index * into the linear top-level directory array of units. */ tail = nil; for(i = 0; sdifc[i] != nil; i++){ if(sdifc[i]->config == nil) break; if((sdev = sdifc[i]->config(on, spec, cf)) == nil) break; if(sdlist != nil) tail->next = sdev; else sdlist = sdev; for(tail = sdev; tail->next != nil; tail = tail->next){ sdev->index = sdnunit; sdnunit += tail->nunit; } tail->index = sdnunit; sdnunit += tail->nunit; } /* * The maximum number of possible units is known, allocate * placeholders for their datastructures; the units will be * probed and structures allocated when attached. * Allocate controller names for the different types. */ if(sdnunit == 0) return -1; if((sdunit = malloc(sdnunit*sizeof(SDunit*))) == nil) return -1; if((sdunitflg = malloc(sdnunit*sizeof(int))) == nil){ free(sdunit); sdunit = nil; return -1; } for(i = 0; sdifc[i] != nil; i++){ /* * BUG: no check is made here or later when a * unit is attached that the id and name are set. */ if(sdifc[i]->id) sdifc[i]->id(sdlist); } return 0; } static int sd2gen(Chan* c, int i, Dir* dp) { Qid q; vlong l; SDpart *pp; SDperm *perm; SDunit *unit; unit = sdunit[UNIT(c->qid)]; switch(i){ case Qlog: q = (Qid){QID(UNIT(c->qid), PART(c->qid), Qlog), unit->vers}; perm = &unit->rawperm; if(perm->user[0] == '\0'){ strncpy(perm->user, eve, NAMELEN); perm->perm = 0666; } devdir(c, q, "log", 0, perm->user, perm->perm, dp); return 1; case Qctl: q = (Qid){QID(UNIT(c->qid), PART(c->qid), Qctl), unit->vers}; perm = &unit->ctlperm; if(perm->user[0] == '\0'){ strncpy(perm->user, eve, NAMELEN); perm->perm = 0640; } devdir(c, q, "ctl", 0, perm->user, perm->perm, dp); return 1; case Qraw: q = (Qid){QID(UNIT(c->qid), PART(c->qid), Qraw), unit->vers}; perm = &unit->rawperm; if(perm->user[0] == '\0'){ strncpy(perm->user, eve, NAMELEN); perm->perm = CHEXCL|0600; } devdir(c, q, "raw", 0, perm->user, perm->perm, dp); return 1; case Qpart: pp = &unit->part[PART(c->qid)]; l = (pp->end - pp->start) * (vlong)unit->secsize; q = (Qid){QID(UNIT(c->qid), PART(c->qid), Qpart), unit->vers+pp->vers}; if(pp->user[0] == '\0') strncpy(pp->user, eve, NAMELEN); devdir(c, q, pp->name, l, pp->user, pp->perm, dp); return 1; } return -1; } static int sd1gen(Chan*, int i, Dir*) { switch(i){ default: return -1; } return -1; } static int sdgen(Chan* c, Dirtab*, int, int s, Dir* dp) { Qid q; vlong l; int i, r; SDpart *pp; SDunit *unit; char name[NAMELEN]; switch(TYPE(c->qid)){ case Qtopdir: if(s == DEVDOTDOT){ q = (Qid){QID(0, 0, Qtopdir)|CHDIR, 0}; snprint(name, NAMELEN, "#%C", sddevtab.dc); devdir(c, q, name, 0, eve, 0555, dp); return 1; } if(s < sdnunit){ if((unit = sdunit[s]) == nil){ if((unit = sdindex2unit(s)) == nil) return 0; } q = (Qid){QID(s, 0, Qunitdir)|CHDIR, 0}; if(unit->user[0] == '\0') strncpy(unit->user, eve, NAMELEN); devdir(c, q, unit->name, 0, unit->user, unit->perm, dp); return 1; } s -= sdnunit; return sd1gen(c, s+Qtopbase, dp); case Qunitdir: if(s == DEVDOTDOT){ q = (Qid){QID(0, 0, Qtopdir)|CHDIR, 0}; snprint(name, NAMELEN, "#%C", sddevtab.dc); devdir(c, q, name, 0, eve, 0555, dp); return 1; } unit = sdunit[UNIT(c->qid)]; qlock(&unit->ctl); /* * Check for media change. * If one has already been detected, sectors will be zero. * If there is one waiting to be detected, online * will return > 1. * Online is a bit of a large hammer but does the job. */ if(unit->sectors == 0 || (unit->dev->ifc->online && unit->dev->ifc->online(unit) > 1)) sdinitpart(unit); i = s+Qunitbase; if(i < Qpart){ r = sd2gen(c, i, dp); qunlock(&unit->ctl); return r; } i -= Qpart; if(unit->part == nil || i >= unit->npart){ qunlock(&unit->ctl); break; } pp = &unit->part[i]; if(!pp->valid){ qunlock(&unit->ctl); return 0; } l = (pp->end - pp->start) * (vlong)unit->secsize; q = (Qid){QID(UNIT(c->qid), i, Qpart), unit->vers+pp->vers}; if(pp->user[0] == '\0') strncpy(pp->user, eve, NAMELEN); devdir(c, q, pp->name, l, pp->user, pp->perm, dp); qunlock(&unit->ctl); return 1; case Qraw: case Qctl: case Qpart: case Qlog: unit = sdunit[UNIT(c->qid)]; qlock(&unit->ctl); r = sd2gen(c, TYPE(c->qid), dp); qunlock(&unit->ctl); return r; default: break; } return -1; } static Chan* sdattach(char* spec) { Chan *c; char *p; SDev *sdev; int idno, subno; if(sdnunit == 0 || *spec == '\0'){ c = devattach(sddevtab.dc, spec); c->qid = (Qid){QID(0, 0, Qtopdir)|CHDIR, 0}; return c; } if(spec[0] != 's' || spec[1] != 'd') error(Ebadspec); idno = spec[2]; subno = strtol(&spec[3], &p, 0); if(p == &spec[3]) error(Ebadspec); for(sdev = sdlist; sdev != nil; sdev = sdev->next){ if(sdev->idno == idno && subno < sdev->nunit) break; } if(sdev == nil || sdgetunit(sdev, subno) == nil) error(Enonexist); c = devattach(sddevtab.dc, spec); c->qid = (Qid){QID(sdev->index+subno, 0, Qunitdir)|CHDIR, 0}; c->dev = sdev->index+subno; return c; } static Chan* sdclone(Chan* c, Chan* nc) { return devclone(c, nc); } static int sdwalk(Chan* c, char* name) { return devwalk(c, name, nil, 0, sdgen); } static void sdstat(Chan* c, char* db) { devstat(c, db, nil, 0, sdgen); } static Chan* sdopen(Chan* c, int omode) { SDpart *pp; SDunit *unit; c = devopen(c, omode, 0, 0, sdgen); switch(TYPE(c->qid)){ default: break; case Qlog: unit = sdunit[UNIT(c->qid)]; logopen(&unit->log); break; case Qctl: unit = sdunit[UNIT(c->qid)]; c->qid.vers = unit->vers; break; case Qraw: unit = sdunit[UNIT(c->qid)]; c->qid.vers = unit->vers; if(!canlock(&unit->rawinuse)){ c->flag &= ~COPEN; error(Einuse); } unit->state = Rawcmd; break; case Qpart: unit = sdunit[UNIT(c->qid)]; qlock(&unit->ctl); if(waserror()){ qunlock(&unit->ctl); c->flag &= ~COPEN; nexterror(); } pp = &unit->part[PART(c->qid)]; c->qid.vers = unit->vers+pp->vers; qunlock(&unit->ctl); poperror(); break; } return c; } static void sdclose(Chan* c) { SDunit *unit; if(c->qid.path & CHDIR) return; if(!(c->flag & COPEN)) return; switch(TYPE(c->qid)){ default: break; case Qlog: unit = sdunit[UNIT(c->qid)]; logclose(&unit->log); break; case Qraw: unit = sdunit[UNIT(c->qid)]; unlock(&unit->rawinuse); break; } } static long sdbio(Chan* c, int write, char* a, long len, vlong off) { int nchange; long l; uchar *b; SDpart *pp; SDunit *unit; ulong bno, max, nb, offset; unit = sdunit[UNIT(c->qid)]; nchange = 0; qlock(&unit->ctl); while(waserror()){ /* notification of media change; go around again */ if(strcmp(up->error, Eio) == 0 && unit->sectors == 0 && nchange++ == 0){ sdinitpart(unit); continue; } /* other errors; give up */ qunlock(&unit->ctl); nexterror(); } pp = &unit->part[PART(c->qid)]; if(unit->vers+pp->vers != c->qid.vers) error(Eio); /* * Check the request is within bounds. * Removeable drives are locked throughout the I/O * in case the media changes unexpectedly. * Non-removeable drives are not locked during the I/O * to allow the hardware to optimise if it can; this is * a little fast and loose. * It's assumed that non-removeable media parameters * (sectors, secsize) can't change once the drive has * been brought online. */ bno = (off/unit->secsize) + pp->start; nb = ((off+len+unit->secsize-1)/unit->secsize) + pp->start - bno; max = SDmaxio/unit->secsize; if(nb > max) nb = max; if(bno+nb > pp->end) nb = pp->end - bno; if(bno >= pp->end || nb == 0){ if(write) error(Eio); qunlock(&unit->ctl); poperror(); return 0; } if(!(unit->inquiry[1] & 0x80)){ qunlock(&unit->ctl); poperror(); } if(unit->log.opens) { int i; uchar lbuf[1+4+8], *p; ulong x[3]; p = lbuf; *p++ = write ? 'w' : 'r'; x[0] = off>>32; x[1] = off; x[2] = len; for(i=0; i<3; i++) { *p++ = x[i]>>24; *p++ = x[i]>>16; *p++ = x[i]>>8; *p++ = x[i]; } logn(&unit->log, 1, lbuf, 1+4+8); } b = sdmalloc(nb*unit->secsize); if(b == nil) error(Enomem); if(waserror()){ sdfree(b); nexterror(); } offset = off%unit->secsize; if(write){ if(offset || (len%unit->secsize)){ l = unit->dev->ifc->bio(unit, 0, 0, b, nb, bno); if(l < 0) error(Eio); if(l < (nb*unit->secsize)){ nb = l/unit->secsize; l = nb*unit->secsize - offset; if(len > l) len = l; } } memmove(b+offset, a, len); l = unit->dev->ifc->bio(unit, 0, 1, b, nb, bno); if(l < 0) error(Eio); if(l < offset) len = 0; else if(len > l - offset) len = l - offset; } else { l = unit->dev->ifc->bio(unit, 0, 0, b, nb, bno); if(l < 0) error(Eio); if(l < offset) len = 0; else if(len > l - offset) len = l - offset; memmove(a, b+offset, len); } sdfree(b); poperror(); if(unit->inquiry[1] & 0x80){ qunlock(&unit->ctl); poperror(); } return len; } static long sdrio(SDreq* r, void* a, long n) { void *data; if(n >= SDmaxio || n < 0) error(Etoobig); data = nil; if(n){ if((data = sdmalloc(n)) == nil) error(Enomem); if(r->write) memmove(data, a, n); } r->data = data; r->dlen = n; if(waserror()){ if(data != nil){ sdfree(data); r->data = nil; } nexterror(); } if(r->unit->dev->ifc->rio(r) != SDok) error(Eio); if(!r->write && r->rlen > 0) memmove(a, data, r->rlen); if(data != nil){ sdfree(data); r->data = nil; } poperror(); return r->rlen; } static long sdread(Chan *c, void *a, long n, vlong off) { char *p; SDpart *pp; SDunit *unit; ulong offset; int i, l, status; offset = off; switch(TYPE(c->qid)){ default: error(Eperm); case Qtopdir: case Qunitdir: return devdirread(c, a, n, 0, 0, sdgen); case Qlog: unit = sdunit[UNIT(c->qid)]; return logread(&unit->log, a, 0, n); case Qctl: unit = sdunit[UNIT(c->qid)]; p = malloc(READSTR); l = snprint(p, READSTR, "inquiry %.48s\n", (char*)unit->inquiry+8); qlock(&unit->ctl); /* * If there's a device specific routine it must * provide all information pertaining to night geometry * and the garscadden trains. */ if(unit->dev->ifc->rctl) l += unit->dev->ifc->rctl(unit, p+l, READSTR-l); if(unit->sectors == 0) sdinitpart(unit); if(unit->sectors){ if(unit->dev->ifc->rctl == nil) l += snprint(p+l, READSTR-l, "geometry %ld %ld\n", unit->sectors, unit->secsize); pp = unit->part; for(i = 0; i < unit->npart; i++){ if(pp->valid) l += snprint(p+l, READSTR-l, "part %.*s %lud %lud\n", NAMELEN, pp->name, pp->start, pp->end); pp++; } } qunlock(&unit->ctl); l = readstr(offset, a, n, p); free(p); return l; case Qraw: unit = sdunit[UNIT(c->qid)]; qlock(&unit->raw); if(waserror()){ qunlock(&unit->raw); nexterror(); } if(unit->state == Rawdata){ unit->state = Rawstatus; i = sdrio(unit->req, a, n); } else if(unit->state == Rawstatus){ status = unit->req->status; unit->state = Rawcmd; free(unit->req); unit->req = nil; i = readnum(0, a, n, status, NUMSIZE); } else i = 0; qunlock(&unit->raw); poperror(); return i; case Qpart: return sdbio(c, 0, a, n, off); } return 0; } static long sdwrite(Chan *c, void *a, long n, vlong off) { Cmdbuf *cb; SDreq *req; SDunit *unit; ulong end, start; switch(TYPE(c->qid)){ default: error(Eperm); case Qlog: error(Ebadctl); case Qctl: cb = parsecmd(a, n); unit = sdunit[UNIT(c->qid)]; qlock(&unit->ctl); if(waserror()){ qunlock(&unit->ctl); free(cb); nexterror(); } if(unit->vers != c->qid.vers) error(Eio); if(cb->nf < 1) error(Ebadctl); if(strcmp(cb->f[0], "part") == 0){ if(cb->nf != 4) error(Ebadctl); if(unit->sectors == 0 && !sdinitpart(unit)) error(Eio); start = strtoul(cb->f[2], 0, 0); end = strtoul(cb->f[3], 0, 0); sdaddpart(unit, cb->f[1], start, end); } else if(strcmp(cb->f[0], "delpart") == 0){ if(cb->nf != 2 || unit->part == nil) error(Ebadctl); sddelpart(unit, cb->f[1]); } else if(unit->dev->ifc->wctl) unit->dev->ifc->wctl(unit, cb); else error(Ebadctl); qunlock(&unit->ctl); poperror(); free(cb); break; case Qraw: unit = sdunit[UNIT(c->qid)]; qlock(&unit->raw); if(waserror()){ qunlock(&unit->raw); nexterror(); } switch(unit->state){ case Rawcmd: if(n < 6 || n > sizeof(req->cmd)) error(Ebadarg); if((req = malloc(sizeof(SDreq))) == nil) error(Enomem); req->unit = unit; memmove(req->cmd, a, n); req->clen = n; req->flags = SDnosense; req->status = ~0; unit->req = req; unit->state = Rawdata; break; case Rawstatus: unit->state = Rawcmd; free(unit->req); unit->req = nil; error(Ebadusefd); case Rawdata: if(unit->state != Rawdata) error(Ebadusefd); unit->state = Rawstatus; unit->req->write = 1; n = sdrio(unit->req, a, n); } qunlock(&unit->raw); poperror(); return n; case Qpart: return sdbio(c, 1, a, n, off); } return n; } static void sdwstat(Chan* c, char* dp) { Dir d; SDpart *pp; SDperm *perm; SDunit *unit; if(c->qid.path & CHDIR) error(Eperm); unit = sdunit[UNIT(c->qid)]; qlock(&unit->ctl); if(waserror()){ qunlock(&unit->ctl); nexterror(); } switch(TYPE(c->qid)){ default: error(Eperm); case Qctl: perm = &unit->ctlperm; break; case Qraw: perm = &unit->rawperm; break; case Qpart: pp = &unit->part[PART(c->qid)]; if(unit->vers+pp->vers != c->qid.vers) error(Enonexist); perm = &pp->SDperm; break; } if(strncmp(up->user, perm->user, NAMELEN) && !iseve()) error(Eperm); convM2D(dp, &d); strncpy(perm->user, d.uid, NAMELEN); perm->perm = (perm->perm & ~0777) | (d.mode & 0777); qunlock(&unit->ctl); poperror(); } Dev sddevtab = { 'S', "sd", sdreset, devinit, sdattach, sdclone, sdwalk, sdstat, sdopen, devcreate, sdclose, sdread, devbread, sdwrite, devbwrite, devremove, sdwstat, nil, /* power management */ sdconfig, }; --upas-cefrnfuwhwfqcaxqtnsykuzaqu Content-Disposition: attachment; filename=sd.h Content-Type: text/plain; charset="US-ASCII" Content-Transfer-Encoding: 7bit /* * Storage Device. */ typedef struct SDev SDev; typedef struct SDifc SDifc; typedef struct SDpart SDpart; typedef struct SDperm SDperm; typedef struct SDreq SDreq; typedef struct SDunit SDunit; typedef struct SDperm { char name[NAMELEN]; char user[NAMELEN]; ulong perm; } SDperm; typedef struct SDpart { ulong start; ulong end; SDperm; int valid; ulong vers; } SDpart; typedef struct SDunit { SDev* dev; int subno; uchar inquiry[256]; /* format follows SCSI spec */ SDperm; Rendez rendez; QLock ctl; ulong sectors; ulong secsize; SDpart* part; /* nil or array of size npart */ int npart; ulong vers; SDperm ctlperm; QLock raw; /* raw read or write in progress */ Lock rawinuse; /* really just a test-and-set */ int state; SDreq* req; SDperm rawperm; Log log; } SDunit; typedef struct SDev { SDifc* ifc; /* pnp/legacy */ void *ctlr; int idno; char name[NAMELEN]; int index; /* into unit space */ int nunit; SDev* next; QLock; /* enable/disable */ int enabled; } SDev; typedef struct SDifc { char* name; SDev* (*pnp)(void); SDev* (*legacy)(int, int); SDev* (*id)(SDev*); int (*enable)(SDev*); int (*disable)(SDev*); int (*verify)(SDunit*); int (*online)(SDunit*); int (*rio)(SDreq*); int (*rctl)(SDunit*, char*, int); int (*wctl)(SDunit*, Cmdbuf*); long (*bio)(SDunit*, int, int, void*, long, long); SDev* (*config)(int on, char *spec, void *cf); } SDifc; typedef struct SDreq { SDunit* unit; int lun; int write; uchar cmd[16]; int clen; void* data; int dlen; int flags; int status; long rlen; uchar sense[256]; } SDreq; enum { SDnosense = 0x00000001, SDvalidsense = 0x00010000, }; enum { SDretry = -5, /* internal to controllers */ SDmalloc = -4, SDeio = -3, SDtimeout = -2, SDnostatus = -1, SDok = 0, SDcheck = 0x02, /* check condition */ SDbusy = 0x08, /* busy */ SDmaxio = 2048*1024, SDnpart = 16, }; #define sdmalloc(n) malloc(n) #define sdfree(p) free(p) /* sdscsi.c */ extern int scsiverify(SDunit*); extern int scsionline(SDunit*); extern long scsibio(SDunit*, int, int, void*, long, long); extern SDev* scsiid(SDev*, SDifc*); --upas-cefrnfuwhwfqcaxqtnsykuzaqu--