/* * Storage Device. */ #include "u.h" #include "mem.h" #include "lib.h" #include "dat.h" #include "fns.h" #include "io.h" #include "ureg.h" #include "error.h" #include "sd.h" #include "dosfs.h" extern SDifc* sdifc[]; static SDev* sdlist; static SDunit** sdunit; static int sdnunit; enum { Rawcmd, Rawdata, Rawstatus, }; void sdaddpart(SDunit* unit, char* name, ulong start, ulong end) { SDpart *pp; int i, partno; //print("add %d %s %s %ld %ld\n", unit->npart, unit->name, name, start, end); /* * Check name not already used * and look for a free slot. */ if(unit->part != nil){ partno = -1; for(i = 0; i < SDnpart; i++){ pp = &unit->part[i]; if(!pp->valid){ if(partno == -1) partno = i; break; } if(strcmp(name, pp->name) == 0){ if(pp->start == start && pp->end == end) return; } } } else{ if((unit->part = malloc(sizeof(SDpart)*SDnpart)) == nil) return; partno = 0; } /* * Check there is a free slot and size and extent are valid. */ if(partno == -1 || start > end || end > unit->sectors) return; pp = &unit->part[partno]; pp->start = start; pp->end = end; strncpy(pp->name, name, NAMELEN); pp->valid = 1; unit->npart++; } 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. * If it's the last valid partition zap the * whole table. */ pp = unit->part; for(i = 0; i < SDnpart; i++){ if(strncmp(name, pp->name, NAMELEN) == 0) break; pp++; } if(i >= SDnpart) return; pp->valid = 0; unit->npart--; if(unit->npart == 0){ free(unit->part); unit->part = nil; } } static int sdinitpart(SDunit* unit) { unit->sectors = unit->secsize = 0; unit->npart = 0; if(unit->part){ free(unit->part); unit->part = nil; } 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 == nil || unit->dev->ifc->online(unit) == 0) return 0; sdaddpart(unit, "data", 0, unit->sectors); 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){ if((unit = malloc(sizeof(SDunit))) == nil){ qunlock(&sdqlock); return nil; } if(sdev->enabled == 0 && sdev->ifc->enable) sdev->ifc->enable(sdev); sdev->enabled = 1; snprint(unit->name, NAMELEN, "sd%c%d", sdev->idno, subno); unit->subno = subno; unit->dev = sdev; /* * 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 _sddetach(void) { SDev *sdev; for(sdev = sdlist; sdev != nil; sdev = sdev->next){ if(sdev->enabled == 0) continue; if(sdev->ifc->disable) sdev->ifc->disable(sdev); sdev->enabled = 0; } } int sdinit(void) { ulong m; int i; SDev *sdev, *tail; SDunit *unit; /* * 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((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 0; if((sdunit = malloc(sdnunit*sizeof(SDunit*))) == nil) return 0; sddetach = _sddetach; for(i = 0; sdifc[i] != nil; i++){ if(sdifc[i]->id) sdifc[i]->id(sdlist); } m = 0; for(i=0; inpart > 0) /* BUG */ m |= (1<name); } SDpart* sdfindpart(SDunit *unit, char *name) { int i; for(i=0; inpart; i++) { if(strcmp(unit->part[i].name, name) == 0) return &unit->part[i]; } return nil; } typedef struct Scsicrud Scsicrud; struct Scsicrud { Dos; ulong offset; SDunit *unit; SDpart *part; }; long sdread(Dos *vdos, void *v, long n) { Scsicrud *dos; long x; dos = (Scsicrud*)vdos; x = sdbio(dos->unit, dos->part, v, n, dos->offset); if(x > 0) dos->offset += x; return x; } long sdseek(Dos *vdos, long seek) { return ((Scsicrud*)vdos)->offset = seek; } void* sdgetdospart(int i, char *s) { SDunit *unit; SDpart *p; Scsicrud *dos; unit = sdindex2unit(i); if((p = sdfindpart(unit, s)) == nil) return nil; if(p->dos == nil) { dos = malloc(sizeof(Scsicrud)); dos->dev = i; dos->read = sdread; dos->seek = sdseek; dos->start = 0; dos->unit = unit; dos->part = p; if(dosinit(dos) < 0) return nil; p->dos = dos; } return p->dos; } /* * Leave partitions around for devsd to pick up. * (Needed by boot process; more extensive * partitioning is done by termrc or cpurc). */ void sdaddconf(int i) { SDunit *unit; SDpart *pp; unit = sdindex2unit(i); /* * If there were no partitions (just data and partition), don't bother. */ if(unit->npart <= 2) return; addconf("%spart=", unit->name); for(i=1, pp=&unit->part[i]; inpart; i++, pp++) /* skip 0, which is "data" */ addconf("%s%s %ld %ld", i==1 ? "" : "/", pp->name, pp->start, pp->end); addconf("\n"); } int sdboot(int dev, char *pname, Boot *b) { char *file; Dos *dos; if((file = strchr(pname, '!')) == nil) { print("syntax is sdC0!partition!file\n"); return -1; } *file++ = '\0'; dos = sdgetdospart(dev, pname); if(dos == nil) { print("no such FAT partition %s!%s\n", sdindex2unit(dev)->name, pname); return -1; } return dosboot(dos, file, b); } long sdbio(SDunit *unit, SDpart *pp, void* va, long len, vlong off) { long l; ulong bno, max, nb, offset; static uchar *b; char *a; static ulong bsz; a = va; memset(a, 0xDA, len); qlock(&unit->ctl); if(unit->changed){ qunlock(&unit->ctl); return 0; } /* * 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-removable 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){ qunlock(&unit->ctl); return 0; } if(!(unit->inquiry[1] & 0x80)) qunlock(&unit->ctl); if(bsz < nb*unit->secsize){ b = malloc(nb*unit->secsize); bsz = nb*unit->secsize; } // b = sdmalloc(nb*unit->secsize); // if(b == nil) // return 0; offset = off%unit->secsize; if((l = unit->dev->ifc->bio(unit, 0, 0, a, nb, bno)) < 0) { // sdfree(b); return 0; } if(l < offset) len = 0; else if(len > l - offset) len = l - offset; // sdfree(b); if(unit->inquiry[1] & 0x80) qunlock(&unit->ctl); return len; } #ifdef DMA long sdrio(SDreq *r, void* a, long n) { if(n >= SDmaxio || n < 0) return 0; r->data = nil; if(n){ if((r->data = malloc(n)) == nil) return 0; if(r->write) memmove(r->data, a, n); } r->dlen = n; if(r->unit->dev->ifc->rio(r) != SDok){ // cgascreenputs("1", 1); if(r->data != nil){ sdfree(r->data); r->data = nil; } return 0; } // cgascreenputs("2", 1); if(!r->write && r->rlen > 0) memmove(a, r->data, r->rlen); // cgascreenputs("3", 1); if(r->data != nil){ sdfree(r->data); r->data = nil; } // cgascreenputs("4", 1); return r->rlen; } #endif /* DMA */ void sleep(void*, int (*fn)(void*), void *v) { int x; x = spllo(); while(!fn(v)) ; splx(x); return; } void tsleep(void*, int (*fn)(void*), void *v, int msec) { int x; ulong start; x = spllo(); for(start = m->ticks; TK2MS(m->ticks - start) < msec && !fn(v); ) ; splx(x); return; } void* sdmalloc(void *p, ulong sz) { if(p != nil) { memset(p, 0, sz); return p; } return malloc(sz); }