#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 Nbuf 2 #define DATASIZE (32*512) #undef DATASIZE #define DATASIZE (64*1024) struct{ Lock; Scsibuf *free; }scsibufalloc; static Scsi staticcmd; /* BUG: should be one per scsi device */ enum { Qdir, 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 == 1) devdir(c, (Qid){qid,0}, "id", 1, 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) { int i; for(i = 0; i < Nbuf; i++) scsifree(scsialloc(DATASIZE)); lock(&scsibufalloc); unlock(&scsibufalloc); resetscsi(); } void scsiinit(void) { initscsi(); } Chan * scsiattach(char *param) { return devattach('S', param); } 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; 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; if(n == 0) return 0; if(c->qid.path & CHDIR) return devdirread(c, a, n, 0, 0, scsigen); if(c->qid.path==1){ if(offset == 0){ /*void scsidump(void); scsidump();*/ *a = scsiownid; n = 1; }else n = 0; }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 = &staticcmd; if(c->qid.path==1 && n>0){ if(offset == 0){ n = 1; scsiownid=*a; scsireset(); }else n = 0; }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; 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; 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 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 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) { 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; } 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; cmd = scsicmd(dev, ScsiRead, b, n*blocksize); if(waserror()){ qunlock(cmd); nexterror(); } cmd->cmdblk[1] = blockno >> 16; cmd->cmdblk[2] = blockno >> 8; cmd->cmdblk[3] = blockno; cmd->cmdblk[4] = n; scsiexec(cmd, ScsiIn); n = cmd->data.ptr - cmd->data.base; poperror(); qunlock(cmd); return n; } int scsibwrite(int dev, Scsibuf *b, long n, long blocksize, long blockno) { Scsi *cmd; cmd = scsicmd(dev, ScsiWrite, b, n*blocksize); if(waserror()){ qunlock(cmd); nexterror(); } cmd->cmdblk[1] = blockno >> 16; cmd->cmdblk[2] = blockno >> 8; cmd->cmdblk[3] = blockno; cmd->cmdblk[4] = n; scsiexec(cmd, ScsiOut); n = cmd->data.ptr - cmd->data.base; poperror(); qunlock(cmd); return n; } /* * get a scsi io buffer of DATASIZE size */ Scsibuf * scsibuf(void) { Scsibuf *b; for(;;) { lock(&scsibufalloc); b = scsibufalloc.free; if(b != 0) { scsibufalloc.free = b->next; unlock(&scsibufalloc); return b; } unlock(&scsibufalloc); resrcwait(0); } return 0; /* not reached */ } void scsifree(Scsibuf *b) { lock(&scsibufalloc); b->next = scsibufalloc.free; scsibufalloc.free = b; unlock(&scsibufalloc); }