mirror of
git://git.9front.org/plan9front/plan9front
synced 2025-01-12 11:10:06 +00:00
nusb: improved
This commit is contained in:
parent
5181f2e576
commit
05d09f086f
22 changed files with 5288 additions and 10 deletions
|
@ -1,3 +1,7 @@
|
|||
nusb/kb
|
||||
csp=0x010103
|
||||
csp=0x020103
|
||||
|
||||
nusb/disk
|
||||
class=8
|
||||
|
||||
|
|
982
sys/src/cmd/nusb/disk/disk.c
Normal file
982
sys/src/cmd/nusb/disk/disk.c
Normal file
|
@ -0,0 +1,982 @@
|
|||
/*
|
||||
* usb/disk - usb mass storage file server
|
||||
*
|
||||
* supports only the scsi command interface, not ata.
|
||||
*/
|
||||
|
||||
#include <u.h>
|
||||
#include <libc.h>
|
||||
#include <ctype.h>
|
||||
#include <fcall.h>
|
||||
#include <thread.h>
|
||||
#include <9p.h>
|
||||
#include "scsireq.h"
|
||||
#include "usb.h"
|
||||
#include "ums.h"
|
||||
|
||||
enum
|
||||
{
|
||||
Qdir = 0,
|
||||
Qctl,
|
||||
Qraw,
|
||||
Qdata,
|
||||
Qpart,
|
||||
Qmax = Maxparts,
|
||||
};
|
||||
|
||||
typedef struct Dirtab Dirtab;
|
||||
struct Dirtab
|
||||
{
|
||||
char *name;
|
||||
int mode;
|
||||
};
|
||||
|
||||
ulong ctlmode = 0664;
|
||||
|
||||
/*
|
||||
* Partition management (adapted from disk/partfs)
|
||||
*/
|
||||
|
||||
Part *
|
||||
lookpart(Umsc *lun, char *name)
|
||||
{
|
||||
Part *part, *p;
|
||||
|
||||
part = lun->part;
|
||||
for(p=part; p < &part[Qmax]; p++){
|
||||
if(p->inuse && strcmp(p->name, name) == 0)
|
||||
return p;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
Part *
|
||||
freepart(Umsc *lun)
|
||||
{
|
||||
Part *part, *p;
|
||||
|
||||
part = lun->part;
|
||||
for(p=part; p < &part[Qmax]; p++){
|
||||
if(!p->inuse)
|
||||
return p;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
int
|
||||
addpart(Umsc *lun, char *name, vlong start, vlong end, ulong mode)
|
||||
{
|
||||
Part *p;
|
||||
|
||||
if(start < 0 || start > end || end > lun->blocks){
|
||||
werrstr("bad partition boundaries");
|
||||
return -1;
|
||||
}
|
||||
if(lookpart(lun, name) != nil) {
|
||||
werrstr("partition name already in use");
|
||||
return -1;
|
||||
}
|
||||
p = freepart(lun);
|
||||
if(p == nil){
|
||||
werrstr("no free partition slots");
|
||||
return -1;
|
||||
}
|
||||
p->inuse = 1;
|
||||
free(p->name);
|
||||
p->id = p - lun->part;
|
||||
p->name = estrdup(name);
|
||||
p->offset = start;
|
||||
p->length = end - start;
|
||||
p->mode = mode;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
delpart(Umsc *lun, char *s)
|
||||
{
|
||||
Part *p;
|
||||
|
||||
p = lookpart(lun, s);
|
||||
if(p == nil || p->id <= Qdata){
|
||||
werrstr("partition not found");
|
||||
return -1;
|
||||
}
|
||||
p->inuse = 0;
|
||||
free(p->name);
|
||||
p->name = nil;
|
||||
p->vers++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
fixlength(Umsc *lun, vlong blocks)
|
||||
{
|
||||
Part *part, *p;
|
||||
|
||||
part = lun->part;
|
||||
part[Qdata].length = blocks;
|
||||
for(p=&part[Qdata+1]; p < &part[Qmax]; p++){
|
||||
if(p->inuse && p->offset + p->length > blocks){
|
||||
if(p->offset > blocks){
|
||||
p->offset =blocks;
|
||||
p->length = 0;
|
||||
}else
|
||||
p->length = blocks - p->offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
makeparts(Umsc *lun)
|
||||
{
|
||||
addpart(lun, "/", 0, 0, DMDIR | 0555);
|
||||
addpart(lun, "ctl", 0, 0, 0664);
|
||||
addpart(lun, "raw", 0, 0, 0640);
|
||||
addpart(lun, "data", 0, lun->blocks, 0640);
|
||||
}
|
||||
|
||||
/*
|
||||
* ctl parsing & formatting (adapted from partfs)
|
||||
*/
|
||||
|
||||
static char*
|
||||
ctlstring(Usbfs *fs)
|
||||
{
|
||||
Part *p, *part;
|
||||
Fmt fmt;
|
||||
Umsc *lun;
|
||||
Ums *ums;
|
||||
|
||||
ums = fs->dev->aux;
|
||||
lun = fs->aux;
|
||||
part = &lun->part[0];
|
||||
|
||||
fmtstrinit(&fmt);
|
||||
fmtprint(&fmt, "dev %s\n", fs->dev->dir);
|
||||
fmtprint(&fmt, "lun %ld\n", lun - &ums->lun[0]);
|
||||
if(lun->flags & Finqok)
|
||||
fmtprint(&fmt, "inquiry %s\n", lun->inq);
|
||||
if(lun->blocks > 0)
|
||||
fmtprint(&fmt, "geometry %llud %ld\n", lun->blocks, lun->lbsize);
|
||||
for (p = &part[Qdata+1]; p < &part[Qmax]; p++)
|
||||
if (p->inuse)
|
||||
fmtprint(&fmt, "part %s %lld %lld\n",
|
||||
p->name, p->offset, p->offset + p->length);
|
||||
return fmtstrflush(&fmt);
|
||||
}
|
||||
|
||||
static int
|
||||
ctlparse(Usbfs *fs, char *msg)
|
||||
{
|
||||
vlong start, end;
|
||||
char *argv[16];
|
||||
int argc;
|
||||
Umsc *lun;
|
||||
|
||||
lun = fs->aux;
|
||||
argc = tokenize(msg, argv, nelem(argv));
|
||||
|
||||
if(argc < 1){
|
||||
werrstr("empty control message");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(strcmp(argv[0], "part") == 0){
|
||||
if(argc != 4){
|
||||
werrstr("part takes 3 args");
|
||||
return -1;
|
||||
}
|
||||
start = strtoll(argv[2], 0, 0);
|
||||
end = strtoll(argv[3], 0, 0);
|
||||
return addpart(lun, argv[1], start, end, 0640);
|
||||
}else if(strcmp(argv[0], "delpart") == 0){
|
||||
if(argc != 2){
|
||||
werrstr("delpart takes 1 arg");
|
||||
return -1;
|
||||
}
|
||||
return delpart(lun, argv[1]);
|
||||
}
|
||||
werrstr("unknown ctl");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* These are used by scuzz scsireq
|
||||
*/
|
||||
int exabyte, force6bytecmds;
|
||||
|
||||
int diskdebug;
|
||||
|
||||
static void
|
||||
ding(void *, char *msg)
|
||||
{
|
||||
if(strstr(msg, "alarm") != nil)
|
||||
noted(NCONT);
|
||||
noted(NDFLT);
|
||||
}
|
||||
|
||||
static int
|
||||
getmaxlun(Dev *dev)
|
||||
{
|
||||
uchar max;
|
||||
int r;
|
||||
|
||||
max = 0;
|
||||
r = Rd2h|Rclass|Riface;
|
||||
if(usbcmd(dev, r, Getmaxlun, 0, 0, &max, 1) < 0){
|
||||
dprint(2, "disk: %s: getmaxlun failed: %r\n", dev->dir);
|
||||
}else{
|
||||
max &= 017; /* 15 is the max. allowed */
|
||||
dprint(2, "disk: %s: maxlun %d\n", dev->dir, max);
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
static int
|
||||
umsreset(Ums *ums)
|
||||
{
|
||||
int r;
|
||||
|
||||
r = Rh2d|Rclass|Riface;
|
||||
if(usbcmd(ums->dev, r, Umsreset, 0, 0, nil, 0) < 0){
|
||||
fprint(2, "disk: reset: %r\n");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
umsrecover(Ums *ums)
|
||||
{
|
||||
if(umsreset(ums) < 0)
|
||||
return -1;
|
||||
if(unstall(ums->dev, ums->epin, Ein) < 0)
|
||||
dprint(2, "disk: unstall epin: %r\n");
|
||||
|
||||
/* do we need this when epin == epout? */
|
||||
if(unstall(ums->dev, ums->epout, Eout) < 0)
|
||||
dprint(2, "disk: unstall epout: %r\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
umsfatal(Ums *ums)
|
||||
{
|
||||
int i;
|
||||
|
||||
devctl(ums->dev, "detach");
|
||||
for(i = 0; i < ums->maxlun; i++)
|
||||
usbfsdel(&ums->lun[i].fs);
|
||||
}
|
||||
|
||||
static int
|
||||
ispow2(uvlong ul)
|
||||
{
|
||||
return (ul & (ul - 1)) == 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* return smallest power of 2 >= n
|
||||
*/
|
||||
static int
|
||||
log2(int n)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i = 0; (1 << i) < n; i++)
|
||||
;
|
||||
return i;
|
||||
}
|
||||
|
||||
static int
|
||||
umscapacity(Umsc *lun)
|
||||
{
|
||||
uchar data[32];
|
||||
|
||||
lun->blocks = 0;
|
||||
lun->capacity = 0;
|
||||
lun->lbsize = 0;
|
||||
memset(data, 0, sizeof data);
|
||||
if(SRrcapacity(lun, data) < 0 && SRrcapacity(lun, data) < 0)
|
||||
return -1;
|
||||
lun->blocks = GETBELONG(data);
|
||||
lun->lbsize = GETBELONG(data+4);
|
||||
if(lun->blocks == 0xFFFFFFFF){
|
||||
if(SRrcapacity16(lun, data) < 0){
|
||||
lun->lbsize = 0;
|
||||
lun->blocks = 0;
|
||||
return -1;
|
||||
}else{
|
||||
lun->lbsize = GETBELONG(data + 8);
|
||||
lun->blocks = (uvlong)GETBELONG(data)<<32 |
|
||||
GETBELONG(data + 4);
|
||||
}
|
||||
}
|
||||
lun->blocks++; /* SRcapacity returns LBA of last block */
|
||||
lun->capacity = (vlong)lun->blocks * lun->lbsize;
|
||||
fixlength(lun, lun->blocks);
|
||||
if(diskdebug)
|
||||
fprint(2, "disk: logical block size %lud, # blocks %llud\n",
|
||||
lun->lbsize, lun->blocks);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
umsinit(Ums *ums)
|
||||
{
|
||||
uchar i;
|
||||
Umsc *lun;
|
||||
int some;
|
||||
|
||||
umsreset(ums);
|
||||
ums->maxlun = getmaxlun(ums->dev);
|
||||
ums->lun = mallocz((ums->maxlun+1) * sizeof(*ums->lun), 1);
|
||||
some = 0;
|
||||
for(i = 0; i <= ums->maxlun; i++){
|
||||
lun = &ums->lun[i];
|
||||
lun->ums = ums;
|
||||
lun->umsc = lun;
|
||||
lun->lun = i;
|
||||
lun->flags = Fopen | Fusb | Frw10;
|
||||
if(SRinquiry(lun) < 0 && SRinquiry(lun) < 0){
|
||||
dprint(2, "disk: lun %d inquiry failed\n", i);
|
||||
continue;
|
||||
}
|
||||
switch(lun->inquiry[0]){
|
||||
case Devdir:
|
||||
case Devworm: /* a little different than the others */
|
||||
case Devcd:
|
||||
case Devmo:
|
||||
break;
|
||||
default:
|
||||
fprint(2, "disk: lun %d is not a disk (type %#02x)\n",
|
||||
i, lun->inquiry[0]);
|
||||
continue;
|
||||
}
|
||||
SRstart(lun, 1);
|
||||
/*
|
||||
* we ignore the device type reported by inquiry.
|
||||
* Some devices return a wrong value but would still work.
|
||||
*/
|
||||
some++;
|
||||
lun->inq = smprint("%.48s", (char *)lun->inquiry+8);
|
||||
umscapacity(lun);
|
||||
}
|
||||
if(some == 0){
|
||||
dprint(2, "disk: all luns failed\n");
|
||||
devctl(ums->dev, "detach");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* called by SR*() commands provided by scuzz's scsireq
|
||||
*/
|
||||
long
|
||||
umsrequest(Umsc *umsc, ScsiPtr *cmd, ScsiPtr *data, int *status)
|
||||
{
|
||||
Cbw cbw;
|
||||
Csw csw;
|
||||
int n, nio, left;
|
||||
Ums *ums;
|
||||
|
||||
ums = umsc->ums;
|
||||
|
||||
memcpy(cbw.signature, "USBC", 4);
|
||||
cbw.tag = ++ums->seq;
|
||||
cbw.datalen = data->count;
|
||||
cbw.flags = data->write? CbwDataOut: CbwDataIn;
|
||||
cbw.lun = umsc->lun;
|
||||
if(cmd->count < 1 || cmd->count > 16)
|
||||
print("disk: umsrequest: bad cmd count: %ld\n", cmd->count);
|
||||
|
||||
cbw.len = cmd->count;
|
||||
assert(cmd->count <= sizeof(cbw.command));
|
||||
memcpy(cbw.command, cmd->p, cmd->count);
|
||||
memset(cbw.command + cmd->count, 0, sizeof(cbw.command) - cmd->count);
|
||||
|
||||
werrstr(""); /* we use %r later even for n == 0 */
|
||||
if(diskdebug){
|
||||
fprint(2, "disk: cmd: tag %#lx: ", cbw.tag);
|
||||
for(n = 0; n < cbw.len; n++)
|
||||
fprint(2, " %2.2x", cbw.command[n]&0xFF);
|
||||
fprint(2, " datalen: %ld\n", cbw.datalen);
|
||||
}
|
||||
|
||||
/* issue tunnelled scsi command */
|
||||
if(write(ums->epout->dfd, &cbw, CbwLen) != CbwLen){
|
||||
fprint(2, "disk: cmd: %r\n");
|
||||
goto Fail;
|
||||
}
|
||||
|
||||
/* transfer the data */
|
||||
nio = data->count;
|
||||
if(nio != 0){
|
||||
if(data->write)
|
||||
n = write(ums->epout->dfd, data->p, nio);
|
||||
else{
|
||||
n = read(ums->epin->dfd, data->p, nio);
|
||||
left = nio - n;
|
||||
if (n >= 0 && left > 0) /* didn't fill data->p? */
|
||||
memset(data->p + n, 0, left);
|
||||
}
|
||||
nio = n;
|
||||
if(diskdebug)
|
||||
if(n < 0)
|
||||
fprint(2, "disk: data: %r\n");
|
||||
else
|
||||
fprint(2, "disk: data: %d bytes\n", n);
|
||||
if(n <= 0)
|
||||
if(data->write == 0)
|
||||
unstall(ums->dev, ums->epin, Ein);
|
||||
}
|
||||
|
||||
/* read the transfer's status */
|
||||
n = read(ums->epin->dfd, &csw, CswLen);
|
||||
if(n <= 0){
|
||||
/* n == 0 means "stalled" */
|
||||
unstall(ums->dev, ums->epin, Ein);
|
||||
n = read(ums->epin->dfd, &csw, CswLen);
|
||||
}
|
||||
|
||||
if(n != CswLen || strncmp(csw.signature, "USBS", 4) != 0){
|
||||
dprint(2, "disk: read n=%d: status: %r\n", n);
|
||||
goto Fail;
|
||||
}
|
||||
if(csw.tag != cbw.tag){
|
||||
dprint(2, "disk: status tag mismatch\n");
|
||||
goto Fail;
|
||||
}
|
||||
if(csw.status >= CswPhaseErr){
|
||||
dprint(2, "disk: phase error\n");
|
||||
goto Fail;
|
||||
}
|
||||
if(csw.dataresidue == 0 || ums->wrongresidues)
|
||||
csw.dataresidue = data->count - nio;
|
||||
if(diskdebug){
|
||||
fprint(2, "disk: status: %2.2ux residue: %ld\n",
|
||||
csw.status, csw.dataresidue);
|
||||
if(cbw.command[0] == ScmdRsense){
|
||||
fprint(2, "sense data:");
|
||||
for(n = 0; n < data->count - csw.dataresidue; n++)
|
||||
fprint(2, " %2.2x", data->p[n]);
|
||||
fprint(2, "\n");
|
||||
}
|
||||
}
|
||||
switch(csw.status){
|
||||
case CswOk:
|
||||
*status = STok;
|
||||
break;
|
||||
case CswFailed:
|
||||
*status = STcheck;
|
||||
break;
|
||||
default:
|
||||
dprint(2, "disk: phase error\n");
|
||||
goto Fail;
|
||||
}
|
||||
ums->nerrs = 0;
|
||||
return data->count - csw.dataresidue;
|
||||
|
||||
Fail:
|
||||
*status = STharderr;
|
||||
if(ums->nerrs++ > 15){
|
||||
fprint(2, "disk: %s: too many errors: device detached\n", ums->dev->dir);
|
||||
umsfatal(ums);
|
||||
}else
|
||||
umsrecover(ums);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
dwalk(Usbfs *fs, Fid *fid, char *name)
|
||||
{
|
||||
Umsc *lun;
|
||||
Part *p;
|
||||
|
||||
lun = fs->aux;
|
||||
|
||||
if((fid->qid.type & QTDIR) == 0){
|
||||
werrstr("walk in non-directory");
|
||||
return -1;
|
||||
}
|
||||
if(strcmp(name, "..") == 0)
|
||||
return 0;
|
||||
|
||||
p = lookpart(lun, name);
|
||||
if(p == nil){
|
||||
werrstr(Enotfound);
|
||||
return -1;
|
||||
}
|
||||
fid->qid.path = p->id | fs->qid;
|
||||
fid->qid.vers = p->vers;
|
||||
fid->qid.type = p->mode >> 24;
|
||||
return 0;
|
||||
}
|
||||
static int
|
||||
dstat(Usbfs *fs, Qid qid, Dir *d);
|
||||
|
||||
static void
|
||||
dostat(Usbfs *fs, int path, Dir *d)
|
||||
{
|
||||
Umsc *lun;
|
||||
Part *p;
|
||||
|
||||
lun = fs->aux;
|
||||
p = &lun->part[path];
|
||||
d->qid.path = path;
|
||||
d->qid.vers = p->vers;
|
||||
d->qid.type =p->mode >> 24;
|
||||
d->mode = p->mode;
|
||||
d->length = (vlong) p->length * lun->lbsize;
|
||||
strecpy(d->name, d->name + Namesz - 1, p->name);
|
||||
}
|
||||
|
||||
static int
|
||||
dirgen(Usbfs *fs, Qid, int n, Dir *d, void*)
|
||||
{
|
||||
Umsc *lun;
|
||||
int i;
|
||||
|
||||
lun = fs->aux;
|
||||
for(i = Qctl; i < Qmax; i++){
|
||||
if(lun->part[i].inuse == 0)
|
||||
continue;
|
||||
if(n-- == 0)
|
||||
break;
|
||||
}
|
||||
if(i == Qmax)
|
||||
return -1;
|
||||
dostat(fs, i, d);
|
||||
d->qid.path |= fs->qid;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
dstat(Usbfs *fs, Qid qid, Dir *d)
|
||||
{
|
||||
int path;
|
||||
|
||||
path = qid.path & ~fs->qid;
|
||||
dostat(fs, path, d);
|
||||
d->qid.path |= fs->qid;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
dopen(Usbfs *fs, Fid *fid, int)
|
||||
{
|
||||
ulong path;
|
||||
Umsc *lun;
|
||||
|
||||
path = fid->qid.path & ~fs->qid;
|
||||
lun = fs->aux;
|
||||
switch(path){
|
||||
case Qraw:
|
||||
lun->phase = Pcmd;
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* check i/o parameters and compute values needed later.
|
||||
* we shift & mask manually to avoid run-time calls to _divv and _modv,
|
||||
* since we don't need general division nor its cost.
|
||||
*/
|
||||
static int
|
||||
setup(Umsc *lun, Part *p, char *data, int count, vlong offset)
|
||||
{
|
||||
long nb, lbsize, lbshift, lbmask;
|
||||
uvlong bno;
|
||||
|
||||
if(count < 0 || lun->lbsize <= 0 && umscapacity(lun) < 0 ||
|
||||
lun->lbsize == 0)
|
||||
return -1;
|
||||
lbsize = lun->lbsize;
|
||||
assert(ispow2(lbsize));
|
||||
lbshift = log2(lbsize);
|
||||
lbmask = lbsize - 1;
|
||||
|
||||
bno = offset >> lbshift; /* offset / lbsize */
|
||||
nb = ((offset + count + lbsize - 1) >> lbshift) - bno;
|
||||
|
||||
if(bno + nb > p->length) /* past end of partition? */
|
||||
nb = p->length - bno;
|
||||
if(nb * lbsize > Maxiosize)
|
||||
nb = Maxiosize / lbsize;
|
||||
lun->nb = nb;
|
||||
if(bno >= p->length || nb == 0)
|
||||
return 0;
|
||||
|
||||
bno += p->offset; /* start of partition */
|
||||
lun->offset = bno;
|
||||
lun->off = offset & lbmask; /* offset % lbsize */
|
||||
if(lun->off == 0 && (count & lbmask) == 0)
|
||||
lun->bufp = data;
|
||||
else
|
||||
/* not transferring full, aligned blocks; need intermediary */
|
||||
lun->bufp = lun->buf;
|
||||
return count;
|
||||
}
|
||||
|
||||
/*
|
||||
* Upon SRread/SRwrite errors we assume the medium may have changed,
|
||||
* and ask again for the capacity of the media.
|
||||
* BUG: How to proceed to avoid confussing dossrv??
|
||||
*/
|
||||
static long
|
||||
dread(Usbfs *fs, Fid *fid, void *data, long count, vlong offset)
|
||||
{
|
||||
long n;
|
||||
ulong path;
|
||||
char buf[64];
|
||||
char *s;
|
||||
Part *p;
|
||||
Umsc *lun;
|
||||
Ums *ums;
|
||||
Qid q;
|
||||
|
||||
q = fid->qid;
|
||||
path = fid->qid.path & ~fs->qid;
|
||||
ums = fs->dev->aux;
|
||||
lun = fs->aux;
|
||||
|
||||
qlock(ums);
|
||||
switch(path){
|
||||
case Qdir:
|
||||
count = usbdirread(fs, q, data, count, offset, dirgen, nil);
|
||||
break;
|
||||
case Qctl:
|
||||
s = ctlstring(fs);
|
||||
count = usbreadbuf(data, count, offset, s, strlen(s));
|
||||
free(s);
|
||||
break;
|
||||
case Qraw:
|
||||
if(lun->lbsize <= 0 && umscapacity(lun) < 0){
|
||||
count = -1;
|
||||
break;
|
||||
}
|
||||
switch(lun->phase){
|
||||
case Pcmd:
|
||||
qunlock(ums);
|
||||
werrstr("phase error");
|
||||
return -1;
|
||||
case Pdata:
|
||||
lun->data.p = data;
|
||||
lun->data.count = count;
|
||||
lun->data.write = 0;
|
||||
count = umsrequest(lun,&lun->cmd,&lun->data,&lun->status);
|
||||
lun->phase = Pstatus;
|
||||
if(count < 0)
|
||||
lun->lbsize = 0; /* medium may have changed */
|
||||
break;
|
||||
case Pstatus:
|
||||
n = snprint(buf, sizeof buf, "%11.0ud ", lun->status);
|
||||
count = usbreadbuf(data, count, 0LL, buf, n);
|
||||
lun->phase = Pcmd;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case Qdata:
|
||||
default:
|
||||
p = &lun->part[path];
|
||||
if(!p->inuse){
|
||||
count = -1;
|
||||
werrstr(Eperm);
|
||||
break;
|
||||
}
|
||||
count = setup(lun, p, data, count, offset);
|
||||
if (count <= 0)
|
||||
break;
|
||||
n = SRread(lun, lun->bufp, lun->nb * lun->lbsize);
|
||||
if(n < 0){
|
||||
lun->lbsize = 0; /* medium may have changed */
|
||||
count = -1;
|
||||
} else if (lun->bufp == data)
|
||||
count = n;
|
||||
else{
|
||||
/*
|
||||
* if n == lun->nb*lun->lbsize (as expected),
|
||||
* just copy count bytes.
|
||||
*/
|
||||
if(lun->off + count > n)
|
||||
count = n - lun->off; /* short read */
|
||||
if(count > 0)
|
||||
memmove(data, lun->bufp + lun->off, count);
|
||||
}
|
||||
break;
|
||||
}
|
||||
qunlock(ums);
|
||||
return count;
|
||||
}
|
||||
|
||||
static long
|
||||
dwrite(Usbfs *fs, Fid *fid, void *data, long count, vlong offset)
|
||||
{
|
||||
long len, ocount;
|
||||
ulong path;
|
||||
uvlong bno;
|
||||
Ums *ums;
|
||||
Part *p;
|
||||
Umsc *lun;
|
||||
char *s;
|
||||
|
||||
ums = fs->dev->aux;
|
||||
lun = fs->aux;
|
||||
path = fid->qid.path & ~fs->qid;
|
||||
|
||||
qlock(ums);
|
||||
switch(path){
|
||||
case Qdir:
|
||||
count = -1;
|
||||
werrstr(Eperm);
|
||||
break;
|
||||
case Qctl:
|
||||
s = emallocz(count+1, 1);
|
||||
memmove(s, data, count);
|
||||
if(s[count-1] == '\n')
|
||||
s[count-1] = 0;
|
||||
if(ctlparse(fs, s) == -1)
|
||||
count = -1;
|
||||
free(s);
|
||||
break;
|
||||
case Qraw:
|
||||
if(lun->lbsize <= 0 && umscapacity(lun) < 0){
|
||||
count = -1;
|
||||
break;
|
||||
}
|
||||
switch(lun->phase){
|
||||
case Pcmd:
|
||||
if(count != 6 && count != 10){
|
||||
qunlock(ums);
|
||||
werrstr("bad command length");
|
||||
return -1;
|
||||
}
|
||||
memmove(lun->rawcmd, data, count);
|
||||
lun->cmd.p = lun->rawcmd;
|
||||
lun->cmd.count = count;
|
||||
lun->cmd.write = 1;
|
||||
lun->phase = Pdata;
|
||||
break;
|
||||
case Pdata:
|
||||
lun->data.p = data;
|
||||
lun->data.count = count;
|
||||
lun->data.write = 1;
|
||||
count = umsrequest(lun,&lun->cmd,&lun->data,&lun->status);
|
||||
lun->phase = Pstatus;
|
||||
if(count < 0)
|
||||
lun->lbsize = 0; /* medium may have changed */
|
||||
break;
|
||||
case Pstatus:
|
||||
lun->phase = Pcmd;
|
||||
werrstr("phase error");
|
||||
count = -1;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case Qdata:
|
||||
default:
|
||||
p = &lun->part[path];
|
||||
if(!p->inuse){
|
||||
count = -1;
|
||||
werrstr(Eperm);
|
||||
break;
|
||||
}
|
||||
len = ocount = count;
|
||||
count = setup(lun, p, data, count, offset);
|
||||
if (count <= 0)
|
||||
break;
|
||||
bno = lun->offset;
|
||||
if (lun->bufp == lun->buf) {
|
||||
count = SRread(lun, lun->bufp, lun->nb * lun->lbsize);
|
||||
if(count < 0) {
|
||||
lun->lbsize = 0; /* medium may have changed */
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* if count == lun->nb*lun->lbsize, as expected, just
|
||||
* copy len (the original count) bytes of user data.
|
||||
*/
|
||||
if(lun->off + len > count)
|
||||
len = count - lun->off; /* short read */
|
||||
if(len > 0)
|
||||
memmove(lun->bufp + lun->off, data, len);
|
||||
}
|
||||
|
||||
lun->offset = bno;
|
||||
count = SRwrite(lun, lun->bufp, lun->nb * lun->lbsize);
|
||||
if(count < 0)
|
||||
lun->lbsize = 0; /* medium may have changed */
|
||||
else{
|
||||
if(lun->off + len > count)
|
||||
count -= lun->off; /* short write */
|
||||
/* never report more bytes written than requested */
|
||||
if(count < 0)
|
||||
count = 0;
|
||||
else if(count > ocount)
|
||||
count = ocount;
|
||||
}
|
||||
break;
|
||||
}
|
||||
qunlock(ums);
|
||||
return count;
|
||||
}
|
||||
|
||||
int
|
||||
findendpoints(Ums *ums)
|
||||
{
|
||||
Ep *ep;
|
||||
Usbdev *ud;
|
||||
ulong csp, sc;
|
||||
int i, epin, epout;
|
||||
|
||||
epin = epout = -1;
|
||||
ud = ums->dev->usb;
|
||||
for(i = 0; i < nelem(ud->ep); i++){
|
||||
if((ep = ud->ep[i]) == nil)
|
||||
continue;
|
||||
csp = ep->iface->csp;
|
||||
sc = Subclass(csp);
|
||||
if(!(Class(csp) == Clstorage && (Proto(csp) == Protobulk)))
|
||||
continue;
|
||||
if(sc != Subatapi && sc != Sub8070 && sc != Subscsi)
|
||||
fprint(2, "disk: subclass %#ulx not supported. trying anyway\n", sc);
|
||||
if(ep->type == Ebulk){
|
||||
if(ep->dir == Eboth || ep->dir == Ein)
|
||||
if(epin == -1)
|
||||
epin = ep->id;
|
||||
if(ep->dir == Eboth || ep->dir == Eout)
|
||||
if(epout == -1)
|
||||
epout = ep->id;
|
||||
}
|
||||
}
|
||||
dprint(2, "disk: ep ids: in %d out %d\n", epin, epout);
|
||||
if(epin == -1 || epout == -1)
|
||||
return -1;
|
||||
ums->epin = openep(ums->dev, epin);
|
||||
if(ums->epin == nil){
|
||||
fprint(2, "disk: openep %d: %r\n", epin);
|
||||
return -1;
|
||||
}
|
||||
if(epout == epin){
|
||||
incref(ums->epin);
|
||||
ums->epout = ums->epin;
|
||||
}else
|
||||
ums->epout = openep(ums->dev, epout);
|
||||
if(ums->epout == nil){
|
||||
fprint(2, "disk: openep %d: %r\n", epout);
|
||||
closedev(ums->epin);
|
||||
return -1;
|
||||
}
|
||||
if(ums->epin == ums->epout)
|
||||
opendevdata(ums->epin, ORDWR);
|
||||
else{
|
||||
opendevdata(ums->epin, OREAD);
|
||||
opendevdata(ums->epout, OWRITE);
|
||||
}
|
||||
if(ums->epin->dfd < 0 || ums->epout->dfd < 0){
|
||||
fprint(2, "disk: open i/o ep data: %r\n");
|
||||
closedev(ums->epin);
|
||||
closedev(ums->epout);
|
||||
return -1;
|
||||
}
|
||||
dprint(2, "disk: ep in %s out %s\n", ums->epin->dir, ums->epout->dir);
|
||||
|
||||
devctl(ums->epin, "timeout 2000");
|
||||
devctl(ums->epout, "timeout 2000");
|
||||
|
||||
if(usbdebug > 1 || diskdebug > 2){
|
||||
devctl(ums->epin, "debug 1");
|
||||
devctl(ums->epout, "debug 1");
|
||||
devctl(ums->dev, "debug 1");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
usage(void)
|
||||
{
|
||||
werrstr("usage: usb/disk [-d] [-N nb]");
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void
|
||||
umsdevfree(void *a)
|
||||
{
|
||||
Ums *ums = a;
|
||||
|
||||
if(ums == nil)
|
||||
return;
|
||||
closedev(ums->epin);
|
||||
closedev(ums->epout);
|
||||
ums->epin = ums->epout = nil;
|
||||
free(ums->lun);
|
||||
free(ums);
|
||||
}
|
||||
|
||||
static Srv diskfs = {
|
||||
.walk = dwalk,
|
||||
.open = dopen,
|
||||
.read = dread,
|
||||
.write = dwrite,
|
||||
.stat = dstat,
|
||||
};
|
||||
|
||||
int
|
||||
diskmain(Dev *dev, int argc, char **argv)
|
||||
{
|
||||
Ums *ums;
|
||||
Umsc *lun;
|
||||
int i, devid;
|
||||
|
||||
devid = dev->id;
|
||||
ARGBEGIN{
|
||||
case 'd':
|
||||
scsidebug(diskdebug);
|
||||
diskdebug++;
|
||||
break;
|
||||
case 'N':
|
||||
devid = atoi(EARGF(usage()));
|
||||
break;
|
||||
default:
|
||||
return usage();
|
||||
}ARGEND
|
||||
if(argc != 0) {
|
||||
return usage();
|
||||
}
|
||||
|
||||
// notify(ding);
|
||||
ums = dev->aux = emallocz(sizeof(Ums), 1);
|
||||
ums->maxlun = -1;
|
||||
ums->dev = dev;
|
||||
dev->free = umsdevfree;
|
||||
if(findendpoints(ums) < 0){
|
||||
werrstr("disk: endpoints not found");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* SanDISK 512M gets residues wrong.
|
||||
*/
|
||||
if(dev->usb->vid == 0x0781 && dev->usb->did == 0x5150)
|
||||
ums->wrongresidues = 1;
|
||||
|
||||
if(umsinit(ums) < 0){
|
||||
dprint(2, "disk: umsinit: %r\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for(i = 0; i <= ums->maxlun; i++){
|
||||
lun = &ums->lun[i];
|
||||
lun->fs = diskfs;
|
||||
snprint(lun->fs.name, sizeof(lun->fs.name), "sdU%d.%d", devid, i);
|
||||
lun->fs.dev = dev;
|
||||
incref(dev);
|
||||
lun->fs.aux = lun;
|
||||
makeparts(lun);
|
||||
usbfsadd(&lun->fs);
|
||||
}
|
||||
return 0;
|
||||
}
|
24
sys/src/cmd/nusb/disk/mkfile
Normal file
24
sys/src/cmd/nusb/disk/mkfile
Normal file
|
@ -0,0 +1,24 @@
|
|||
</$objtype/mkfile
|
||||
|
||||
TARG=disk
|
||||
OFILES=\
|
||||
disk.$O\
|
||||
scsireq.$O\
|
||||
scsierrs.$O\
|
||||
|
||||
HFILES =\
|
||||
scsireq.h\
|
||||
../lib/usb.h\
|
||||
ums.h\
|
||||
|
||||
LIB=../lib/usb.a$O
|
||||
|
||||
BIN=/$objtype/bin/usb
|
||||
|
||||
</sys/src/cmd/mkone
|
||||
CFLAGS=-I../lib $CFLAGS
|
||||
CLEANFILES=scsierrs.c
|
||||
|
||||
scsierrs.c: /sys/lib/scsicodes mkscsierrs
|
||||
mkscsierrs >scsierrs.c
|
||||
|
32
sys/src/cmd/nusb/disk/mkscsierrs
Executable file
32
sys/src/cmd/nusb/disk/mkscsierrs
Executable file
|
@ -0,0 +1,32 @@
|
|||
#!/bin/rc
|
||||
|
||||
cat <<EOF
|
||||
#include <u.h>
|
||||
#include <libc.h>
|
||||
|
||||
typedef struct Err Err;
|
||||
struct Err
|
||||
{
|
||||
int n;
|
||||
char *s;
|
||||
};
|
||||
|
||||
static Err scsierrs[] = {
|
||||
EOF
|
||||
|
||||
grep '^[0-9a-c][0-9a-c][0-9a-c][0-9a-c][ ]' /sys/lib/scsicodes |
|
||||
sed -e 's/^(....) (.*)/ {0x\1, "\2"},\n/'
|
||||
cat <<EOF
|
||||
};
|
||||
|
||||
char*
|
||||
scsierrmsg(int n)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i = 0; i < nelem(scsierrs); i++)
|
||||
if(scsierrs[i].n == n)
|
||||
return scsierrs[i].s;
|
||||
return "scsi error";
|
||||
}
|
||||
EOF
|
986
sys/src/cmd/nusb/disk/scsireq.c
Normal file
986
sys/src/cmd/nusb/disk/scsireq.c
Normal file
|
@ -0,0 +1,986 @@
|
|||
/*
|
||||
* This is /sys/src/cmd/scuzz/scsireq.c
|
||||
* changed to add more debug support, to keep
|
||||
* disk compiling without a scuzz that includes these changes.
|
||||
* Also, this includes minor tweaks for usb:
|
||||
* we set req.lun/unit to rp->lun/unit in SRreqsense
|
||||
* we set the rp->sense[0] bit Sd0valid in SRreqsense
|
||||
* This does not use libdisk to retrieve the scsi error to make
|
||||
* user see the diagnostics if we boot with debug enabled.
|
||||
*
|
||||
* BUGS:
|
||||
* no luns
|
||||
* and incomplete in many other ways
|
||||
*/
|
||||
|
||||
#include <u.h>
|
||||
#include <libc.h>
|
||||
#include <fcall.h>
|
||||
#include "scsireq.h"
|
||||
|
||||
enum {
|
||||
Debug = 0,
|
||||
};
|
||||
|
||||
/*
|
||||
* exabyte tape drives, at least old ones like the 8200 and 8505,
|
||||
* are dumb: you have to read the exact block size on the tape,
|
||||
* they don't take 10-byte SCSI commands, and various other fine points.
|
||||
*/
|
||||
extern int exabyte, force6bytecmds;
|
||||
|
||||
static int debug = Debug;
|
||||
|
||||
static char *scmdnames[256] = {
|
||||
[ScmdTur] "Tur",
|
||||
[ScmdRewind] "Rewind",
|
||||
[ScmdRsense] "Rsense",
|
||||
[ScmdFormat] "Format",
|
||||
[ScmdRblimits] "Rblimits",
|
||||
[ScmdRead] "Read",
|
||||
[ScmdWrite] "Write",
|
||||
[ScmdSeek] "Seek",
|
||||
[ScmdFmark] "Fmark",
|
||||
[ScmdSpace] "Space",
|
||||
[ScmdInq] "Inq",
|
||||
[ScmdMselect6] "Mselect6",
|
||||
[ScmdMselect10] "Mselect10",
|
||||
[ScmdMsense6] "Msense6",
|
||||
[ScmdMsense10] "Msense10",
|
||||
[ScmdStart] "Start",
|
||||
[ScmdRcapacity] "Rcapacity",
|
||||
[ScmdRcapacity16] "Rcap16",
|
||||
[ScmdExtread] "Extread",
|
||||
[ScmdExtwrite] "Extwrite",
|
||||
[ScmdExtseek] "Extseek",
|
||||
|
||||
[ScmdSynccache] "Synccache",
|
||||
[ScmdRTOC] "RTOC",
|
||||
[ScmdRdiscinfo] "Rdiscinfo",
|
||||
[ScmdRtrackinfo] "Rtrackinfo",
|
||||
[ScmdReserve] "Reserve",
|
||||
[ScmdBlank] "Blank",
|
||||
|
||||
[ScmdCDpause] "CDpause",
|
||||
[ScmdCDstop] "CDstop",
|
||||
[ScmdCDplay] "CDplay",
|
||||
[ScmdCDload] "CDload",
|
||||
[ScmdCDscan] "CDscan",
|
||||
[ScmdCDstatus] "CDstatus",
|
||||
[Scmdgetconf] "getconf",
|
||||
};
|
||||
|
||||
long
|
||||
SRready(ScsiReq *rp)
|
||||
{
|
||||
uchar cmd[6];
|
||||
|
||||
memset(cmd, 0, sizeof cmd);
|
||||
rp->cmd.p = cmd;
|
||||
rp->cmd.count = sizeof cmd;
|
||||
rp->data.p = cmd;
|
||||
rp->data.count = 0;
|
||||
rp->data.write = 1;
|
||||
return SRrequest(rp);
|
||||
}
|
||||
|
||||
long
|
||||
SRrewind(ScsiReq *rp)
|
||||
{
|
||||
uchar cmd[6];
|
||||
|
||||
memset(cmd, 0, sizeof cmd);
|
||||
cmd[0] = ScmdRewind;
|
||||
rp->cmd.p = cmd;
|
||||
rp->cmd.count = sizeof cmd;
|
||||
rp->data.p = cmd;
|
||||
rp->data.count = 0;
|
||||
rp->data.write = 1;
|
||||
if(SRrequest(rp) >= 0){
|
||||
rp->offset = 0;
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
long
|
||||
SRreqsense(ScsiReq *rp)
|
||||
{
|
||||
uchar cmd[6];
|
||||
ScsiReq req;
|
||||
long status;
|
||||
|
||||
if(rp->status == Status_SD){
|
||||
rp->status = STok;
|
||||
return 0;
|
||||
}
|
||||
memset(cmd, 0, sizeof cmd);
|
||||
cmd[0] = ScmdRsense;
|
||||
cmd[4] = sizeof(req.sense);
|
||||
memset(&req, 0, sizeof(req));
|
||||
if(rp->flags&Fusb)
|
||||
req.flags |= Fusb;
|
||||
req.lun = rp->lun;
|
||||
req.unit = rp->unit;
|
||||
req.fd = rp->fd;
|
||||
req.umsc = rp->umsc;
|
||||
req.cmd.p = cmd;
|
||||
req.cmd.count = sizeof cmd;
|
||||
req.data.p = rp->sense;
|
||||
req.data.count = sizeof(rp->sense);
|
||||
req.data.write = 0;
|
||||
status = SRrequest(&req);
|
||||
rp->status = req.status;
|
||||
if(status != -1)
|
||||
rp->sense[0] |= Sd0valid;
|
||||
return status;
|
||||
}
|
||||
|
||||
long
|
||||
SRformat(ScsiReq *rp)
|
||||
{
|
||||
uchar cmd[6];
|
||||
|
||||
memset(cmd, 0, sizeof cmd);
|
||||
cmd[0] = ScmdFormat;
|
||||
rp->cmd.p = cmd;
|
||||
rp->cmd.count = sizeof cmd;
|
||||
rp->data.p = cmd;
|
||||
rp->data.count = 6;
|
||||
rp->data.write = 0;
|
||||
return SRrequest(rp);
|
||||
}
|
||||
|
||||
long
|
||||
SRrblimits(ScsiReq *rp, uchar *list)
|
||||
{
|
||||
uchar cmd[6];
|
||||
|
||||
memset(cmd, 0, sizeof cmd);
|
||||
cmd[0] = ScmdRblimits;
|
||||
rp->cmd.p = cmd;
|
||||
rp->cmd.count = sizeof cmd;
|
||||
rp->data.p = list;
|
||||
rp->data.count = 6;
|
||||
rp->data.write = 0;
|
||||
return SRrequest(rp);
|
||||
}
|
||||
|
||||
static int
|
||||
dirdevrw(ScsiReq *rp, uchar *cmd, long nbytes)
|
||||
{
|
||||
long n;
|
||||
|
||||
n = nbytes / rp->lbsize;
|
||||
if(rp->offset <= Max24off && n <= 256 && (rp->flags & Frw10) == 0){
|
||||
PUTBE24(cmd+1, rp->offset);
|
||||
cmd[4] = n;
|
||||
cmd[5] = 0;
|
||||
return 6;
|
||||
}
|
||||
cmd[0] |= ScmdExtread;
|
||||
cmd[1] = 0;
|
||||
PUTBELONG(cmd+2, rp->offset);
|
||||
cmd[6] = 0;
|
||||
cmd[7] = n>>8;
|
||||
cmd[8] = n;
|
||||
cmd[9] = 0;
|
||||
return 10;
|
||||
}
|
||||
|
||||
static int
|
||||
seqdevrw(ScsiReq *rp, uchar *cmd, long nbytes)
|
||||
{
|
||||
long n;
|
||||
|
||||
/* don't set Cmd1sili; we want the ILI bit instead of a fatal error */
|
||||
cmd[1] = rp->flags&Fbfixed? Cmd1fixed: 0;
|
||||
n = nbytes / rp->lbsize;
|
||||
PUTBE24(cmd+2, n);
|
||||
cmd[5] = 0;
|
||||
return 6;
|
||||
}
|
||||
|
||||
extern int diskdebug;
|
||||
|
||||
long
|
||||
SRread(ScsiReq *rp, void *buf, long nbytes)
|
||||
{
|
||||
uchar cmd[10];
|
||||
long n;
|
||||
|
||||
if(rp->lbsize == 0 || (nbytes % rp->lbsize) || nbytes > Maxiosize){
|
||||
if(diskdebug)
|
||||
if (nbytes % rp->lbsize)
|
||||
fprint(2, "disk: i/o size %ld %% %ld != 0\n",
|
||||
nbytes, rp->lbsize);
|
||||
else
|
||||
fprint(2, "disk: i/o size %ld > %d\n",
|
||||
nbytes, Maxiosize);
|
||||
rp->status = Status_BADARG;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* set up scsi read cmd */
|
||||
cmd[0] = ScmdRead;
|
||||
if(rp->flags & Fseqdev)
|
||||
rp->cmd.count = seqdevrw(rp, cmd, nbytes);
|
||||
else
|
||||
rp->cmd.count = dirdevrw(rp, cmd, nbytes);
|
||||
rp->cmd.p = cmd;
|
||||
rp->data.p = buf;
|
||||
rp->data.count = nbytes;
|
||||
rp->data.write = 0;
|
||||
|
||||
/* issue it */
|
||||
n = SRrequest(rp);
|
||||
if(n != -1){ /* it worked? */
|
||||
rp->offset += n / rp->lbsize;
|
||||
return n;
|
||||
}
|
||||
|
||||
/* request failed; maybe we just read a short record? */
|
||||
if (exabyte) {
|
||||
fprint(2, "read error\n");
|
||||
rp->status = STcheck;
|
||||
return n;
|
||||
}
|
||||
if(rp->status != Status_SD || !(rp->sense[0] & Sd0valid))
|
||||
return -1;
|
||||
/* compute # of bytes not read */
|
||||
n = GETBELONG(rp->sense+3) * rp->lbsize;
|
||||
if(!(rp->flags & Fseqdev))
|
||||
return -1;
|
||||
|
||||
/* device is a tape or something similar */
|
||||
if (rp->sense[2] == Sd2filemark || rp->sense[2] == 0x08 ||
|
||||
rp->sense[2] & Sd2ili && n > 0)
|
||||
rp->data.count = nbytes - n;
|
||||
else
|
||||
return -1;
|
||||
n = rp->data.count;
|
||||
if (!rp->readblock++ || debug)
|
||||
fprint(2, "SRread: tape data count %ld%s\n", n,
|
||||
(rp->sense[2] & Sd2ili? " with ILI": ""));
|
||||
rp->status = STok;
|
||||
rp->offset += n / rp->lbsize;
|
||||
return n;
|
||||
}
|
||||
|
||||
long
|
||||
SRwrite(ScsiReq *rp, void *buf, long nbytes)
|
||||
{
|
||||
uchar cmd[10];
|
||||
long n;
|
||||
|
||||
if(rp->lbsize == 0 || (nbytes % rp->lbsize) || nbytes > Maxiosize){
|
||||
if(diskdebug)
|
||||
if (nbytes % rp->lbsize)
|
||||
fprint(2, "disk: i/o size %ld %% %ld != 0\n",
|
||||
nbytes, rp->lbsize);
|
||||
else
|
||||
fprint(2, "disk: i/o size %ld > %d\n",
|
||||
nbytes, Maxiosize);
|
||||
rp->status = Status_BADARG;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* set up scsi write cmd */
|
||||
cmd[0] = ScmdWrite;
|
||||
if(rp->flags & Fseqdev)
|
||||
rp->cmd.count = seqdevrw(rp, cmd, nbytes);
|
||||
else
|
||||
rp->cmd.count = dirdevrw(rp, cmd, nbytes);
|
||||
rp->cmd.p = cmd;
|
||||
rp->data.p = buf;
|
||||
rp->data.count = nbytes;
|
||||
rp->data.write = 1;
|
||||
|
||||
/* issue it */
|
||||
if((n = SRrequest(rp)) == -1){
|
||||
if (exabyte) {
|
||||
fprint(2, "write error\n");
|
||||
rp->status = STcheck;
|
||||
return n;
|
||||
}
|
||||
if(rp->status != Status_SD || rp->sense[2] != Sd2eom)
|
||||
return -1;
|
||||
if(rp->sense[0] & Sd0valid){
|
||||
n -= GETBELONG(rp->sense+3) * rp->lbsize;
|
||||
rp->data.count = nbytes - n;
|
||||
}
|
||||
else
|
||||
rp->data.count = nbytes;
|
||||
n = rp->data.count;
|
||||
}
|
||||
rp->offset += n / rp->lbsize;
|
||||
return n;
|
||||
}
|
||||
|
||||
long
|
||||
SRseek(ScsiReq *rp, long offset, int type)
|
||||
{
|
||||
uchar cmd[10];
|
||||
|
||||
switch(type){
|
||||
|
||||
case 0:
|
||||
break;
|
||||
|
||||
case 1:
|
||||
offset += rp->offset;
|
||||
if(offset >= 0)
|
||||
break;
|
||||
/*FALLTHROUGH*/
|
||||
|
||||
default:
|
||||
if(diskdebug)
|
||||
fprint(2, "disk: seek failed\n");
|
||||
rp->status = Status_BADARG;
|
||||
return -1;
|
||||
}
|
||||
memset(cmd, 0, sizeof cmd);
|
||||
if(offset <= Max24off && (rp->flags & Frw10) == 0){
|
||||
cmd[0] = ScmdSeek;
|
||||
PUTBE24(cmd+1, offset & Max24off);
|
||||
rp->cmd.count = 6;
|
||||
}else{
|
||||
cmd[0] = ScmdExtseek;
|
||||
PUTBELONG(cmd+2, offset);
|
||||
rp->cmd.count = 10;
|
||||
}
|
||||
rp->cmd.p = cmd;
|
||||
rp->data.p = cmd;
|
||||
rp->data.count = 0;
|
||||
rp->data.write = 1;
|
||||
SRrequest(rp);
|
||||
if(rp->status == STok) {
|
||||
rp->offset = offset;
|
||||
return offset;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
long
|
||||
SRfilemark(ScsiReq *rp, ulong howmany)
|
||||
{
|
||||
uchar cmd[6];
|
||||
|
||||
memset(cmd, 0, sizeof cmd);
|
||||
cmd[0] = ScmdFmark;
|
||||
PUTBE24(cmd+2, howmany);
|
||||
rp->cmd.p = cmd;
|
||||
rp->cmd.count = sizeof cmd;
|
||||
rp->data.p = cmd;
|
||||
rp->data.count = 0;
|
||||
rp->data.write = 1;
|
||||
return SRrequest(rp);
|
||||
}
|
||||
|
||||
long
|
||||
SRspace(ScsiReq *rp, uchar code, long howmany)
|
||||
{
|
||||
uchar cmd[6];
|
||||
|
||||
memset(cmd, 0, sizeof cmd);
|
||||
cmd[0] = ScmdSpace;
|
||||
cmd[1] = code;
|
||||
PUTBE24(cmd+2, howmany);
|
||||
rp->cmd.p = cmd;
|
||||
rp->cmd.count = sizeof cmd;
|
||||
rp->data.p = cmd;
|
||||
rp->data.count = 0;
|
||||
rp->data.write = 1;
|
||||
/*
|
||||
* what about rp->offset?
|
||||
*/
|
||||
return SRrequest(rp);
|
||||
}
|
||||
|
||||
long
|
||||
SRinquiry(ScsiReq *rp)
|
||||
{
|
||||
uchar cmd[6];
|
||||
|
||||
memset(cmd, 0, sizeof cmd);
|
||||
cmd[0] = ScmdInq;
|
||||
cmd[4] = sizeof rp->inquiry;
|
||||
rp->cmd.p = cmd;
|
||||
rp->cmd.count = sizeof cmd;
|
||||
memset(rp->inquiry, 0, sizeof rp->inquiry);
|
||||
rp->data.p = rp->inquiry;
|
||||
rp->data.count = sizeof rp->inquiry;
|
||||
rp->data.write = 0;
|
||||
if(SRrequest(rp) >= 0){
|
||||
rp->flags |= Finqok;
|
||||
return 0;
|
||||
}
|
||||
rp->flags &= ~Finqok;
|
||||
return -1;
|
||||
}
|
||||
|
||||
long
|
||||
SRmodeselect6(ScsiReq *rp, uchar *list, long nbytes)
|
||||
{
|
||||
uchar cmd[6];
|
||||
|
||||
memset(cmd, 0, sizeof cmd);
|
||||
cmd[0] = ScmdMselect6;
|
||||
if((rp->flags & Finqok) && (rp->inquiry[2] & 0x07) >= 2)
|
||||
cmd[1] = 0x10;
|
||||
cmd[4] = nbytes;
|
||||
rp->cmd.p = cmd;
|
||||
rp->cmd.count = sizeof cmd;
|
||||
rp->data.p = list;
|
||||
rp->data.count = nbytes;
|
||||
rp->data.write = 1;
|
||||
return SRrequest(rp);
|
||||
}
|
||||
|
||||
long
|
||||
SRmodeselect10(ScsiReq *rp, uchar *list, long nbytes)
|
||||
{
|
||||
uchar cmd[10];
|
||||
|
||||
memset(cmd, 0, sizeof cmd);
|
||||
if((rp->flags & Finqok) && (rp->inquiry[2] & 0x07) >= 2)
|
||||
cmd[1] = 0x10;
|
||||
cmd[0] = ScmdMselect10;
|
||||
cmd[7] = nbytes>>8;
|
||||
cmd[8] = nbytes;
|
||||
rp->cmd.p = cmd;
|
||||
rp->cmd.count = sizeof cmd;
|
||||
rp->data.p = list;
|
||||
rp->data.count = nbytes;
|
||||
rp->data.write = 1;
|
||||
return SRrequest(rp);
|
||||
}
|
||||
|
||||
long
|
||||
SRmodesense6(ScsiReq *rp, uchar page, uchar *list, long nbytes)
|
||||
{
|
||||
uchar cmd[6];
|
||||
|
||||
memset(cmd, 0, sizeof cmd);
|
||||
cmd[0] = ScmdMsense6;
|
||||
cmd[2] = page;
|
||||
cmd[4] = nbytes;
|
||||
rp->cmd.p = cmd;
|
||||
rp->cmd.count = sizeof cmd;
|
||||
rp->data.p = list;
|
||||
rp->data.count = nbytes;
|
||||
rp->data.write = 0;
|
||||
return SRrequest(rp);
|
||||
}
|
||||
|
||||
long
|
||||
SRmodesense10(ScsiReq *rp, uchar page, uchar *list, long nbytes)
|
||||
{
|
||||
uchar cmd[10];
|
||||
|
||||
memset(cmd, 0, sizeof cmd);
|
||||
cmd[0] = ScmdMsense10;
|
||||
cmd[2] = page;
|
||||
cmd[7] = nbytes>>8;
|
||||
cmd[8] = nbytes;
|
||||
rp->cmd.p = cmd;
|
||||
rp->cmd.count = sizeof cmd;
|
||||
rp->data.p = list;
|
||||
rp->data.count = nbytes;
|
||||
rp->data.write = 0;
|
||||
return SRrequest(rp);
|
||||
}
|
||||
|
||||
long
|
||||
SRstart(ScsiReq *rp, uchar code)
|
||||
{
|
||||
uchar cmd[6];
|
||||
|
||||
memset(cmd, 0, sizeof cmd);
|
||||
cmd[0] = ScmdStart;
|
||||
cmd[4] = code;
|
||||
rp->cmd.p = cmd;
|
||||
rp->cmd.count = sizeof cmd;
|
||||
rp->data.p = cmd;
|
||||
rp->data.count = 0;
|
||||
rp->data.write = 1;
|
||||
return SRrequest(rp);
|
||||
}
|
||||
|
||||
long
|
||||
SRrcapacity(ScsiReq *rp, uchar *data)
|
||||
{
|
||||
uchar cmd[10];
|
||||
|
||||
memset(cmd, 0, sizeof cmd);
|
||||
cmd[0] = ScmdRcapacity;
|
||||
rp->cmd.p = cmd;
|
||||
rp->cmd.count = sizeof cmd;
|
||||
rp->data.p = data;
|
||||
rp->data.count = 8;
|
||||
rp->data.write = 0;
|
||||
return SRrequest(rp);
|
||||
}
|
||||
|
||||
long
|
||||
SRrcapacity16(ScsiReq *rp, uchar *data)
|
||||
{
|
||||
uchar cmd[16];
|
||||
uint i;
|
||||
|
||||
i = 32;
|
||||
memset(cmd, 0, sizeof cmd);
|
||||
cmd[0] = ScmdRcapacity16;
|
||||
cmd[1] = 0x10;
|
||||
cmd[10] = i>>24;
|
||||
cmd[11] = i>>16;
|
||||
cmd[12] = i>>8;
|
||||
cmd[13] = i;
|
||||
|
||||
rp->cmd.p = cmd;
|
||||
rp->cmd.count = sizeof cmd;
|
||||
rp->data.p = data;
|
||||
rp->data.count = i;
|
||||
rp->data.write = 0;
|
||||
return SRrequest(rp);
|
||||
}
|
||||
|
||||
void
|
||||
scsidebug(int d)
|
||||
{
|
||||
debug = d;
|
||||
if(debug)
|
||||
fprint(2, "scsidebug on\n");
|
||||
}
|
||||
|
||||
static long
|
||||
request(int fd, ScsiPtr *cmd, ScsiPtr *data, int *status)
|
||||
{
|
||||
long n, r;
|
||||
char buf[16];
|
||||
|
||||
/* this was an experiment but it seems to be a good idea */
|
||||
*status = STok;
|
||||
|
||||
/* send SCSI command */
|
||||
if(write(fd, cmd->p, cmd->count) != cmd->count){
|
||||
fprint(2, "scsireq: write cmd: %r\n");
|
||||
*status = Status_SW;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* read or write actual data */
|
||||
werrstr("");
|
||||
// alarm(5*1000);
|
||||
if(data->write)
|
||||
n = write(fd, data->p, data->count);
|
||||
else {
|
||||
n = read(fd, data->p, data->count);
|
||||
if (n < 0)
|
||||
memset(data->p, 0, data->count);
|
||||
else if (n < data->count)
|
||||
memset(data->p + n, 0, data->count - n);
|
||||
}
|
||||
// alarm(0);
|
||||
if (n != data->count && n <= 0) {
|
||||
if (debug)
|
||||
fprint(2,
|
||||
"request: tried to %s %ld bytes of data for cmd 0x%x but got %r\n",
|
||||
(data->write? "write": "read"),
|
||||
data->count, cmd->p[0]);
|
||||
} else if (n != data->count && (data->write || debug))
|
||||
fprint(2, "request: %s %ld of %ld bytes of actual data\n",
|
||||
(data->write? "wrote": "read"), n, data->count);
|
||||
|
||||
/* read status */
|
||||
buf[0] = '\0';
|
||||
r = read(fd, buf, sizeof buf-1);
|
||||
if(exabyte && r <= 0 || !exabyte && r < 0){
|
||||
fprint(2, "scsireq: read status: %r\n");
|
||||
*status = Status_SW;
|
||||
return -1;
|
||||
}
|
||||
if (r >= 0)
|
||||
buf[r] = '\0';
|
||||
*status = atoi(buf);
|
||||
if(n < 0 && (exabyte || *status != STcheck))
|
||||
fprint(2, "scsireq: status 0x%2.2uX: data transfer: %r\n",
|
||||
*status);
|
||||
return n;
|
||||
}
|
||||
|
||||
static char*
|
||||
seprintcmd(char *s, char* e, char *cmd, int count, int args)
|
||||
{
|
||||
uint c;
|
||||
|
||||
if(count < 6)
|
||||
return seprint(s, e, "<short cmd>");
|
||||
c = cmd[0];
|
||||
if(scmdnames[c] != nil)
|
||||
s = seprint(s, e, "%s", scmdnames[c]);
|
||||
else
|
||||
s = seprint(s, e, "cmd:%#02uX", c);
|
||||
if(args != 0)
|
||||
switch(c){
|
||||
case ScmdRsense:
|
||||
case ScmdInq:
|
||||
case ScmdMselect6:
|
||||
case ScmdMsense6:
|
||||
s = seprint(s, e, " sz %d", cmd[4]);
|
||||
break;
|
||||
case ScmdSpace:
|
||||
s = seprint(s, e, " code %d", cmd[1]);
|
||||
break;
|
||||
case ScmdStart:
|
||||
s = seprint(s, e, " code %d", cmd[4]);
|
||||
break;
|
||||
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
static char*
|
||||
seprintdata(char *s, char *se, uchar *p, int count)
|
||||
{
|
||||
int i;
|
||||
|
||||
if(count == 0)
|
||||
return s;
|
||||
for(i = 0; i < 20 && i < count; i++)
|
||||
s = seprint(s, se, " %02x", p[i]);
|
||||
return s;
|
||||
}
|
||||
|
||||
static void
|
||||
SRdumpReq(ScsiReq *rp)
|
||||
{
|
||||
char buf[128];
|
||||
char *s;
|
||||
char *se;
|
||||
|
||||
se = buf+sizeof(buf);
|
||||
s = seprint(buf, se, "lun %d ", rp->lun);
|
||||
s = seprintcmd(s, se, (char*)rp->cmd.p, rp->cmd.count, 1);
|
||||
s = seprint(s, se, " [%ld]", rp->data.count);
|
||||
if(rp->cmd.write)
|
||||
seprintdata(s, se, rp->data.p, rp->data.count);
|
||||
fprint(2, "scsi⇒ %s\n", buf);
|
||||
}
|
||||
|
||||
static void
|
||||
SRdumpRep(ScsiReq *rp)
|
||||
{
|
||||
char buf[128];
|
||||
char *s;
|
||||
char *se;
|
||||
|
||||
se = buf+sizeof(buf);
|
||||
s = seprint(buf, se, "lun %d ", rp->lun);
|
||||
s = seprintcmd(s, se, (char*)rp->cmd.p, rp->cmd.count, 0);
|
||||
switch(rp->status){
|
||||
case STok:
|
||||
s = seprint(s, se, " good [%ld] ", rp->data.count);
|
||||
if(rp->cmd.write == 0)
|
||||
s = seprintdata(s, se, rp->data.p, rp->data.count);
|
||||
break;
|
||||
case STnomem:
|
||||
s = seprint(s, se, " buffer allocation failed");
|
||||
break;
|
||||
case STharderr:
|
||||
s = seprint(s, se, " controller error");
|
||||
break;
|
||||
case STtimeout:
|
||||
s = seprint(s, se, " bus timeout");
|
||||
break;
|
||||
case STcheck:
|
||||
s = seprint(s, se, " check condition");
|
||||
break;
|
||||
case STcondmet:
|
||||
s = seprint(s, se, " condition met/good");
|
||||
break;
|
||||
case STbusy:
|
||||
s = seprint(s, se, " busy");
|
||||
break;
|
||||
case STintok:
|
||||
s = seprint(s, se, " intermediate/good");
|
||||
break;
|
||||
case STintcondmet:
|
||||
s = seprint(s, se, " intermediate/condition met/good");
|
||||
break;
|
||||
case STresconf:
|
||||
s = seprint(s, se, " reservation conflict");
|
||||
break;
|
||||
case STterminated:
|
||||
s = seprint(s, se, " command terminated");
|
||||
break;
|
||||
case STqfull:
|
||||
s = seprint(s, se, " queue full");
|
||||
break;
|
||||
default:
|
||||
s = seprint(s, se, " sts=%#x", rp->status);
|
||||
}
|
||||
USED(s);
|
||||
fprint(2, "scsi← %s\n", buf);
|
||||
}
|
||||
|
||||
static char*
|
||||
scsierr(ScsiReq *rp)
|
||||
{
|
||||
int ec;
|
||||
|
||||
switch(rp->status){
|
||||
case 0:
|
||||
return "";
|
||||
case Status_SD:
|
||||
ec = (rp->sense[12] << 8) | rp->sense[13];
|
||||
return scsierrmsg(ec);
|
||||
case Status_SW:
|
||||
return "software error";
|
||||
case Status_BADARG:
|
||||
return "bad argument";
|
||||
case Status_RO:
|
||||
return "device is read only";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
SRdumpErr(ScsiReq *rp)
|
||||
{
|
||||
char buf[128];
|
||||
char *se;
|
||||
|
||||
se = buf+sizeof(buf);
|
||||
seprintcmd(buf, se, (char*)rp->cmd.p, rp->cmd.count, 0);
|
||||
print("\t%s status: %s\n", buf, scsierr(rp));
|
||||
}
|
||||
|
||||
long
|
||||
SRrequest(ScsiReq *rp)
|
||||
{
|
||||
long n;
|
||||
int status;
|
||||
|
||||
retry:
|
||||
if(debug)
|
||||
SRdumpReq(rp);
|
||||
if(rp->flags&Fusb)
|
||||
n = umsrequest(rp->umsc, &rp->cmd, &rp->data, &status);
|
||||
else
|
||||
n = request(rp->fd, &rp->cmd, &rp->data, &status);
|
||||
rp->status = status;
|
||||
if(status == STok)
|
||||
rp->data.count = n;
|
||||
if(debug)
|
||||
SRdumpRep(rp);
|
||||
switch(status){
|
||||
case STok:
|
||||
break;
|
||||
case STcheck:
|
||||
if(rp->cmd.p[0] != ScmdRsense && SRreqsense(rp) != -1)
|
||||
rp->status = Status_SD;
|
||||
if(debug || exabyte)
|
||||
SRdumpErr(rp);
|
||||
werrstr("%s", scsierr(rp));
|
||||
return -1;
|
||||
case STbusy:
|
||||
sleep(1000); /* TODO: try a shorter sleep? */
|
||||
goto retry;
|
||||
default:
|
||||
if(debug || exabyte)
|
||||
SRdumpErr(rp);
|
||||
werrstr("%s", scsierr(rp));
|
||||
return -1;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
int
|
||||
SRclose(ScsiReq *rp)
|
||||
{
|
||||
if((rp->flags & Fopen) == 0){
|
||||
if(diskdebug)
|
||||
fprint(2, "disk: closing closed file\n");
|
||||
rp->status = Status_BADARG;
|
||||
return -1;
|
||||
}
|
||||
close(rp->fd);
|
||||
rp->flags = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
dirdevopen(ScsiReq *rp)
|
||||
{
|
||||
uvlong blocks;
|
||||
uchar data[8+4+20]; /* 16-byte result: lba, blksize, reserved */
|
||||
|
||||
memset(data, 0, sizeof data);
|
||||
if(SRstart(rp, 1) == -1 || SRrcapacity(rp, data) == -1)
|
||||
return -1;
|
||||
rp->lbsize = GETBELONG(data+4);
|
||||
blocks = GETBELONG(data);
|
||||
if(debug)
|
||||
fprint(2, "disk: dirdevopen: 10-byte logical block size %lud, "
|
||||
"# blocks %llud\n", rp->lbsize, blocks);
|
||||
if(blocks == 0xffffffff){
|
||||
if(SRrcapacity16(rp, data) == -1)
|
||||
return -1;
|
||||
rp->lbsize = GETBELONG(data + 8);
|
||||
blocks = (vlong)GETBELONG(data)<<32 | GETBELONG(data + 4);
|
||||
if(debug)
|
||||
fprint(2, "disk: dirdevopen: 16-byte logical block size"
|
||||
" %lud, # blocks %llud\n", rp->lbsize, blocks);
|
||||
}
|
||||
/* some newer dev's don't support 6-byte commands */
|
||||
if(blocks > Max24off && !force6bytecmds)
|
||||
rp->flags |= Frw10;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
seqdevopen(ScsiReq *rp)
|
||||
{
|
||||
uchar mode[16], limits[6];
|
||||
|
||||
if(SRrblimits(rp, limits) == -1)
|
||||
return -1;
|
||||
if(limits[1] == 0 && limits[2] == limits[4] && limits[3] == limits[5]){
|
||||
rp->flags |= Fbfixed;
|
||||
rp->lbsize = limits[4]<<8 | limits[5];
|
||||
if(debug)
|
||||
fprint(2, "disk: seqdevopen: 10-byte logical block size %lud\n",
|
||||
rp->lbsize);
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
* On some older hardware the optional 10-byte
|
||||
* modeselect command isn't implemented.
|
||||
*/
|
||||
if (force6bytecmds)
|
||||
rp->flags |= Fmode6;
|
||||
if(!(rp->flags & Fmode6)){
|
||||
/* try 10-byte command first */
|
||||
memset(mode, 0, sizeof mode);
|
||||
mode[3] = 0x10; /* device-specific param. */
|
||||
mode[7] = 8; /* block descriptor length */
|
||||
/*
|
||||
* exabytes can't handle this, and
|
||||
* modeselect(10) is optional.
|
||||
*/
|
||||
if(SRmodeselect10(rp, mode, sizeof mode) != -1){
|
||||
rp->lbsize = 1;
|
||||
return 0; /* success */
|
||||
}
|
||||
/* can't do 10-byte commands, back off to 6-byte ones */
|
||||
rp->flags |= Fmode6;
|
||||
}
|
||||
|
||||
/* 6-byte command */
|
||||
memset(mode, 0, sizeof mode);
|
||||
mode[2] = 0x10; /* device-specific param. */
|
||||
mode[3] = 8; /* block descriptor length */
|
||||
/*
|
||||
* bsd sez exabytes need this bit (NBE: no busy enable) in
|
||||
* vendor-specific page (0), but so far we haven't needed it.
|
||||
mode[12] |= 8;
|
||||
*/
|
||||
if(SRmodeselect6(rp, mode, 4+8) == -1)
|
||||
return -1;
|
||||
rp->lbsize = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
wormdevopen(ScsiReq *rp)
|
||||
{
|
||||
long status;
|
||||
uchar list[MaxDirData];
|
||||
|
||||
if (SRstart(rp, 1) == -1 ||
|
||||
(status = SRmodesense10(rp, Allmodepages, list, sizeof list)) == -1)
|
||||
return -1;
|
||||
/* nbytes = list[0]<<8 | list[1]; */
|
||||
|
||||
/* # of bytes of block descriptors of 8 bytes each; not even 1? */
|
||||
if((list[6]<<8 | list[7]) < 8)
|
||||
rp->lbsize = 2048;
|
||||
else
|
||||
/* last 3 bytes of block 0 descriptor */
|
||||
rp->lbsize = GETBE24(list+13);
|
||||
if(debug)
|
||||
fprint(2, "disk: wormdevopen: 10-byte logical block size %lud\n",
|
||||
rp->lbsize);
|
||||
return status;
|
||||
}
|
||||
|
||||
int
|
||||
SRopenraw(ScsiReq *rp, char *unit)
|
||||
{
|
||||
char name[128];
|
||||
|
||||
if(rp->flags & Fopen){
|
||||
if(diskdebug)
|
||||
fprint(2, "disk: opening open file\n");
|
||||
rp->status = Status_BADARG;
|
||||
return -1;
|
||||
}
|
||||
memset(rp, 0, sizeof *rp);
|
||||
rp->unit = unit;
|
||||
|
||||
snprint(name, sizeof name, "%s/raw", unit);
|
||||
if((rp->fd = open(name, ORDWR)) == -1){
|
||||
rp->status = STtimeout;
|
||||
return -1;
|
||||
}
|
||||
rp->flags = Fopen;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
SRopen(ScsiReq *rp, char *unit)
|
||||
{
|
||||
if(SRopenraw(rp, unit) == -1)
|
||||
return -1;
|
||||
SRready(rp);
|
||||
if(SRinquiry(rp) >= 0){
|
||||
switch(rp->inquiry[0]){
|
||||
|
||||
default:
|
||||
fprint(2, "unknown device type 0x%.2x\n", rp->inquiry[0]);
|
||||
rp->status = Status_SW;
|
||||
break;
|
||||
|
||||
case Devdir:
|
||||
case Devcd:
|
||||
case Devmo:
|
||||
if(dirdevopen(rp) == -1)
|
||||
break;
|
||||
return 0;
|
||||
|
||||
case Devseq:
|
||||
rp->flags |= Fseqdev;
|
||||
if(seqdevopen(rp) == -1)
|
||||
break;
|
||||
return 0;
|
||||
|
||||
case Devprint:
|
||||
rp->flags |= Fprintdev;
|
||||
return 0;
|
||||
|
||||
case Devworm:
|
||||
rp->flags |= Fwormdev;
|
||||
if(wormdevopen(rp) == -1)
|
||||
break;
|
||||
return 0;
|
||||
|
||||
case Devjuke:
|
||||
rp->flags |= Fchanger;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
SRclose(rp);
|
||||
return -1;
|
||||
}
|
238
sys/src/cmd/nusb/disk/scsireq.h
Normal file
238
sys/src/cmd/nusb/disk/scsireq.h
Normal file
|
@ -0,0 +1,238 @@
|
|||
/*
|
||||
* This is /sys/src/cmd/scuzz/scsireq.h
|
||||
* changed to add more debug support, and to keep
|
||||
* disk compiling without a scuzz that includes these changes.
|
||||
*
|
||||
* scsireq.h is also included by usb/disk and cdfs.
|
||||
*/
|
||||
typedef struct Umsc Umsc;
|
||||
#pragma incomplete Umsc
|
||||
|
||||
enum { /* fundamental constants/defaults */
|
||||
MaxDirData = 255, /* max. direct data returned */
|
||||
/*
|
||||
* Because we are accessed via devmnt, we can never get i/o counts
|
||||
* larger than 8216 (Msgsize and devmnt's offered iounit) - 24
|
||||
* (IOHDRSZ) = 8K.
|
||||
*/
|
||||
Maxiosize = 8216 - IOHDRSZ, /* max. I/O transfer size */
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
uchar *p;
|
||||
long count;
|
||||
uchar write;
|
||||
} ScsiPtr;
|
||||
|
||||
typedef struct {
|
||||
int flags;
|
||||
char *unit; /* unit directory */
|
||||
int lun;
|
||||
ulong lbsize;
|
||||
uvlong offset; /* in blocks of lbsize bytes */
|
||||
int fd;
|
||||
Umsc *umsc; /* lun */
|
||||
ScsiPtr cmd;
|
||||
ScsiPtr data;
|
||||
int status; /* returned status */
|
||||
uchar sense[MaxDirData]; /* returned sense data */
|
||||
uchar inquiry[MaxDirData]; /* returned inquiry data */
|
||||
int readblock; /* flag: read a block since open */
|
||||
} ScsiReq;
|
||||
|
||||
enum { /* software flags */
|
||||
Fopen = 0x0001, /* open */
|
||||
Fseqdev = 0x0002, /* sequential-access device */
|
||||
Fwritten = 0x0004, /* device written */
|
||||
Fronly = 0x0008, /* device is read-only */
|
||||
Fwormdev = 0x0010, /* write-once read-multiple device */
|
||||
Fprintdev = 0x0020, /* printer */
|
||||
Fbfixed = 0x0040, /* fixed block size */
|
||||
Fchanger = 0x0080, /* medium-changer device */
|
||||
Finqok = 0x0100, /* inquiry data is OK */
|
||||
Fmode6 = 0x0200, /* use 6-byte modeselect */
|
||||
Frw10 = 0x0400, /* use 10-byte read/write */
|
||||
Fusb = 0x0800, /* USB transparent scsi */
|
||||
};
|
||||
|
||||
enum {
|
||||
STnomem =-4, /* buffer allocation failed */
|
||||
STharderr =-3, /* controller error of some kind */
|
||||
STtimeout =-2, /* bus timeout */
|
||||
STok = 0, /* good */
|
||||
STcheck = 0x02, /* check condition */
|
||||
STcondmet = 0x04, /* condition met/good */
|
||||
STbusy = 0x08, /* busy */
|
||||
STintok = 0x10, /* intermediate/good */
|
||||
STintcondmet = 0x14, /* intermediate/condition met/good */
|
||||
STresconf = 0x18, /* reservation conflict */
|
||||
STterminated = 0x22, /* command terminated */
|
||||
STqfull = 0x28, /* queue full */
|
||||
};
|
||||
|
||||
enum { /* status */
|
||||
Status_SD = 0x80, /* sense-data available */
|
||||
Status_SW = 0x83, /* internal software error */
|
||||
Status_BADARG = 0x84, /* bad argument to request */
|
||||
Status_RO = 0x85, /* device is read-only */
|
||||
};
|
||||
|
||||
enum { /* SCSI command codes */
|
||||
ScmdTur = 0x00, /* test unit ready */
|
||||
ScmdRewind = 0x01, /* rezero/rewind */
|
||||
ScmdRsense = 0x03, /* request sense */
|
||||
ScmdFormat = 0x04, /* format unit */
|
||||
ScmdRblimits = 0x05, /* read block limits */
|
||||
ScmdRead = 0x08, /* read */
|
||||
ScmdWrite = 0x0A, /* write */
|
||||
ScmdSeek = 0x0B, /* seek */
|
||||
ScmdFmark = 0x10, /* write filemarks */
|
||||
ScmdSpace = 0x11, /* space forward/backward */
|
||||
ScmdInq = 0x12, /* inquiry */
|
||||
ScmdMselect6 = 0x15, /* mode select */
|
||||
ScmdMselect10 = 0x55, /* mode select */
|
||||
ScmdMsense6 = 0x1A, /* mode sense */
|
||||
ScmdMsense10 = 0x5A, /* mode sense */
|
||||
ScmdStart = 0x1B, /* start/stop unit */
|
||||
ScmdRcapacity = 0x25, /* read capacity */
|
||||
ScmdRcapacity16 = 0x9e, /* long read capacity */
|
||||
ScmdExtread = 0x28, /* extended read */
|
||||
ScmdExtwrite = 0x2A, /* extended write */
|
||||
ScmdExtseek = 0x2B, /* extended seek */
|
||||
|
||||
ScmdSynccache = 0x35, /* flush cache */
|
||||
ScmdRTOC = 0x43, /* read TOC data */
|
||||
ScmdRdiscinfo = 0x51, /* read disc information */
|
||||
ScmdRtrackinfo = 0x52, /* read track information */
|
||||
ScmdReserve = 0x53, /* reserve track */
|
||||
ScmdBlank = 0xA1, /* blank *-RW media */
|
||||
|
||||
ScmdCDpause = 0x4B, /* pause/resume */
|
||||
ScmdCDstop = 0x4E, /* stop play/scan */
|
||||
ScmdCDplay = 0xA5, /* play audio */
|
||||
ScmdCDload = 0xA6, /* load/unload */
|
||||
ScmdCDscan = 0xBA, /* fast forward/reverse */
|
||||
ScmdCDstatus = 0xBD, /* mechanism status */
|
||||
Scmdgetconf = 0x46, /* get configuration */
|
||||
|
||||
ScmdEInitialise = 0x07, /* initialise element status */
|
||||
ScmdMMove = 0xA5, /* move medium */
|
||||
ScmdEStatus = 0xB8, /* read element status */
|
||||
ScmdMExchange = 0xA6, /* exchange medium */
|
||||
ScmdEposition = 0x2B, /* position to element */
|
||||
|
||||
ScmdReadDVD = 0xAD, /* read dvd structure */
|
||||
ScmdReportKey = 0xA4, /* read dvd key */
|
||||
ScmdSendKey = 0xA3, /* write dvd key */
|
||||
|
||||
ScmdClosetracksess= 0x5B,
|
||||
ScmdRead12 = 0xA8,
|
||||
ScmdSetcdspeed = 0xBB,
|
||||
ScmdReadcd = 0xBE,
|
||||
|
||||
/* vendor-specific */
|
||||
ScmdFwaddr = 0xE2, /* first writeable address */
|
||||
ScmdTreserve = 0xE4, /* reserve track */
|
||||
ScmdTinfo = 0xE5, /* read track info */
|
||||
ScmdTwrite = 0xE6, /* write track */
|
||||
ScmdMload = 0xE7, /* medium load/unload */
|
||||
ScmdFixation = 0xE9, /* fixation */
|
||||
};
|
||||
|
||||
enum {
|
||||
/* sense data byte 0 */
|
||||
Sd0valid = 0x80, /* valid sense data present */
|
||||
|
||||
/* sense data byte 2 */
|
||||
/* incorrect-length indicator, difference in bytes 3—6 */
|
||||
Sd2ili = 0x20,
|
||||
Sd2eom = 0x40, /* end of medium (tape) */
|
||||
Sd2filemark = 0x80, /* at a filemark (tape) */
|
||||
|
||||
/* command byte 1 */
|
||||
Cmd1fixed = 1, /* use fixed-length blocks */
|
||||
Cmd1sili = 2, /* don't set Sd2ili */
|
||||
|
||||
/* limit of block #s in 24-bit ccbs */
|
||||
Max24off = (1<<21) - 1, /* 2ⁱ - 1 */
|
||||
|
||||
/* mode pages */
|
||||
Allmodepages = 0x3F,
|
||||
};
|
||||
|
||||
/* scsi device types, from the scsi standards */
|
||||
enum {
|
||||
Devdir, /* usually disk */
|
||||
Devseq, /* usually tape */
|
||||
Devprint,
|
||||
Dev3,
|
||||
Devworm, /* also direct, but special */
|
||||
Devcd, /* also direct */
|
||||
Dev6,
|
||||
Devmo, /* also direct */
|
||||
Devjuke,
|
||||
};
|
||||
|
||||
/* p arguments should be of type uchar* */
|
||||
#define GETBELONG(p) ((ulong)(p)[0]<<24 | (ulong)(p)[1]<<16 | (p)[2]<<8 | (p)[3])
|
||||
#define PUTBELONG(p, ul) ((p)[0] = (ul)>>24, (p)[1] = (ul)>>16, \
|
||||
(p)[2] = (ul)>>8, (p)[3] = (ul))
|
||||
#define GETBE24(p) ((ulong)(p)[0]<<16 | (p)[1]<<8 | (p)[2])
|
||||
#define PUTBE24(p, ul) ((p)[0] = (ul)>>16, (p)[1] = (ul)>>8, (p)[2] = (ul))
|
||||
|
||||
long SRready(ScsiReq*);
|
||||
long SRrewind(ScsiReq*);
|
||||
long SRreqsense(ScsiReq*);
|
||||
long SRformat(ScsiReq*);
|
||||
long SRrblimits(ScsiReq*, uchar*);
|
||||
long SRread(ScsiReq*, void*, long);
|
||||
long SRwrite(ScsiReq*, void*, long);
|
||||
long SRseek(ScsiReq*, long, int);
|
||||
long SRfilemark(ScsiReq*, ulong);
|
||||
long SRspace(ScsiReq*, uchar, long);
|
||||
long SRinquiry(ScsiReq*);
|
||||
long SRmodeselect6(ScsiReq*, uchar*, long);
|
||||
long SRmodeselect10(ScsiReq*, uchar*, long);
|
||||
long SRmodesense6(ScsiReq*, uchar, uchar*, long);
|
||||
long SRmodesense10(ScsiReq*, uchar, uchar*, long);
|
||||
long SRstart(ScsiReq*, uchar);
|
||||
long SRrcapacity(ScsiReq*, uchar*);
|
||||
long SRrcapacity16(ScsiReq*, uchar*);
|
||||
|
||||
long SRblank(ScsiReq*, uchar, uchar); /* MMC CD-R/CD-RW commands */
|
||||
long SRsynccache(ScsiReq*);
|
||||
long SRTOC(ScsiReq*, void*, int, uchar, uchar);
|
||||
long SRrdiscinfo(ScsiReq*, void*, int);
|
||||
long SRrtrackinfo(ScsiReq*, void*, int, int);
|
||||
|
||||
long SRcdpause(ScsiReq*, int); /* MMC CD audio commands */
|
||||
long SRcdstop(ScsiReq*);
|
||||
long SRcdload(ScsiReq*, int, int);
|
||||
long SRcdplay(ScsiReq*, int, long, long);
|
||||
long SRcdstatus(ScsiReq*, uchar*, int);
|
||||
long SRgetconf(ScsiReq*, uchar*, int);
|
||||
|
||||
/* old CD-R/CD-RW commands */
|
||||
long SRfwaddr(ScsiReq*, uchar, uchar, uchar, uchar*);
|
||||
long SRtreserve(ScsiReq*, long);
|
||||
long SRtinfo(ScsiReq*, uchar, uchar*);
|
||||
long SRwtrack(ScsiReq*, void*, long, uchar, uchar);
|
||||
long SRmload(ScsiReq*, uchar);
|
||||
long SRfixation(ScsiReq*, uchar);
|
||||
|
||||
long SReinitialise(ScsiReq*); /* CHANGER commands */
|
||||
long SRestatus(ScsiReq*, uchar, uchar*, int);
|
||||
long SRmmove(ScsiReq*, int, int, int, int);
|
||||
|
||||
long SRrequest(ScsiReq*);
|
||||
int SRclose(ScsiReq*);
|
||||
int SRopenraw(ScsiReq*, char*);
|
||||
int SRopen(ScsiReq*, char*);
|
||||
|
||||
void makesense(ScsiReq*);
|
||||
|
||||
long umsrequest(struct Umsc*, ScsiPtr*, ScsiPtr*, int*);
|
||||
|
||||
void scsidebug(int);
|
||||
|
||||
char* scsierrmsg(int n);
|
124
sys/src/cmd/nusb/disk/ums.h
Normal file
124
sys/src/cmd/nusb/disk/ums.h
Normal file
|
@ -0,0 +1,124 @@
|
|||
/*
|
||||
* mass storage transport protocols and subclasses,
|
||||
* from usb mass storage class specification overview rev 1.2
|
||||
*/
|
||||
|
||||
typedef struct Umsc Umsc;
|
||||
typedef struct Ums Ums;
|
||||
typedef struct Cbw Cbw; /* command block wrapper */
|
||||
typedef struct Csw Csw; /* command status wrapper */
|
||||
typedef struct Part Part;
|
||||
|
||||
enum
|
||||
{
|
||||
Protocbi = 0, /* control/bulk/interrupt; mainly floppies */
|
||||
Protocb = 1, /* " with no interrupt; mainly floppies */
|
||||
Protobulk = 0x50, /* bulk only */
|
||||
|
||||
Subrbc = 1, /* reduced blk cmds */
|
||||
Subatapi = 2, /* cd/dvd using sff-8020i or mmc-2 cmd blks */
|
||||
Subqic = 3, /* QIC-157 tapes */
|
||||
Subufi = 4, /* floppy */
|
||||
Sub8070 = 5, /* removable media, atapi-like */
|
||||
Subscsi = 6, /* scsi transparent cmd set */
|
||||
Subisd200 = 7, /* ISD200 ATA */
|
||||
Subdev = 0xff, /* use device's value */
|
||||
|
||||
Umsreset = 0xFF,
|
||||
Getmaxlun = 0xFE,
|
||||
|
||||
// Maxlun = 256,
|
||||
Maxlun = 32,
|
||||
|
||||
CMreset = 1,
|
||||
|
||||
Pcmd = 0,
|
||||
Pdata,
|
||||
Pstatus,
|
||||
|
||||
CbwLen = 31,
|
||||
CbwDataIn = 0x80,
|
||||
CbwDataOut = 0x00,
|
||||
CswLen = 13,
|
||||
CswOk = 0,
|
||||
CswFailed = 1,
|
||||
CswPhaseErr = 2,
|
||||
|
||||
Maxparts = 16,
|
||||
};
|
||||
|
||||
/*
|
||||
* corresponds to a lun.
|
||||
* these are ~600+Maxiosize bytes each; ScsiReq is not tiny.
|
||||
*/
|
||||
|
||||
struct Part
|
||||
{
|
||||
int id;
|
||||
int inuse;
|
||||
int vers;
|
||||
ulong mode;
|
||||
char *name;
|
||||
vlong offset; /* in lbsize units */
|
||||
vlong length; /* in lbsize units */
|
||||
};
|
||||
|
||||
|
||||
struct Umsc
|
||||
{
|
||||
ScsiReq;
|
||||
uvlong blocks;
|
||||
vlong capacity;
|
||||
|
||||
/* from setup */
|
||||
char *bufp;
|
||||
long off; /* offset within a block */
|
||||
long nb; /* byte count */
|
||||
|
||||
/* partitions */
|
||||
Part part[Maxparts];
|
||||
|
||||
uchar rawcmd[10];
|
||||
uchar phase;
|
||||
char *inq;
|
||||
Ums *ums;
|
||||
char buf[Maxiosize];
|
||||
};
|
||||
|
||||
struct Ums
|
||||
{
|
||||
QLock;
|
||||
Dev *dev;
|
||||
Dev *epin;
|
||||
Dev *epout;
|
||||
Umsc *lun;
|
||||
uchar maxlun;
|
||||
int seq;
|
||||
int nerrs;
|
||||
int wrongresidues;
|
||||
};
|
||||
|
||||
/*
|
||||
* USB transparent SCSI devices
|
||||
*/
|
||||
struct Cbw
|
||||
{
|
||||
char signature[4]; /* "USBC" */
|
||||
long tag;
|
||||
long datalen;
|
||||
uchar flags;
|
||||
uchar lun;
|
||||
uchar len;
|
||||
char command[16];
|
||||
};
|
||||
|
||||
struct Csw
|
||||
{
|
||||
char signature[4]; /* "USBS" */
|
||||
long tag;
|
||||
long dataresidue;
|
||||
uchar status;
|
||||
};
|
||||
|
||||
|
||||
int diskmain(Dev*, int, char**);
|
65
sys/src/cmd/nusb/kb/hid.h
Normal file
65
sys/src/cmd/nusb/kb/hid.h
Normal file
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* USB keyboard/mouse constants
|
||||
*/
|
||||
enum {
|
||||
|
||||
Stack = 32 * 1024,
|
||||
|
||||
/* HID class subclass protocol ids */
|
||||
PtrCSP = 0x020103, /* mouse.boot.hid */
|
||||
KbdCSP = 0x010103, /* keyboard.boot.hid */
|
||||
|
||||
/* Requests */
|
||||
Getreport = 0x01,
|
||||
Setreport = 0x09,
|
||||
Getproto = 0x03,
|
||||
Setproto = 0x0b,
|
||||
|
||||
/* protocols for SET_PROTO request */
|
||||
Bootproto = 0,
|
||||
Reportproto = 1,
|
||||
|
||||
/* protocols for SET_REPORT request */
|
||||
Reportout = 0x0200,
|
||||
};
|
||||
|
||||
enum {
|
||||
/* keyboard modifier bits */
|
||||
Mlctrl = 0,
|
||||
Mlshift = 1,
|
||||
Mlalt = 2,
|
||||
Mlgui = 3,
|
||||
Mrctrl = 4,
|
||||
Mrshift = 5,
|
||||
Mralt = 6,
|
||||
Mrgui = 7,
|
||||
|
||||
/* masks for byte[0] */
|
||||
Mctrl = 1<<Mlctrl | 1<<Mrctrl,
|
||||
Mshift = 1<<Mlshift | 1<<Mrshift,
|
||||
Malt = 1<<Mlalt | 1<<Mralt,
|
||||
Mcompose = 1<<Mlalt,
|
||||
Maltgr = 1<<Mralt,
|
||||
Mgui = 1<<Mlgui | 1<<Mrgui,
|
||||
|
||||
MaxAcc = 3, /* max. ptr acceleration */
|
||||
PtrMask= 0xf, /* 4 buttons: should allow for more. */
|
||||
|
||||
};
|
||||
|
||||
/*
|
||||
* Plan 9 keyboard driver constants.
|
||||
*/
|
||||
enum {
|
||||
/* Scan codes (see kbd.c) */
|
||||
SCesc1 = 0xe0, /* first of a 2-character sequence */
|
||||
SCesc2 = 0xe1,
|
||||
SClshift = 0x2a,
|
||||
SCrshift = 0x36,
|
||||
SCctrl = 0x1d,
|
||||
SCcompose = 0x38,
|
||||
Keyup = 0x80, /* flag bit */
|
||||
Keymask = 0x7f, /* regular scan code bits */
|
||||
};
|
||||
|
||||
int kbmain(Dev *d, int argc, char*argv[]);
|
595
sys/src/cmd/nusb/kb/kb.c
Normal file
595
sys/src/cmd/nusb/kb/kb.c
Normal file
|
@ -0,0 +1,595 @@
|
|||
/*
|
||||
* USB Human Interaction Device: keyboard and mouse.
|
||||
*
|
||||
* If there's no usb keyboard, it tries to setup the mouse, if any.
|
||||
* It should be started at boot time.
|
||||
*
|
||||
* Mouse events are converted to the format of mouse(3)'s
|
||||
* mousein file.
|
||||
* Keyboard keycodes are translated to scan codes and sent to kbin(3).
|
||||
*
|
||||
*/
|
||||
|
||||
#include <u.h>
|
||||
#include <libc.h>
|
||||
#include <thread.h>
|
||||
#include "usb.h"
|
||||
#include "hid.h"
|
||||
|
||||
enum
|
||||
{
|
||||
Awakemsg=0xdeaddead,
|
||||
Diemsg = 0xbeefbeef,
|
||||
};
|
||||
|
||||
typedef struct KDev KDev;
|
||||
typedef struct Kin Kin;
|
||||
|
||||
struct KDev
|
||||
{
|
||||
Dev* dev; /* usb device*/
|
||||
Dev* ep; /* endpoint to get events */
|
||||
Kin* in; /* used to send events to kernel */
|
||||
Channel*repeatc; /* only for keyboard */
|
||||
int accel; /* only for mouse */
|
||||
};
|
||||
|
||||
/*
|
||||
* Kbdin and mousein files must be shared among all instances.
|
||||
*/
|
||||
struct Kin
|
||||
{
|
||||
int ref;
|
||||
int fd;
|
||||
char* name;
|
||||
};
|
||||
|
||||
/*
|
||||
* Map for the logitech bluetooth mouse with 8 buttons and wheels.
|
||||
* { ptr ->mouse}
|
||||
* { 0x01, 0x01 }, // left
|
||||
* { 0x04, 0x02 }, // middle
|
||||
* { 0x02, 0x04 }, // right
|
||||
* { 0x40, 0x08 }, // up
|
||||
* { 0x80, 0x10 }, // down
|
||||
* { 0x10, 0x08 }, // side up
|
||||
* { 0x08, 0x10 }, // side down
|
||||
* { 0x20, 0x02 }, // page
|
||||
* besides wheel and regular up/down report the 4th byte as 1/-1
|
||||
*/
|
||||
|
||||
/*
|
||||
* key code to scan code; for the page table used by
|
||||
* the logitech bluetooth keyboard.
|
||||
*/
|
||||
static char sctab[256] =
|
||||
{
|
||||
[0x00] 0x0, 0x0, 0x0, 0x0, 0x1e, 0x30, 0x2e, 0x20,
|
||||
[0x08] 0x12, 0x21, 0x22, 0x23, 0x17, 0x24, 0x25, 0x26,
|
||||
[0x10] 0x32, 0x31, 0x18, 0x19, 0x10, 0x13, 0x1f, 0x14,
|
||||
[0x18] 0x16, 0x2f, 0x11, 0x2d, 0x15, 0x2c, 0x2, 0x3,
|
||||
[0x20] 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb,
|
||||
[0x28] 0x1c, 0x1, 0xe, 0xf, 0x39, 0xc, 0xd, 0x1a,
|
||||
[0x30] 0x1b, 0x2b, 0x2b, 0x27, 0x28, 0x29, 0x33, 0x34,
|
||||
[0x38] 0x35, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40,
|
||||
[0x40] 0x41, 0x42, 0x43, 0x44, 0x57, 0x58, 0x63, 0x46,
|
||||
[0x48] 0x77, 0x52, 0x47, 0x49, 0x53, 0x4f, 0x51, 0x4d,
|
||||
[0x50] 0x4b, 0x50, 0x48, 0x45, 0x35, 0x37, 0x4a, 0x4e,
|
||||
[0x58] 0x1c, 0x4f, 0x50, 0x51, 0x4b, 0x4c, 0x4d, 0x47,
|
||||
[0x60] 0x48, 0x49, 0x52, 0x53, 0x56, 0x7f, 0x74, 0x75,
|
||||
[0x68] 0x55, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
|
||||
[0x70] 0x78, 0x79, 0x7a, 0x7b, 0x0, 0x0, 0x0, 0x0,
|
||||
[0x78] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x71,
|
||||
[0x80] 0x73, 0x72, 0x0, 0x0, 0x0, 0x7c, 0x0, 0x0,
|
||||
[0x88] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
[0x90] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
[0x98] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
[0xa0] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
[0xa8] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
[0xb0] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
[0xb8] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
[0xc0] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
[0xc8] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
[0xd0] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
[0xd8] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
[0xe0] 0x1d, 0x2a, 0x38, 0x7d, 0x61, 0x36, 0x64, 0x7e,
|
||||
[0xe8] 0x0, 0x0, 0x0, 0x0, 0x0, 0x73, 0x72, 0x71,
|
||||
[0xf0] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
[0xf8] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
};
|
||||
|
||||
static QLock inlck;
|
||||
static Kin kbdin =
|
||||
{
|
||||
.ref = 0,
|
||||
.name = "/dev/kbin",
|
||||
.fd = -1,
|
||||
};
|
||||
static Kin ptrin =
|
||||
{
|
||||
.ref = 0,
|
||||
.name = "#m/mousein",
|
||||
.fd = -1,
|
||||
};
|
||||
|
||||
static int kbdebug;
|
||||
|
||||
static int
|
||||
setbootproto(KDev* f, int eid)
|
||||
{
|
||||
int r, id;
|
||||
|
||||
r = Rh2d|Rclass|Riface;
|
||||
id = f->dev->usb->ep[eid]->iface->id;
|
||||
return usbcmd(f->dev, r, Setproto, Bootproto, id, nil, 0);
|
||||
}
|
||||
|
||||
static int
|
||||
setleds(KDev* f, int, uchar leds)
|
||||
{
|
||||
return usbcmd(f->dev, Rh2d|Rclass|Riface, Setreport, Reportout, 0, &leds, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to recover from a babble error. A port reset is the only way out.
|
||||
* BUG: we should be careful not to reset a bundle with several devices.
|
||||
*/
|
||||
static void
|
||||
recoverkb(KDev *f)
|
||||
{
|
||||
int i;
|
||||
|
||||
close(f->dev->dfd); /* it's for usbd now */
|
||||
devctl(f->dev, "reset");
|
||||
for(i = 0; i < 10; i++){
|
||||
sleep(500);
|
||||
if(opendevdata(f->dev, ORDWR) >= 0){
|
||||
setbootproto(f, f->ep->id);
|
||||
break;
|
||||
}
|
||||
/* else usbd still working... */
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
kbfatal(KDev *kd, char *sts)
|
||||
{
|
||||
Dev *dev;
|
||||
|
||||
if(sts != nil)
|
||||
fprint(2, "kb: fatal: %s\n", sts);
|
||||
else
|
||||
fprint(2, "kb: exiting\n");
|
||||
if(kd->repeatc != nil)
|
||||
nbsendul(kd->repeatc, Diemsg);
|
||||
dev = kd->dev;
|
||||
kd->dev = nil;
|
||||
if(kd->ep != nil)
|
||||
closedev(kd->ep);
|
||||
kd->ep = nil;
|
||||
devctl(dev, "detach");
|
||||
closedev(dev);
|
||||
/*
|
||||
* free(kd); done by closedev.
|
||||
*/
|
||||
threadexits(sts);
|
||||
}
|
||||
|
||||
static int
|
||||
scale(KDev *f, int x)
|
||||
{
|
||||
int sign = 1;
|
||||
|
||||
if(x < 0){
|
||||
sign = -1;
|
||||
x = -x;
|
||||
}
|
||||
switch(x){
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
break;
|
||||
case 4:
|
||||
x = 6 + (f->accel>>2);
|
||||
break;
|
||||
case 5:
|
||||
x = 9 + (f->accel>>1);
|
||||
break;
|
||||
default:
|
||||
x *= MaxAcc;
|
||||
break;
|
||||
}
|
||||
return sign*x;
|
||||
}
|
||||
|
||||
/*
|
||||
* ps2 mouse is processed mostly at interrupt time.
|
||||
* for usb we do what we can.
|
||||
*/
|
||||
static void
|
||||
sethipri(void)
|
||||
{
|
||||
char fn[30];
|
||||
int fd;
|
||||
|
||||
snprint(fn, sizeof(fn), "/proc/%d/ctl", getpid());
|
||||
fd = open(fn, OWRITE);
|
||||
if(fd < 0)
|
||||
return;
|
||||
fprint(fd, "pri 13");
|
||||
close(fd);
|
||||
}
|
||||
|
||||
static void
|
||||
ptrwork(void* a)
|
||||
{
|
||||
static char maptab[] = {0x0, 0x1, 0x4, 0x5, 0x2, 0x3, 0x6, 0x7};
|
||||
int x, y, b, c, ptrfd;
|
||||
int mfd, nerrs;
|
||||
char buf[32];
|
||||
char mbuf[80];
|
||||
KDev* f = a;
|
||||
int hipri;
|
||||
|
||||
hipri = nerrs = 0;
|
||||
ptrfd = f->ep->dfd;
|
||||
mfd = f->in->fd;
|
||||
|
||||
if(f->ep->maxpkt < 3 || f->ep->maxpkt > sizeof buf)
|
||||
kbfatal(f, "weird mouse maxpkt");
|
||||
for(;;){
|
||||
memset(buf, 0, sizeof buf);
|
||||
if(f->ep == nil)
|
||||
kbfatal(f, nil);
|
||||
c = read(ptrfd, buf, f->ep->maxpkt);
|
||||
assert(f->dev != nil);
|
||||
assert(f->ep != nil);
|
||||
if(c < 0){
|
||||
dprint(2, "kb: mouse: %s: read: %r\n", f->ep->dir);
|
||||
if(++nerrs < 3){
|
||||
recoverkb(f);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if(c <= 0)
|
||||
kbfatal(f, nil);
|
||||
if(c < 3)
|
||||
continue;
|
||||
if(f->accel){
|
||||
x = scale(f, buf[1]);
|
||||
y = scale(f, buf[2]);
|
||||
}else{
|
||||
x = buf[1];
|
||||
y = buf[2];
|
||||
}
|
||||
b = maptab[buf[0] & 0x7];
|
||||
if(c > 3 && buf[3] == 1) /* up */
|
||||
b |= 0x08;
|
||||
if(c > 3 && buf[3] == -1) /* down */
|
||||
b |= 0x10;
|
||||
if(kbdebug > 1)
|
||||
fprint(2, "kb: m%11d %11d %11d\n", x, y, b);
|
||||
seprint(mbuf, mbuf+sizeof(mbuf), "m%11d %11d %11d", x, y,b);
|
||||
if(write(mfd, mbuf, strlen(mbuf)) < 0)
|
||||
kbfatal(f, "mousein i/o");
|
||||
if(hipri == 0){
|
||||
sethipri();
|
||||
hipri = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
stoprepeat(KDev *f)
|
||||
{
|
||||
sendul(f->repeatc, Awakemsg);
|
||||
}
|
||||
|
||||
static void
|
||||
startrepeat(KDev *f, uchar esc1, uchar sc)
|
||||
{
|
||||
ulong c;
|
||||
|
||||
if(esc1)
|
||||
c = SCesc1 << 8 | (sc & 0xff);
|
||||
else
|
||||
c = sc;
|
||||
sendul(f->repeatc, c);
|
||||
}
|
||||
|
||||
static void
|
||||
putscan(int kbinfd, uchar esc, uchar sc)
|
||||
{
|
||||
uchar s[2] = {SCesc1, 0};
|
||||
|
||||
if(sc == 0x41){
|
||||
kbdebug += 2;
|
||||
return;
|
||||
}
|
||||
if(sc == 0x42){
|
||||
kbdebug = 0;
|
||||
return;
|
||||
}
|
||||
if(kbdebug)
|
||||
fprint(2, "sc: %x %x\n", (esc? SCesc1: 0), sc);
|
||||
s[1] = sc;
|
||||
if(esc && sc != 0)
|
||||
write(kbinfd, s, 2);
|
||||
else if(sc != 0)
|
||||
write(kbinfd, s+1, 1);
|
||||
}
|
||||
|
||||
static void
|
||||
repeatproc(void* a)
|
||||
{
|
||||
KDev *f;
|
||||
Channel *repeatc;
|
||||
int kbdinfd;
|
||||
ulong l, t, i;
|
||||
uchar esc1, sc;
|
||||
|
||||
/*
|
||||
* too many jumps here.
|
||||
* Rewrite instead of debug, if needed.
|
||||
*/
|
||||
f = a;
|
||||
repeatc = f->repeatc;
|
||||
kbdinfd = f->in->fd;
|
||||
l = Awakemsg;
|
||||
Repeat:
|
||||
if(l == Diemsg)
|
||||
goto Abort;
|
||||
while(l == Awakemsg)
|
||||
l = recvul(repeatc);
|
||||
if(l == Diemsg)
|
||||
goto Abort;
|
||||
esc1 = l >> 8;
|
||||
sc = l;
|
||||
t = 160;
|
||||
for(;;){
|
||||
for(i = 0; i < t; i += 5){
|
||||
if(l = nbrecvul(repeatc))
|
||||
goto Repeat;
|
||||
sleep(5);
|
||||
}
|
||||
putscan(kbdinfd, esc1, sc);
|
||||
t = 30;
|
||||
}
|
||||
Abort:
|
||||
chanfree(repeatc);
|
||||
threadexits("aborted");
|
||||
|
||||
}
|
||||
|
||||
|
||||
#define hasesc1(sc) (((sc) > 0x47) || ((sc) == 0x38))
|
||||
|
||||
static void
|
||||
putmod(int fd, uchar mods, uchar omods, uchar mask, uchar esc, uchar sc)
|
||||
{
|
||||
/* BUG: Should be a single write */
|
||||
if((mods&mask) && !(omods&mask))
|
||||
putscan(fd, esc, sc);
|
||||
if(!(mods&mask) && (omods&mask))
|
||||
putscan(fd, esc, Keyup|sc);
|
||||
}
|
||||
|
||||
/*
|
||||
* This routine diffs the state with the last known state
|
||||
* and invents the scan codes that would have been sent
|
||||
* by a non-usb keyboard in that case. This also requires supplying
|
||||
* the extra esc1 byte as well as keyup flags.
|
||||
* The aim is to allow future addition of other keycode pages
|
||||
* for other keyboards.
|
||||
*/
|
||||
static uchar
|
||||
putkeys(KDev *f, uchar buf[], uchar obuf[], int n, uchar dk)
|
||||
{
|
||||
int i, j;
|
||||
uchar uk;
|
||||
int fd;
|
||||
|
||||
fd = f->in->fd;
|
||||
putmod(fd, buf[0], obuf[0], Mctrl, 0, SCctrl);
|
||||
putmod(fd, buf[0], obuf[0], (1<<Mlshift), 0, SClshift);
|
||||
putmod(fd, buf[0], obuf[0], (1<<Mrshift), 0, SCrshift);
|
||||
putmod(fd, buf[0], obuf[0], Mcompose, 0, SCcompose);
|
||||
putmod(fd, buf[0], obuf[0], Maltgr, 1, SCcompose);
|
||||
|
||||
/* Report key downs */
|
||||
for(i = 2; i < n; i++){
|
||||
for(j = 2; j < n; j++)
|
||||
if(buf[i] == obuf[j])
|
||||
break;
|
||||
if(j == n && buf[i] != 0){
|
||||
dk = sctab[buf[i]];
|
||||
putscan(fd, hasesc1(dk), dk);
|
||||
startrepeat(f, hasesc1(dk), dk);
|
||||
}
|
||||
}
|
||||
|
||||
/* Report key ups */
|
||||
uk = 0;
|
||||
for(i = 2; i < n; i++){
|
||||
for(j = 2; j < n; j++)
|
||||
if(obuf[i] == buf[j])
|
||||
break;
|
||||
if(j == n && obuf[i] != 0){
|
||||
uk = sctab[obuf[i]];
|
||||
putscan(fd, hasesc1(uk), uk|Keyup);
|
||||
}
|
||||
}
|
||||
if(uk && (dk == 0 || dk == uk)){
|
||||
stoprepeat(f);
|
||||
dk = 0;
|
||||
}
|
||||
return dk;
|
||||
}
|
||||
|
||||
static int
|
||||
kbdbusy(uchar* buf, int n)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i = 1; i < n; i++)
|
||||
if(buf[i] == 0 || buf[i] != buf[0])
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
kbdwork(void *a)
|
||||
{
|
||||
int c, i, kbdfd, nerrs;
|
||||
uchar dk, buf[64], lbuf[64];
|
||||
char err[128];
|
||||
KDev *f = a;
|
||||
|
||||
kbdfd = f->ep->dfd;
|
||||
|
||||
if(f->ep->maxpkt < 3 || f->ep->maxpkt > sizeof buf)
|
||||
kbfatal(f, "weird maxpkt");
|
||||
|
||||
f->repeatc = chancreate(sizeof(ulong), 0);
|
||||
if(f->repeatc == nil)
|
||||
kbfatal(f, "chancreate failed");
|
||||
|
||||
proccreate(repeatproc, f, Stack);
|
||||
memset(lbuf, 0, sizeof lbuf);
|
||||
dk = nerrs = 0;
|
||||
for(;;){
|
||||
memset(buf, 0, sizeof buf);
|
||||
c = read(kbdfd, buf, f->ep->maxpkt);
|
||||
assert(f->dev != nil);
|
||||
assert(f->ep != nil);
|
||||
if(c < 0){
|
||||
rerrstr(err, sizeof(err));
|
||||
fprint(2, "kb: %s: read: %s\n", f->ep->dir, err);
|
||||
if(strstr(err, "babble") != 0 && ++nerrs < 3){
|
||||
recoverkb(f);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if(c <= 0)
|
||||
kbfatal(f, nil);
|
||||
if(c < 3)
|
||||
continue;
|
||||
if(kbdbusy(buf + 2, c - 2))
|
||||
continue;
|
||||
if(usbdebug > 2 || kbdebug > 1){
|
||||
fprint(2, "kbd mod %x: ", buf[0]);
|
||||
for(i = 2; i < c; i++)
|
||||
fprint(2, "kc %x ", buf[i]);
|
||||
fprint(2, "\n");
|
||||
}
|
||||
dk = putkeys(f, buf, lbuf, f->ep->maxpkt, dk);
|
||||
memmove(lbuf, buf, c);
|
||||
nerrs = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
freekdev(void *a)
|
||||
{
|
||||
KDev *kd;
|
||||
|
||||
kd = a;
|
||||
if(kd->in != nil){
|
||||
qlock(&inlck);
|
||||
if(--kd->in->ref == 0){
|
||||
close(kd->in->fd);
|
||||
kd->in->fd = -1;
|
||||
}
|
||||
qunlock(&inlck);
|
||||
}
|
||||
dprint(2, "freekdev\n");
|
||||
free(kd);
|
||||
}
|
||||
|
||||
static void
|
||||
kbstart(Dev *d, Ep *ep, Kin *in, void (*f)(void*), int accel)
|
||||
{
|
||||
KDev *kd;
|
||||
|
||||
qlock(&inlck);
|
||||
if(in->fd < 0){
|
||||
in->fd = open(in->name, OWRITE);
|
||||
if(in->fd < 0){
|
||||
fprint(2, "kb: %s: %r\n", in->name);
|
||||
qunlock(&inlck);
|
||||
return;
|
||||
}
|
||||
}
|
||||
in->ref++; /* for kd->in = in */
|
||||
qunlock(&inlck);
|
||||
kd = d->aux = emallocz(sizeof(KDev), 1);
|
||||
d->free = freekdev;
|
||||
kd->in = in;
|
||||
kd->dev = d;
|
||||
if(setbootproto(kd, ep->id) < 0){
|
||||
fprint(2, "kb: %s: bootproto: %r\n", d->dir);
|
||||
return;
|
||||
}
|
||||
kd->accel = accel;
|
||||
kd->ep = openep(d, ep->id);
|
||||
if(kd->ep == nil){
|
||||
fprint(2, "kb: %s: openep %d: %r\n", d->dir, ep->id);
|
||||
return;
|
||||
}
|
||||
if(opendevdata(kd->ep, OREAD) < 0){
|
||||
fprint(2, "kb: %s: opendevdata: %r\n", kd->ep->dir);
|
||||
closedev(kd->ep);
|
||||
kd->ep = nil;
|
||||
return;
|
||||
}
|
||||
if(setleds(kd, ep->id, 0) < 0){
|
||||
fprint(2, "kb: %s: setleds: %r\n", d->dir);
|
||||
return;
|
||||
}
|
||||
incref(d);
|
||||
proccreate(f, kd, Stack);
|
||||
}
|
||||
|
||||
static void
|
||||
usage(void)
|
||||
{
|
||||
werrstr("usage: usb/kb [-dkm] [-a n] [-N nb]");
|
||||
threadexits("usage");
|
||||
}
|
||||
|
||||
void
|
||||
threadmain(int argc, char* argv[])
|
||||
{
|
||||
int accel, i;
|
||||
Dev *d;
|
||||
Ep *ep;
|
||||
Usbdev *ud;
|
||||
|
||||
accel = 0;
|
||||
ARGBEGIN{
|
||||
case 'a':
|
||||
accel = strtol(EARGF(usage()), nil, 0);
|
||||
break;
|
||||
case 'd':
|
||||
kbdebug++;
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
}ARGEND;
|
||||
if(argc != 1)
|
||||
usage();
|
||||
d = getdev(atoi(*argv));
|
||||
if(d == nil)
|
||||
sysfatal("getdev: %r");
|
||||
ud = d->usb;
|
||||
for(i = 0; i < nelem(ud->ep); i++){
|
||||
if((ep = ud->ep[i]) == nil)
|
||||
break;
|
||||
if(ep->type == Eintr && ep->dir == Ein && ep->iface->csp == KbdCSP)
|
||||
kbstart(d, ep, &kbdin, kbdwork, accel);
|
||||
if(ep->type == Eintr && ep->dir == Ein && ep->iface->csp == PtrCSP)
|
||||
kbstart(d, ep, &ptrin, ptrwork, accel);
|
||||
}
|
||||
threadexits(nil);
|
||||
}
|
20
sys/src/cmd/nusb/kb/mkfile
Normal file
20
sys/src/cmd/nusb/kb/mkfile
Normal file
|
@ -0,0 +1,20 @@
|
|||
</$objtype/mkfile
|
||||
|
||||
TARG=kb
|
||||
OFILES=kb.$O
|
||||
HFILES=\
|
||||
../lib/usb.h\
|
||||
hid.h\
|
||||
|
||||
LIB=../lib/usb.a$O
|
||||
|
||||
BIN=/$objtype/bin/nusb
|
||||
|
||||
UPDATE=\
|
||||
mkfile\
|
||||
$HFILES\
|
||||
${OFILES:%.$O=%.c}\
|
||||
|
||||
</sys/src/cmd/mkone
|
||||
CFLAGS=-I../lib $CFLAGS
|
||||
|
512
sys/src/cmd/nusb/lib/dev.c
Normal file
512
sys/src/cmd/nusb/lib/dev.c
Normal file
|
@ -0,0 +1,512 @@
|
|||
#include <u.h>
|
||||
#include <libc.h>
|
||||
#include <thread.h>
|
||||
#include "usb.h"
|
||||
|
||||
/*
|
||||
* epN.M -> N
|
||||
*/
|
||||
static int
|
||||
nameid(char *s)
|
||||
{
|
||||
char *r;
|
||||
char nm[20];
|
||||
|
||||
r = strrchr(s, 'p');
|
||||
if(r == nil)
|
||||
return -1;
|
||||
strecpy(nm, nm+sizeof(nm), r+1);
|
||||
r = strchr(nm, '.');
|
||||
if(r == nil)
|
||||
return -1;
|
||||
*r = 0;
|
||||
return atoi(nm);
|
||||
}
|
||||
|
||||
Dev*
|
||||
openep(Dev *d, int id)
|
||||
{
|
||||
char *mode; /* How many modes? */
|
||||
Ep *ep;
|
||||
Altc *ac;
|
||||
Dev *epd;
|
||||
Usbdev *ud;
|
||||
char name[40];
|
||||
|
||||
if(access("/dev/usb", AEXIST) < 0 && bind("#u", "/dev", MBEFORE) < 0)
|
||||
return nil;
|
||||
if(d->cfd < 0 || d->usb == nil){
|
||||
werrstr("device not configured");
|
||||
return nil;
|
||||
}
|
||||
ud = d->usb;
|
||||
if(id < 0 || id >= nelem(ud->ep) || ud->ep[id] == nil){
|
||||
werrstr("bad enpoint number");
|
||||
return nil;
|
||||
}
|
||||
ep = ud->ep[id];
|
||||
mode = "rw";
|
||||
if(ep->dir == Ein)
|
||||
mode = "r";
|
||||
if(ep->dir == Eout)
|
||||
mode = "w";
|
||||
snprint(name, sizeof(name), "/dev/usb/ep%d.%d", d->id, id);
|
||||
if(access(name, AEXIST) == 0){
|
||||
dprint(2, "%s: %s already exists; trying to open\n", argv0, name);
|
||||
epd = opendev(name);
|
||||
if(epd != nil)
|
||||
epd->maxpkt = ep->maxpkt; /* guess */
|
||||
return epd;
|
||||
}
|
||||
if(devctl(d, "new %d %d %s", id, ep->type, mode) < 0){
|
||||
dprint(2, "%s: %s: new: %r\n", argv0, d->dir);
|
||||
return nil;
|
||||
}
|
||||
epd = opendev(name);
|
||||
if(epd == nil)
|
||||
return nil;
|
||||
epd->id = id;
|
||||
if(devctl(epd, "maxpkt %d", ep->maxpkt) < 0)
|
||||
fprint(2, "%s: %s: openep: maxpkt: %r\n", argv0, epd->dir);
|
||||
else
|
||||
dprint(2, "%s: %s: maxpkt %d\n", argv0, epd->dir, ep->maxpkt);
|
||||
epd->maxpkt = ep->maxpkt;
|
||||
ac = ep->iface->altc[0];
|
||||
if(ep->ntds > 1 && devctl(epd, "ntds %d", ep->ntds) < 0)
|
||||
fprint(2, "%s: %s: openep: ntds: %r\n", argv0, epd->dir);
|
||||
else
|
||||
dprint(2, "%s: %s: ntds %d\n", argv0, epd->dir, ep->ntds);
|
||||
|
||||
/*
|
||||
* For iso endpoints and high speed interrupt endpoints the pollival is
|
||||
* actually 2ⁿ and not n.
|
||||
* The kernel usb driver must take that into account.
|
||||
* It's simpler this way.
|
||||
*/
|
||||
|
||||
if(ac != nil && (ep->type == Eintr || ep->type == Eiso) && ac->interval != 0)
|
||||
if(devctl(epd, "pollival %d", ac->interval) < 0)
|
||||
fprint(2, "%s: %s: openep: pollival: %r\n", argv0, epd->dir);
|
||||
return epd;
|
||||
}
|
||||
|
||||
Dev*
|
||||
opendev(char *fn)
|
||||
{
|
||||
Dev *d;
|
||||
int l;
|
||||
|
||||
if(access("/dev/usb", AEXIST) < 0 && bind("#u", "/dev", MBEFORE) < 0)
|
||||
return nil;
|
||||
d = emallocz(sizeof(Dev), 1);
|
||||
incref(d);
|
||||
|
||||
l = strlen(fn);
|
||||
d->dfd = -1;
|
||||
/*
|
||||
* +30 to allocate extra size to concat "/<epfilename>"
|
||||
* we should probably remove that feature from the manual
|
||||
* and from the code after checking out that nobody relies on
|
||||
* that.
|
||||
*/
|
||||
d->dir = emallocz(l + 30, 0);
|
||||
strcpy(d->dir, fn);
|
||||
strcpy(d->dir+l, "/ctl");
|
||||
d->cfd = open(d->dir, ORDWR|OCEXEC);
|
||||
d->dir[l] = 0;
|
||||
d->id = nameid(fn);
|
||||
if(d->cfd < 0){
|
||||
werrstr("can't open endpoint %s: %r", d->dir);
|
||||
free(d->dir);
|
||||
free(d);
|
||||
return nil;
|
||||
}
|
||||
dprint(2, "%s: opendev %#p %s\n", argv0, d, fn);
|
||||
return d;
|
||||
}
|
||||
|
||||
int
|
||||
opendevdata(Dev *d, int mode)
|
||||
{
|
||||
char buf[80]; /* more than enough for a usb path */
|
||||
|
||||
seprint(buf, buf+sizeof(buf), "%s/data", d->dir);
|
||||
d->dfd = open(buf, mode|OCEXEC);
|
||||
return d->dfd;
|
||||
}
|
||||
|
||||
enum
|
||||
{
|
||||
/*
|
||||
* Max device conf is also limited by max control request size as
|
||||
* limited by Maxctllen in the kernel usb.h (both limits are arbitrary).
|
||||
*/
|
||||
Maxdevconf = 4 * 1024, /* asking for 16K kills Newsham's disk */
|
||||
};
|
||||
|
||||
int
|
||||
loaddevconf(Dev *d, int n)
|
||||
{
|
||||
uchar *buf;
|
||||
int nr;
|
||||
int type;
|
||||
|
||||
if(n >= nelem(d->usb->conf)){
|
||||
werrstr("loaddevconf: bug: out of configurations in device");
|
||||
fprint(2, "%s: %r\n", argv0);
|
||||
return -1;
|
||||
}
|
||||
buf = emallocz(Maxdevconf, 0);
|
||||
type = Rd2h|Rstd|Rdev;
|
||||
nr = usbcmd(d, type, Rgetdesc, Dconf<<8|n, 0, buf, Maxdevconf);
|
||||
if(nr < Dconflen){
|
||||
free(buf);
|
||||
return -1;
|
||||
}
|
||||
if(d->usb->conf[n] == nil)
|
||||
d->usb->conf[n] = emallocz(sizeof(Conf), 1);
|
||||
nr = parseconf(d->usb, d->usb->conf[n], buf, nr);
|
||||
free(buf);
|
||||
return nr;
|
||||
}
|
||||
|
||||
Ep*
|
||||
mkep(Usbdev *d, int id)
|
||||
{
|
||||
Ep *ep;
|
||||
|
||||
d->ep[id] = ep = emallocz(sizeof(Ep), 1);
|
||||
ep->id = id;
|
||||
return ep;
|
||||
}
|
||||
|
||||
static char*
|
||||
mkstr(uchar *b, int n)
|
||||
{
|
||||
Rune r;
|
||||
char *us;
|
||||
char *s;
|
||||
char *e;
|
||||
|
||||
if(n <= 2 || (n & 1) != 0)
|
||||
return strdup("none");
|
||||
n = (n - 2)/2;
|
||||
b += 2;
|
||||
us = s = emallocz(n*UTFmax+1, 0);
|
||||
e = s + n*UTFmax+1;
|
||||
for(; --n >= 0; b += 2){
|
||||
r = GET2(b);
|
||||
s = seprint(s, e, "%C", r);
|
||||
}
|
||||
return us;
|
||||
}
|
||||
|
||||
char*
|
||||
loaddevstr(Dev *d, int sid)
|
||||
{
|
||||
uchar buf[128];
|
||||
int type;
|
||||
int nr;
|
||||
|
||||
if(sid == 0)
|
||||
return estrdup("none");
|
||||
type = Rd2h|Rstd|Rdev;
|
||||
nr=usbcmd(d, type, Rgetdesc, Dstr<<8|sid, 0, buf, sizeof(buf));
|
||||
return mkstr(buf, nr);
|
||||
}
|
||||
|
||||
int
|
||||
loaddevdesc(Dev *d)
|
||||
{
|
||||
uchar buf[Ddevlen+255];
|
||||
int nr;
|
||||
int type;
|
||||
Ep *ep0;
|
||||
|
||||
type = Rd2h|Rstd|Rdev;
|
||||
nr = sizeof(buf);
|
||||
memset(buf, 0, Ddevlen);
|
||||
if((nr=usbcmd(d, type, Rgetdesc, Ddev<<8|0, 0, buf, nr)) < 0)
|
||||
return -1;
|
||||
/*
|
||||
* Several hubs are returning descriptors of 17 bytes, not 18.
|
||||
* We accept them and leave number of configurations as zero.
|
||||
* (a get configuration descriptor also fails for them!)
|
||||
*/
|
||||
if(nr < Ddevlen){
|
||||
print("%s: %s: warning: device with short descriptor\n",
|
||||
argv0, d->dir);
|
||||
if(nr < Ddevlen-1){
|
||||
werrstr("short device descriptor (%d bytes)", nr);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
d->usb = emallocz(sizeof(Usbdev), 1);
|
||||
ep0 = mkep(d->usb, 0);
|
||||
ep0->dir = Eboth;
|
||||
ep0->type = Econtrol;
|
||||
ep0->maxpkt = d->maxpkt = 8; /* a default */
|
||||
nr = parsedev(d, buf, nr);
|
||||
if(nr >= 0){
|
||||
d->usb->vendor = loaddevstr(d, d->usb->vsid);
|
||||
if(strcmp(d->usb->vendor, "none") != 0){
|
||||
d->usb->product = loaddevstr(d, d->usb->psid);
|
||||
d->usb->serial = loaddevstr(d, d->usb->ssid);
|
||||
}
|
||||
}
|
||||
return nr;
|
||||
}
|
||||
|
||||
int
|
||||
configdev(Dev *d)
|
||||
{
|
||||
int i;
|
||||
|
||||
if(d->dfd < 0)
|
||||
opendevdata(d, ORDWR);
|
||||
if(d->dfd < 0)
|
||||
return -1;
|
||||
if(loaddevdesc(d) < 0)
|
||||
return -1;
|
||||
for(i = 0; i < d->usb->nconf; i++)
|
||||
if(loaddevconf(d, i) < 0)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
closeconf(Conf *c)
|
||||
{
|
||||
int i;
|
||||
int a;
|
||||
|
||||
if(c == nil)
|
||||
return;
|
||||
for(i = 0; i < nelem(c->iface); i++)
|
||||
if(c->iface[i] != nil){
|
||||
for(a = 0; a < nelem(c->iface[i]->altc); a++)
|
||||
free(c->iface[i]->altc[a]);
|
||||
free(c->iface[i]);
|
||||
}
|
||||
free(c);
|
||||
}
|
||||
|
||||
void
|
||||
closedev(Dev *d)
|
||||
{
|
||||
int i;
|
||||
Usbdev *ud;
|
||||
|
||||
if(d==nil || decref(d) != 0)
|
||||
return;
|
||||
dprint(2, "%s: closedev %#p %s\n", argv0, d, d->dir);
|
||||
if(d->free != nil)
|
||||
d->free(d->aux);
|
||||
if(d->cfd >= 0)
|
||||
close(d->cfd);
|
||||
if(d->dfd >= 0)
|
||||
close(d->dfd);
|
||||
d->cfd = d->dfd = -1;
|
||||
free(d->dir);
|
||||
d->dir = nil;
|
||||
ud = d->usb;
|
||||
d->usb = nil;
|
||||
if(ud != nil){
|
||||
free(ud->vendor);
|
||||
free(ud->product);
|
||||
free(ud->serial);
|
||||
for(i = 0; i < nelem(ud->ep); i++)
|
||||
free(ud->ep[i]);
|
||||
for(i = 0; i < nelem(ud->ddesc); i++)
|
||||
free(ud->ddesc[i]);
|
||||
|
||||
for(i = 0; i < nelem(ud->conf); i++)
|
||||
closeconf(ud->conf[i]);
|
||||
free(ud);
|
||||
}
|
||||
free(d);
|
||||
}
|
||||
|
||||
static char*
|
||||
reqstr(int type, int req)
|
||||
{
|
||||
char *s;
|
||||
static char* ds[] = { "dev", "if", "ep", "oth" };
|
||||
static char buf[40];
|
||||
|
||||
if(type&Rd2h)
|
||||
s = seprint(buf, buf+sizeof(buf), "d2h");
|
||||
else
|
||||
s = seprint(buf, buf+sizeof(buf), "h2d");
|
||||
if(type&Rclass)
|
||||
s = seprint(s, buf+sizeof(buf), "|cls");
|
||||
else if(type&Rvendor)
|
||||
s = seprint(s, buf+sizeof(buf), "|vnd");
|
||||
else
|
||||
s = seprint(s, buf+sizeof(buf), "|std");
|
||||
s = seprint(s, buf+sizeof(buf), "|%s", ds[type&3]);
|
||||
|
||||
switch(req){
|
||||
case Rgetstatus: s = seprint(s, buf+sizeof(buf), " getsts"); break;
|
||||
case Rclearfeature: s = seprint(s, buf+sizeof(buf), " clrfeat"); break;
|
||||
case Rsetfeature: s = seprint(s, buf+sizeof(buf), " setfeat"); break;
|
||||
case Rsetaddress: s = seprint(s, buf+sizeof(buf), " setaddr"); break;
|
||||
case Rgetdesc: s = seprint(s, buf+sizeof(buf), " getdesc"); break;
|
||||
case Rsetdesc: s = seprint(s, buf+sizeof(buf), " setdesc"); break;
|
||||
case Rgetconf: s = seprint(s, buf+sizeof(buf), " getcnf"); break;
|
||||
case Rsetconf: s = seprint(s, buf+sizeof(buf), " setcnf"); break;
|
||||
case Rgetiface: s = seprint(s, buf+sizeof(buf), " getif"); break;
|
||||
case Rsetiface: s = seprint(s, buf+sizeof(buf), " setif"); break;
|
||||
}
|
||||
USED(s);
|
||||
return buf;
|
||||
}
|
||||
|
||||
static int
|
||||
cmdreq(Dev *d, int type, int req, int value, int index, uchar *data, int count)
|
||||
{
|
||||
int ndata, n;
|
||||
uchar *wp;
|
||||
uchar buf[8];
|
||||
char *hd, *rs;
|
||||
|
||||
assert(d != nil);
|
||||
if(data == nil){
|
||||
wp = buf;
|
||||
ndata = 0;
|
||||
}else{
|
||||
ndata = count;
|
||||
wp = emallocz(8+ndata, 0);
|
||||
}
|
||||
wp[0] = type;
|
||||
wp[1] = req;
|
||||
PUT2(wp+2, value);
|
||||
PUT2(wp+4, index);
|
||||
PUT2(wp+6, count);
|
||||
if(data != nil)
|
||||
memmove(wp+8, data, ndata);
|
||||
if(usbdebug>2){
|
||||
hd = hexstr(wp, ndata+8);
|
||||
rs = reqstr(type, req);
|
||||
fprint(2, "%s: %s val %d|%d idx %d cnt %d out[%d] %s\n",
|
||||
d->dir, rs, value>>8, value&0xFF,
|
||||
index, count, ndata+8, hd);
|
||||
free(hd);
|
||||
}
|
||||
n = write(d->dfd, wp, 8+ndata);
|
||||
if(wp != buf)
|
||||
free(wp);
|
||||
if(n < 0)
|
||||
return -1;
|
||||
if(n != 8+ndata){
|
||||
dprint(2, "%s: cmd: short write: %d\n", argv0, n);
|
||||
return -1;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
static int
|
||||
cmdrep(Dev *d, void *buf, int nb)
|
||||
{
|
||||
char *hd;
|
||||
|
||||
nb = read(d->dfd, buf, nb);
|
||||
if(nb >0 && usbdebug > 2){
|
||||
hd = hexstr(buf, nb);
|
||||
fprint(2, "%s: in[%d] %s\n", d->dir, nb, hd);
|
||||
free(hd);
|
||||
}
|
||||
return nb;
|
||||
}
|
||||
|
||||
int
|
||||
usbcmd(Dev *d, int type, int req, int value, int index, uchar *data, int count)
|
||||
{
|
||||
int i, r, nerr;
|
||||
char err[64];
|
||||
|
||||
/*
|
||||
* Some devices do not respond to commands some times.
|
||||
* Others even report errors but later work just fine. Retry.
|
||||
*/
|
||||
r = -1;
|
||||
*err = 0;
|
||||
for(i = nerr = 0; i < Uctries; i++){
|
||||
if(type & Rd2h)
|
||||
r = cmdreq(d, type, req, value, index, nil, count);
|
||||
else
|
||||
r = cmdreq(d, type, req, value, index, data, count);
|
||||
if(r > 0){
|
||||
if((type & Rd2h) == 0)
|
||||
break;
|
||||
r = cmdrep(d, data, count);
|
||||
if(r > 0)
|
||||
break;
|
||||
if(r == 0)
|
||||
werrstr("no data from device");
|
||||
}
|
||||
nerr++;
|
||||
if(*err == 0)
|
||||
rerrstr(err, sizeof(err));
|
||||
sleep(Ucdelay);
|
||||
}
|
||||
if(r > 0 && i >= 2)
|
||||
/* let the user know the device is not in good shape */
|
||||
fprint(2, "%s: usbcmd: %s: required %d attempts (%s)\n",
|
||||
argv0, d->dir, i, err);
|
||||
return r;
|
||||
}
|
||||
|
||||
int
|
||||
unstall(Dev *dev, Dev *ep, int dir)
|
||||
{
|
||||
int r;
|
||||
|
||||
if(dir == Ein)
|
||||
dir = 0x80;
|
||||
else
|
||||
dir = 0;
|
||||
r = Rh2d|Rstd|Rep;
|
||||
if(usbcmd(dev, r, Rclearfeature, Fhalt, ep->id|dir, nil, 0)<0){
|
||||
werrstr("unstall: %s: %r", ep->dir);
|
||||
return -1;
|
||||
}
|
||||
if(devctl(ep, "clrhalt") < 0){
|
||||
werrstr("clrhalt: %s: %r", ep->dir);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* To be sure it uses a single write.
|
||||
*/
|
||||
int
|
||||
devctl(Dev *dev, char *fmt, ...)
|
||||
{
|
||||
char buf[128];
|
||||
va_list arg;
|
||||
char *e;
|
||||
|
||||
va_start(arg, fmt);
|
||||
e = vseprint(buf, buf+sizeof(buf), fmt, arg);
|
||||
va_end(arg);
|
||||
return write(dev->cfd, buf, e-buf);
|
||||
}
|
||||
|
||||
Dev *
|
||||
getdev(int id)
|
||||
{
|
||||
Dev *d;
|
||||
char buf[40];
|
||||
|
||||
snprint(buf, sizeof buf, "/dev/usb/ep%d.0", id);
|
||||
d = opendev(buf);
|
||||
if(d == nil)
|
||||
return nil;
|
||||
if(configdev(d) < 0){
|
||||
closedev(d);
|
||||
return nil;
|
||||
}
|
||||
return d;
|
||||
}
|
176
sys/src/cmd/nusb/lib/dump.c
Normal file
176
sys/src/cmd/nusb/lib/dump.c
Normal file
|
@ -0,0 +1,176 @@
|
|||
#include <u.h>
|
||||
#include <libc.h>
|
||||
#include <thread.h>
|
||||
#include <bio.h>
|
||||
#include "usb.h"
|
||||
|
||||
int usbdebug;
|
||||
|
||||
static char *edir[] = {"in", "out", "inout"};
|
||||
static char *etype[] = {"ctl", "iso", "bulk", "intr"};
|
||||
static char* cnames[] =
|
||||
{
|
||||
"none", "audio", "comms", "hid", "",
|
||||
"", "", "printer", "storage", "hub", "data"
|
||||
};
|
||||
static char* devstates[] =
|
||||
{
|
||||
"detached", "attached", "enabled", "assigned", "configured"
|
||||
};
|
||||
|
||||
char*
|
||||
classname(int c)
|
||||
{
|
||||
static char buf[30];
|
||||
|
||||
if(c >= 0 && c < nelem(cnames))
|
||||
return cnames[c];
|
||||
else{
|
||||
seprint(buf, buf+30, "%d", c);
|
||||
return buf;
|
||||
}
|
||||
}
|
||||
|
||||
char *
|
||||
hexstr(void *a, int n)
|
||||
{
|
||||
int i;
|
||||
char *dbuff, *s, *e;
|
||||
uchar *b;
|
||||
|
||||
b = a;
|
||||
dbuff = s = emallocz(1024, 0);
|
||||
*s = 0;
|
||||
e = s + 1024;
|
||||
for(i = 0; i < n; i++)
|
||||
s = seprint(s, e, " %.2ux", b[i]);
|
||||
if(s == e)
|
||||
fprint(2, "%s: usb/lib: hexdump: bug: small buffer\n", argv0);
|
||||
return dbuff;
|
||||
}
|
||||
|
||||
static char *
|
||||
seprintiface(char *s, char *e, Iface *i)
|
||||
{
|
||||
int j;
|
||||
Altc *a;
|
||||
Ep *ep;
|
||||
char *eds, *ets;
|
||||
|
||||
s = seprint(s, e, "\t\tiface csp %s.%uld.%uld\n",
|
||||
classname(Class(i->csp)), Subclass(i->csp), Proto(i->csp));
|
||||
for(j = 0; j < Naltc; j++){
|
||||
a=i->altc[j];
|
||||
if(a == nil)
|
||||
break;
|
||||
s = seprint(s, e, "\t\t alt %d attr %d ival %d",
|
||||
j, a->attrib, a->interval);
|
||||
if(a->aux != nil)
|
||||
s = seprint(s, e, " devspec %p\n", a->aux);
|
||||
else
|
||||
s = seprint(s, e, "\n");
|
||||
}
|
||||
for(j = 0; j < Nep; j++){
|
||||
ep = i->ep[j];
|
||||
if(ep == nil)
|
||||
break;
|
||||
eds = ets = "";
|
||||
if(ep->dir <= nelem(edir))
|
||||
eds = edir[ep->dir];
|
||||
if(ep->type <= nelem(etype))
|
||||
ets = etype[ep->type];
|
||||
s = seprint(s, e, "\t\t ep id %d addr %d dir %s type %s"
|
||||
" itype %d maxpkt %d ntds %d\n",
|
||||
ep->id, ep->addr, eds, ets, ep->isotype,
|
||||
ep->maxpkt, ep->ntds);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
static char*
|
||||
seprintconf(char *s, char *e, Usbdev *d, int ci)
|
||||
{
|
||||
int i;
|
||||
Conf *c;
|
||||
char *hd;
|
||||
|
||||
c = d->conf[ci];
|
||||
s = seprint(s, e, "\tconf: cval %d attrib %x %d mA\n",
|
||||
c->cval, c->attrib, c->milliamps);
|
||||
for(i = 0; i < Niface; i++)
|
||||
if(c->iface[i] == nil)
|
||||
break;
|
||||
else
|
||||
s = seprintiface(s, e, c->iface[i]);
|
||||
for(i = 0; i < Nddesc; i++)
|
||||
if(d->ddesc[i] == nil)
|
||||
break;
|
||||
else if(d->ddesc[i]->conf == c){
|
||||
hd = hexstr((uchar*)&d->ddesc[i]->data,
|
||||
d->ddesc[i]->data.bLength);
|
||||
s = seprint(s, e, "\t\tdev desc %x[%d]: %s\n",
|
||||
d->ddesc[i]->data.bDescriptorType,
|
||||
d->ddesc[i]->data.bLength, hd);
|
||||
free(hd);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
int
|
||||
Ufmt(Fmt *f)
|
||||
{
|
||||
int i;
|
||||
Dev *d;
|
||||
Usbdev *ud;
|
||||
char buf[1024];
|
||||
char *s, *e;
|
||||
|
||||
s = buf;
|
||||
e = buf+sizeof(buf);
|
||||
d = va_arg(f->args, Dev*);
|
||||
if(d == nil)
|
||||
return fmtprint(f, "<nildev>\n");
|
||||
s = seprint(s, e, "%s", d->dir);
|
||||
ud = d->usb;
|
||||
if(ud == nil)
|
||||
return fmtprint(f, "%s %ld refs\n", buf, d->ref);
|
||||
s = seprint(s, e, " csp %s.%uld.%uld",
|
||||
classname(Class(ud->csp)), Subclass(ud->csp), Proto(ud->csp));
|
||||
s = seprint(s, e, " vid %#ux did %#ux", ud->vid, ud->did);
|
||||
s = seprint(s, e, " refs %ld\n", d->ref);
|
||||
s = seprint(s, e, "\t%s %s %s\n", ud->vendor, ud->product, ud->serial);
|
||||
for(i = 0; i < Nconf; i++){
|
||||
if(ud->conf[i] == nil)
|
||||
break;
|
||||
else
|
||||
s = seprintconf(s, e, ud, i);
|
||||
}
|
||||
return fmtprint(f, "%s", buf);
|
||||
}
|
||||
|
||||
char*
|
||||
estrdup(char *s)
|
||||
{
|
||||
char *d;
|
||||
|
||||
d = strdup(s);
|
||||
if(d == nil)
|
||||
sysfatal("strdup: %r");
|
||||
setmalloctag(d, getcallerpc(&s));
|
||||
return d;
|
||||
}
|
||||
|
||||
void*
|
||||
emallocz(ulong size, int zero)
|
||||
{
|
||||
void *x;
|
||||
|
||||
x = malloc(size);
|
||||
if(x == nil)
|
||||
sysfatal("malloc: %r");
|
||||
if(zero)
|
||||
memset(x, 0, size);
|
||||
setmalloctag(x, getcallerpc(&size));
|
||||
return x;
|
||||
}
|
||||
|
24
sys/src/cmd/nusb/lib/mkfile
Normal file
24
sys/src/cmd/nusb/lib/mkfile
Normal file
|
@ -0,0 +1,24 @@
|
|||
</$objtype/mkfile
|
||||
|
||||
LIB=usb.a$O
|
||||
OFILES=\
|
||||
dev.$O\
|
||||
dump.$O\
|
||||
parse.$O\
|
||||
|
||||
HFILES=\
|
||||
usb.h\
|
||||
|
||||
UPDATE=\
|
||||
$HFILES\
|
||||
${OFILES:%.$O=%.c}\
|
||||
mkfile\
|
||||
|
||||
</sys/src/cmd/mklib
|
||||
|
||||
install:V: $LIB
|
||||
date
|
||||
safeinstall:V: install
|
||||
safeinstallall:V: installall
|
||||
nuke:V:
|
||||
rm -f *.[$OS] y.tab.? y.output y.error *.a[$OS]
|
270
sys/src/cmd/nusb/lib/parse.c
Normal file
270
sys/src/cmd/nusb/lib/parse.c
Normal file
|
@ -0,0 +1,270 @@
|
|||
#include <u.h>
|
||||
#include <libc.h>
|
||||
#include <thread.h>
|
||||
#include <bio.h>
|
||||
#include "usb.h"
|
||||
|
||||
int
|
||||
parsedev(Dev *xd, uchar *b, int n)
|
||||
{
|
||||
Usbdev *d;
|
||||
DDev *dd;
|
||||
char *hd;
|
||||
|
||||
d = xd->usb;
|
||||
assert(d != nil);
|
||||
dd = (DDev*)b;
|
||||
if(usbdebug>1){
|
||||
hd = hexstr(b, Ddevlen);
|
||||
fprint(2, "%s: parsedev %s: %s\n", argv0, xd->dir, hd);
|
||||
free(hd);
|
||||
}
|
||||
if(dd->bLength < Ddevlen){
|
||||
werrstr("short dev descr. (%d < %d)", dd->bLength, Ddevlen);
|
||||
return -1;
|
||||
}
|
||||
if(dd->bDescriptorType != Ddev){
|
||||
werrstr("%d is not a dev descriptor", dd->bDescriptorType);
|
||||
return -1;
|
||||
}
|
||||
d->csp = CSP(dd->bDevClass, dd->bDevSubClass, dd->bDevProtocol);
|
||||
d->ep[0]->maxpkt = xd->maxpkt = dd->bMaxPacketSize0;
|
||||
d->class = dd->bDevClass;
|
||||
d->nconf = dd->bNumConfigurations;
|
||||
if(d->nconf == 0)
|
||||
dprint(2, "%s: %s: no configurations\n", argv0, xd->dir);
|
||||
d->vid = GET2(dd->idVendor);
|
||||
d->did = GET2(dd->idProduct);
|
||||
d->dno = GET2(dd->bcdDev);
|
||||
d->vsid = dd->iManufacturer;
|
||||
d->psid = dd->iProduct;
|
||||
d->ssid = dd->iSerialNumber;
|
||||
if(n > Ddevlen && usbdebug>1)
|
||||
fprint(2, "%s: %s: parsedev: %d bytes left",
|
||||
argv0, xd->dir, n - Ddevlen);
|
||||
return Ddevlen;
|
||||
}
|
||||
|
||||
static int
|
||||
parseiface(Usbdev *d, Conf *c, uchar *b, int n, Iface **ipp, Altc **app)
|
||||
{
|
||||
int class, subclass, proto;
|
||||
int ifid, altid;
|
||||
DIface *dip;
|
||||
Iface *ip;
|
||||
|
||||
assert(d != nil && c != nil);
|
||||
if(n < Difacelen){
|
||||
werrstr("short interface descriptor");
|
||||
return -1;
|
||||
}
|
||||
dip = (DIface *)b;
|
||||
ifid = dip->bInterfaceNumber;
|
||||
if(ifid < 0 || ifid >= nelem(c->iface)){
|
||||
werrstr("bad interface number %d", ifid);
|
||||
return -1;
|
||||
}
|
||||
if(c->iface[ifid] == nil)
|
||||
c->iface[ifid] = emallocz(sizeof(Iface), 1);
|
||||
ip = c->iface[ifid];
|
||||
class = dip->bInterfaceClass;
|
||||
subclass = dip->bInterfaceSubClass;
|
||||
proto = dip->bInterfaceProtocol;
|
||||
ip->csp = CSP(class, subclass, proto);
|
||||
if(d->csp == 0) /* use csp from 1st iface */
|
||||
d->csp = ip->csp; /* if device has none */
|
||||
if(d->class == 0)
|
||||
d->class = class;
|
||||
ip->id = ifid;
|
||||
if(c == d->conf[0] && ifid == 0) /* ep0 was already there */
|
||||
d->ep[0]->iface = ip;
|
||||
altid = dip->bAlternateSetting;
|
||||
if(altid < 0 || altid >= nelem(ip->altc)){
|
||||
werrstr("bad alternate conf. number %d", altid);
|
||||
return -1;
|
||||
}
|
||||
if(ip->altc[altid] == nil)
|
||||
ip->altc[altid] = emallocz(sizeof(Altc), 1);
|
||||
*ipp = ip;
|
||||
*app = ip->altc[altid];
|
||||
return Difacelen;
|
||||
}
|
||||
|
||||
extern Ep* mkep(Usbdev *, int);
|
||||
|
||||
static int
|
||||
parseendpt(Usbdev *d, Conf *c, Iface *ip, Altc *altc, uchar *b, int n, Ep **epp)
|
||||
{
|
||||
int i, dir, epid;
|
||||
Ep *ep;
|
||||
DEp *dep;
|
||||
|
||||
assert(d != nil && c != nil && ip != nil && altc != nil);
|
||||
if(n < Deplen){
|
||||
werrstr("short endpoint descriptor");
|
||||
return -1;
|
||||
}
|
||||
dep = (DEp *)b;
|
||||
altc->attrib = dep->bmAttributes; /* here? */
|
||||
altc->interval = dep->bInterval;
|
||||
|
||||
epid = dep->bEndpointAddress & 0xF;
|
||||
assert(epid < nelem(d->ep));
|
||||
if(dep->bEndpointAddress & 0x80)
|
||||
dir = Ein;
|
||||
else
|
||||
dir = Eout;
|
||||
ep = d->ep[epid];
|
||||
if(ep == nil){
|
||||
ep = mkep(d, epid);
|
||||
ep->dir = dir;
|
||||
}else if((ep->addr & 0x80) != (dep->bEndpointAddress & 0x80))
|
||||
ep->dir = Eboth;
|
||||
ep->maxpkt = GET2(dep->wMaxPacketSize);
|
||||
ep->ntds = 1 + ((ep->maxpkt >> 11) & 3);
|
||||
ep->maxpkt &= 0x7FF;
|
||||
ep->addr = dep->bEndpointAddress;
|
||||
ep->type = dep->bmAttributes & 0x03;
|
||||
ep->isotype = (dep->bmAttributes>>2) & 0x03;
|
||||
ep->conf = c;
|
||||
ep->iface = ip;
|
||||
for(i = 0; i < nelem(ip->ep); i++)
|
||||
if(ip->ep[i] == nil)
|
||||
break;
|
||||
if(i == nelem(ip->ep)){
|
||||
werrstr("parseendpt: bug: too many end points on interface "
|
||||
"with csp %#lux", ip->csp);
|
||||
fprint(2, "%s: %r\n", argv0);
|
||||
return -1;
|
||||
}
|
||||
*epp = ip->ep[i] = ep;
|
||||
return Dep;
|
||||
}
|
||||
|
||||
static char*
|
||||
dname(int dtype)
|
||||
{
|
||||
switch(dtype){
|
||||
case Ddev: return "device";
|
||||
case Dconf: return "config";
|
||||
case Dstr: return "string";
|
||||
case Diface: return "interface";
|
||||
case Dep: return "endpoint";
|
||||
case Dreport: return "report";
|
||||
case Dphysical: return "phys";
|
||||
default: return "desc";
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
parsedesc(Usbdev *d, Conf *c, uchar *b, int n)
|
||||
{
|
||||
int len, nd, tot;
|
||||
Iface *ip;
|
||||
Ep *ep;
|
||||
Altc *altc;
|
||||
char *hd;
|
||||
|
||||
assert(d != nil && c != nil);
|
||||
tot = 0;
|
||||
ip = nil;
|
||||
ep = nil;
|
||||
altc = nil;
|
||||
for(nd = 0; nd < nelem(d->ddesc); nd++)
|
||||
if(d->ddesc[nd] == nil)
|
||||
break;
|
||||
|
||||
while(n > 2 && b[0] != 0 && b[0] <= n){
|
||||
len = b[0];
|
||||
if(usbdebug>1){
|
||||
hd = hexstr(b, len);
|
||||
fprint(2, "%s:\t\tparsedesc %s %x[%d] %s\n",
|
||||
argv0, dname(b[1]), b[1], b[0], hd);
|
||||
free(hd);
|
||||
}
|
||||
switch(b[1]){
|
||||
case Ddev:
|
||||
case Dconf:
|
||||
werrstr("unexpected descriptor %d", b[1]);
|
||||
ddprint(2, "%s\tparsedesc: %r", argv0);
|
||||
break;
|
||||
case Diface:
|
||||
if(parseiface(d, c, b, n, &ip, &altc) < 0){
|
||||
ddprint(2, "%s\tparsedesc: %r\n", argv0);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case Dep:
|
||||
if(ip == nil || altc == nil){
|
||||
werrstr("unexpected endpoint descriptor");
|
||||
break;
|
||||
}
|
||||
if(parseendpt(d, c, ip, altc, b, n, &ep) < 0){
|
||||
ddprint(2, "%s\tparsedesc: %r\n", argv0);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if(nd == nelem(d->ddesc)){
|
||||
fprint(2, "%s: parsedesc: too many "
|
||||
"device-specific descriptors for device"
|
||||
" %s %s\n",
|
||||
argv0, d->vendor, d->product);
|
||||
break;
|
||||
}
|
||||
d->ddesc[nd] = emallocz(sizeof(Desc)+b[0], 0);
|
||||
d->ddesc[nd]->iface = ip;
|
||||
d->ddesc[nd]->ep = ep;
|
||||
d->ddesc[nd]->altc = altc;
|
||||
d->ddesc[nd]->conf = c;
|
||||
memmove(&d->ddesc[nd]->data, b, len);
|
||||
++nd;
|
||||
}
|
||||
n -= len;
|
||||
b += len;
|
||||
tot += len;
|
||||
}
|
||||
return tot;
|
||||
}
|
||||
|
||||
int
|
||||
parseconf(Usbdev *d, Conf *c, uchar *b, int n)
|
||||
{
|
||||
DConf* dc;
|
||||
int l;
|
||||
int nr;
|
||||
char *hd;
|
||||
|
||||
assert(d != nil && c != nil);
|
||||
dc = (DConf*)b;
|
||||
if(usbdebug>1){
|
||||
hd = hexstr(b, Dconflen);
|
||||
fprint(2, "%s:\tparseconf %s\n", argv0, hd);
|
||||
free(hd);
|
||||
}
|
||||
if(dc->bLength < Dconflen){
|
||||
werrstr("short configuration descriptor");
|
||||
return -1;
|
||||
}
|
||||
if(dc->bDescriptorType != Dconf){
|
||||
werrstr("not a configuration descriptor");
|
||||
return -1;
|
||||
}
|
||||
c->cval = dc->bConfigurationValue;
|
||||
c->attrib = dc->bmAttributes;
|
||||
c->milliamps = dc->MaxPower*2;
|
||||
l = GET2(dc->wTotalLength);
|
||||
if(n < l){
|
||||
werrstr("truncated configuration info");
|
||||
return -1;
|
||||
}
|
||||
n -= Dconflen;
|
||||
b += Dconflen;
|
||||
nr = 0;
|
||||
if(n > 0 && (nr=parsedesc(d, c, b, n)) < 0)
|
||||
return -1;
|
||||
n -= nr;
|
||||
if(n > 0 && usbdebug>1)
|
||||
fprint(2, "%s:\tparseconf: %d bytes left\n", argv0, n);
|
||||
return l;
|
||||
}
|
361
sys/src/cmd/nusb/lib/usb.h
Normal file
361
sys/src/cmd/nusb/lib/usb.h
Normal file
|
@ -0,0 +1,361 @@
|
|||
typedef struct Altc Altc;
|
||||
typedef struct Conf Conf;
|
||||
typedef struct DConf DConf;
|
||||
typedef struct DDesc DDesc;
|
||||
typedef struct DDev DDev;
|
||||
typedef struct DEp DEp;
|
||||
typedef struct DIface DIface;
|
||||
typedef struct Desc Desc;
|
||||
typedef struct Dev Dev;
|
||||
typedef struct Ep Ep;
|
||||
typedef struct Iface Iface;
|
||||
typedef struct Usbdev Usbdev;
|
||||
|
||||
enum {
|
||||
/* fundamental constants */
|
||||
Nep = 256, /* max. endpoints per usb device & per interface */
|
||||
|
||||
/* tunable parameters */
|
||||
Nconf = 16, /* max. configurations per usb device */
|
||||
Nddesc = 8*Nep, /* max. device-specific descriptors per usb device */
|
||||
Niface = 16, /* max. interfaces per configuration */
|
||||
Naltc = 256, /* max. alt configurations per interface */
|
||||
Uctries = 4, /* no. of tries for usbcmd */
|
||||
Ucdelay = 50, /* delay before retrying */
|
||||
|
||||
/* request type */
|
||||
Rh2d = 0<<7, /* host to device */
|
||||
Rd2h = 1<<7, /* device to host */
|
||||
|
||||
Rstd = 0<<5, /* types */
|
||||
Rclass = 1<<5,
|
||||
Rvendor = 2<<5,
|
||||
|
||||
Rdev = 0, /* recipients */
|
||||
Riface = 1,
|
||||
Rep = 2, /* endpoint */
|
||||
Rother = 3,
|
||||
|
||||
/* standard requests */
|
||||
Rgetstatus = 0,
|
||||
Rclearfeature = 1,
|
||||
Rsetfeature = 3,
|
||||
Rsetaddress = 5,
|
||||
Rgetdesc = 6,
|
||||
Rsetdesc = 7,
|
||||
Rgetconf = 8,
|
||||
Rsetconf = 9,
|
||||
Rgetiface = 10,
|
||||
Rsetiface = 11,
|
||||
Rsynchframe = 12,
|
||||
|
||||
Rgetcur = 0x81,
|
||||
Rgetmin = 0x82,
|
||||
Rgetmax = 0x83,
|
||||
Rgetres = 0x84,
|
||||
Rsetcur = 0x01,
|
||||
Rsetmin = 0x02,
|
||||
Rsetmax = 0x03,
|
||||
Rsetres = 0x04,
|
||||
|
||||
/* dev classes */
|
||||
Clnone = 0, /* not in usb */
|
||||
Claudio = 1,
|
||||
Clcomms = 2,
|
||||
Clhid = 3,
|
||||
Clprinter = 7,
|
||||
Clstorage = 8,
|
||||
Clhub = 9,
|
||||
Cldata = 10,
|
||||
|
||||
/* standard descriptor sizes */
|
||||
Ddevlen = 18,
|
||||
Dconflen = 9,
|
||||
Difacelen = 9,
|
||||
Deplen = 7,
|
||||
|
||||
/* descriptor types */
|
||||
Ddev = 1,
|
||||
Dconf = 2,
|
||||
Dstr = 3,
|
||||
Diface = 4,
|
||||
Dep = 5,
|
||||
Dreport = 0x22,
|
||||
Dfunction = 0x24,
|
||||
Dphysical = 0x23,
|
||||
|
||||
/* feature selectors */
|
||||
Fdevremotewakeup = 1,
|
||||
Fhalt = 0,
|
||||
|
||||
/* device state */
|
||||
Detached = 0,
|
||||
Attached,
|
||||
Enabled,
|
||||
Assigned,
|
||||
Configured,
|
||||
|
||||
/* endpoint direction */
|
||||
Ein = 0,
|
||||
Eout,
|
||||
Eboth,
|
||||
|
||||
/* endpoint type */
|
||||
Econtrol = 0,
|
||||
Eiso = 1,
|
||||
Ebulk = 2,
|
||||
Eintr = 3,
|
||||
|
||||
/* endpoint isotype */
|
||||
Eunknown = 0,
|
||||
Easync = 1,
|
||||
Eadapt = 2,
|
||||
Esync = 3,
|
||||
|
||||
/* config attrib */
|
||||
Cbuspowered = 1<<7,
|
||||
Cselfpowered = 1<<6,
|
||||
Cremotewakeup = 1<<5,
|
||||
|
||||
/* report types */
|
||||
Tmtype = 3<<2,
|
||||
Tmitem = 0xF0,
|
||||
Tmain = 0<<2,
|
||||
Tinput = 0x80,
|
||||
Toutput = 0x90,
|
||||
Tfeature = 0xB0,
|
||||
Tcoll = 0xA0,
|
||||
Tecoll = 0xC0,
|
||||
Tglobal = 1<<2,
|
||||
Tusagepage = 0x00,
|
||||
Tlmin = 0x10,
|
||||
Tlmax = 0x20,
|
||||
Tpmin = 0x30,
|
||||
Tpmax = 0x40,
|
||||
Tunitexp = 0x50,
|
||||
Tunit = 0x60,
|
||||
Trepsize = 0x70,
|
||||
TrepID = 0x80,
|
||||
Trepcount = 0x90,
|
||||
Tpush = 0xA0,
|
||||
Tpop = 0xB0,
|
||||
Tlocal = 2<<2,
|
||||
Tusage = 0x00,
|
||||
Tumin = 0x10,
|
||||
Tumax = 0x20,
|
||||
Tdindex = 0x30,
|
||||
Tdmin = 0x40,
|
||||
Tdmax = 0x50,
|
||||
Tsindex = 0x70,
|
||||
Tsmin = 0x80,
|
||||
Tsmax = 0x90,
|
||||
Tsetdelim = 0xA0,
|
||||
Treserved = 3<<2,
|
||||
Tlong = 0xFE,
|
||||
|
||||
};
|
||||
|
||||
/*
|
||||
* Usb device (when used for ep0s) or endpoint.
|
||||
* RC: One ref because of existing, another one per ogoing I/O.
|
||||
* per-driver resources (including FS if any) are released by aux
|
||||
* once the last ref is gone. This may include other Devs using
|
||||
* to access endpoints for actual I/O.
|
||||
*/
|
||||
struct Dev
|
||||
{
|
||||
Ref;
|
||||
char* dir; /* path for the endpoint dir */
|
||||
int id; /* usb id for device or ep. number */
|
||||
int dfd; /* descriptor for the data file */
|
||||
int cfd; /* descriptor for the control file */
|
||||
int maxpkt; /* cached from usb description */
|
||||
Ref nerrs; /* number of errors in requests */
|
||||
Usbdev* usb; /* USB description */
|
||||
void* aux; /* for the device driver */
|
||||
void (*free)(void*); /* idem. to release aux */
|
||||
};
|
||||
|
||||
/*
|
||||
* device description as reported by USB (unpacked).
|
||||
*/
|
||||
struct Usbdev
|
||||
{
|
||||
ulong csp; /* USB class/subclass/proto */
|
||||
int vid; /* vendor id */
|
||||
int did; /* product (device) id */
|
||||
int dno; /* device release number */
|
||||
char* vendor;
|
||||
char* product;
|
||||
char* serial;
|
||||
int vsid;
|
||||
int psid;
|
||||
int ssid;
|
||||
int class; /* from descriptor */
|
||||
int nconf; /* from descriptor */
|
||||
Conf* conf[Nconf]; /* configurations */
|
||||
Ep* ep[Nep]; /* all endpoints in device */
|
||||
Desc* ddesc[Nddesc]; /* (raw) device specific descriptors */
|
||||
};
|
||||
|
||||
struct Ep
|
||||
{
|
||||
uchar addr; /* endpt address, 0-15 (|0x80 if Ein) */
|
||||
uchar dir; /* direction, Ein/Eout */
|
||||
uchar type; /* Econtrol, Eiso, Ebulk, Eintr */
|
||||
uchar isotype; /* Eunknown, Easync, Eadapt, Esync */
|
||||
int id;
|
||||
int maxpkt; /* max. packet size */
|
||||
int ntds; /* nb. of Tds per µframe */
|
||||
Conf* conf; /* the endpoint belongs to */
|
||||
Iface* iface; /* the endpoint belongs to */
|
||||
};
|
||||
|
||||
struct Altc
|
||||
{
|
||||
int attrib;
|
||||
int interval;
|
||||
void* aux; /* for the driver program */
|
||||
};
|
||||
|
||||
struct Iface
|
||||
{
|
||||
int id; /* interface number */
|
||||
ulong csp; /* USB class/subclass/proto */
|
||||
Altc* altc[Naltc];
|
||||
Ep* ep[Nep];
|
||||
void* aux; /* for the driver program */
|
||||
};
|
||||
|
||||
struct Conf
|
||||
{
|
||||
int cval; /* value for set configuration */
|
||||
int attrib;
|
||||
int milliamps; /* maximum power in this config. */
|
||||
Iface* iface[Niface];
|
||||
};
|
||||
|
||||
/*
|
||||
* Device-specific descriptors.
|
||||
* They show up mixed with other descriptors
|
||||
* within a configuration.
|
||||
* These are unknown to the library but handed to the driver.
|
||||
*/
|
||||
struct DDesc
|
||||
{
|
||||
uchar bLength;
|
||||
uchar bDescriptorType;
|
||||
uchar bbytes[1];
|
||||
/* extra bytes allocated here to keep the rest of it */
|
||||
};
|
||||
|
||||
struct Desc
|
||||
{
|
||||
Conf* conf; /* where this descriptor was read */
|
||||
Iface* iface; /* last iface before desc in conf. */
|
||||
Ep* ep; /* last endpt before desc in conf. */
|
||||
Altc* altc; /* last alt.c. before desc in conf. */
|
||||
DDesc data; /* unparsed standard USB descriptor */
|
||||
};
|
||||
|
||||
/*
|
||||
* layout of standard descriptor types
|
||||
*/
|
||||
struct DDev
|
||||
{
|
||||
uchar bLength;
|
||||
uchar bDescriptorType;
|
||||
uchar bcdUSB[2];
|
||||
uchar bDevClass;
|
||||
uchar bDevSubClass;
|
||||
uchar bDevProtocol;
|
||||
uchar bMaxPacketSize0;
|
||||
uchar idVendor[2];
|
||||
uchar idProduct[2];
|
||||
uchar bcdDev[2];
|
||||
uchar iManufacturer;
|
||||
uchar iProduct;
|
||||
uchar iSerialNumber;
|
||||
uchar bNumConfigurations;
|
||||
};
|
||||
|
||||
struct DConf
|
||||
{
|
||||
uchar bLength;
|
||||
uchar bDescriptorType;
|
||||
uchar wTotalLength[2];
|
||||
uchar bNumInterfaces;
|
||||
uchar bConfigurationValue;
|
||||
uchar iConfiguration;
|
||||
uchar bmAttributes;
|
||||
uchar MaxPower;
|
||||
};
|
||||
|
||||
struct DIface
|
||||
{
|
||||
uchar bLength;
|
||||
uchar bDescriptorType;
|
||||
uchar bInterfaceNumber;
|
||||
uchar bAlternateSetting;
|
||||
uchar bNumEndpoints;
|
||||
uchar bInterfaceClass;
|
||||
uchar bInterfaceSubClass;
|
||||
uchar bInterfaceProtocol;
|
||||
uchar iInterface;
|
||||
};
|
||||
|
||||
struct DEp
|
||||
{
|
||||
uchar bLength;
|
||||
uchar bDescriptorType;
|
||||
uchar bEndpointAddress;
|
||||
uchar bmAttributes;
|
||||
uchar wMaxPacketSize[2];
|
||||
uchar bInterval;
|
||||
};
|
||||
|
||||
#define Class(csp) ((csp) & 0xff)
|
||||
#define Subclass(csp) (((csp)>>8) & 0xff)
|
||||
#define Proto(csp) (((csp)>>16) & 0xff)
|
||||
#define CSP(c, s, p) ((c) | (s)<<8 | (p)<<16)
|
||||
|
||||
#define GET2(p) (((p)[1] & 0xFF)<<8 | ((p)[0] & 0xFF))
|
||||
#define PUT2(p,v) {(p)[0] = (v); (p)[1] = (v)>>8;}
|
||||
#define GET4(p) (((p)[3]&0xFF)<<24 | ((p)[2]&0xFF)<<16 | \
|
||||
((p)[1]&0xFF)<<8 | ((p)[0]&0xFF))
|
||||
#define PUT4(p,v) {(p)[0] = (v); (p)[1] = (v)>>8; \
|
||||
(p)[2] = (v)>>16; (p)[3] = (v)>>24;}
|
||||
|
||||
#define dprint if(usbdebug)fprint
|
||||
#define ddprint if(usbdebug > 1)fprint
|
||||
|
||||
#pragma varargck type "U" Dev*
|
||||
#pragma varargck argpos devctl 2
|
||||
|
||||
int Ufmt(Fmt *f);
|
||||
char* classname(int c);
|
||||
void closedev(Dev *d);
|
||||
int configdev(Dev *d);
|
||||
int devctl(Dev *dev, char *fmt, ...);
|
||||
void* emallocz(ulong size, int zero);
|
||||
char* estrdup(char *s);
|
||||
int matchdevcsp(char *info, void *a);
|
||||
int finddevs(int (*matchf)(char*,void*), void *farg, char** dirs, int ndirs);
|
||||
char* hexstr(void *a, int n);
|
||||
int loaddevconf(Dev *d, int n);
|
||||
int loaddevdesc(Dev *d);
|
||||
char* loaddevstr(Dev *d, int sid);
|
||||
Dev* opendev(char *fn);
|
||||
int opendevdata(Dev *d, int mode);
|
||||
Dev* openep(Dev *d, int id);
|
||||
int parseconf(Usbdev *d, Conf *c, uchar *b, int n);
|
||||
int parsedesc(Usbdev *d, Conf *c, uchar *b, int n);
|
||||
int parsedev(Dev *xd, uchar *b, int n);
|
||||
void startdevs(char *args, char *argv[], int argc, int (*mf)(char*,void*), void*ma, int (*df)(Dev*,int,char**));
|
||||
int unstall(Dev *dev, Dev *ep, int dir);
|
||||
int usbcmd(Dev *d, int type, int req, int value, int index, uchar *data, int count);
|
||||
Dev* getdev(int id);
|
||||
|
||||
extern int usbdebug; /* more messages for bigger values */
|
||||
|
||||
|
|
@ -1,7 +1,8 @@
|
|||
</$objtype/mkfile
|
||||
|
||||
# order matters here. build lib first and usbd last.
|
||||
DIRS=\
|
||||
lib\
|
||||
kb\
|
||||
usbd\
|
||||
|
||||
UPDATE=\
|
||||
|
@ -21,7 +22,6 @@ install installall safeinstall safeinstallall:V:
|
|||
for (i in $DIRS) @{
|
||||
cd $i && mk $target
|
||||
}
|
||||
cp probe /$objtype/bin/usb/probe
|
||||
|
||||
update:V:
|
||||
update $UPDATEFLAGS $UPDATE
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
typedef struct Rule Rule;
|
||||
typedef struct Cond Cond;
|
||||
typedef struct Dev Dev;
|
||||
typedef struct Hub Hub;
|
||||
typedef struct DHub DHub;
|
||||
typedef struct Port Port;
|
||||
|
||||
struct Rule {
|
||||
char **argv;
|
||||
|
@ -17,6 +19,110 @@ struct Cond {
|
|||
Cond *and, *or;
|
||||
};
|
||||
|
||||
struct Dev {
|
||||
u32int class, vid, did;
|
||||
enum
|
||||
{
|
||||
Stack = 32*1024,
|
||||
|
||||
Dhub = 0x29, /* hub descriptor type */
|
||||
Dhublen = 9, /* hub descriptor length */
|
||||
|
||||
/* hub class feature selectors */
|
||||
Fhublocalpower = 0,
|
||||
Fhubovercurrent = 1,
|
||||
|
||||
Fportconnection = 0,
|
||||
Fportenable = 1,
|
||||
Fportsuspend = 2,
|
||||
Fportovercurrent = 3,
|
||||
Fportreset = 4,
|
||||
Fportpower = 8,
|
||||
Fportlowspeed = 9,
|
||||
Fcportconnection = 16,
|
||||
Fcportenable = 17,
|
||||
Fcportsuspend = 18,
|
||||
Fcportovercurrent= 19,
|
||||
Fcportreset = 20,
|
||||
Fportindicator = 22,
|
||||
|
||||
/* Port status and status change bits
|
||||
* Constants at /sys/src/9/pc/usb.h starting with HP-
|
||||
* must have the same values or root hubs won't work.
|
||||
*/
|
||||
PSpresent = 0x0001,
|
||||
PSenable = 0x0002,
|
||||
PSsuspend = 0x0004,
|
||||
PSovercurrent = 0x0008,
|
||||
PSreset = 0x0010,
|
||||
PSpower = 0x0100,
|
||||
PSslow = 0x0200,
|
||||
PShigh = 0x0400,
|
||||
|
||||
PSstatuschg = 0x10000, /* PSpresent changed */
|
||||
PSchange = 0x20000, /* PSenable changed */
|
||||
|
||||
|
||||
/* port/device state */
|
||||
Pdisabled = 0, /* must be 0 */
|
||||
Pattached,
|
||||
Pconfiged,
|
||||
|
||||
/* Delays, timeouts (ms) */
|
||||
// Spawndelay = 1000, /* how often may we re-spawn a driver */
|
||||
Spawndelay = 250, /* how often may we re-spawn a driver */
|
||||
// Connectdelay = 1000, /* how much to wait after a connect */
|
||||
Connectdelay = 500, /* how much to wait after a connect */
|
||||
Resetdelay = 20, /* how much to wait after a reset */
|
||||
Enabledelay = 20, /* how much to wait after an enable */
|
||||
Powerdelay = 100, /* after powering up ports */
|
||||
Pollms = 250, /* port poll interval */
|
||||
Chgdelay = 100, /* waiting for port become stable */
|
||||
Chgtmout = 1000, /* ...but at most this much */
|
||||
|
||||
/*
|
||||
* device tab for embedded usb drivers.
|
||||
*/
|
||||
DCL = 0x01000000, /* csp identifies just class */
|
||||
DSC = 0x02000000, /* csp identifies just subclass */
|
||||
DPT = 0x04000000, /* csp identifies just proto */
|
||||
|
||||
};
|
||||
|
||||
struct Hub
|
||||
{
|
||||
uchar pwrmode;
|
||||
uchar compound;
|
||||
uchar pwrms; /* time to wait in ms */
|
||||
uchar maxcurrent; /* after powering port*/
|
||||
int leds; /* has port indicators? */
|
||||
int maxpkt;
|
||||
uchar nport;
|
||||
Port *port;
|
||||
int failed; /* I/O error while enumerating */
|
||||
int isroot; /* set if root hub */
|
||||
Dev *dev; /* for this hub */
|
||||
Hub *next; /* in list of hubs */
|
||||
};
|
||||
|
||||
struct Port
|
||||
{
|
||||
int state; /* state of the device */
|
||||
int sts; /* old port status */
|
||||
uchar removable;
|
||||
uchar pwrctl;
|
||||
Dev *dev; /* attached device (if non-nil) */
|
||||
Hub *hub; /* non-nil if hub attached */
|
||||
int devnb; /* device number */
|
||||
uvlong *devmaskp; /* ptr to dev mask */
|
||||
};
|
||||
|
||||
/* USB HUB descriptor */
|
||||
struct DHub
|
||||
{
|
||||
uchar bLength;
|
||||
uchar bDescriptorType;
|
||||
uchar bNbrPorts;
|
||||
uchar wHubCharacteristics[2];
|
||||
uchar bPwrOn2PwrGood;
|
||||
uchar bHubContrCurrent;
|
||||
uchar DeviceRemovable[1]; /* variable length */
|
||||
};
|
||||
|
|
|
@ -1,2 +1,4 @@
|
|||
void parserules(char*);
|
||||
Rule* rulesmatch(Dev*);
|
||||
Rule* rulesmatch(Usbdev*);
|
||||
int startdev(Port*);
|
||||
void work(void);
|
||||
|
|
690
sys/src/cmd/nusb/usbd/hub.c
Normal file
690
sys/src/cmd/nusb/usbd/hub.c
Normal file
|
@ -0,0 +1,690 @@
|
|||
#include <u.h>
|
||||
#include <libc.h>
|
||||
#include <thread.h>
|
||||
#include "usb.h"
|
||||
#include "dat.h"
|
||||
#include "fns.h"
|
||||
|
||||
static Hub *hubs;
|
||||
static int nhubs;
|
||||
static int mustdump;
|
||||
static int pollms = Pollms;
|
||||
static Lock masklck;
|
||||
|
||||
static char *dsname[] = { "disabled", "attached", "configed" };
|
||||
|
||||
int
|
||||
getdevnb(uvlong *maskp)
|
||||
{
|
||||
int i;
|
||||
|
||||
lock(&masklck);
|
||||
for(i = 0; i < 8 * sizeof *maskp; i++)
|
||||
if((*maskp & (1ULL<<i)) == 0){
|
||||
*maskp |= 1ULL<<i;
|
||||
unlock(&masklck);
|
||||
return i;
|
||||
}
|
||||
unlock(&masklck);
|
||||
return -1;
|
||||
}
|
||||
|
||||
void
|
||||
putdevnb(uvlong *maskp, int id)
|
||||
{
|
||||
lock(&masklck);
|
||||
if(id >= 0)
|
||||
*maskp &= ~(1ULL<<id);
|
||||
unlock(&masklck);
|
||||
}
|
||||
|
||||
static int
|
||||
hubfeature(Hub *h, int port, int f, int on)
|
||||
{
|
||||
int cmd;
|
||||
|
||||
if(on)
|
||||
cmd = Rsetfeature;
|
||||
else
|
||||
cmd = Rclearfeature;
|
||||
return usbcmd(h->dev, Rh2d|Rclass|Rother, cmd, f, port, nil, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* This may be used to detect overcurrent on the hub
|
||||
*/
|
||||
static void
|
||||
checkhubstatus(Hub *h)
|
||||
{
|
||||
uchar buf[4];
|
||||
int sts;
|
||||
|
||||
if(h->isroot) /* not for root hubs */
|
||||
return;
|
||||
if(usbcmd(h->dev, Rd2h|Rclass|Rdev, Rgetstatus, 0, 0, buf, 4) < 0){
|
||||
dprint(2, "%s: get hub status: %r\n", h->dev->dir);
|
||||
return;
|
||||
}
|
||||
sts = GET2(buf);
|
||||
dprint(2, "hub %s: status %#ux\n", h->dev->dir, sts);
|
||||
}
|
||||
|
||||
static int
|
||||
confighub(Hub *h)
|
||||
{
|
||||
int type;
|
||||
uchar buf[128]; /* room for extra descriptors */
|
||||
int i;
|
||||
Usbdev *d;
|
||||
DHub *dd;
|
||||
Port *pp;
|
||||
int nr;
|
||||
int nmap;
|
||||
uchar *PortPwrCtrlMask;
|
||||
int offset;
|
||||
int mask;
|
||||
|
||||
d = h->dev->usb;
|
||||
for(i = 0; i < nelem(d->ddesc); i++)
|
||||
if(d->ddesc[i] == nil)
|
||||
break;
|
||||
else if(d->ddesc[i]->data.bDescriptorType == Dhub){
|
||||
dd = (DHub*)&d->ddesc[i]->data;
|
||||
nr = Dhublen;
|
||||
goto Config;
|
||||
}
|
||||
type = Rd2h|Rclass|Rdev;
|
||||
nr = usbcmd(h->dev, type, Rgetdesc, Dhub<<8|0, 0, buf, sizeof buf);
|
||||
if(nr < Dhublen){
|
||||
dprint(2, "%s: %s: getdesc hub: %r\n", argv0, h->dev->dir);
|
||||
return -1;
|
||||
}
|
||||
dd = (DHub*)buf;
|
||||
Config:
|
||||
h->nport = dd->bNbrPorts;
|
||||
nmap = 1 + h->nport/8;
|
||||
if(nr < 7 + 2*nmap){
|
||||
fprint(2, "%s: %s: descr. too small\n", argv0, h->dev->dir);
|
||||
return -1;
|
||||
}
|
||||
h->port = emallocz((h->nport+1)*sizeof(Port), 1);
|
||||
h->pwrms = dd->bPwrOn2PwrGood*2;
|
||||
if(h->pwrms < Powerdelay)
|
||||
h->pwrms = Powerdelay;
|
||||
h->maxcurrent = dd->bHubContrCurrent;
|
||||
h->pwrmode = dd->wHubCharacteristics[0] & 3;
|
||||
h->compound = (dd->wHubCharacteristics[0] & (1<<2))!=0;
|
||||
h->leds = (dd->wHubCharacteristics[0] & (1<<7)) != 0;
|
||||
PortPwrCtrlMask = dd->DeviceRemovable + nmap;
|
||||
for(i = 1; i <= h->nport; i++){
|
||||
pp = &h->port[i];
|
||||
offset = i/8;
|
||||
mask = 1<<(i%8);
|
||||
pp->removable = (dd->DeviceRemovable[offset] & mask) != 0;
|
||||
pp->pwrctl = (PortPwrCtrlMask[offset] & mask) != 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
configroothub(Hub *h)
|
||||
{
|
||||
Dev *d;
|
||||
char buf[128];
|
||||
char *p;
|
||||
int nr;
|
||||
|
||||
d = h->dev;
|
||||
h->nport = 2;
|
||||
h->maxpkt = 8;
|
||||
seek(d->cfd, 0, 0);
|
||||
nr = read(d->cfd, buf, sizeof(buf)-1);
|
||||
if(nr < 0)
|
||||
goto Done;
|
||||
buf[nr] = 0;
|
||||
|
||||
p = strstr(buf, "ports ");
|
||||
if(p == nil)
|
||||
fprint(2, "%s: %s: no port information\n", argv0, d->dir);
|
||||
else
|
||||
h->nport = atoi(p+6);
|
||||
p = strstr(buf, "maxpkt ");
|
||||
if(p == nil)
|
||||
fprint(2, "%s: %s: no maxpkt information\n", argv0, d->dir);
|
||||
else
|
||||
h->maxpkt = atoi(p+7);
|
||||
Done:
|
||||
h->port = emallocz((h->nport+1)*sizeof(Port), 1);
|
||||
dprint(2, "%s: %s: ports %d maxpkt %d\n", argv0, d->dir, h->nport, h->maxpkt);
|
||||
}
|
||||
|
||||
Hub*
|
||||
newhub(char *fn, Dev *d)
|
||||
{
|
||||
Hub *h;
|
||||
int i;
|
||||
Usbdev *ud;
|
||||
|
||||
h = emallocz(sizeof(Hub), 1);
|
||||
h->isroot = (d == nil);
|
||||
if(h->isroot){
|
||||
h->dev = opendev(fn);
|
||||
if(h->dev == nil){
|
||||
fprint(2, "%s: opendev: %s: %r", argv0, fn);
|
||||
goto Fail;
|
||||
}
|
||||
if(opendevdata(h->dev, ORDWR) < 0){
|
||||
fprint(2, "%s: opendevdata: %s: %r\n", argv0, fn);
|
||||
goto Fail;
|
||||
}
|
||||
configroothub(h); /* never fails */
|
||||
}else{
|
||||
h->dev = d;
|
||||
if(confighub(h) < 0){
|
||||
fprint(2, "%s: %s: config: %r\n", argv0, fn);
|
||||
goto Fail;
|
||||
}
|
||||
}
|
||||
if(h->dev == nil){
|
||||
fprint(2, "%s: opendev: %s: %r\n", argv0, fn);
|
||||
goto Fail;
|
||||
}
|
||||
devctl(h->dev, "hub");
|
||||
ud = h->dev->usb;
|
||||
if(h->isroot)
|
||||
devctl(h->dev, "info roothub csp %#08ux ports %d",
|
||||
0x000009, h->nport);
|
||||
else{
|
||||
devctl(h->dev, "info hub csp %#08ulx ports %d %q %q",
|
||||
ud->csp, h->nport, ud->vendor, ud->product);
|
||||
for(i = 1; i <= h->nport; i++)
|
||||
if(hubfeature(h, i, Fportpower, 1) < 0)
|
||||
fprint(2, "%s: %s: power: %r\n", argv0, fn);
|
||||
sleep(h->pwrms);
|
||||
for(i = 1; i <= h->nport; i++)
|
||||
if(h->leds != 0)
|
||||
hubfeature(h, i, Fportindicator, 1);
|
||||
}
|
||||
h->next = hubs;
|
||||
hubs = h;
|
||||
nhubs++;
|
||||
dprint(2, "%s: hub %#p allocated:", argv0, h);
|
||||
dprint(2, " ports %d pwrms %d max curr %d pwrm %d cmp %d leds %d\n",
|
||||
h->nport, h->pwrms, h->maxcurrent,
|
||||
h->pwrmode, h->compound, h->leds);
|
||||
incref(h->dev);
|
||||
return h;
|
||||
Fail:
|
||||
if(d != nil)
|
||||
devctl(d, "detach");
|
||||
free(h->port);
|
||||
free(h);
|
||||
dprint(2, "%s: hub %#p failed to start:", argv0, h);
|
||||
return nil;
|
||||
}
|
||||
|
||||
static void portdetach(Hub *h, int p);
|
||||
|
||||
/*
|
||||
* If during enumeration we get an I/O error the hub is gone or
|
||||
* in pretty bad shape. Because of retries of failed usb commands
|
||||
* (and the sleeps they include) it can take a while to detach all
|
||||
* ports for the hub. This detaches all ports and makes the hub void.
|
||||
* The parent hub will detect a detach (probably right now) and
|
||||
* close it later.
|
||||
*/
|
||||
static void
|
||||
hubfail(Hub *h)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i = 1; i <= h->nport; i++)
|
||||
portdetach(h, i);
|
||||
h->failed = 1;
|
||||
}
|
||||
|
||||
static void
|
||||
closehub(Hub *h)
|
||||
{
|
||||
Hub **hl;
|
||||
|
||||
dprint(2, "%s: closing hub %#p\n", argv0, h);
|
||||
for(hl = &hubs; *hl != nil; hl = &(*hl)->next)
|
||||
if(*hl == h)
|
||||
break;
|
||||
if(*hl == nil)
|
||||
sysfatal("closehub: no hub");
|
||||
*hl = h->next;
|
||||
nhubs--;
|
||||
hubfail(h); /* detach all ports */
|
||||
free(h->port);
|
||||
assert(h->dev != nil);
|
||||
devctl(h->dev, "detach");
|
||||
closedev(h->dev);
|
||||
free(h);
|
||||
}
|
||||
|
||||
static int
|
||||
portstatus(Hub *h, int p)
|
||||
{
|
||||
Dev *d;
|
||||
uchar buf[4];
|
||||
int t;
|
||||
int sts;
|
||||
int dbg;
|
||||
|
||||
dbg = usbdebug;
|
||||
if(dbg != 0 && dbg < 4)
|
||||
usbdebug = 1; /* do not be too chatty */
|
||||
d = h->dev;
|
||||
t = Rd2h|Rclass|Rother;
|
||||
if(usbcmd(d, t, Rgetstatus, 0, p, buf, sizeof(buf)) < 0)
|
||||
sts = -1;
|
||||
else
|
||||
sts = GET2(buf);
|
||||
usbdebug = dbg;
|
||||
return sts;
|
||||
}
|
||||
|
||||
static char*
|
||||
stsstr(int sts)
|
||||
{
|
||||
static char s[80];
|
||||
char *e;
|
||||
|
||||
e = s;
|
||||
if(sts&PSsuspend)
|
||||
*e++ = 'z';
|
||||
if(sts&PSreset)
|
||||
*e++ = 'r';
|
||||
if(sts&PSslow)
|
||||
*e++ = 'l';
|
||||
if(sts&PShigh)
|
||||
*e++ = 'h';
|
||||
if(sts&PSchange)
|
||||
*e++ = 'c';
|
||||
if(sts&PSenable)
|
||||
*e++ = 'e';
|
||||
if(sts&PSstatuschg)
|
||||
*e++ = 's';
|
||||
if(sts&PSpresent)
|
||||
*e++ = 'p';
|
||||
if(e == s)
|
||||
*e++ = '-';
|
||||
*e = 0;
|
||||
return s;
|
||||
}
|
||||
|
||||
static int
|
||||
getmaxpkt(Dev *d, int islow)
|
||||
{
|
||||
uchar buf[64]; /* More room to try to get device-specific descriptors */
|
||||
DDev *dd;
|
||||
|
||||
dd = (DDev*)buf;
|
||||
if(islow)
|
||||
dd->bMaxPacketSize0 = 8;
|
||||
else
|
||||
dd->bMaxPacketSize0 = 64;
|
||||
if(usbcmd(d, Rd2h|Rstd|Rdev, Rgetdesc, Ddev<<8|0, 0, buf, sizeof(buf)) < 0)
|
||||
return -1;
|
||||
return dd->bMaxPacketSize0;
|
||||
}
|
||||
|
||||
/*
|
||||
* BUG: does not consider max. power avail.
|
||||
*/
|
||||
static Dev*
|
||||
portattach(Hub *h, int p, int sts)
|
||||
{
|
||||
Dev *d;
|
||||
Port *pp;
|
||||
Dev *nd;
|
||||
char fname[80];
|
||||
char buf[40];
|
||||
char *sp;
|
||||
int mp;
|
||||
int nr;
|
||||
|
||||
d = h->dev;
|
||||
pp = &h->port[p];
|
||||
nd = nil;
|
||||
pp->state = Pattached;
|
||||
dprint(2, "%s: %s: port %d attach sts %#ux\n", argv0, d->dir, p, sts);
|
||||
sleep(Connectdelay);
|
||||
if(hubfeature(h, p, Fportenable, 1) < 0)
|
||||
dprint(2, "%s: %s: port %d: enable: %r\n", argv0, d->dir, p);
|
||||
sleep(Enabledelay);
|
||||
if(hubfeature(h, p, Fportreset, 1) < 0){
|
||||
dprint(2, "%s: %s: port %d: reset: %r\n", argv0, d->dir, p);
|
||||
goto Fail;
|
||||
}
|
||||
sleep(Resetdelay);
|
||||
sts = portstatus(h, p);
|
||||
if(sts < 0)
|
||||
goto Fail;
|
||||
if((sts & PSenable) == 0){
|
||||
dprint(2, "%s: %s: port %d: not enabled?\n", argv0, d->dir, p);
|
||||
hubfeature(h, p, Fportenable, 1);
|
||||
sts = portstatus(h, p);
|
||||
if((sts & PSenable) == 0)
|
||||
goto Fail;
|
||||
}
|
||||
sp = "full";
|
||||
if(sts & PSslow)
|
||||
sp = "low";
|
||||
if(sts & PShigh)
|
||||
sp = "high";
|
||||
dprint(2, "%s: %s: port %d: attached status %#ux\n", argv0, d->dir, p, sts);
|
||||
|
||||
if(devctl(d, "newdev %s %d", sp, p) < 0){
|
||||
fprint(2, "%s: %s: port %d: newdev: %r\n", argv0, d->dir, p);
|
||||
goto Fail;
|
||||
}
|
||||
seek(d->cfd, 0, 0);
|
||||
nr = read(d->cfd, buf, sizeof(buf)-1);
|
||||
if(nr == 0){
|
||||
fprint(2, "%s: %s: port %d: newdev: eof\n", argv0, d->dir, p);
|
||||
goto Fail;
|
||||
}
|
||||
if(nr < 0){
|
||||
fprint(2, "%s: %s: port %d: newdev: %r\n", argv0, d->dir, p);
|
||||
goto Fail;
|
||||
}
|
||||
buf[nr] = 0;
|
||||
snprint(fname, sizeof(fname), "/dev/usb/%s", buf);
|
||||
nd = opendev(fname);
|
||||
if(nd == nil){
|
||||
fprint(2, "%s: %s: port %d: opendev: %r\n", argv0, d->dir, p);
|
||||
goto Fail;
|
||||
}
|
||||
if(usbdebug > 2)
|
||||
devctl(nd, "debug 1");
|
||||
if(opendevdata(nd, ORDWR) < 0){
|
||||
fprint(2, "%s: %s: opendevdata: %r\n", argv0, nd->dir);
|
||||
goto Fail;
|
||||
}
|
||||
if(usbcmd(nd, Rh2d|Rstd|Rdev, Rsetaddress, nd->id, 0, nil, 0) < 0){
|
||||
dprint(2, "%s: %s: port %d: setaddress: %r\n", argv0, d->dir, p);
|
||||
goto Fail;
|
||||
}
|
||||
if(devctl(nd, "address") < 0){
|
||||
dprint(2, "%s: %s: port %d: set address: %r\n", argv0, d->dir, p);
|
||||
goto Fail;
|
||||
}
|
||||
|
||||
mp=getmaxpkt(nd, strcmp(sp, "low") == 0);
|
||||
if(mp < 0){
|
||||
dprint(2, "%s: %s: port %d: getmaxpkt: %r\n", argv0, d->dir, p);
|
||||
goto Fail;
|
||||
}else{
|
||||
dprint(2, "%s; %s: port %d: maxpkt %d\n", argv0, d->dir, p, mp);
|
||||
devctl(nd, "maxpkt %d", mp);
|
||||
}
|
||||
if((sts & PSslow) != 0 && strcmp(sp, "full") == 0)
|
||||
dprint(2, "%s: %s: port %d: %s is full speed when port is low\n",
|
||||
argv0, d->dir, p, nd->dir);
|
||||
if(configdev(nd) < 0){
|
||||
dprint(2, "%s: %s: port %d: configdev: %r\n", argv0, d->dir, p);
|
||||
goto Fail;
|
||||
}
|
||||
/*
|
||||
* We always set conf #1. BUG.
|
||||
*/
|
||||
if(usbcmd(nd, Rh2d|Rstd|Rdev, Rsetconf, 1, 0, nil, 0) < 0){
|
||||
dprint(2, "%s: %s: port %d: setconf: %r\n", argv0, d->dir, p);
|
||||
unstall(nd, nd, Eout);
|
||||
if(usbcmd(nd, Rh2d|Rstd|Rdev, Rsetconf, 1, 0, nil, 0) < 0)
|
||||
goto Fail;
|
||||
}
|
||||
dprint(2, "%s: %U", argv0, nd);
|
||||
pp->state = Pconfiged;
|
||||
dprint(2, "%s: %s: port %d: configed: %s\n",
|
||||
argv0, d->dir, p, nd->dir);
|
||||
return pp->dev = nd;
|
||||
Fail:
|
||||
pp->state = Pdisabled;
|
||||
pp->sts = 0;
|
||||
if(pp->hub != nil)
|
||||
pp->hub = nil; /* hub closed by enumhub */
|
||||
hubfeature(h, p, Fportenable, 0);
|
||||
if(nd != nil)
|
||||
devctl(nd, "detach");
|
||||
closedev(nd);
|
||||
return nil;
|
||||
}
|
||||
|
||||
static void
|
||||
portdetach(Hub *h, int p)
|
||||
{
|
||||
Dev *d;
|
||||
Port *pp;
|
||||
d = h->dev;
|
||||
pp = &h->port[p];
|
||||
|
||||
/*
|
||||
* Clear present, so that we detect an attach on reconnects.
|
||||
*/
|
||||
pp->sts &= ~(PSpresent|PSenable);
|
||||
|
||||
if(pp->state == Pdisabled)
|
||||
return;
|
||||
pp->state = Pdisabled;
|
||||
dprint(2, "%s: %s: port %d: detached\n", argv0, d->dir, p);
|
||||
|
||||
if(pp->hub != nil){
|
||||
closehub(pp->hub);
|
||||
pp->hub = nil;
|
||||
}
|
||||
if(pp->devmaskp != nil)
|
||||
putdevnb(pp->devmaskp, pp->devnb);
|
||||
pp->devmaskp = nil;
|
||||
if(pp->dev != nil){
|
||||
devctl(pp->dev, "detach");
|
||||
closedev(pp->dev);
|
||||
pp->dev = nil;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The next two functions are included to
|
||||
* perform a port reset asked for by someone (usually a driver).
|
||||
* This must be done while no other device is in using the
|
||||
* configuration address and with care to keep the old address.
|
||||
* To keep drivers decoupled from usbd they write the reset request
|
||||
* to the #u/usb/epN.0/ctl file and then exit.
|
||||
* This is unfortunate because usbd must now poll twice as much.
|
||||
*
|
||||
* An alternative to this reset process would be for the driver to detach
|
||||
* the device. The next function could see that, issue a port reset, and
|
||||
* then restart the driver once to see if it's a temporary error.
|
||||
*
|
||||
* The real fix would be to use interrupt endpoints for non-root hubs
|
||||
* (would probably make some hubs fail) and add an events file to
|
||||
* the kernel to report events to usbd. This is a severe change not
|
||||
* yet implemented.
|
||||
*/
|
||||
static int
|
||||
portresetwanted(Hub *h, int p)
|
||||
{
|
||||
char buf[5];
|
||||
Port *pp;
|
||||
Dev *nd;
|
||||
|
||||
pp = &h->port[p];
|
||||
nd = pp->dev;
|
||||
if(nd != nil && nd->cfd >= 0 && pread(nd->cfd, buf, 5, 0LL) == 5)
|
||||
return strncmp(buf, "reset", 5) == 0;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
portreset(Hub *h, int p)
|
||||
{
|
||||
int sts;
|
||||
Dev *d, *nd;
|
||||
Port *pp;
|
||||
|
||||
d = h->dev;
|
||||
pp = &h->port[p];
|
||||
nd = pp->dev;
|
||||
dprint(2, "%s: %s: port %d: resetting\n", argv0, d->dir, p);
|
||||
if(hubfeature(h, p, Fportreset, 1) < 0){
|
||||
dprint(2, "%s: %s: port %d: reset: %r\n", argv0, d->dir, p);
|
||||
goto Fail;
|
||||
}
|
||||
sleep(Resetdelay);
|
||||
sts = portstatus(h, p);
|
||||
if(sts < 0)
|
||||
goto Fail;
|
||||
if((sts & PSenable) == 0){
|
||||
dprint(2, "%s: %s: port %d: not enabled?\n", argv0, d->dir, p);
|
||||
hubfeature(h, p, Fportenable, 1);
|
||||
sts = portstatus(h, p);
|
||||
if((sts & PSenable) == 0)
|
||||
goto Fail;
|
||||
}
|
||||
nd = pp->dev;
|
||||
opendevdata(nd, ORDWR);
|
||||
if(usbcmd(nd, Rh2d|Rstd|Rdev, Rsetaddress, nd->id, 0, nil, 0) < 0){
|
||||
dprint(2, "%s: %s: port %d: setaddress: %r\n", argv0, d->dir, p);
|
||||
goto Fail;
|
||||
}
|
||||
if(devctl(nd, "address") < 0){
|
||||
dprint(2, "%s: %s: port %d: set address: %r\n", argv0, d->dir, p);
|
||||
goto Fail;
|
||||
}
|
||||
if(usbcmd(nd, Rh2d|Rstd|Rdev, Rsetconf, 1, 0, nil, 0) < 0){
|
||||
dprint(2, "%s: %s: port %d: setconf: %r\n", argv0, d->dir, p);
|
||||
unstall(nd, nd, Eout);
|
||||
if(usbcmd(nd, Rh2d|Rstd|Rdev, Rsetconf, 1, 0, nil, 0) < 0)
|
||||
goto Fail;
|
||||
}
|
||||
if(nd->dfd >= 0)
|
||||
close(nd->dfd);
|
||||
return;
|
||||
Fail:
|
||||
pp->state = Pdisabled;
|
||||
pp->sts = 0;
|
||||
if(pp->hub != nil)
|
||||
pp->hub = nil; /* hub closed by enumhub */
|
||||
hubfeature(h, p, Fportenable, 0);
|
||||
if(nd != nil)
|
||||
devctl(nd, "detach");
|
||||
closedev(nd);
|
||||
}
|
||||
|
||||
static int
|
||||
portgone(Port *pp, int sts)
|
||||
{
|
||||
if(sts < 0)
|
||||
return 1;
|
||||
/*
|
||||
* If it was enabled and it's not now then it may be reconnect.
|
||||
* We pretend it's gone and later we'll see it as attached.
|
||||
*/
|
||||
if((pp->sts & PSenable) != 0 && (sts & PSenable) == 0)
|
||||
return 1;
|
||||
return (pp->sts & PSpresent) != 0 && (sts & PSpresent) == 0;
|
||||
}
|
||||
|
||||
static int
|
||||
enumhub(Hub *h, int p)
|
||||
{
|
||||
int sts;
|
||||
Dev *d;
|
||||
Port *pp;
|
||||
int onhubs;
|
||||
|
||||
if(h->failed)
|
||||
return 0;
|
||||
d = h->dev;
|
||||
if(usbdebug > 3)
|
||||
fprint(2, "%s: %s: port %d enumhub\n", argv0, d->dir, p);
|
||||
|
||||
sts = portstatus(h, p);
|
||||
if(sts < 0){
|
||||
hubfail(h); /* avoid delays on detachment */
|
||||
return -1;
|
||||
}
|
||||
pp = &h->port[p];
|
||||
onhubs = nhubs;
|
||||
if((sts & PSsuspend) != 0){
|
||||
if(hubfeature(h, p, Fportenable, 1) < 0)
|
||||
dprint(2, "%s: %s: port %d: enable: %r\n", argv0, d->dir, p);
|
||||
sleep(Enabledelay);
|
||||
sts = portstatus(h, p);
|
||||
fprint(2, "%s: %s: port %d: resumed (sts %#ux)\n", argv0, d->dir, p, sts);
|
||||
}
|
||||
if((pp->sts & PSpresent) == 0 && (sts & PSpresent) != 0){
|
||||
if(portattach(h, p, sts) != nil)
|
||||
if(startdev(pp) < 0)
|
||||
portdetach(h, p);
|
||||
}else if(portgone(pp, sts))
|
||||
portdetach(h, p);
|
||||
else if(portresetwanted(h, p))
|
||||
portreset(h, p);
|
||||
else if(pp->sts != sts){
|
||||
dprint(2, "%s: %s port %d: sts %s %#x ->",
|
||||
argv0, d->dir, p, stsstr(pp->sts), pp->sts);
|
||||
dprint(2, " %s %#x\n",stsstr(sts), sts);
|
||||
}
|
||||
pp->sts = sts;
|
||||
if(onhubs != nhubs)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
dump(void)
|
||||
{
|
||||
Hub *h;
|
||||
int i;
|
||||
|
||||
mustdump = 0;
|
||||
for(h = hubs; h != nil; h = h->next)
|
||||
for(i = 1; i <= h->nport; i++)
|
||||
fprint(2, "%s: hub %#p %s port %d: %U",
|
||||
argv0, h, h->dev->dir, i, h->port[i].dev);
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
work(void)
|
||||
{
|
||||
char *fn;
|
||||
Hub *h;
|
||||
int i;
|
||||
|
||||
hubs = nil;
|
||||
while((fn = rendezvous(work, nil)) != nil){
|
||||
dprint(2, "%s: %s starting\n", argv0, fn);
|
||||
h = newhub(fn, nil);
|
||||
if(h == nil)
|
||||
fprint(2, "%s: %s: newhub failed: %r\n", argv0, fn);
|
||||
free(fn);
|
||||
}
|
||||
/*
|
||||
* Enumerate (and acknowledge after first enumeration).
|
||||
* Do NOT perform enumeration concurrently for the same
|
||||
* controller. new devices attached respond to a default
|
||||
* address (0) after reset, thus enumeration has to work
|
||||
* one device at a time at least before addresses have been
|
||||
* assigned.
|
||||
* Do not use hub interrupt endpoint because we
|
||||
* have to poll the root hub(s) in any case.
|
||||
*/
|
||||
for(;;){
|
||||
Again:
|
||||
for(h = hubs; h != nil; h = h->next)
|
||||
for(i = 1; i <= h->nport; i++)
|
||||
if(enumhub(h, i) < 0){
|
||||
/* changes in hub list; repeat */
|
||||
goto Again;
|
||||
}
|
||||
sleep(pollms);
|
||||
if(mustdump)
|
||||
dump();
|
||||
}
|
||||
}
|
|
@ -3,6 +3,7 @@
|
|||
OFILES=\
|
||||
usbd.$O\
|
||||
rules.$O\
|
||||
hub.$O\
|
||||
|
||||
HFILES=\
|
||||
dat.h\
|
||||
|
@ -10,4 +11,6 @@ HFILES=\
|
|||
|
||||
TARG=usbd
|
||||
BIN=/$objtype/bin/nusb
|
||||
LIB=../lib/usb.a$O
|
||||
</sys/src/cmd/mkone
|
||||
CFLAGS=-I../lib $CFLAGS
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include <libc.h>
|
||||
#include <thread.h>
|
||||
#include <ctype.h>
|
||||
#include "usb.h"
|
||||
#include "dat.h"
|
||||
#include "fns.h"
|
||||
|
||||
|
@ -63,7 +64,7 @@ parsesh(int *argc, char ***argv)
|
|||
}
|
||||
}
|
||||
|
||||
static Dev dummy;
|
||||
static Usbdev dummy;
|
||||
|
||||
struct field {
|
||||
char *s;
|
||||
|
@ -72,6 +73,7 @@ struct field {
|
|||
"class", &dummy.class,
|
||||
"vid", &dummy.vid,
|
||||
"did", &dummy.did,
|
||||
"csp", &dummy.csp,
|
||||
nil, nil,
|
||||
};
|
||||
|
||||
|
@ -218,7 +220,7 @@ parserules(char *s)
|
|||
}
|
||||
|
||||
Rule *
|
||||
rulesmatch(Dev *dev)
|
||||
rulesmatch(Usbdev *dev)
|
||||
{
|
||||
Rule *r;
|
||||
Cond *c;
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include <thread.h>
|
||||
#include <fcall.h>
|
||||
#include <9p.h>
|
||||
#include "usb.h"
|
||||
#include "dat.h"
|
||||
#include "fns.h"
|
||||
|
||||
|
@ -56,12 +57,73 @@ readrules(void)
|
|||
close(fd);
|
||||
}
|
||||
|
||||
void
|
||||
main()
|
||||
int
|
||||
startdev(Port *p)
|
||||
{
|
||||
Rule *r;
|
||||
char buf[14];
|
||||
|
||||
if(p->dev == nil || p->dev->usb == nil){
|
||||
fprint(2, "okay what?\n");
|
||||
return -1;
|
||||
}
|
||||
rlock(&rulelock);
|
||||
r = rulesmatch(p->dev->usb);
|
||||
if(r == nil || r->argv == nil){
|
||||
fprint(2, "no driver for device\n");
|
||||
runlock(&rulelock);
|
||||
return -1;
|
||||
}
|
||||
snprint(buf, sizeof buf, "%d", p->dev->id);
|
||||
r->argv[r->argc] = buf;
|
||||
closedev(p->dev);
|
||||
switch(fork()){
|
||||
case -1:
|
||||
fprint(2, "fork: %r");
|
||||
runlock(&rulelock);
|
||||
return -1;
|
||||
case 0:
|
||||
chdir("/bin");
|
||||
exec(r->argv[0], r->argv);
|
||||
sysfatal("exec: %r");
|
||||
}
|
||||
runlock(&rulelock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
int fd, i, nd;
|
||||
Dir *d;
|
||||
|
||||
readrules();
|
||||
parserules(rules);
|
||||
luser = getuser();
|
||||
|
||||
argc--; argv++;
|
||||
|
||||
rfork(RFNOTEG);
|
||||
switch(rfork(RFPROC|RFMEM)){
|
||||
case -1: sysfatal("rfork: %r");
|
||||
case 0: work(); exits(nil);
|
||||
}
|
||||
if(argc == 0){
|
||||
fd = open("/dev/usb", OREAD);
|
||||
if(fd < 0)
|
||||
sysfatal("/dev/usb: %r");
|
||||
nd = dirreadall(fd, &d);
|
||||
close(fd);
|
||||
if(nd < 2)
|
||||
sysfatal("/dev/usb: no hubs");
|
||||
for(i = 0; i < nd; i++)
|
||||
if(strcmp(d[i].name, "ctl") != 0)
|
||||
rendezvous(work, smprint("/dev/usb/%s", d[i].name));
|
||||
free(d);
|
||||
}else
|
||||
for(i = 0; i < argc; i++)
|
||||
rendezvous(work, strdup(argv[i]));
|
||||
rendezvous(work, nil);
|
||||
usbdsrv.tree = alloctree(luser, luser, 0555, nil);
|
||||
usbdb = createfile(usbdsrv.tree->root, "usbdb", luser, 0775, nil);
|
||||
postsharesrv(&usbdsrv, nil, "usb", "usbd", "b");
|
||||
|
|
Loading…
Reference in a new issue