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
|
nusb/disk
|
||||||
class=8
|
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
|
</$objtype/mkfile
|
||||||
|
|
||||||
# order matters here. build lib first and usbd last.
|
|
||||||
DIRS=\
|
DIRS=\
|
||||||
|
lib\
|
||||||
|
kb\
|
||||||
usbd\
|
usbd\
|
||||||
|
|
||||||
UPDATE=\
|
UPDATE=\
|
||||||
|
@ -21,7 +22,6 @@ install installall safeinstall safeinstallall:V:
|
||||||
for (i in $DIRS) @{
|
for (i in $DIRS) @{
|
||||||
cd $i && mk $target
|
cd $i && mk $target
|
||||||
}
|
}
|
||||||
cp probe /$objtype/bin/usb/probe
|
|
||||||
|
|
||||||
update:V:
|
update:V:
|
||||||
update $UPDATEFLAGS $UPDATE
|
update $UPDATEFLAGS $UPDATE
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
typedef struct Rule Rule;
|
typedef struct Rule Rule;
|
||||||
typedef struct Cond Cond;
|
typedef struct Cond Cond;
|
||||||
typedef struct Dev Dev;
|
typedef struct Hub Hub;
|
||||||
|
typedef struct DHub DHub;
|
||||||
|
typedef struct Port Port;
|
||||||
|
|
||||||
struct Rule {
|
struct Rule {
|
||||||
char **argv;
|
char **argv;
|
||||||
|
@ -17,6 +19,110 @@ struct Cond {
|
||||||
Cond *and, *or;
|
Cond *and, *or;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Dev {
|
enum
|
||||||
u32int class, vid, did;
|
{
|
||||||
|
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*);
|
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=\
|
OFILES=\
|
||||||
usbd.$O\
|
usbd.$O\
|
||||||
rules.$O\
|
rules.$O\
|
||||||
|
hub.$O\
|
||||||
|
|
||||||
HFILES=\
|
HFILES=\
|
||||||
dat.h\
|
dat.h\
|
||||||
|
@ -10,4 +11,6 @@ HFILES=\
|
||||||
|
|
||||||
TARG=usbd
|
TARG=usbd
|
||||||
BIN=/$objtype/bin/nusb
|
BIN=/$objtype/bin/nusb
|
||||||
|
LIB=../lib/usb.a$O
|
||||||
</sys/src/cmd/mkone
|
</sys/src/cmd/mkone
|
||||||
|
CFLAGS=-I../lib $CFLAGS
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#include <libc.h>
|
#include <libc.h>
|
||||||
#include <thread.h>
|
#include <thread.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
#include "usb.h"
|
||||||
#include "dat.h"
|
#include "dat.h"
|
||||||
#include "fns.h"
|
#include "fns.h"
|
||||||
|
|
||||||
|
@ -63,7 +64,7 @@ parsesh(int *argc, char ***argv)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static Dev dummy;
|
static Usbdev dummy;
|
||||||
|
|
||||||
struct field {
|
struct field {
|
||||||
char *s;
|
char *s;
|
||||||
|
@ -72,6 +73,7 @@ struct field {
|
||||||
"class", &dummy.class,
|
"class", &dummy.class,
|
||||||
"vid", &dummy.vid,
|
"vid", &dummy.vid,
|
||||||
"did", &dummy.did,
|
"did", &dummy.did,
|
||||||
|
"csp", &dummy.csp,
|
||||||
nil, nil,
|
nil, nil,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -218,7 +220,7 @@ parserules(char *s)
|
||||||
}
|
}
|
||||||
|
|
||||||
Rule *
|
Rule *
|
||||||
rulesmatch(Dev *dev)
|
rulesmatch(Usbdev *dev)
|
||||||
{
|
{
|
||||||
Rule *r;
|
Rule *r;
|
||||||
Cond *c;
|
Cond *c;
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include <thread.h>
|
#include <thread.h>
|
||||||
#include <fcall.h>
|
#include <fcall.h>
|
||||||
#include <9p.h>
|
#include <9p.h>
|
||||||
|
#include "usb.h"
|
||||||
#include "dat.h"
|
#include "dat.h"
|
||||||
#include "fns.h"
|
#include "fns.h"
|
||||||
|
|
||||||
|
@ -56,12 +57,73 @@ readrules(void)
|
||||||
close(fd);
|
close(fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
int
|
||||||
main()
|
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();
|
readrules();
|
||||||
parserules(rules);
|
parserules(rules);
|
||||||
luser = getuser();
|
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);
|
usbdsrv.tree = alloctree(luser, luser, 0555, nil);
|
||||||
usbdb = createfile(usbdsrv.tree->root, "usbdb", luser, 0775, nil);
|
usbdb = createfile(usbdsrv.tree->root, "usbdb", luser, 0775, nil);
|
||||||
postsharesrv(&usbdsrv, nil, "usb", "usbd", "b");
|
postsharesrv(&usbdsrv, nil, "usb", "usbd", "b");
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue