#include <u.h>
#include <libc.h>
#include <disk.h>
#include <fis.h>
#include </sys/src/cmd/scuzz/scsireq.h>
#include "smart.h"
enum{
Replysz = 16,
};
typedef struct Rcmd Rcmd;
struct Rcmd{
uchar proto;
uchar cdbsz;
uchar cdb[16];
};
typedef struct Req Req;
struct Req {
char haverfis;
Rcmd cmd;
char sdstat[16];
uchar sense[0x100];
uchar data[0x200];
uint count;
};
void
turcdb(Req *r)
{
uchar *cmd;
cmd = r->cmd.cdb;
r->cmd.cdbsz = 6;
r->cmd.proto = Pin;
memset(cmd, 0, 6);
r->count = 0;
}
void
reqsensecdb(Req *r)
{
uchar *cmd;
cmd = r->cmd.cdb;
r->cmd.cdbsz = 6;
r->cmd.proto = Pin;
memset(cmd, 0, 6);
cmd[0] = ScmdRsense;
cmd[4] = 128;
r->count = 128;
}
static void
sensetrace(uchar *cdb, uchar *u)
{
char *e;
USED(cdb);
if(1)
return;
e = scsierror(u[12], u[13]);
fprint(2, "sense %.2ux: %.2ux%.2ux%.2ux %s\n", cdb[0], u[2], u[12], u[13], e);
}
static int
issuescsi(Req *r, Sdisk *d)
{
uchar *u;
int ok, rv, n;
Req sense;
if(write(d->fd, r->cmd.cdb, r->cmd.cdbsz) != r->cmd.cdbsz){
eprint(d, "cdb write error: %r\n");
return -1;
}
werrstr("");
switch(r->cmd.proto){
default:
case Pin:
n = read(d->fd, r->data, r->count);
ok = n >= 0;
r->count = 0;
if(ok)
r->count = n;
break;
case Pout:
n = write(d->fd, r->data, r->count);
ok = n == r->count;
break;
}
rv = 0;
memset(r->sdstat, 0, sizeof r->sdstat);
if(read(d->fd, r->sdstat, Replysz) < 1){
eprint(d, "status reply read error: %r\n");
return -1;
}
if(n == -1)
rv = -1; /* scsi not supported; don't whine */
else if(rv == 0 && (rv = atoi(r->sdstat)) != 0){
memset(&sense, 0, sizeof sense);
reqsensecdb(&sense);
if(issuescsi(&sense, d) == 0){
memmove(r->sense, sense.data, sense.count);
u = r->sense;
rv = u[2];
sensetrace(r->cmd.cdb, u);
}else
rv = -1;
}
return ok? rv: -1;
}
void
modesensecdb(Req *r, uchar page, uint n)
{
uchar *cmd;
cmd = r->cmd.cdb;
r->cmd.cdbsz = 10;
r->cmd.proto = Pin;
memset(cmd, 0, 10);
cmd[0] = ScmdMsense10;
cmd[2] = page;
cmd[7] = n>>8;
cmd[8] = n;
r->count = n;
}
void
modeselectcdb(Req *r, uint n)
{
uchar *cmd;
cmd = r->cmd.cdb;
r->cmd.proto = Pout;
r->cmd.cdbsz = 10;
memset(cmd, 0, 10);
cmd[0] = ScmdMselect10;
cmd[1] = 0x10; /* assume scsi2 ! */
cmd[7] = n>>8;
cmd[8] = n;
r->count = n;
}
int
scsiprobe(Sdisk *d)
{
Req r;
memset(&r, 0, sizeof r);
turcdb(&r);
if(issuescsi(&r, d) == -1)
return -1;
memset(&r, 0, sizeof r);
modesensecdb(&r, 0x1c, sizeof r.data);
if(issuescsi(&r, d) != 0 || r.count < 8)
return -1;
return 0;
}
enum{
/* mrie bits */
Mnone = 0,
Masync = 1, /* obs */
Mattn = 2, /* generate unit attention */
Mcrerror = 3, /* conditionally generate recovered error */
Mrerror = 4, /* unconditionally " */
Mnosense = 5, /* generate no sense */
Mreqonly = 6, /* report only in response to req sense */
/* byte 2 bits */
Perf = 1<<7, /* smart may not cause delays */
Ebf = 1<<5, /* enable bacground functions */
Ewasc = 1<<4, /* enable warnings */
Dexcpt = 1<<3, /* disable smart */
Smarttst = 1<<4, /* generate spurious smart error 5dff */
Logerr = 1<<0, /* enable reporting */
};
int
scsienable(Sdisk *d)
{
Req r;
memset(&r, 0, sizeof r);
r.data[8 + 0] = 0x1c;
r.data[8 + 1] = 0xa;
r.data[8 + 2] = Ebf | Ewasc | Logerr;
r.data[8 + 3] = Mreqonly;
r.data[8 +11] = 1;
modeselectcdb(&r, 12 + 8);
if(issuescsi(&r, d) != 0)
return -1;
return 0;
}
int
scsistatus(Sdisk *d, char *s, int l)
{
char *err;
uchar *u;
int rv;
Req r;
memset(&r, 0, sizeof r);
reqsensecdb(&r);
rv = issuescsi(&r, d);
if(rv == 0 && r.count > 12){
u = r.data;
if(u[12] + u[13] == 0)
err = "normal";
else{
err = scsierror(u[12], u[13]);
rv = -1;
}
if(err == nil)
err = "unknown";
snprint(s, l, "%s", err);
}else
snprint(s, l, "smart error");
return rv;
}
|