#include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "../port/error.h" #include "devtab.h" #define DPRINT if(debug)kprint #define DATASIZE (256*512) #define NBUS 2 static Scsi staticcmd[NBUS]; /* BUG: should be one per scsi device */ enum { Qscsiid = 1, /* Top level */ Qdir = 0, /* Sub-directory */ Qcmd, Qdata, Qdebug, }; static Dirtab scsidir[]={ "cmd", {Qcmd}, 0, 0666, "data", {Qdata}, 0, 0666, "debug", {Qdebug}, 1, 0666, }; #define NSCSI (sizeof scsidir/sizeof(Dirtab)) extern int scsidebugs[]; extern int scsiownid; static int scsigen1(Chan *c, long qid, Dir *dp) { if (qid == CHDIR) devdir(c, (Qid){qid,0}, ".", 0, eve, 0555, dp); else if (qid == Qscsiid) devdir(c, (Qid){Qscsiid,0}, "scsiid", NUMSIZE, eve, 0666, dp); else if (qid&CHDIR) { char name[2]; name[0] = '0'+((qid>>4)&7), name[1] = 0; devdir(c, (Qid){qid,0}, name, 0, eve, 0555, dp); } else { Dirtab *tab = &scsidir[(qid&7)-1]; devdir(c, (Qid){qid,0}, tab->name, tab->length, eve, tab->perm, dp); } return 1; } static int scsigeno(Chan *c, Dirtab *tab, long ntab, long s, Dir *dp) { USED(tab, ntab, s); return scsigen1(c, c->qid.path, dp); } static int scsigen(Chan *c, Dirtab *tab, long ntab, long s, Dir *dp) { USED(tab, ntab); if (c->qid.path == CHDIR) { if (0<=s && s<=7) return scsigen1(c, CHDIR|0x100|(s<<4), dp); else if (s == 8) return scsigen1(c, 1, dp); else return -1; } if (s >= NSCSI) return -1; return scsigen1(c, (c->qid.path&~CHDIR)+s+1, dp); } void scsireset(void) { scsibufreset(DATASIZE); resetscsi(); } void scsiinit(void) { int i; for(i = 0; i < NBUS; i++) staticcmd[i].bus = i; initscsi(); } Chan * scsiattach(char *param) { Chan *c; c = devattach('S', param); c->dev = 0; if(param[0] == '1') c->dev = 1; return c; } Chan * scsiclone(Chan *c, Chan *nc) { return devclone(c, nc); } int scsiwalk(Chan *c, char *name) { return devwalk(c, name, 0, 0, scsigen); } void scsistat(Chan *c, char *db) { devstat(c, db, 0, 0, scsigeno); } Chan * scsiopen(Chan *c, int omode) { return devopen(c, omode, 0, 0, scsigeno); } void scsicreate(Chan *c, char *name, int omode, ulong perm) { USED(c, name, omode, perm); error(Eperm); } void scsiclose(Chan *c) { Scsi *cmd = &staticcmd[c->dev]; if((c->qid.path & CHDIR) || c->qid.path==1) return; if((c->qid.path & 0xf) == Qcmd){ if(canqlock(cmd) || cmd->pid == u->p->pid){ cmd->pid = 0; qunlock(cmd); } } } long scsiread(Chan *c, char *a, long n, ulong offset) { Scsi *cmd = &staticcmd[c->dev]; if(n == 0) return 0; if(c->qid.path & CHDIR) return devdirread(c, a, n, 0, 0, scsigen); if(c->qid.path==Qscsiid){ n = readnum(offset, a, n, scsiownid, NUMSIZE); }else switch(c->qid.path & 0xf){ case Qcmd: if(n < 4) error(Ebadarg); if(canqlock(cmd)){ qunlock(cmd); error(Egreg); } if(cmd->pid != u->p->pid) error(Egreg); n = 4; *a++ = 0; *a++ = 0; *a++ = cmd->status >> 8; *a = cmd->status; cmd->pid = 0; qunlock(cmd); break; case Qdata: if(canqlock(cmd)){ qunlock(cmd); error(Egreg); } if(cmd->pid != u->p->pid) error(Egreg); if (n > DATASIZE) error(Ebadarg); cmd->b = scsibuf(); cmd->data.base = cmd->b->virt; if(waserror()){ scsifree(cmd->b); nexterror(); } cmd->data.lim = cmd->data.base + n; cmd->data.ptr = cmd->data.base; cmd->save = cmd->data.base; scsiexec(cmd, ScsiIn); n = cmd->data.ptr - cmd->data.base; memmove(a, cmd->data.base, n); poperror(); scsifree(cmd->b); break; case Qdebug: if(offset == 0){ n=1; *a="01"[scsidebugs[(c->qid.path>>4)&7]!=0]; }else n = 0; break; default: panic("scsiread"); } return n; } long scsiwrite(Chan *c, char *a, long n, ulong offset) { Scsi *cmd; int id, m; char buf[NUMSIZE+1]; cmd = &staticcmd[c->dev]; if(c->qid.path==Qscsiid && n>0) { m = n; if(m > NUMSIZE) m = NUMSIZE; memmove(buf, a, m); buf[m] = '\0'; id = strtoul(buf, 0, 0); if(id < 0 || id > 15) error(Ebadarg); scsiownid=id; scsireset(); }else switch(c->qid.path & 0xf){ case Qcmd: if(n < 6 || n > sizeof cmd->cmdblk) error(Ebadarg); qlock(cmd); cmd->pid = u->p->pid; cmd->cmd.base = cmd->cmdblk; memmove(cmd->cmd.base, a, n); cmd->cmd.lim = cmd->cmd.base + n; cmd->cmd.ptr = cmd->cmd.base; cmd->target = (c->qid.path>>4)&7; cmd->lun = (a[1]>>5)&7; cmd->status = 0xFFFF; break; case Qdata: if(canqlock(cmd)){ qunlock(cmd); error(Egreg); } if(cmd->pid != u->p->pid) error(Egreg); if (n > DATASIZE) error(Ebadarg); cmd->b = scsibuf(); cmd->data.base = cmd->b->virt; if(waserror()){ scsifree(cmd->b); nexterror(); } cmd->data.lim = cmd->data.base + n; cmd->data.ptr = cmd->data.base; cmd->save = cmd->data.base; memmove(cmd->data.base, a, n); scsiexec(cmd, ScsiOut); n = cmd->data.ptr - cmd->data.base; poperror(); scsifree(cmd->b); break; case Qdebug: if(offset == 0){ scsidebugs[(c->qid.path>>4)&7] = (*a=='1'); n = 1; }else n = 0; break; default: panic("scsiwrite"); } return n; } void scsiremove(Chan *c) { USED(c); error(Eperm); } void scsiwstat(Chan *c, char *dp) { USED(c, dp); error(Eperm); } Scsi * scsicmd(int dev, int cmdbyte, Scsibuf *b, long size) { Scsi *cmd = &staticcmd[0]; qlock(cmd); cmd->target = dev >> 3; cmd->lun = dev & 7; cmd->cmd.base = cmd->cmdblk; cmd->cmd.ptr = cmd->cmd.base; memset(cmd->cmdblk, 0, sizeof cmd->cmdblk); cmd->cmdblk[0] = cmdbyte; cmd->cmdblk[1] = cmd->lun << 5; switch(cmdbyte >> 5){ case 0: cmd->cmd.lim = &cmd->cmdblk[6]; break; case 1: cmd->cmd.lim = &cmd->cmdblk[10]; break; default: cmd->cmd.lim = &cmd->cmdblk[12]; break; } switch(cmdbyte){ case ScsiTestunit: break; case ScsiStartunit: cmd->cmdblk[4] = 1; break; case ScsiModesense: cmd->cmdblk[2] = 1; /* fall through */ case ScsiExtsens: case ScsiInquiry: cmd->cmdblk[4] = size; break; case ScsiGetcap: break; } cmd->b = b; cmd->data.base = b->virt; cmd->data.lim = cmd->data.base + size; cmd->data.ptr = cmd->data.base; cmd->save = cmd->data.base; return cmd; } int scsiready(int dev) { Scsi *cmd; int status; cmd = scsicmd(dev, ScsiTestunit, scsibuf(), 0); if(waserror()){ scsifree(cmd->b); qunlock(cmd); nexterror(); } status = scsiexec(cmd, ScsiOut); poperror(); scsifree(cmd->b); qunlock(cmd); if((status&0xff00) != 0x6000) error(Eio); return status&0xff; } int scsistartstop(int dev, int cmdbyte) { Scsi *cmd; int status; cmd = scsicmd(dev, cmdbyte, scsibuf(), 0); if(waserror()){ scsifree(cmd->b); qunlock(cmd); nexterror(); } status = scsiexec(cmd, ScsiOut); poperror(); scsifree(cmd->b); qunlock(cmd); if((status&0xff00) != 0x6000) error(Eio); return status&0xff; } int scsisense(int dev, void *p) { Scsi *cmd; int status; cmd = scsicmd(dev, ScsiExtsens, scsibuf(), 18); if(waserror()){ scsifree(cmd->b); qunlock(cmd); nexterror(); } status = scsiexec(cmd, ScsiIn); memmove(p, cmd->data.base, 18); poperror(); scsifree(cmd->b); qunlock(cmd); if((status&0xff00) != 0x6000) error(Eio); return status&0xff; } int scsicap(int dev, void *p) { Scsi *cmd; int status; cmd = scsicmd(dev, ScsiGetcap, scsibuf(), 8); if(waserror()){ scsifree(cmd->b); qunlock(cmd); nexterror(); } status = scsiexec(cmd, ScsiIn); memmove(p, cmd->data.base, 8); poperror(); scsifree(cmd->b); qunlock(cmd); if((status&0xff00) != 0x6000) error(Eio); if(status & 0xFF) scsisense(dev, p); return status&0xff; } int scsiinquiry(int dev, void *p, int size) { Scsi *cmd; int status; cmd = scsicmd(dev, ScsiInquiry, scsibuf(), size); if(waserror()){ scsifree(cmd->b); qunlock(cmd); nexterror(); } status = scsiexec(cmd, ScsiIn); memmove(p, cmd->data.base, size); poperror(); scsifree(cmd->b); qunlock(cmd); if((status&0xff00) != 0x6000) error(Eio); if(status & 0xFF) scsisense(dev, p); return status&0xff; } int scsiwp(int dev) { /* Device specific Scsi *cmd; int r, status; cmd = scsicmd(dev, ScsiModesense, scsibuf(), 12); if(waserror()){ scsifree(cmd->b); qunlock(cmd); nexterror(); } status = scsiexec(cmd, ScsiIn); r = cmd->data.base[2] & 0x80; poperror(); scsifree(cmd->b); qunlock(cmd); if ((status&0xffff) != 0x6000) error(Eio); return r; */ USED(dev); return 0; } int scsimodesense(int dev, int page, void *p, int size) { Scsi *cmd; int status; cmd = scsicmd(dev, ScsiModesense, scsibuf(), size); cmd->cmdblk[2] = page; if(waserror()){ scsifree(cmd->b); qunlock(cmd); nexterror(); } status = scsiexec(cmd, ScsiIn); memmove(p, cmd->data.base, size); poperror(); scsifree(cmd->b); qunlock(cmd); if ((status&0xffff) != 0x6000) error(Eio); return status&0xff; } int scsibread(int dev, Scsibuf *b, long n, long blocksize, long blockno) { Scsi *cmd; int cmdbyte, status; if(blockno <= 0x1fffff && n <= 256) cmdbyte = ScsiRead; else cmdbyte = ScsiExtread; cmd = scsicmd(dev, cmdbyte, b, n*blocksize); if(waserror()){ qunlock(cmd); nexterror(); } switch(cmdbyte){ case ScsiRead: cmd->cmdblk[1] |= blockno >> 16; cmd->cmdblk[2] = blockno >> 8; cmd->cmdblk[3] = blockno; cmd->cmdblk[4] = n; break; default: cmd->cmdblk[2] = blockno >> 24; cmd->cmdblk[3] = blockno >> 16; cmd->cmdblk[4] = blockno >> 8; cmd->cmdblk[5] = blockno; cmd->cmdblk[7] = n>>8; cmd->cmdblk[8] = n; break; } status = scsiexec(cmd, ScsiIn); n = cmd->data.ptr - cmd->data.base; poperror(); qunlock(cmd); if(n <= 0 && status == 0x6002) error(Eio); return n; } int scsibwrite(int dev, Scsibuf *b, long n, long blocksize, long blockno) { Scsi *cmd; int cmdbyte, status; if(blockno <= 0x1fffff && n <= 256) cmdbyte = ScsiWrite; else cmdbyte = ScsiExtwrite; cmd = scsicmd(dev, cmdbyte, b, n*blocksize); if(waserror()){ qunlock(cmd); nexterror(); } switch(cmdbyte){ case ScsiWrite: cmd->cmdblk[1] |= blockno >> 16; cmd->cmdblk[2] = blockno >> 8; cmd->cmdblk[3] = blockno; cmd->cmdblk[4] = n; break; default: cmd->cmdblk[2] = blockno >> 24; cmd->cmdblk[3] = blockno >> 16; cmd->cmdblk[4] = blockno >> 8; cmd->cmdblk[5] = blockno; cmd->cmdblk[7] = n>>8; cmd->cmdblk[8] = n; break; } status = scsiexec(cmd, ScsiOut); n = cmd->data.ptr - cmd->data.base; poperror(); qunlock(cmd); if(n <= 0 && status == 0x6002) error(Eio); return n; }