## diffname pc/devusb.c 1999/1005
## diff -e /dev/null /n/emeliedump/1999/1005/sys/src/brazil/pc/devusb.c
0a
/*
* UHCI USB driver
* (c) 1998, 1999 C H Forsyth, [email protected]
* to do:
* endpoint open/close
* build Endpt on open from attributes stored in Udev?
* build data0/data1 rings for bulk and interrupt endpoints
* endpoint TD rings (can there be prefetch?)
* hubs?
* special handling of isochronous traffic?
* is use of Queues justified? (could have client clean TD rings on wakeup)
* bandwidth check
*/
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"
#include "../port/error.h"
/*
#ifndef Inferno
#include "devtab.h"
#include "brazilia.h"
#endif
*/
#define Pprint
#define Chatty 1
#define DPRINT if(Chatty)print
#define XPRINT if(0)print
/*
* USB packet definitions
*/
enum {
TokIN = 0x69,
TokOUT = 0xE1,
TokSETUP = 0x2D,
/* request type */
RH2D = 0<<7,
RD2H = 1<<7,
Rstandard = 0<<5,
Rclass = 1<<5,
Rvendor = 2<<5,
Rdevice = 0,
Rinterface = 1,
Rendpt = 2,
Rother = 3,
};
typedef uchar byte;
typedef struct Ctlr Ctlr;
typedef struct Endpt Endpt;
typedef struct QTree QTree;
typedef struct Udev Udev;
/*
* UHCI hardware structures, aligned on 16-byte boundary
*/
typedef struct QH QH;
typedef struct TD TD;
struct TD {
ulong link;
ulong status; /* controller r/w */
ulong dev;
ulong buffer;
/* software */
ulong flags;
Block* bp;
Endpt* ep;
TD* next;
};
#define TFOL(p) ((TD*)KADDR((ulong)(p) & ~0xF))
struct QH {
ulong head;
ulong entries; /* address of next TD or QH to process (updated by controller) */
/* software */
union {
TD* first;
QH* next; /* free list */
};
TD* last;
};
#define QFOL(p) ((QH*)KADDR((ulong)(p) & ~0xF))
/*
* UHCI interface registers and bits
*/
enum {
/* i/o space */
Cmd = 0,
Status = 2,
Usbintr = 4,
Frnum = 6,
Flbaseadd = 8,
SOFMod = 0xC,
Portsc0 = 0x10,
Portsc1 = 0x12,
/* port status */
Suspend = 1<<12,
PortReset = 1<<9,
SlowDevice = 1<<8,
ResumeDetect = 1<<6,
PortChange = 1<<3, /* write 1 to clear */
PortEnable = 1<<2,
StatusChange = 1<<1, /* write 1 to clear */
DevicePresent = 1<<0,
FRAMESIZE= 4096, /* fixed by hardware; aligned to same */
NFRAME = FRAMESIZE/4,
Vf = 1<<2, /* TD only */
IsQH = 1<<1,
Terminate = 1<<0,
/* TD.status */
SPD = 1<<29,
ErrLimit0 = 0<<27,
ErrLimit1 = 1<<27,
ErrLimit2 = 2<<27,
ErrLimit3 = 3<<27,
LowSpeed = 1<<26,
IsoSelect = 1<<25,
IOC = 1<<24,
Active = 1<<23,
Stalled = 1<<22,
DataBufferErr = 1<<21,
Babbling = 1<<20,
NAKed = 1<<19,
CRCorTimeout = 1<<18,
BitstuffErr = 1<<17,
AnyError = (Stalled | DataBufferErr | Babbling | NAKed | CRCorTimeout | BitstuffErr),
/* TD.dev */
IsDATA1 = 1<<19,
/* TD.flags (software) */
CancelTD= 1<<0,
};
/*
* software structures
*/
struct QTree {
QLock;
int nel;
int depth;
QH* root;
ulong* bw;
};
#define GET2(p) ((((p)[1]&0xFF)<<8)|((p)[0]&0xFF))
#define PUT2(p,v) (((p)[0] = (v)), ((p)[1] = (v)>>8))
/*
* active USB device
*/
struct Udev {
Ref;
Lock;
int x; /* index in usbdev[] */
int busy;
int state;
int id;
byte port; /* port number on connecting hub */
byte class;
byte subclass;
byte proto;
int ls;
int npt;
Endpt* ep[16]; /* active end points */
Udev* ports; /* active ports, if hub */
Udev* next; /* next device on this hub */
};
/* device parameters */
enum {
/* Udev.state */
Disabled = 0,
Attached,
Enabled,
Assigned,
Configured,
/* Udev.class */
Noclass = 0,
Hubclass = 9,
};
static char *devstates[] = {
[Disabled] "Disabled",
[Attached] "Attached",
[Enabled] "Enabled",
[Assigned] "Assigned",
[Configured] "Configured",
};
/*
* device endpoint
*/
struct Endpt {
Ref;
Lock;
int x; /* index in Udev.ep */
int id; /* hardware endpoint address */
int maxpkt; /* maximum packet size (from endpoint descriptor) */
int data01; /* 0=DATA0, 1=DATA1 */
byte eof;
byte class;
byte subclass;
byte proto;
byte mode; /* OREAD, OWRITE, ORDWR */
byte nbuf; /* number of buffers allowed */
byte periodic;
byte iso;
byte debug;
byte active; /* listed for examination by interrupts */
int sched; /* schedule index; -1 if undefined or aperiodic */
int setin;
ulong bw; /* bandwidth requirement */
int pollms; /* polling interval in msec */
QH* epq; /* queue of TDs for this endpoint */
QLock rlock;
Rendez rr;
Queue* rq;
QLock wlock;
Rendez wr;
Queue* wq;
int ntd;
char* err;
Udev* dev; /* owning device */
Endpt* activef; /* active endpoint list */
ulong nbytes;
ulong nblocks;
};
struct Ctlr {
Lock; /* protects state shared with interrupt (eg, free list) */
int io;
ulong* frames; /* frame list */
int idgen; /* version number to distinguish new connections */
QLock resetl; /* lock controller during USB reset */
TD* tdpool;
TD* freetd;
QH* qhpool;
QH* freeqh;
QTree* tree; /* tree for periodic Endpt i/o */
QH* ctlq; /* queue for control i/o */
QH* bwsop; /* empty bandwidth sop (to PIIX4 errata specifications) */
QH* bulkq; /* queue for bulk i/o (points back to bandwidth sop) */
Udev* ports[2];
};
#define IN(x) ins(ub->io+(x))
#define OUT(x, v) outs(ub->io+(x), (v))
static Ctlr ubus;
static char Estalled[] = "usb endpoint stalled";
static QLock usbstate; /* protects name space state */
static Udev* usbdev[32];
static struct {
Lock;
Endpt* f;
} activends;
static long readusb(Endpt*, void*, long);
static long writeusb(Endpt*, void*, long, int);
static TD *
alloctd(Ctlr *ub)
{
TD *t;
ilock(ub);
t = ub->freetd;
if(t == nil)
panic("alloctd"); /* TO DO */
ub->freetd = t->next;
t->next = nil;
iunlock(ub);
t->ep = nil;
t->bp = nil;
t->status = 0;
t->link = Terminate;
t->buffer = 0;
t->flags = 0;
return t;
}
static void
freetd(TD *t)
{
Ctlr *ub;
ub = &ubus;
t->ep = nil;
if(t->bp)
freeb(t->bp);
t->bp = nil;
ilock(ub);
t->buffer = 0xdeadbeef;
t->next = ub->freetd;
ub->freetd = t;
iunlock(ub);
}
static void
dumpdata(Block *b, int n)
{
int i;
XPRINT("\tb %8.8lux[%d]: ", (ulong)b->rp, n);
if(n > 16)
n = 16;
for(i=0; i<n; i++)
XPRINT(" %2.2ux", b->rp[i]);
XPRINT("\n");
}
static void
dumptd(TD *t, int follow)
{
int i, n;
char buf[20], *s;
TD *t0;
t0 = t;
while(t){
i = t->dev & 0xFF;
if(i == TokOUT || i == TokSETUP)
n = ((t->dev>>21) + 1) & 0x7FF;
else if((t->status & Active) == 0)
n = (t->status + 1) & 0x7FF;
else
n = 0;
s = buf;
if(t->status & Active)
*s++ = 'A';
if(t->status & Stalled)
*s++ = 'S';
if(t->status & DataBufferErr)
*s++ = 'D';
if(t->status & Babbling)
*s++ = 'B';
if(t->status & NAKed)
*s++ = 'N';
if(t->status & CRCorTimeout)
*s++ = 'T';
if(t->status & BitstuffErr)
*s++ = 'b';
if(t->status & LowSpeed)
*s++ = 'L';
*s = 0;
print("td %8.8lux: l=%8.8lux s=%8.8lux d=%8.8lux b=%8.8lux %8.8lux f=%8.8lux\n",
t, t->link, t->status, t->dev, t->buffer, t->bp?(ulong)t->bp->rp:0, t->flags);
print("\ts=%s,ep=%ld,d=%ld,D=%ld\n", buf, (t->dev>>15)&0xF, (t->dev>>8)&0xFF, (t->dev>>19)&1);
if(t->bp)
dumpdata(t->bp, n);
if(!follow || t->link & Terminate || t->link & IsQH)
break;
t = TFOL(t->link);
if(t == t0)
break; /* looped */
}
}
static TD *
alloctde(Endpt *e, int pid, int n)
{
TD *t;
int tog, id;
t = alloctd(&ubus);
id = (e->x<<7)|(e->dev->x&0x7F);
tog = 0;
if(e->data01 && pid != TokSETUP)
tog = IsDATA1;
t->ep = e;
t->status = ErrLimit3 | Active | IOC; /* or put IOC only on last? */
if(e->dev->ls)
t->status |= LowSpeed;
t->dev = ((n-1)<<21) | ((id&0x7FF)<<8) | pid | tog;
return t;
}
static TD *
alloctdring(int nbuf, Endpt *e, int pid, int maxn)
{
TD *t, *lt, *ft;
int id, i;
ulong status, dev;
if(!e->iso)
nbuf = (nbuf+1)&~1;
id = (e->x<<7)|(e->dev->x&0x7F);
dev = ((maxn-1)<<21) | ((id&0x7FF)<<8) | pid;
status = 0;
if(e->dev->ls)
status |= LowSpeed;
ft = lt = nil;
for(i=0; i<nbuf; i++){
t = alloctd(&ubus);
t->ep = e;
t->status = status;
t->dev = dev;
if(!e->iso)
dev ^= IsDATA1;
if(pid == TokIN){
t->bp = allocb(maxn);
t->buffer = PADDR(t->bp->wp);
}
if(ft != nil){
lt->next = t;
lt->link = PADDR(t);
}else
ft = t;
lt = t;
}
if(lt != nil)
lt->link = PADDR(ft); /* loop to form ring */
return ft;
}
static QH *
allocqh(Ctlr *ub)
{
QH *qh;
ilock(ub);
qh = ub->freeqh;
if(qh == nil)
panic("allocqh"); /* TO DO */
ub->freeqh = qh->next;
qh->next = nil;
iunlock(ub);
qh->head = Terminate;
qh->entries = Terminate;
qh->first = nil;
qh->last = nil;
return qh;
}
static void
freeqh(Ctlr *ub, QH *qh)
{
ilock(ub);
qh->next = ub->freeqh;
ub->freeqh = qh;
iunlock(ub);
}
static void
dumpqh(QH *q)
{
int i;
QH *q0;
q0 = q;
for(i = 0; q != nil && i < 10; i++){
pprint("qh %8.8lux: %8.8lux %8.8lux\n", q, q->head, q->entries);
if((q->entries & Terminate) == 0)
dumptd(TFOL(q->entries), 1);
if(q->head & Terminate)
break;
if((q->head & IsQH) == 0){
pprint("head:");
dumptd(TFOL(q->head), 1);
break;
}
q = QFOL(q->head);
if(q == q0)
break; /* looped */
}
}
static void
queuetd(Ctlr *ub, QH *q, TD *t, int vf)
{
TD *lt;
for(lt = t; lt->next != nil; lt = lt->next)
lt->link = PADDR(lt->next) | vf;
lt->link = Terminate;
ilock(ub);
if(q->first != nil){
q->last->link = PADDR(t) | vf;
q->last->next = t;
}else{
q->first = t;
q->entries = PADDR(t);
}
q->last = lt;
iunlock(ub);
}
static void
cleantd(TD *t, int discard)
{
Block *b;
int n, err;
XPRINT("cleanTD: %8.8lux %8.8lux %8.8lux %8.8lux\n", t->link, t->status, t->dev, t->buffer);
if(t->ep != nil && t->ep->debug)
dumptd(t, 0);
if(t->status & Active)
panic("cleantd Active");
err = t->status & (AnyError&~NAKed);
/* TO DO: on t->status&AnyError, q->entries will not have advanced */
if (err)
print("cleanTD: Error %8.8lux %8.8lux %8.8lux %8.8lux\n", t->link, t->status, t->dev, t->buffer);
switch(t->dev&0xFF){
case TokIN:
if(discard || (t->flags & CancelTD) || t->ep == nil || t->ep->x!=0&&err){
if(t->ep != nil){
if(err != 0)
t->ep->err = err==Stalled? Estalled: Eio;
wakeup(&t->ep->rr); /* in case anyone cares */
}
break;
}
b = t->bp;
n = (t->status + 1) & 0x7FF;
if(n > b->lim - b->wp)
n = 0;
b->wp += n;
if(Chatty)
dumpdata(b, n);
t->bp = nil;
t->ep->nbytes += n;
t->ep->nblocks++;
qpass(t->ep->rq, b); /* TO DO: flow control */
wakeup(&t->ep->rr); /* TO DO */
break;
case TokSETUP:
XPRINT("cleanTD: TokSETUP %lux\n", &t->ep);
/* don't really need to wakeup: subsequent IN or OUT gives status */
if(t->ep != nil) {
wakeup(&t->ep->wr); /* TO DO */
XPRINT("cleanTD: wakeup %lux\n", &t->ep->wr);
}
break;
case TokOUT:
/* TO DO: mark it done somewhere */
XPRINT("cleanTD: TokOut %lux\n", &t->ep);
if(t->ep != nil){
if(t->bp){
n = BLEN(t->bp);
t->ep->nbytes += n;
t->ep->nblocks++;
}
if(t->ep->x!=0 && err != 0)
t->ep->err = err==Stalled? Estalled: Eio;
if(--t->ep->ntd < 0)
panic("cleantd ntd");
wakeup(&t->ep->wr); /* TO DO */
XPRINT("cleanTD: wakeup %lux\n", &t->ep->wr);
}
break;
}
freetd(t);
}
static void
cleanq(QH *q, int discard)
{
TD *t;
Ctlr *ub;
ub = &ubus;
ilock(ub);
for(t = q->first; t != nil;){
// XPRINT("cleanq: %8.8lux %8.8lux %8.8lux %8.8lux\n", t->link, t->status, t->dev, t->buffer);
if(t->status & Active){
if(t->flags & CancelTD){
XPRINT("cancelTD: %8.8lux\n", (ulong)t);
t->status = (t->status & ~Active) | IOC; /* ensure interrupt next frame */
t = t->next;
continue;
}
break;
}
t->status &= ~IOC;
/* TO DO: need to deal with TDs as chain if necessary */
q->first = t->next;
if(q->first != nil)
q->entries = PADDR(q->first);
else
q->entries = Terminate;
t->next = nil;
iunlock(ub);
cleantd(t, discard);
ilock(ub);
t = q->first;
}
iunlock(ub);
}
static void
canceltds(Ctlr *ub, QH *q, Endpt *e)
{
TD *t;
if(q != nil){
ilock(ub);
for(t = q->first; t != nil; t = t->next)
if(t->ep == e)
t->flags |= CancelTD;
iunlock(ub);
pprint("cancel:\n");
dumpqh(q);
}
}
static void
eptcancel(Endpt *e)
{
Ctlr *ub;
if(e == nil)
return;
ub = &ubus;
canceltds(ub, e->epq, e);
canceltds(ub, ub->ctlq, e);
canceltds(ub, ub->bulkq, e);
}
static void
eptactivate(Endpt *e)
{
ilock(&activends);
if(e->active == 0){
e->active = 1;
e->activef = activends.f;
activends.f = e;
}
iunlock(&activends);
}
static void
eptdeactivate(Endpt *e)
{
Endpt **l;
/* could be O(1) but not worth it yet */
ilock(&activends);
if(e->active){
e->active = 0;
for(l = &activends.f; *l != e; l = &(*l)->activef)
if(*l == nil){
iunlock(&activends);
panic("usb eptdeactivate");
}
*l = e->activef;
}
iunlock(&activends);
}
static QH*
qxmit(Endpt *e, Block *b, int pid)
{
TD *t;
int n, vf;
Ctlr *ub;
QH *qh;
if(b != nil){
n = BLEN(b);
t = alloctde(e, pid, n);
t->bp = b;
t->buffer = PADDR(b->rp);
}else
t = alloctde(e, pid, 0);
ub = &ubus;
ilock(ub);
e->ntd++;
iunlock(ub);
if(e->debug)pprint("QTD: %8.8lux n=%ld\n", t, b?BLEN(b): 0);
vf = 0;
if(e->x == 0){
qh = ub->ctlq;
vf = 0;
}else if((qh = e->epq) == nil || e->mode != OWRITE){
qh = ub->bulkq;
vf = Vf;
}
queuetd(ub, qh, t, vf);
return qh;
}
static QH*
qrcv(Endpt *e)
{
TD *t;
Block *b;
Ctlr *ub;
QH *qh;
int vf;
t = alloctde(e, TokIN, e->maxpkt);
b = allocb(e->maxpkt);
t->bp = b;
t->buffer = PADDR(b->wp);
ub = &ubus;
vf = 0;
if(e->x == 0){
qh = ub->ctlq;
vf = 0;
}else if((qh = e->epq) == nil || e->mode != OREAD){
qh = ub->bulkq;
vf = Vf;
}
queuetd(ub, qh, t, vf);
return qh;
}
static Block *
usbreq(int type, int req, int value, int offset, int count)
{
Block *b;
b = allocb(8);
b->wp[0] = type;
b->wp[1] = req;
PUT2(b->wp+2, value);
PUT2(b->wp+4, offset);
PUT2(b->wp+6, count);
b->wp += 8;
return b;
}
/*
* return smallest power of 2 >= n
*/
static int
flog2(int n)
{
int i;
for(i=0; (1<<i)<n; i++)
;
return i;
}
/*
* build the periodic scheduling tree:
* framesize must be a multiple of the tree size
*/
static QTree *
mkqhtree(ulong *frame, int framesize, int maxms)
{
int i, n, d, o, leaf0, depth;
QH *tree, *qh;
QTree *qt;
depth = flog2(maxms);
n = (1<<(depth+1))-1;
qt = mallocz(sizeof(*qt), 1);
if(qt == nil)
return nil;
qt->nel = n;
qt->depth = depth;
qt->bw = mallocz(n*sizeof(qt->bw), 1);
if(qt->bw == nil){
free(qt);
return nil;
}
tree = xspanalloc(n*sizeof(QH), 16, 0);
if(tree == nil){
free(qt);
return nil;
}
qt->root = tree;
tree->head = Terminate; /* root */
tree->entries = Terminate;
for(i=1; i<n; i++){
qh = &tree[i];
qh->head = PADDR(&tree[(i-1)/2]) | IsQH;
qh->entries = Terminate;
}
/* distribute leaves evenly round the frame list */
leaf0 = n/2;
for(i=0; i<framesize; i++){
o = 0;
for(d=0; d<depth; d++){
o <<= 1;
if(i & (1<<d))
o |= 1;
}
if(leaf0+o >= n){
pprint("leaf0=%d o=%d i=%d n=%d\n", leaf0, o, i, n);
break;
}
frame[i] = PADDR(&tree[leaf0+o]) | IsQH;
}
return qt;
}
static void
dumpframe(int f, int t)
{
QH *q, *tree;
ulong p, *frame;
int i, n;
n = ubus.tree->nel;
tree = ubus.tree->root;
frame = ubus.frames;
if(f < 0)
f = 0;
if(t < 0)
t = 32;
for(i=f; i<t; i++){
pprint("F%.2d %8.8lux %8.8lux\n", i, frame[i], QFOL(frame[i])->head);
for(p=frame[i]; (p & IsQH) && (p &Terminate) == 0; p = q->head){
q = QFOL(p);
if(!(q >= tree && q < &tree[n])){
pprint("Q: p=%8.8lux out of range\n", p);
break;
}
pprint(" -> %8.8lux h=%8.8lux e=%8.8lux\n", p, q->head, q->entries);
}
}
}
static int
pickschedq(QTree *qt, int pollms, ulong bw, ulong limit)
{
int i, j, d, ub, q;
ulong best, worst, total;
d = flog2(pollms);
if(d > qt->depth)
d = qt->depth;
q = -1;
worst = 0;
best = ~0;
ub = (1<<(d+1))-1;
for(i=(1<<d)-1; i<ub; i++){
total = qt->bw[0];
for(j=i; j > 0; j=(j-1)/2)
total += qt->bw[j];
if(total < best){
best = total;
q = i;
}
if(total > worst)
worst = total;
}
if(worst+bw >= limit)
return -1;
return q;
}
static int
schedendpt(Endpt *e)
{
Ctlr *ub;
QH *qh;
int q;
if(!e->periodic || e->sched >= 0)
return 0;
ub = &ubus;
if(e->epq == nil){
e->epq = allocqh(ub);
if(e->epq == nil)
return -1;
}
qlock(ub->tree);
q = pickschedq(ub->tree, e->pollms, e->bw, ~0); /* TO DO: bus bandwidth limit */
if(q < 0)
return -1;
ub->tree->bw[q] += e->bw;
qh = &ub->tree->root[q];
e->sched = q;
e->epq->head = qh->entries;
e->epq->entries = Terminate;
qh->entries = PADDR(e->epq) | IsQH;
qunlock(ub->tree);
return 0;
}
static void
unschedendpt(Endpt *e)
{
Ctlr *ub;
ulong p;
QH *qh;
int q;
ub = &ubus;
if(e->epq == nil || (q = e->sched) < 0)
return;
p = PADDR(e->epq) | IsQH;
qlock(ub->tree);
ub->tree->bw[q] -= e->bw;
qh = &ub->tree->root[q];
for(; qh->entries != p; qh = QFOL(qh->entries))
if(qh->entries & Terminate || (qh->entries & IsQH) == 0){
qunlock(ub->tree);
panic("usb: unschedendpt");
}
qh->entries = e->epq->head;
qunlock(ub->tree);
e->epq->head = Terminate;
}
static Endpt *
devendpt(Udev *d, int id, int add)
{
Endpt *e, **p;
p = &d->ep[id&0xF];
lock(d);
if((e = *p) != nil){
incref(e);
unlock(d);
return e;
}
unlock(d);
if(!add)
return nil;
e = mallocz(sizeof(*e), 1);
e->ref = 1;
e->x = id&0xF;
e->id = id;
e->periodic = 0;
e->sched = -1;
e->maxpkt = 8;
e->pollms = 0;
e->bw = 0;
e->nbuf = 1;
e->dev = d;
e->active = 0;
lock(d);
if(*p != nil){
incref(*p);
unlock(d);
free(e);
return *p;
}
*p = e;
unlock(d);
e->rq = qopen(8*1024, 0, nil, e);
e->wq = qopen(8*1024, 0, nil, e);
return e;
}
static void
freept(Endpt *e)
{
if(e != nil && decref(e) == 0){
XPRINT("freept(%d,%d)\n", e->dev->x, e->x);
unschedendpt(e);
e->dev->ep[e->x] = nil;
eptdeactivate(e);
if(e->epq != nil)
freeqh(&ubus, e->epq);
free(e);
}
}
static void
usbdevreset(Udev *d)
{
d->state = Disabled;
if(d->class == Hubclass)
for(d = d->ports; d != nil; d = d->next)
usbdevreset(d);
}
static void
freedev(Udev *d)
{
int i;
if(d != nil && decref(d) == 0){
for(i=0; i<nelem(d->ep); i++)
freept(d->ep[i]);
if(d->x >= 0)
usbdev[d->x] = nil;
free(d);
}
}
static void
hubportreset(Udev *h, int p)
{
USED(h, p);
/* reset state of each attached device? */
}
static int ioports[] = {-1, Portsc0, Portsc1};
static void
portreset(int port)
{
Ctlr *ub;
int i, p;
/* should check that device not being configured on other port? */
p = ioports[port];
ub = &ubus;
qlock(&ub->resetl);
if(waserror()){
qunlock(&ub->resetl);
nexterror();
}
XPRINT("r: %x\n", IN(p));
ilock(ub);
OUT(p, PortReset);
delay(12); /* BUG */
XPRINT("r2: %x\n", IN(p));
OUT(p, IN(p) & ~PortReset);
XPRINT("r3: %x\n", IN(p));
OUT(p, IN(p) | PortEnable);
microdelay(64);
for(i=0; i<1000 && (IN(p) & PortEnable) == 0; i++)
;
XPRINT("r': %x %d\n", IN(p), i);
OUT(p, (IN(p) & ~PortReset)|PortEnable);
iunlock(ub);
hubportreset(nil, port);
poperror();
qunlock(&ub->resetl);
}
static void
portenable(int port, int on)
{
Ctlr *ub;
int w, p;
/* should check that device not being configured on other port? */
p = ioports[port];
ub = &ubus;
qlock(&ub->resetl);
if(waserror()){
qunlock(&ub->resetl);
nexterror();
}
ilock(ub);
w = IN(p);
if(on)
w |= PortEnable;
else
w &= ~PortEnable;
OUT(p, w);
microdelay(64);
iunlock(ub);
XPRINT("e: %x\n", IN(p));
if(!on)
hubportreset(nil, port);
poperror();
qunlock(&ub->resetl);
}
static int
portinfo(Ctlr *ub, int *p0, int *p1)
{
int m, v;
ilock(ub);
m = 0;
if((v = IN(Portsc0)) & PortChange){
OUT(Portsc0, v);
m |= 1<<0;
}
*p0 = v;
if((v = IN(Portsc1)) & PortChange){
OUT(Portsc1, v);
m |= 1<<1;
}
*p1 = v;
iunlock(ub);
return m;
}
static void
interrupt(Ureg*, void *a)
{
Ctlr *ub;
Endpt *e;
int s;
ub = a;
s = IN(Status);
if (s & 0x1a) {
print("usbint: #%x f%d\n", s, IN(Frnum));
print("cmd #%x sofmod #%x\n", IN(Cmd), inb(ub->io+SOFMod));
print("sc0 #%x sc1 #%x\n", IN(Portsc0), IN(Portsc1));
}
OUT(Status, s);
// XPRINT("cleanq(ub->ctlq, 0)\n");
cleanq(ub->ctlq, 0);
// XPRINT("cleanq(ub->bulkq, 0)\n");
cleanq(ub->bulkq, 0);
ilock(&activends);
for(e = activends.f; e != nil; e = e->activef)
if(e->epq != nil) {
// XPRINT("cleanq(e->epq, 0)\n");
cleanq(e->epq, 0);
}
iunlock(&activends);
}
static void
resetusbctlr(void)
{
Ctlr *ub;
Pcidev *cfg;
int i;
ulong port;
QTree *qt;
TD *t;
ub = &ubus;
memset(&cfg, 0, sizeof(cfg));
cfg = pcimatch(0, 0x8086, 0x7112); /* Intel chipset PIIX 4*/
if(cfg == nil) {
// cfg = pcimatch(0, 0x8086, 0x7112); /* Intel chipset PIIX 3*/
// if(cfg == nil) {
cfg = pcimatch(0, 0x1106, 0x0586); /* Via chipset */
if(cfg == nil) {
DPRINT("No USB device found\n");
return;
}
// }
}
port = cfg->mem[4].bar & ~0x0F;
if (port == 0) {
print("usb: failed to map registers\n");
return;
}
print("USB: %x/%x port 0x%lux size 0x%x irq %d\n",
cfg->vid, cfg->did, port, cfg->mem[4].size, cfg->intl);
i = inb(port+SOFMod);
if(0){
OUT(Cmd, 4); /* global reset */
delay(15);
OUT(Cmd, 0); /* end reset */
delay(4);
}
outb(port+SOFMod, i);
// Interrupt handler
intrenable(cfg->intl, interrupt, ub, cfg->tbdf, "usb");
ub->io = port;
ub->tdpool = xspanalloc(128*sizeof(TD), 16, 0);
for(i=128; --i>=0;){
ub->tdpool[i].next = ub->freetd;
ub->freetd = &ub->tdpool[i];
}
ub->qhpool = xspanalloc(32*sizeof(QH), 16, 0);
for(i=32; --i>=0;){
ub->qhpool[i].next = ub->freeqh;
ub->freeqh = &ub->qhpool[i];
}
/*
* the root of the periodic (interrupt & isochronous) scheduling tree
* points to the control queue and the bandwidth sop for bulk traffic.
* this is looped following the instructions in PIIX4 errata 29773804.pdf:
* a QH links to a looped but inactive TD as its sole entry,
* with its head entry leading on to the bulk traffic, the last QH of which
* links back to the empty QH.
*/
ub->bulkq = allocqh(ub);
t = alloctd(ub); /* inactive TD, looped */
t->link = PADDR(t);
ub->bwsop = allocqh(ub);
ub->bwsop->head = PADDR(ub->bulkq) | IsQH;
ub->bwsop->entries = PADDR(t);
ub->ctlq = allocqh(ub);
ub->ctlq->head = PADDR(ub->bwsop) | IsQH;
// ub->bulkq->head = PADDR(ub->bwsop) | IsQH; /* loop back */
print("usbcmd\t0x%.4x\nusbsts\t0x%.4x\nusbintr\t0x%.4x\nfrnum\t0x%.2x\n",
IN(Cmd), IN(Status), IN(Usbintr), inb(port+Frnum));
print("frbaseadd\t0x%.4x\nsofmod\t0x%x\nportsc1\t0x%.4x\nportsc2\t0x%.4x\n",
IN(Flbaseadd), inb(port+SOFMod), IN(Portsc0), IN(Portsc1));
OUT(Cmd, 0); /* stop */
ub->frames = xspanalloc(FRAMESIZE, FRAMESIZE, 0);
qt = mkqhtree(ub->frames, NFRAME, 32);
if(qt == nil){
print("usb: can't allocate scheduling tree\n");
ub->io = 0;
return;
}
qt->root->head = PADDR(ub->ctlq) | IsQH;
ub->tree = qt;
print("usb tree: nel=%d depth=%d\n", qt->nel, qt->depth);
outl(port+Flbaseadd, PADDR(ub->frames));
OUT(Frnum, 0);
OUT(Usbintr, 0xF); /* enable all interrupts */
print("cmd 0x%x sofmod 0x%x\n", IN(Cmd), inb(port+SOFMod));
print("sc0 0x%x sc1 0x%x\n", IN(Portsc0), IN(Portsc1));
}
enum
{
Qtopdir = 0,
Q2nd,
Qbusctl,
Qnew,
Qport,
Q3rd,
Qctl,
Qsetup,
Qdebug,
Qstatus,
Qep0,
/* other endpoint files */
};
/*
* Qid path is:
* 8 bits of file type (qids above)
* 10 bits of slot number +1; 0 means not attached to device
*/
#define QSHIFT 8 /* location in qid of device # */
#define QMASK ((1<<QSHIFT)-1)
#define QID(q) ((q).path&QMASK)
#define DEVPATH(q) (((q)&~CHDIR)>>QSHIFT)
static Dirtab usbdir2[] = {
"new", {Qnew}, 0, 0666,
"ctl", {Qbusctl}, 0, 0666,
"port", {Qport}, 0, 0444,
};
static Dirtab usbdir3[]={
"ctl", {Qctl}, 0, 0666,
"setup", {Qsetup}, 0, 0666,
"status", {Qstatus}, 0, 0444,
"debug", {Qdebug}, 1, 0666,
/* epNdata names are generated on demand */
};
static Udev *
usbdeviceofpath(ulong path)
{
int s;
s = DEVPATH(path);
if(s == 0)
return nil;
return usbdev[s-1];
}
static Udev *
usbdevice(Chan *c)
{
Udev *d;
d = usbdeviceofpath(c->qid.path);
if(d == nil || d->id != c->qid.vers || d->state == Disabled)
error(Ehungup);
return d;
}
static Udev *
usbnewdevice(void)
{
Udev *d;
Endpt *e;
int i;
d = nil;
qlock(&usbstate);
if(waserror()){
qunlock(&usbstate);
nexterror();
}
for(i=0; i<nelem(usbdev); i++)
if(usbdev[i] == nil){
ubus.idgen++;
d = mallocz(sizeof(*d), 1);
d->ref = 1;
d->x = i;
d->id = (ubus.idgen << 8) | i;
d->state = Enabled;
e = devendpt(d, 0, 1); /* always provide control endpoint 0 */
e->mode = ORDWR;
e->periodic = 0;
e->sched = -1;
usbdev[i] = d;
break;
}
poperror();
qunlock(&usbstate);
return d;
}
static void
usbreset(void)
{
resetusbctlr();
}
void
usbinit(void)
{
Udev *d;
if(ubus.io != 0 && usbdev[0] == nil){
d = usbnewdevice(); /* reserve device 0 for configuration */
incref(d);
d->state = Attached;
}
}
Chan *
usbattach(char *spec)
{
Ctlr *ub;
ub = &ubus;
if(ub->io == 0)
error(Enodev);
if((IN(Cmd)&1)==0 || *spec)
OUT(Cmd, 1); /* run */
// pprint("at: c=%x s=%x c0=%x\n", IN(Cmd), IN(Status), IN(Portsc0));
return devattach('U', spec);
}
Chan *
usbclone(Chan *c, Chan *nc)
{
return devclone(c, nc);
}
static int
usbgen(Chan *c, Dirtab*, int, int s, Dir *dp)
{
int t;
Qid q;
ulong path;
Udev *d;
Dirtab *tab;
Endpt *e;
char buf[NAMELEN];
/*
* Top level directory contains the name of the device.
*/
if(c->qid.path == CHDIR){
if(s == 0){
devdir(c, (Qid){CHDIR|Q2nd, 0}, "usb", 0, eve, 0555, dp);
return 1;
}
return -1;
}
/*
* Second level contains "new" plus all the clients.
*/
t = QID(c->qid);
if(t < Q3rd){
if(s < nelem(usbdir2)){
tab = &usbdir2[s];
devdir(c, tab->qid, tab->name, tab->length, eve, tab->perm, dp);
return 1;
}
if((s -= nelem(usbdir2)) >= 0 && s < nelem(usbdev)){
d = usbdev[s];
if(d == nil)
return 0;
sprint(buf, "%d", s);
q = (Qid){CHDIR|((s+1)<<QSHIFT)|Q3rd, d->id};
devdir(c, q, buf, 0, eve, 0555, dp);
return 1;
}
return -1;
}
/*
* Third level.
*/
path = c->qid.path&~(CHDIR|QMASK); /* slot component */
q.vers = c->qid.vers;
if(s < nelem(usbdir3)){
Dirtab *tab = &usbdir3[s];
q.path = path | tab->qid.path;
devdir(c, q, tab->name, tab->length, eve, tab->perm, dp);
return 1;
}
/* active endpoints */
d = usbdeviceofpath(path);
if(d == nil)
return -1;
s -= nelem(usbdir3);
if(s < 0 || s >= nelem(d->ep))
return -1;
if(s == 0 || (e = d->ep[s]) == nil) /* ep0data is called "setup" */
return 0;
sprint(buf, "ep%ddata", s);
q.path = path | (Qep0+s);
devdir(c, q, buf, 0, eve, e->mode==OREAD? 0444: e->mode==OWRITE? 0222: 0666, dp);
return 1;
}
int
usbwalk(Chan *c, char *name)
{
if(strcmp(name, "..") == 0){
switch(QID(c->qid)){
case Qtopdir:
return 1;
case Q2nd:
c->qid = (Qid){CHDIR|Qtopdir, 0};
break;
case Q3rd:
c->qid = (Qid){CHDIR|Q2nd, 0};
break;
default:
panic("usbwalk %lux", c->qid.path);
}
return 1;
}
return devwalk(c, name, nil, 0, usbgen);
}
void
usbstat(Chan *c, char *db)
{
devstat(c, db, nil, 0, usbgen);
}
Chan *
usbopen(Chan *c, int omode)
{
Udev *d;
int f, s;
if(c->qid.path & CHDIR)
return devopen(c, omode, nil, 0, usbgen);
f = 0;
if(QID(c->qid) == Qnew){
d = usbnewdevice();
if(d == nil)
error(Enodev);
c->qid.path = Qctl|((d->x+1)<<QSHIFT);
c->qid.vers = d->id;
f = 1;
}
if(c->qid.path < Q3rd)
return devopen(c, omode, nil, 0, usbgen);
qlock(&usbstate);
if(waserror()){
qunlock(&usbstate);
nexterror();
}
switch(QID(c->qid)){
case Qctl:
d = usbdevice(c);
if(0&&d->busy)
error(Einuse);
d->busy = 1;
if(!f)
incref(d);
break;
default:
d = usbdevice(c);
s = QID(c->qid) - Qep0;
if(s >= 0 && s < nelem(d->ep)){
Endpt *e;
if((e = d->ep[s]) == nil)
error(Enodev);
if(schedendpt(e) < 0)
error("can't schedule USB endpoint");
eptactivate(e);
}
incref(d);
break;
}
poperror();
qunlock(&usbstate);
c->mode = openmode(omode);
c->flag |= COPEN;
c->offset = 0;
return c;
}
void
usbcreate(Chan *c, char *name, int omode, ulong perm)
{
USED(c, name, omode, perm);
error(Eperm);
}
void
usbremove(Chan*)
{
error(Eperm);
}
void
usbwstat(Chan *c, char *dp)
{
USED(c, dp);
error(Eperm);
}
void
usbclose(Chan *c)
{
Udev *d;
if(c->qid.path & CHDIR || c->qid.path < Q3rd)
return;
qlock(&usbstate);
if(waserror()){
qunlock(&usbstate);
nexterror();
}
d = usbdevice(c);
if(QID(c->qid) == Qctl)
d->busy = 0;
if(c->flag & COPEN)
freedev(d);
poperror();
qunlock(&usbstate);
}
static int
eptinput(void *arg)
{
Endpt *e;
e = arg;
return e->eof || e->err || qcanread(e->rq);
}
static long
readusb(Endpt *e, void *a, long n)
{
Block *b;
uchar *p;
long l, i;
qlock(&e->rlock);
if(waserror()){
qunlock(&e->rlock);
eptcancel(e);
nexterror();
}
p = a;
do {
if(e->eof) {
pprint("e->eof\n");
break;
}
if(e->err)
error(e->err);
qrcv(e);
if(!e->iso)
e->data01 ^= 1;
sleep(&e->rr, eptinput, e);
if(e->err)
error(e->err);
b = qget(e->rq); /* TO DO */
if(b == nil) {
pprint("b == nil\n");
break;
}
if(waserror()){
freeb(b);
nexterror();
}
l = BLEN(b);
if((i = l) > n)
i = n;
if(i > 0){
memmove(p, b->rp, i);
p += i;
}
poperror();
freeb(b);
n -= i;
} while (l == e->maxpkt && n > 0);
poperror();
qunlock(&e->rlock);
return p-(uchar*)a;
}
long
usbread(Chan *c, void *a, long n, vlong offset)
{
Endpt *e;
Udev *d;
char buf[48], *s;
int t, w0, w1, ps, l, i;
if(c->qid.path & CHDIR)
return devdirread(c, a, n, nil, 0, usbgen);
t = QID(c->qid);
switch(t){
case Qbusctl:
snprint(buf, sizeof(buf), "%11d %11d ", 0, 0);
return readstr(offset, a, n, buf);
case Qport:
ps = portinfo(&ubus, &w0, &w1);
snprint(buf, sizeof(buf), "0x%ux 0x%ux 0x%ux ", ps, w0, w1);
return readstr(offset, a, n, buf);
case Qctl:
d = usbdevice(c);
sprint(buf, "%11d %11d ", d->x, d->id);
return readstr(offset, a, n, buf);
case Qsetup: /* endpoint 0 */
d = usbdevice(c);
if((e = d->ep[0]) == nil)
error(Eio); /* can't happen */
e->data01 = 1;
n = readusb(e, a, n);
if(e->setin){
e->setin = 0;
e->data01 = 1;
writeusb(e, "", 0, TokOUT);
}
break;
case Qdebug:
n=0;
break;
case Qstatus:
d = usbdevice(c);
s = smalloc(READSTR);
if(waserror()){
free(s);
nexterror();
}
l = snprint(s, READSTR, "%s %d %d %d\n", devstates[d->state], d->class, d->subclass, d->proto);
for(i=0; i<nelem(d->ep); i++)
if((e = d->ep[i]) != nil) /* TO DO: freeze e */
l += snprint(s+l, READSTR-l, "%d bytes %lud blocks %lud\n", i, e->nbytes, e->nblocks);
n = readstr(offset, a, n, s);
poperror();
free(s);
break;
default:
d = usbdevice(c);
if((t -= Qep0) < 0 || t >= nelem(d->ep))
error(Eio);
if((e = d->ep[t]) == nil || e->mode == OWRITE)
error(Eio); /* can't happen */
n=readusb(e, a, n);
break;
}
return n;
}
static int
qisempty(void *arg)
{
return ((QH*)arg)->entries & Terminate;
}
static long
writeusb(Endpt *e, void *a, long n, int tok)
{
long i;
Block *b;
uchar *p;
QH *qh;
p = a;
qlock(&e->wlock);
if(waserror()){
qunlock(&e->wlock);
eptcancel(e);
nexterror();
}
do {
int j;
if(e->err)
error(e->err);
if((i = n) >= e->maxpkt)
i = e->maxpkt;
b = allocb(i);
if(waserror()){
freeb(b);
nexterror();
}
XPRINT("out [%ld]", i);
for (j = 0; j < i; j++) XPRINT(" %.2x", p[j]);
XPRINT("\n");
memmove(b->wp, p, i);
b->wp += i;
p += i;
n -= i;
poperror();
qh = qxmit(e, b, tok);
tok = TokOUT;
if(!e->iso)
e->data01 ^= 1;
if(e->ntd >= e->nbuf) {
XPRINT("writeusb: sleep %lux\n", &e->wr);
sleep(&e->wr, qisempty, qh);
XPRINT("writeusb: awake\n");
}
} while(n > 0);
poperror();
qunlock(&e->wlock);
return p-(uchar*)a;
}
int
getfields(char *ss, char **sp, int nptrs)
{
uchar *s = (uchar*)ss;
uchar **p = (uchar**)sp;
uint c;
c = 0;
for(;;){
if(--nptrs < 0)
break;
*p++ = s;
do {
c = *s++;
if (c == ' ') break;
if (c == '\t') break;
if (c == '\n') break;
} while(c);
if(c == 0)
break;
s[-1] = 0;
}
if(nptrs > 0)
*p = 0;
else
if(--s >= (uchar*)ss)
*s = c;
return p - (uchar**)sp;
}
long
usbwrite(Chan *c, void *a, long n, vlong)
{
Udev *d;
Endpt *e;
int id, nw, nf, t, i;
char cmd[50], *fields[10];
if(c->qid.path & CHDIR)
error(Egreg);
t = QID(c->qid);
if(t == Qbusctl){
if(n >= sizeof(cmd)-1)
n = sizeof(cmd)-1;
memmove(cmd, a, n);
cmd[n] = 0;
nf = getfields(cmd, fields, nelem(fields));
if(nf==1 && strcmp(fields[0], "dump")==0){
dumpframe(-1, -1);
return n;
}
if(nf < 2)
error(Ebadarg);
id = strtol(fields[1], nil, 0);
if(id != 1 && id != 2)
error(Ebadarg); /* there are two ports on the root hub */
if(strcmp(fields[0], "reset") == 0)
portreset(id);
else if(strcmp(fields[0], "enable") == 0)
portenable(id, 1);
else if(strcmp(fields[0], "disable") == 0)
portenable(id, 0);
else
error(Ebadarg);
return n;
}
d = usbdevice(c);
t = QID(c->qid);
switch(t){
case Qctl:
if(n >= sizeof(cmd)-1)
n = sizeof(cmd)-1;
memmove(cmd, a, n);
cmd[n] = 0;
nf = getfields(cmd, fields, nelem(fields));
if(nf > 1 && strcmp(fields[0], "speed") == 0){
d->ls = strtoul(fields[1], nil, 0) == 0;
} else if(nf > 4 && strcmp(fields[0], "class") == 0){
/* class class subclass proto */
d->class = strtoul(fields[4], nil, 0);
d->subclass = strtoul(fields[5], nil, 0);
d->proto = strtoul(fields[6], nil, 0);
}else if(nf > 2 && strcmp(fields[0], "data") == 0){
i = strtoul(fields[1], nil, 0);
if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil)
error(Ebadarg);
e = d->ep[i];
e->data01 = strtoul(fields[2], nil, 0) != 0;
}else if(nf > 2 && strcmp(fields[0], "maxpkt") == 0){
i = strtoul(fields[1], nil, 0);
if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil)
error(Ebadarg);
e = d->ep[i];
e->maxpkt = strtoul(fields[2], nil, 0);
if(e->maxpkt > 1500)
e->maxpkt = 1500;
}else if(nf > 2 && strcmp(fields[0], "debug") == 0){
i = strtoul(fields[1], nil, 0);
if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil)
error(Ebadarg);
e = d->ep[i];
e->debug = strtoul(fields[2], nil, 0);
}else if(nf > 1 && strcmp(fields[0], "unstall") == 0){
i = strtoul(fields[1], nil, 0);
if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil)
error(Ebadarg);
e = d->ep[i];
e->err = nil;
}else if(nf == 6 && strcmp(fields[0], "ep") == 0){
/* ep n maxpkt mode poll nbuf */
i = strtoul(fields[1], nil, 0);
if(i < 0 || i >= nelem(d->ep))
error(Ebadarg);
if(d->ep[i] != nil)
error(Einuse);
e = devendpt(d, i, 1);
if(waserror()){
freept(e);
nexterror();
}
i = strtoul(fields[2], nil, 0);
if(i < 8 || i > 1023)
i = 8;
e->maxpkt = i;
e->mode = strcmp(fields[3],"r")==0? OREAD: strcmp(fields[3],"w") == 0? OWRITE: ORDWR;
e->periodic = 0;
e->sched = -1;
if(strcmp(fields[4], "bulk") != 0){
e->periodic = 1;
i = strtoul(fields[4], nil, 0);
if(i > 0 && i <= 1000)
e->pollms = i;
else
error(Ebadarg);
}
i = strtoul(fields[5], nil, 0);
if(i >= 1 && i <= 32)
e->nbuf = i;
poperror();
}else
error(Ebadarg);
return n;
case Qsetup: /* SETUP endpoint 0 */
/* should canqlock etc */
if((e = d->ep[0]) == nil)
error(Eio); /* can't happen */
if(n < 8 || n > 1023)
error(Eio);
nw = *(uchar*)a & RD2H;
e->data01 = 0;
n = writeusb(e, a, n, TokSETUP);
if(nw == 0){ /* host to device: use IN[DATA1] to ack */
e->data01 = 1;
nw = readusb(e, cmd, 8);
if(nw != 0)
error(Eio); /* could provide more status */
}else
e->setin = 1; /* two-phase */
break;
default: /* sends DATA[01] */
if((t -= Qep0) < 0 || t >= nelem(d->ep))
error(Eio);
if((e = d->ep[t]) == nil || e->mode == OREAD)
error(Eio); /* can't happen */
n = writeusb(e, a, n, TokOUT);
break;
}
return n;
}
Dev usbdevtab = {
'U',
"usb",
usbreset,
usbinit,
usbattach,
usbclone,
usbwalk,
usbstat,
usbopen,
devcreate,
usbclose,
usbread,
devbread,
usbwrite,
devbwrite,
devremove,
devwstat,
};
.
## diffname pc/devusb.c 1999/1006
## diff -e /n/emeliedump/1999/1005/sys/src/brazil/pc/devusb.c /n/emeliedump/1999/1006/sys/src/brazil/pc/devusb.c
1883a
}
.
1882c
}else {
pprint("command %s, fields %d\n", fields[0], nf);
.
1876a
}
.
1875c
else {
pprint("field 4: 0 <= %d <= 1000\n", i);
.
1855a
}
.
1854c
if(i < 0 || i >= nelem(d->ep)) {
pprint("field 1: 0 <= %d < %d\n", i, nelem(d->ep));
.
1320c
Ctlr *ub;
Pcidev *cfg;
int i;
ulong port;
QTree *qt;
TD *t;
ub = &ubus;
memset(&cfg, 0, sizeof(cfg));
cfg = pcimatch(0, 0x8086, 0x7112); /* Intel chipset PIIX 4*/
if(cfg == nil) {
// cfg = pcimatch(0, 0x8086, 0x7112); /* Intel chipset PIIX 3*/
// if(cfg == nil) {
cfg = pcimatch(0, 0x1106, 0x0586); /* Via chipset */
if(cfg == nil) {
DPRINT("No USB device found\n");
return;
}
// }
}
port = cfg->mem[4].bar & ~0x0F;
if (port == 0) {
print("usb: failed to map registers\n");
return;
}
print("USB: %x/%x port 0x%lux size 0x%x irq %d\n",
cfg->vid, cfg->did, port, cfg->mem[4].size, cfg->intl);
i = inb(port+SOFMod);
if(0){
OUT(Cmd, 4); /* global reset */
delay(15);
OUT(Cmd, 0); /* end reset */
delay(4);
}
outb(port+SOFMod, i);
// Interrupt handler
intrenable(cfg->intl, interrupt, ub, cfg->tbdf, "usb");
ub->io = port;
ub->tdpool = xspanalloc(128*sizeof(TD), 16, 0);
for(i=128; --i>=0;){
ub->tdpool[i].next = ub->freetd;
ub->freetd = &ub->tdpool[i];
}
ub->qhpool = xspanalloc(32*sizeof(QH), 16, 0);
for(i=32; --i>=0;){
ub->qhpool[i].next = ub->freeqh;
ub->freeqh = &ub->qhpool[i];
}
/*
* the root of the periodic (interrupt & isochronous) scheduling tree
* points to the control queue and the bandwidth sop for bulk traffic.
* this is looped following the instructions in PIIX4 errata 29773804.pdf:
* a QH links to a looped but inactive TD as its sole entry,
* with its head entry leading on to the bulk traffic, the last QH of which
* links back to the empty QH.
*/
ub->bulkq = allocqh(ub);
t = alloctd(ub); /* inactive TD, looped */
t->link = PADDR(t);
ub->bwsop = allocqh(ub);
ub->bwsop->head = PADDR(ub->bulkq) | IsQH;
ub->bwsop->entries = PADDR(t);
ub->ctlq = allocqh(ub);
ub->ctlq->head = PADDR(ub->bwsop) | IsQH;
// ub->bulkq->head = PADDR(ub->bwsop) | IsQH; /* loop back */
print("usbcmd\t0x%.4x\nusbsts\t0x%.4x\nusbintr\t0x%.4x\nfrnum\t0x%.2x\n",
IN(Cmd), IN(Status), IN(Usbintr), inb(port+Frnum));
print("frbaseadd\t0x%.4x\nsofmod\t0x%x\nportsc1\t0x%.4x\nportsc2\t0x%.4x\n",
IN(Flbaseadd), inb(port+SOFMod), IN(Portsc0), IN(Portsc1));
OUT(Cmd, 0); /* stop */
ub->frames = xspanalloc(FRAMESIZE, FRAMESIZE, 0);
qt = mkqhtree(ub->frames, NFRAME, 32);
if(qt == nil){
print("usb: can't allocate scheduling tree\n");
ub->io = 0;
return;
}
qt->root->head = PADDR(ub->ctlq) | IsQH;
ub->tree = qt;
print("usb tree: nel=%d depth=%d\n", qt->nel, qt->depth);
outl(port+Flbaseadd, PADDR(ub->frames));
OUT(Frnum, 0);
OUT(Usbintr, 0xF); /* enable all interrupts */
print("cmd 0x%x sofmod 0x%x\n", IN(Cmd), inb(port+SOFMod));
print("sc0 0x%x sc1 0x%x\n", IN(Portsc0), IN(Portsc1));
.
1126,1220d
## diffname pc/devusb.c 1999/1007
## diff -e /n/emeliedump/1999/1006/sys/src/brazil/pc/devusb.c /n/emeliedump/1999/1007/sys/src/brazil/pc/devusb.c
1881c
XPRINT("command %s, fields %d\n", fields[0], nf);
.
1872c
XPRINT("field 4: 0 <= %d <= 1000\n", i);
.
1849c
XPRINT("field 1: 0 <= %d < %d\n", i, nelem(d->ep));
.
1586c
XPRINT("b == nil\n");
.
1573c
XPRINT("e->eof\n");
.
1107,1109c
XPRINT("usbint: #%x f%d\n", s, IN(Frnum));
XPRINT("cmd #%x sofmod #%x\n", IN(Cmd), inb(ub->io+SOFMod));
XPRINT("sc0 #%x sc1 #%x\n", IN(Portsc0), IN(Portsc1));
.
838c
XPRINT(" -> %8.8lux h=%8.8lux e=%8.8lux\n", p, q->head, q->entries);
.
835c
XPRINT("Q: p=%8.8lux out of range\n", p);
.
831c
XPRINT("F%.2d %8.8lux %8.8lux\n", i, frame[i], QFOL(frame[i])->head);
.
808c
XPRINT("leaf0=%d o=%d i=%d n=%d\n", leaf0, o, i, n);
.
627c
XPRINT("cancel:\n");
.
528c
XPRINT("cleanTD: Error %8.8lux %8.8lux %8.8lux %8.8lux\n", t->link, t->status, t->dev, t->buffer);
.
484c
XPRINT("head:");
.
478c
XPRINT("qh %8.8lux: %8.8lux %8.8lux\n", q, q->head, q->entries);
.
404,441d
374c
XPRINT("\ts=%s,ep=%ld,d=%ld,D=%ld\n", buf, (t->dev>>15)&0xF, (t->dev>>8)&0xFF, (t->dev>>19)&1);
.
372c
XPRINT("td %8.8lux: l=%8.8lux s=%8.8lux d=%8.8lux b=%8.8lux %8.8lux f=%8.8lux\n",
.
## diffname pc/devusb.c 1999/1116
## diff -e /n/emeliedump/1999/1007/sys/src/brazil/pc/devusb.c /n/emeliedump/1999/1116/sys/src/9/pc/devusb.c
1799,1800c
if (i == -1)
debug = 0;
else {
debug = 1;
e = d->ep[i];
e->debug = strtoul(fields[2], nil, 0);
}
.
1797c
if(i < -1 || i >= nelem(d->ep) || d->ep[i] == nil)
.
1526a
XPRINT("got qlock(%p)\n", &e->rlock);
.
1525a
XPRINT("qlock(%p)\n", &e->rlock);
.
1255c
ub->bulkq->head = PADDR(ub->bwsop) | IsQH; /* loop back */
.
1082,1083c
XPRINT("cleanq(e->epq, 0, 0)\n");
cleanq(e->epq, 0, 0);
.
1075,1078c
XPRINT("cleanq(ub->ctlq, 0, 0)\n");
cleanq(ub->ctlq, 0, 0);
XPRINT("cleanq(ub->bulkq, 0, Vf)\n");
cleanq(ub->bulkq, 0, Vf);
.
1069d
1067a
XPRINT("usbint: #%x f%d\n", s, IN(Frnum));
.
914a
e->epq = allocqh(ub);
if(e->epq == nil)
panic("devendpt");
.
688a
XPRINT("bulkenq\n");
.
687d
685a
XPRINT("enq\n");
.
678a
XPRINT("qrcv\n");
.
573c
if (tp)
t = tp->next;
else
t = q->first;
XPRINT("t = %8.8lux\n", t);
dumpqh(q);
.
563,569c
if (tp == nil) {
q->first = t->next;
if(q->first != nil)
q->entries = PADDR(q->first);
else
q->entries = Terminate;
} else {
tp->next = t->next;
if (t->next != nil)
tp->link = PADDR(t->next) | vf;
else
tp->link = Terminate;
}
if (q->last == t)
q->last = tp;
.
559a
tp = t;
t = t->next;
continue;
.
556a
tp = t;
.
553a
if(t->status & NAKed){
t->status = (t->status & ~NAKed) | IOC; /* ensure interrupt next frame */
tp = t;
t = t->next;
continue;
}
.
552c
XPRINT("cleanq: %8.8lux %8.8lux %8.8lux %8.8lux %8.8lux %8.8lux\n", t->link, t->status, t->dev, t->buffer, t->flags, t->next);
.
550a
tp = nil;
.
546c
TD *t, *tp;
.
544c
cleanq(QH *q, int discard, int vf)
.
472a
dumpqh(q);
.
241c
int ntd;
.
231,232c
int pollms; /* polling interval in msec */
QH* epq; /* queue of TDs for this endpoint */
.
228,229c
int sched; /* schedule index; -1 if undefined or aperiodic */
int setin;
.
214,217c
int x; /* index in Udev.ep */
int id; /* hardware endpoint address */
int maxpkt; /* maximum packet size (from endpoint descriptor) */
int data01; /* 0=DATA0, 1=DATA1 */
.
34a
static int debug = 1;
.
33c
#define XPRINT if(debug)print
.
## diffname pc/devusb.c 1999/1117
## diff -e /n/emeliedump/1999/1116/sys/src/9/pc/devusb.c /n/emeliedump/1999/1117/sys/src/9/pc/devusb.c
1868c
if(strcmp(fields[4], "bulk") == 0){
Ctlr *ub;
ub = &ubus;
/* Each bulk device gets a queue head hanging off the
* bulk queue head
*/
if (e->epq == nil) {
e->epq = allocqh(ub);
if(e->epq == nil)
panic("usbwrite: allocqh");
}
queueqh(e->epq);
} else {
.
1288c
ub->bwsop->head = PADDR(ub->bulkq) | IsQH;
ub->bulkq->head = PADDR(ub->recvq) | IsQH;
ub->recvq->head = PADDR(ub->bwsop) | IsQH; /* loop back */
.
1286d
1283,1284d
1280a
ub->recvq = allocqh(ub);
XPRINT("bulkq: 0x%8.8lux\n", ub->bulkq);
XPRINT("recvq: 0x%8.8lux\n", ub->recvq);
XPRINT("bwsop: 0x%8.8lux\n", ub->bwsop);
XPRINT("ctlq: 0x%8.8lux\n", ub->ctlq);
.
1279a
ub->ctlq = allocqh(ub);
ub->bwsop = allocqh(ub);
.
1225a
Ctlr *ub;
.
1220d
1111a
XPRINT("clean recvq\n");
for (q = ub->recvq->next; q; q = q->hlink) {
XPRINT("cleanq(q, 0, Vf)\n");
cleanq(q, 0, Vf);
}
.
1097a
QH *q;
.
921a
ub = &ubus;
.
920a
Ctlr *ub;
.
901c
if(!e->periodic || (q = e->sched) < 0)
.
689a
XPRINT("bulkenq\n");
.
686a
XPRINT("enq\n");
.
672a
XPRINT("qxmit\n");
.
664a
static void
queueqh(QH *qh) {
QH *q;
Ctlr *ub;
ub = &ubus;
// See if it's already queued
for (q = ub->recvq->next; q; q = q->hlink)
if (q == qh)
return;
if ((qh->hlink = ub->recvq->next) == nil)
qh->head = Terminate;
else
qh->head = PADDR(ub->recvq->next) | IsQH;
ub->recvq->next = qh;
ub->recvq->entries = PADDR(qh) | IsQH;
}
.
419a
qh->hlink = nil;
.
377,378c
// if(t->bp && (t->flags & CancelTD) == 0 && (t->status & Active) == 0)
// dumpdata(t->bp, n);
.
374,375c
XPRINT("td %8.8lux: ", t);
XPRINT("l=%8.8lux s=%8.8lux d=%8.8lux b=%8.8lux %8.8lux f=%8.8lux\n",
t->link, t->status, t->dev, t->buffer, t->bp?(ulong)t->bp->rp:0, t->flags);
.
269a
QH* recvq; /* receive queues for bulk i/o */
.
89,93c
QH* hlink;
TD* first;
QH* next; /* free list */
TD* last;
ulong _d1; /* fillers */
ulong _d2;
.
35c
static int debug = 0;
.
## diffname pc/devusb.c 1999/1118
## diff -e /n/emeliedump/1999/1117/sys/src/9/pc/devusb.c /n/emeliedump/1999/1118/sys/src/9/pc/devusb.c
1347,1348c
XPRINT("cmd 0x%x sofmod 0x%x\n", IN(Cmd), inb(port+SOFMod));
XPRINT("sc0 0x%x sc1 0x%x\n", IN(Portsc0), IN(Portsc1));
.
1342c
XPRINT("usb tree: nel=%d depth=%d\n", qt->nel, qt->depth);
.
1330c
XPRINT("frbaseadd\t0x%.4x\nsofmod\t0x%x\nportsc1\t0x%.4x\nportsc2\t0x%.4x\n",
.
1328c
XPRINT("usbcmd\t0x%.4x\nusbsts\t0x%.4x\nusbintr\t0x%.4x\nfrnum\t0x%.2x\n",
.
1317,1320d
1279c
DPRINT("USB: %x/%x port 0x%lux size 0x%x irq %d\n",
.
1188c
"ctl", {Qctl}, 0, 0666,
.
1184c
"port", {Qport}, 0, 0444,
.
1182c
"new", {Qnew}, 0, 0666,
.
742d
739d
731d
714d
710d
707c
if(e->debug) pprint("QTD: %8.8lux n=%ld\n", t, b?BLEN(b): 0);
.
695d
380,381c
if(debug && t->bp && (t->flags & CancelTD) == 0)
dumpdata(t->bp, n);
.
30d
## diffname pc/devusb.c 1999/1119
## diff -e /n/emeliedump/1999/1118/sys/src/9/pc/devusb.c /n/emeliedump/1999/1119/sys/src/9/pc/devusb.c
1841,1843c
d->class = strtoul(fields[1], nil, 0);
d->subclass = strtoul(fields[2], nil, 0);
d->proto = strtoul(fields[3], nil, 0);
.
1839c
} else if(nf > 3 && strcmp(fields[0], "class") == 0){
.
## diffname pc/devusb.c 1999/1120
## diff -e /n/emeliedump/1999/1119/sys/src/9/pc/devusb.c /n/emeliedump/1999/1120/sys/src/9/pc/devusb.c
1882,1884c
if((e = d->ep[i]) == nil)
e = devendpt(d, i, 1);
.
1839,1843c
} else if(nf > 5 && strcmp(fields[0], "class") == 0){
i = strtoul(fields[2], nil, 0);
d->npt = strtoul(fields[1], nil, 0);
/* class config# class subclass proto */
if (i < 0 || i >= nelem(d->ep)
|| d->npt > nelem(d->ep) || i >= d->npt)
error(Ebadarg);
if (i == 0) {
d->class = strtoul(fields[3], nil, 0);
d->subclass = strtoul(fields[4], nil, 0);
d->proto = strtoul(fields[5], nil, 0);
}
if(d->ep[i] == nil)
d->ep[i] = devendpt(d, i, 1);
d->ep[i]->class = strtoul(fields[3], nil, 0);
d->ep[i]->subclass = strtoul(fields[4], nil, 0);
d->ep[i]->proto = strtoul(fields[5], nil, 0);
.
1688c
l += snprint(s+l, READSTR-l, "%2d class 0x%2.2ux subclass 0x%2.2ux proto 0x%2.2ux bytes %10lud blocks %10lud\n", i, e->class, e->subclass, e->proto, e->nbytes, e->nblocks);
.
203,207c
[Disabled] "Disabled",
[Attached] "Attached",
[Enabled] "Enabled",
[Assigned] "Assigned",
[Configured] "Configured",
.
184c
Udev* ports; /* active ports, if hub */
.
181,182c
int ls;
int npt;
.
173,176c
int x; /* index in usbdev[] */
int busy;
int state;
int id;
.
## diffname pc/devusb.c 1999/1209
## diff -e /n/emeliedump/1999/1120/sys/src/9/pc/devusb.c /n/emeliedump/1999/1209/sys/src/9/pc/devusb.c
1283c
/*
* Interrupt handler.
* Bail out if no IRQ assigned by the BIOS.
*/
if(cfg->intl == 0xFF)
return;
.
## diffname pc/devusb.c 1999/1230
## diff -e /n/emeliedump/1999/1209/sys/src/9/pc/devusb.c /n/emeliedump/1999/1230/sys/src/9/pc/devusb.c
1425a
if(s == DEVDOTDOT){
devdir(c, (Qid){CHDIR|Q2nd, 0}, "usb", 0, eve, 0555, dp);
return 1;
}
.
1403a
if(s == DEVDOTDOT){
devdir(c, (Qid){CHDIR, 0}, "#U", 0, eve, 0555, dp);
return 1;
}
.
1391a
if(s == DEVDOTDOT){
devdir(c, (Qid){CHDIR, 0}, "#U", 0, eve, 0555, dp);
return 1;
}
.
## diffname pc/devusb.c 2000/0308
## diff -e /n/emeliedump/1999/1230/sys/src/9/pc/devusb.c /n/emeliedump/2000/0308/sys/src/9/pc/devusb.c
1853c
nf = getfields(cmd, fields, nelem(fields), 0, " \t\n");
.
1825c
nf = getfields(cmd, fields, nelem(fields), 0, " \t\n");
.
1779,1808d
## diffname pc/devusb.c 2000/0517
## diff -e /n/emeliedump/2000/0308/sys/src/9/pc/devusb.c /n/emeliedump/2000/0517/sys/src/9/pc/devusb.c
1287c
if(cfg->intl == 0xFF || cfg->intl == 0)
.
## diffname pc/devusb.c 2000/0719
## diff -e /n/emeliedump/2000/0517/sys/src/9/pc/devusb.c /n/emeliedump/2000/0719/sys/src/9/pc/devusb.c
23,29d
## diffname pc/devusb.c 2000/0720
## diff -e /n/emeliedump/2000/0719/sys/src/9/pc/devusb.c /n/emeliedump/2000/0720/sys/src/9/pc/devusb.c
1250,1257c
cfg = pcimatch(0, 0x1106, 0x0586); /* Via chipset */
if(cfg == nil) {
DPRINT("No USB device found\n");
return;
}
.
## diffname pc/devusb.c 2000/0722
## diff -e /n/emeliedump/2000/0720/sys/src/9/pc/devusb.c /n/emeliedump/2000/0722/sys/src/9/pc/devusb.c
1695c
l += snprint(s+l, READSTR-l, "%2d %ud.%ud.%ud bytes %10lud blocks %10lud\n", i, e->class, e->subclass, e->proto, e->nbytes, e->nblocks);
.
1692c
l = snprint(s, READSTR, "%s %d.%d.%d\n", devstates[d->state], d->class, d->subclass, d->proto);
.
## diffname pc/devusb.c 2000/0724
## diff -e /n/emeliedump/2000/0722/sys/src/9/pc/devusb.c /n/emeliedump/2000/0724/sys/src/9/pc/devusb.c
1830,1832c
d->ep[i]->csp = strtoul(fields[3], nil, 0);
.
1824,1826c
d->csp = strtoul(fields[3], nil, 0);
.
1819c
/* class config# csp ( == class subclass proto) */
.
1816c
} else if(nf > 3 && strcmp(fields[0], "class") == 0){
.
1695c
l += snprint(s+l, READSTR-l, "%2d %#6.6x %10lud bytes %10lud blocks\n", i, e->csp, e->nbytes, e->nblocks);
.
1692c
l = snprint(s, READSTR, "%s %#6.6x\n", devstates[d->state], d->csp);
.
993c
if(Class(d->csp) == Hubclass)
.
214,216c
ulong csp;
.
171,173c
ulong csp;
.
61a
#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))
.
## diffname pc/devusb.c 2000/0725
## diff -e /n/emeliedump/2000/0724/sys/src/9/pc/devusb.c /n/emeliedump/2000/0725/sys/src/9/pc/devusb.c
1936a
}
.
1935c
}
if((e = d->ep[t]) == nil || e->mode == OREAD) {
.
1933c
if((t -= Qep0) < 0 || t >= nelem(d->ep)) {
print("t = %d\n", t);
.
1878c
e->mode = strcmp(fields[3],"r") == 0? OREAD :
strcmp(fields[3],"w") == 0? OWRITE : ORDWR;
.
1696c
l += snprint(s+l, READSTR-l, "%2d %#6.6lux %10lud bytes %10lud blocks\n", i, e->csp, e->nbytes, e->nblocks);
.
1693c
l = snprint(s, READSTR, "%s %#6.6lux\n", devstates[d->state], d->csp);
.
## diffname pc/devusb.c 2001/0126
## diff -e /n/emeliedump/2000/0725/sys/src/9/pc/devusb.c /n/emeliedump/2001/0126/sys/src/9/pc/devusb.c
1263c
port = cfg->mem[4].bar & ~0x0F;
DPRINT("USB: %x/%x port 0x%ux size 0x%x irq %d\n",
.
1261d
1257,1259c
if(cfg == nil)
.
1255a
if((cfg->mem[4].bar & ~0x0F) != 0)
break;
.
1248,1254c
for(cfg = pcimatch(nil, 0, 0); cfg != nil; cfg = pcimatch(cfg, 0, 0)){
/*
* Look for devices with the correct class and
* sub-class code and known device and vendor ID.
*/
if(cfg->ccrb != 0x0C || cfg->ccru != 0x03)
continue;
switch((cfg->did<<16)|cfg->vid){
default:
continue;
case (0x7112<<16)|0x8086: /* 82371[AE]B (PIIX4[E]) */
case (0x719A<<16)|0x8086: /* 82443MX */
case (0x1106<<16)|0x0586: /* VIA 82C586 */
break;
.
1241,1242c
int i, port;
.
## diffname pc/devusb.c 2001/0131
## diff -e /n/emeliedump/2001/0126/sys/src/9/pc/devusb.c /n/emeliedump/2001/0131/sys/src/9/pc/devusb.c
1319a
}
.
1312a
if(0){ /* DISABLE ERRATA FOR NOW */
.
841c
pprint(" -> %8.8lux h=%8.8lux e=%8.8lux\n", p, q->head, q->entries);
.
838c
pprint("Q: p=%8.8lux out of range\n", p);
.
834c
pprint("F%.2d %8.8lux %8.8lux\n", i, frame[i], QFOL(frame[i])->head);
.
800a
print("after loop tree root %8.8lux head %8.8lux entries %8.8lux\n", tree, tree->head, tree->entries);
.
793a
print("tree root %8.8lux\n", tree);
.
## diffname pc/devusb.c 2001/0201
## diff -e /n/emeliedump/2001/0131/sys/src/9/pc/devusb.c /n/emeliedump/2001/0201/sys/src/9/pc/devusb.c
1315c
if(0){
.
## diffname pc/devusb.c 2001/0216
## diff -e /n/emeliedump/2001/0201/sys/src/9/pc/devusb.c /n/emeliedump/2001/0216/sys/src/9/pc/devusb.c
802d
794d
## diffname pc/devusb.c 2001/0503
## diff -e /n/emeliedump/2001/0216/sys/src/9/pc/devusb.c /n/emeliedump/2001/0503/sys/src/9/pc/devusb.c
1815a
if (d == nil)
error(Ehungup);
.
1712a
if (d == nil)
error(Ehungup);
.
1696a
if (d == nil)
error(Ehungup);
.
1679a
if (d == nil)
error(Ehungup);
.
1674a
if (d == nil)
error(Ehungup);
.
1579,1584d
1575,1577c
d = usbdeviceofpath(c->qid.path);
if (d && d->id == c->qid.vers) {
if(QID(c->qid) == Qctl)
d->busy = 0;
if(c->flag & COPEN)
freedev(d);
.
1526a
if (d == nil)
error(Ehungup);
.
1517a
if (d == nil)
error(Ehungup);
.
1200c
return nil;
.
## diffname pc/devusb.c 2001/0504
## diff -e /n/emeliedump/2001/0503/sys/src/9/pc/devusb.c /n/emeliedump/2001/0504/sys/src/9/pc/devusb.c
735,749d
722a
t = alloctde(e, TokIN, e->maxpkt);
.
721d
## diffname pc/devusb.c 2001/0527
## diff -e /n/emeliedump/2001/0504/sys/src/9/pc/devusb.c /n/emeliedump/2001/0527/sys/src/9/pc/devusb.c
1810,1811d
1705,1706d
1687,1688d
1668,1669d
1661,1662d
1570a
d = usbdevice(c);
if(QID(c->qid) == Qctl)
d->busy = 0;
if(c->flag & COPEN)
freedev(d);
poperror();
.
1564,1569c
if(waserror()){
qunlock(&usbstate);
nexterror();
.
1514,1515d
1503,1504d
1306d
1298d
1253,1255c
DPRINT("USB: %x/%x port 0x%lux size 0x%x irq %d\n",
.
1251a
}
.
1250c
port = cfg->mem[4].bar & ~0x0F;
if (port == 0) {
print("usb: failed to map registers\n");
.
1247,1248d
1232,1245c
memset(&cfg, 0, sizeof(cfg));
cfg = pcimatch(0, 0x8086, 0x7112); /* Intel chipset PIIX 4*/
if(cfg == nil) {
cfg = pcimatch(0, 0x1106, 0x0586); /* Via chipset */
if(cfg == nil) {
DPRINT("No USB device found\n");
return;
.
1226c
int i;
ulong port;
.
1185c
error(Ehungup);
.
826c
XPRINT(" -> %8.8lux h=%8.8lux e=%8.8lux\n", p, q->head, q->entries);
.
823c
XPRINT("Q: p=%8.8lux out of range\n", p);
.
819c
XPRINT("F%.2d %8.8lux %8.8lux\n", i, frame[i], QFOL(frame[i])->head);
.
736a
static Block *
usbreq(int type, int req, int value, int offset, int count)
{
Block *b;
b = allocb(8);
b->wp[0] = type;
b->wp[1] = req;
PUT2(b->wp+2, value);
PUT2(b->wp+4, offset);
PUT2(b->wp+6, count);
b->wp += 8;
return b;
}
.
722a
b = allocb(e->maxpkt);
.
721d
## diffname pc/devusb.c 2001/0624
## diff -e /n/emeliedump/2001/0527/sys/src/9/pc/devusb.c /n/emeliedump/2001/0624/sys/src/9/pc/devusb.c
882,1966c
if(!e->periodic || e->sched
.
## diffname pc/devusb.c 2001/0626
## diff -e /n/emeliedump/2001/0624/sys/src/9/pc/devusb.c /n/emeliedump/2001/0626/sys/src/9/pc/devusb.c
882c
if(!e->periodic || e->sched >= 0)
return 0;
ub = &ubus;
if(e->epq == nil){
e->epq = allocqh(ub);
if(e->epq == nil)
return -1;
}
qlock(ub->tree);
q = pickschedq(ub->tree, e->pollms, e->bw, ~0); /* TO DO: bus bandwidth limit */
if(q < 0)
return -1;
ub->tree->bw[q] += e->bw;
qh = &ub->tree->root[q];
e->sched = q;
e->epq->head = qh->entries;
e->epq->entries = Terminate;
qh->entries = PADDR(e->epq) | IsQH;
qunlock(ub->tree);
return 0;
}
static void
unschedendpt(Endpt *e)
{
Ctlr *ub;
ulong p;
QH *qh;
int q;
ub = &ubus;
if(!e->periodic || (q = e->sched) < 0)
return;
p = PADDR(e->epq) | IsQH;
qlock(ub->tree);
ub->tree->bw[q] -= e->bw;
qh = &ub->tree->root[q];
for(; qh->entries != p; qh = QFOL(qh->entries))
if(qh->entries & Terminate || (qh->entries & IsQH) == 0){
qunlock(ub->tree);
panic("usb: unschedendpt");
}
qh->entries = e->epq->head;
qunlock(ub->tree);
e->epq->head = Terminate;
}
static Endpt *
devendpt(Udev *d, int id, int add)
{
Endpt *e, **p;
Ctlr *ub;
ub = &ubus;
p = &d->ep[id&0xF];
lock(d);
if((e = *p) != nil){
incref(e);
unlock(d);
return e;
}
unlock(d);
if(!add)
return nil;
e = mallocz(sizeof(*e), 1);
e->ref = 1;
e->x = id&0xF;
e->id = id;
e->periodic = 0;
e->sched = -1;
e->maxpkt = 8;
e->pollms = 0;
e->bw = 0;
e->nbuf = 1;
e->dev = d;
e->active = 0;
e->epq = allocqh(ub);
if(e->epq == nil)
panic("devendpt");
lock(d);
if(*p != nil){
incref(*p);
unlock(d);
free(e);
return *p;
}
*p = e;
unlock(d);
e->rq = qopen(8*1024, 0, nil, e);
e->wq = qopen(8*1024, 0, nil, e);
return e;
}
static void
freept(Endpt *e)
{
if(e != nil && decref(e) == 0){
XPRINT("freept(%d,%d)\n", e->dev->x, e->x);
unschedendpt(e);
e->dev->ep[e->x] = nil;
eptdeactivate(e);
if(e->epq != nil)
freeqh(&ubus, e->epq);
free(e);
}
}
static void
usbdevreset(Udev *d)
{
d->state = Disabled;
if(Class(d->csp) == Hubclass)
for(d = d->ports; d != nil; d = d->next)
usbdevreset(d);
}
static void
freedev(Udev *d)
{
int i;
if(d != nil && decref(d) == 0){
for(i=0; i<nelem(d->ep); i++)
freept(d->ep[i]);
if(d->x >= 0)
usbdev[d->x] = nil;
free(d);
}
}
static void
hubportreset(Udev *h, int p)
{
USED(h, p);
/* reset state of each attached device? */
}
static int ioports[] = {-1, Portsc0, Portsc1};
static void
portreset(int port)
{
Ctlr *ub;
int i, p;
/* should check that device not being configured on other port? */
p = ioports[port];
ub = &ubus;
qlock(&ub->resetl);
if(waserror()){
qunlock(&ub->resetl);
nexterror();
}
XPRINT("r: %x\n", IN(p));
ilock(ub);
OUT(p, PortReset);
delay(12); /* BUG */
XPRINT("r2: %x\n", IN(p));
OUT(p, IN(p) & ~PortReset);
XPRINT("r3: %x\n", IN(p));
OUT(p, IN(p) | PortEnable);
microdelay(64);
for(i=0; i<1000 && (IN(p) & PortEnable) == 0; i++)
;
XPRINT("r': %x %d\n", IN(p), i);
OUT(p, (IN(p) & ~PortReset)|PortEnable);
iunlock(ub);
hubportreset(nil, port);
poperror();
qunlock(&ub->resetl);
}
static void
portenable(int port, int on)
{
Ctlr *ub;
int w, p;
/* should check that device not being configured on other port? */
p = ioports[port];
ub = &ubus;
qlock(&ub->resetl);
if(waserror()){
qunlock(&ub->resetl);
nexterror();
}
ilock(ub);
w = IN(p);
if(on)
w |= PortEnable;
else
w &= ~PortEnable;
OUT(p, w);
microdelay(64);
iunlock(ub);
XPRINT("e: %x\n", IN(p));
if(!on)
hubportreset(nil, port);
poperror();
qunlock(&ub->resetl);
}
static int
portinfo(Ctlr *ub, int *p0, int *p1)
{
int m, v;
ilock(ub);
m = 0;
if((v = IN(Portsc0)) & PortChange){
OUT(Portsc0, v);
m |= 1<<0;
}
*p0 = v;
if((v = IN(Portsc1)) & PortChange){
OUT(Portsc1, v);
m |= 1<<1;
}
*p1 = v;
iunlock(ub);
return m;
}
static void
interrupt(Ureg*, void *a)
{
Ctlr *ub;
Endpt *e;
int s;
QH *q;
ub = a;
s = IN(Status);
OUT(Status, s);
if ((s & 0x1f) == 0)
return;
XPRINT("usbint: #%x f%d\n", s, IN(Frnum));
if (s & 0x1a) {
XPRINT("cmd #%x sofmod #%x\n", IN(Cmd), inb(ub->io+SOFMod));
XPRINT("sc0 #%x sc1 #%x\n", IN(Portsc0), IN(Portsc1));
}
XPRINT("cleanq(ub->ctlq, 0, 0)\n");
cleanq(ub->ctlq, 0, 0);
XPRINT("cleanq(ub->bulkq, 0, Vf)\n");
cleanq(ub->bulkq, 0, Vf);
XPRINT("clean recvq\n");
for (q = ub->recvq->next; q; q = q->hlink) {
XPRINT("cleanq(q, 0, Vf)\n");
cleanq(q, 0, Vf);
}
ilock(&activends);
for(e = activends.f; e != nil; e = e->activef)
if(e->epq != nil) {
XPRINT("cleanq(e->epq, 0, 0)\n");
cleanq(e->epq, 0, 0);
}
iunlock(&activends);
}
enum
{
Qtopdir = 0,
Q2nd,
Qbusctl,
Qnew,
Qport,
Q3rd,
Qctl,
Qsetup,
Qdebug,
Qstatus,
Qep0,
/* other endpoint files */
};
/*
* Qid path is:
* 8 bits of file type (qids above)
* 10 bits of slot number +1; 0 means not attached to device
*/
#define QSHIFT 8 /* location in qid of device # */
#define QMASK ((1<<QSHIFT)-1)
#define QID(q) ((ulong)(q).path&QMASK)
#define DEVPATH(p) ((p)>>QSHIFT)
static Dirtab usbdir2[] = {
"new", {Qnew}, 0, 0666,
"ctl", {Qbusctl}, 0, 0666,
"port", {Qport}, 0, 0444,
};
static Dirtab usbdir3[]={
"ctl", {Qctl}, 0, 0666,
"setup", {Qsetup}, 0, 0666,
"status", {Qstatus}, 0, 0444,
"debug", {Qdebug}, 0, 0666,
/* epNdata names are generated on demand */
};
static Udev *
usbdeviceofpath(ulong path)
{
int s;
s = DEVPATH(path);
if(s == 0)
return nil;
return usbdev[s-1];
}
static Udev *
usbdevice(Chan *c)
{
Udev *d;
d = usbdeviceofpath(c->qid.path);
if(d == nil || d->id != c->qid.vers || d->state == Disabled)
error(Ehungup);
return d;
}
static Udev *
usbnewdevice(void)
{
Udev *d;
Endpt *e;
int i;
d = nil;
qlock(&usbstate);
if(waserror()){
qunlock(&usbstate);
nexterror();
}
for(i=0; i<nelem(usbdev); i++)
if(usbdev[i] == nil){
ubus.idgen++;
d = mallocz(sizeof(*d), 1);
d->ref = 1;
d->x = i;
d->id = (ubus.idgen << 8) | i;
d->state = Enabled;
e = devendpt(d, 0, 1); /* always provide control endpoint 0 */
e->mode = ORDWR;
e->periodic = 0;
e->sched = -1;
usbdev[i] = d;
break;
}
poperror();
qunlock(&usbstate);
return d;
}
static void
usbreset(void)
{
Pcidev *cfg;
int i;
ulong port;
QTree *qt;
TD *t;
Ctlr *ub;
ISAConf isa;
if(isaconfig("usb", 0, &isa) == 0) {
XPRINT("usb not in plan9.ini\n");
// return;
}
ub = &ubus;
memset(&cfg, 0, sizeof(cfg));
cfg = pcimatch(0, 0x8086, 0x7112); /* Intel chipset PIIX 4*/
if(cfg == nil) {
cfg = pcimatch(0, 0x1106, 0x0586); /* Via chipset */
if(cfg == nil) {
DPRINT("No USB device found\n");
return;
}
}
port = cfg->mem[4].bar & ~0x0F;
if (port == 0) {
print("usb: failed to map registers\n");
return;
}
DPRINT("USB: %x/%x port 0x%lux size 0x%x irq %d\n",
cfg->vid, cfg->did, port, cfg->mem[4].size, cfg->intl);
i = inb(port+SOFMod);
if(1){
OUT(Cmd, 4); /* global reset */
delay(15);
OUT(Cmd, 0); /* end reset */
delay(4);
}
outb(port+SOFMod, i);
/*
* Interrupt handler.
* Bail out if no IRQ assigned by the BIOS.
*/
if(cfg->intl == 0xFF || cfg->intl == 0)
return;
intrenable(cfg->intl, interrupt, ub, cfg->tbdf, "usb");
ub->io = port;
ub->tdpool = xspanalloc(128*sizeof(TD), 16, 0);
for(i=128; --i>=0;){
ub->tdpool[i].next = ub->freetd;
ub->freetd = &ub->tdpool[i];
}
ub->qhpool = xspanalloc(32*sizeof(QH), 16, 0);
for(i=32; --i>=0;){
ub->qhpool[i].next = ub->freeqh;
ub->freeqh = &ub->qhpool[i];
}
/*
* the root of the periodic (interrupt & isochronous) scheduling tree
* points to the control queue and the bandwidth sop for bulk traffic.
* this is looped following the instructions in PIIX4 errata 29773804.pdf:
* a QH links to a looped but inactive TD as its sole entry,
* with its head entry leading on to the bulk traffic, the last QH of which
* links back to the empty QH.
*/
ub->ctlq = allocqh(ub);
ub->bwsop = allocqh(ub);
ub->bulkq = allocqh(ub);
ub->recvq = allocqh(ub);
t = alloctd(ub); /* inactive TD, looped */
t->link = PADDR(t);
ub->bwsop->entries = PADDR(t);
ub->ctlq->head = PADDR(ub->bwsop) | IsQH;
ub->bwsop->head = PADDR(ub->bulkq) | IsQH;
ub->bulkq->head = PADDR(ub->recvq) | IsQH;
ub->recvq->head = PADDR(ub->bwsop) | IsQH; /* loop back */
XPRINT("usbcmd\t0x%.4x\nusbsts\t0x%.4x\nusbintr\t0x%.4x\nfrnum\t0x%.2x\n",
IN(Cmd), IN(Status), IN(Usbintr), inb(port+Frnum));
XPRINT("frbaseadd\t0x%.4x\nsofmod\t0x%x\nportsc1\t0x%.4x\nportsc2\t0x%.4x\n",
IN(Flbaseadd), inb(port+SOFMod), IN(Portsc0), IN(Portsc1));
OUT(Cmd, 0); /* stop */
ub->frames = xspanalloc(FRAMESIZE, FRAMESIZE, 0);
qt = mkqhtree(ub->frames, NFRAME, 32);
if(qt == nil){
print("usb: can't allocate scheduling tree\n");
ub->io = 0;
return;
}
qt->root->head = PADDR(ub->ctlq) | IsQH;
ub->tree = qt;
XPRINT("usb tree: nel=%d depth=%d\n", qt->nel, qt->depth);
outl(port+Flbaseadd, PADDR(ub->frames));
OUT(Frnum, 0);
OUT(Usbintr, 0xF); /* enable all interrupts */
XPRINT("cmd 0x%x sofmod 0x%x\n", IN(Cmd), inb(port+SOFMod));
XPRINT("sc0 0x%x sc1 0x%x\n", IN(Portsc0), IN(Portsc1));
}
void
usbinit(void)
{
Udev *d;
if(ubus.io != 0 && usbdev[0] == nil){
d = usbnewdevice(); /* reserve device 0 for configuration */
incref(d);
d->state = Attached;
}
}
Chan *
usbattach(char *spec)
{
Ctlr *ub;
ub = &ubus;
if(ub->io == 0) {
XPRINT("usbattach failed\n");
error(Enodev);
}
if((IN(Cmd)&1)==0 || *spec)
OUT(Cmd, 1); /* run */
// pprint("at: c=%x s=%x c0=%x\n", IN(Cmd), IN(Status), IN(Portsc0));
return devattach('U', spec);
}
static int
usbgen(Chan *c, char *, Dirtab*, int, int s, Dir *dp)
{
int t;
Qid q;
ulong path;
Udev *d;
Dirtab *tab;
Endpt *e;
/*
* Top level directory contains the name of the device.
*/
if(c->qid.path == Qtopdir){
if(s == DEVDOTDOT){
mkqid(&q, Qtopdir, 0, QTDIR);
devdir(c, q, "#U", 0, eve, 0555, dp);
return 1;
}
if(s == 0){
mkqid(&q, Q2nd, 0, QTDIR);
devdir(c, q, "usb", 0, eve, 0555, dp);
return 1;
}
return -1;
}
/*
* Second level contains "new" plus all the clients.
*/
t = QID(c->qid);
if(t < Q3rd){
if(s == DEVDOTDOT){
mkqid(&q, Qtopdir, 0, QTDIR);
devdir(c, q, "#U", 0, eve, 0555, dp);
return 1;
}
if(s < nelem(usbdir2)){
tab = &usbdir2[s];
devdir(c, tab->qid, tab->name, tab->length, eve, tab->perm, dp);
return 1;
}
s -= nelem(usbdir2);
if(s >= 0 && s < nelem(usbdev)){
d = usbdev[s];
if(d == nil)
return -1;
sprint(up->genbuf, "%d", s);
mkqid(&q, ((s+1)<<QSHIFT)|Q3rd, d->id, QTDIR);
devdir(c, q, up->genbuf, 0, eve, 0555, dp);
return 1;
}
return -1;
}
/*
* Third level.
*/
path = c->qid.path & ~QMASK; /* slot component */
if(s == DEVDOTDOT){
mkqid(&q, Q2nd, c->qid.vers, QTDIR);
devdir(c, q, "usb", 0, eve, 0555, dp);
return 1;
}
if(s < nelem(usbdir3)){
Dirtab *tab = &usbdir3[s];
mkqid(&q, path | tab->qid.path, c->qid.vers, QTFILE);
devdir(c, q, tab->name, tab->length, eve, tab->perm, dp);
return 1;
}
/* active endpoints */
d = usbdeviceofpath(path);
if(d == nil)
return -1;
s -= nelem(usbdir3);
if(s < 0 || s >= nelem(d->ep))
return -1;
if(s == 0 || (e = d->ep[s]) == nil) /* ep0data is called "setup" */
return 0;
sprint(up->genbuf, "ep%ddata", s);
mkqid(&q, path | (Qep0+s), c->qid.vers, QTFILE);
devdir(c, q, up->genbuf, 0, eve, e->mode==OREAD? 0444: e->mode==OWRITE? 0222: 0666, dp);
return 1;
}
static Walkqid*
usbwalk(Chan *c, Chan *nc, char **name, int nname)
{
return devwalk(c, nc, name, nname, nil, 0, usbgen);
}
static int
usbstat(Chan *c, uchar *db, int n)
{
return devstat(c, db, n, nil, 0, usbgen);
}
Chan *
usbopen(Chan *c, int omode)
{
Udev *d;
int f, s;
if(c->qid.type == QTDIR)
return devopen(c, omode, nil, 0, usbgen);
f = 0;
if(QID(c->qid) == Qnew){
d = usbnewdevice();
if(d == nil) {
XPRINT("usbopen failed (usbnewdevice)\n");
error(Enodev);
}
c->qid.path = Qctl|((d->x+1)<<QSHIFT);
c->qid.vers = d->id;
f = 1;
}
if(c->qid.path < Q3rd)
return devopen(c, omode, nil, 0, usbgen);
qlock(&usbstate);
if(waserror()){
qunlock(&usbstate);
nexterror();
}
switch(QID(c->qid)){
case Qctl:
d = usbdevice(c);
if(0&&d->busy)
error(Einuse);
d->busy = 1;
if(!f)
incref(d);
break;
default:
d = usbdevice(c);
s = QID(c->qid) - Qep0;
if(s >= 0 && s < nelem(d->ep)){
Endpt *e;
if((e = d->ep[s]) == nil) {
XPRINT("usbopen failed (endpoint)\n");
error(Enodev);
}
if(schedendpt(e) < 0)
error("can't schedule USB endpoint");
eptactivate(e);
}
incref(d);
break;
}
poperror();
qunlock(&usbstate);
c->mode = openmode(omode);
c->flag |= COPEN;
c->offset = 0;
return c;
}
void
usbcreate(Chan *c, char *name, int omode, ulong perm)
{
USED(c, name, omode, perm);
error(Eperm);
}
void
usbremove(Chan*)
{
error(Eperm);
}
void
usbwstat(Chan *c, char *dp)
{
USED(c, dp);
error(Eperm);
}
void
usbclose(Chan *c)
{
Udev *d;
if(c->qid.type == QTDIR || c->qid.path < Q3rd)
return;
qlock(&usbstate);
if(waserror()){
qunlock(&usbstate);
nexterror();
}
d = usbdevice(c);
if(QID(c->qid) == Qctl)
d->busy = 0;
if(c->flag & COPEN)
freedev(d);
poperror();
qunlock(&usbstate);
}
static int
eptinput(void *arg)
{
Endpt *e;
e = arg;
return e->eof || e->err || qcanread(e->rq);
}
static long
readusb(Endpt *e, void *a, long n)
{
Block *b;
uchar *p;
long l, i;
XPRINT("qlock(%p)\n", &e->rlock);
qlock(&e->rlock);
XPRINT("got qlock(%p)\n", &e->rlock);
if(waserror()){
qunlock(&e->rlock);
eptcancel(e);
nexterror();
}
p = a;
do {
if(e->eof) {
XPRINT("e->eof\n");
break;
}
if(e->err)
error(e->err);
qrcv(e);
if(!e->iso)
e->data01 ^= 1;
sleep(&e->rr, eptinput, e);
if(e->err)
error(e->err);
b = qget(e->rq); /* TO DO */
if(b == nil) {
XPRINT("b == nil\n");
break;
}
if(waserror()){
freeb(b);
nexterror();
}
l = BLEN(b);
if((i = l) > n)
i = n;
if(i > 0){
memmove(p, b->rp, i);
p += i;
}
poperror();
freeb(b);
n -= i;
} while (l == e->maxpkt && n > 0);
poperror();
qunlock(&e->rlock);
return p-(uchar*)a;
}
long
usbread(Chan *c, void *a, long n, vlong offset)
{
Endpt *e;
Udev *d;
char buf[48], *s;
int t, w0, w1, ps, l, i;
if(c->qid.type == QTDIR)
return devdirread(c, a, n, nil, 0, usbgen);
t = QID(c->qid);
switch(t){
case Qbusctl:
snprint(buf, sizeof(buf), "%11d %11d ", 0, 0);
return readstr(offset, a, n, buf);
case Qport:
ps = portinfo(&ubus, &w0, &w1);
snprint(buf, sizeof(buf), "0x%ux 0x%ux 0x%ux ", ps, w0, w1);
return readstr(offset, a, n, buf);
case Qctl:
d = usbdevice(c);
sprint(buf, "%11d %11d ", d->x, d->id);
return readstr(offset, a, n, buf);
case Qsetup: /* endpoint 0 */
d = usbdevice(c);
if((e = d->ep[0]) == nil)
error(Eio); /* can't happen */
e->data01 = 1;
n = readusb(e, a, n);
if(e->setin){
e->setin = 0;
e->data01 = 1;
writeusb(e, "", 0, TokOUT);
}
break;
case Qdebug:
n=0;
break;
case Qstatus:
d = usbdevice(c);
s = smalloc(READSTR);
if(waserror()){
free(s);
nexterror();
}
l = snprint(s, READSTR, "%s %#6.6lux\n", devstates[d->state], d->csp);
for(i=0; i<nelem(d->ep); i++)
if((e = d->ep[i]) != nil) /* TO DO: freeze e */
l += snprint(s+l, READSTR-l, "%2d %#6.6lux %10lud bytes %10lud blocks\n", i, e->csp, e->nbytes, e->nblocks);
n = readstr(offset, a, n, s);
poperror();
free(s);
break;
default:
d = usbdevice(c);
if((t -= Qep0) < 0 || t >= nelem(d->ep))
error(Eio);
if((e = d->ep[t]) == nil || e->mode == OWRITE)
error(Eio); /* can't happen */
n=readusb(e, a, n);
break;
}
return n;
}
static int
qisempty(void *arg)
{
return ((QH*)arg)->entries & Terminate;
}
static long
writeusb(Endpt *e, void *a, long n, int tok)
{
long i;
Block *b;
uchar *p;
QH *qh;
p = a;
qlock(&e->wlock);
if(waserror()){
qunlock(&e->wlock);
eptcancel(e);
nexterror();
}
do {
int j;
if(e->err)
error(e->err);
if((i = n) >= e->maxpkt)
i = e->maxpkt;
b = allocb(i);
if(waserror()){
freeb(b);
nexterror();
}
XPRINT("out [%ld]", i);
for (j = 0; j < i; j++) XPRINT(" %.2x", p[j]);
XPRINT("\n");
memmove(b->wp, p, i);
b->wp += i;
p += i;
n -= i;
poperror();
qh = qxmit(e, b, tok);
tok = TokOUT;
if(!e->iso)
e->data01 ^= 1;
if(e->ntd >= e->nbuf) {
XPRINT("writeusb: sleep %lux\n", &e->wr);
sleep(&e->wr, qisempty, qh);
XPRINT("writeusb: awake\n");
}
} while(n > 0);
poperror();
qunlock(&e->wlock);
return p-(uchar*)a;
}
long
usbwrite(Chan *c, void *a, long n, vlong)
{
Udev *d;
Endpt *e;
int id, nw, nf, t, i;
char cmd[50], *fields[10];
if(c->qid.type == QTDIR)
error(Egreg);
t = QID(c->qid);
if(t == Qbusctl){
if(n >= sizeof(cmd)-1)
n = sizeof(cmd)-1;
memmove(cmd, a, n);
cmd[n] = 0;
nf = getfields(cmd, fields, nelem(fields), 0, " \t\n");
if(nf==1 && strcmp(fields[0], "dump")==0){
dumpframe(-1, -1);
return n;
}
if(nf < 2)
error(Ebadarg);
id = strtol(fields[1], nil, 0);
if(id != 1 && id != 2)
error(Ebadarg); /* there are two ports on the root hub */
if(strcmp(fields[0], "reset") == 0)
portreset(id);
else if(strcmp(fields[0], "enable") == 0)
portenable(id, 1);
else if(strcmp(fields[0], "disable") == 0)
portenable(id, 0);
else
error(Ebadarg);
return n;
}
d = usbdevice(c);
t = QID(c->qid);
switch(t){
case Qctl:
if(n >= sizeof(cmd)-1)
n = sizeof(cmd)-1;
memmove(cmd, a, n);
cmd[n] = 0;
nf = getfields(cmd, fields, nelem(fields), 0, " \t\n");
if(nf > 1 && strcmp(fields[0], "speed") == 0){
d->ls = strtoul(fields[1], nil, 0) == 0;
} else if(nf > 3 && strcmp(fields[0], "class") == 0){
i = strtoul(fields[2], nil, 0);
d->npt = strtoul(fields[1], nil, 0);
/* class config# csp ( == class subclass proto) */
if (i < 0 || i >= nelem(d->ep)
|| d->npt > nelem(d->ep) || i >= d->npt)
error(Ebadarg);
if (i == 0) {
d->csp = strtoul(fields[3], nil, 0);
}
if(d->ep[i] == nil)
d->ep[i] = devendpt(d, i, 1);
d->ep[i]->csp = strtoul(fields[3], nil, 0);
}else if(nf > 2 && strcmp(fields[0], "data") == 0){
i = strtoul(fields[1], nil, 0);
if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil)
error(Ebadarg);
e = d->ep[i];
e->data01 = strtoul(fields[2], nil, 0) != 0;
}else if(nf > 2 && strcmp(fields[0], "maxpkt") == 0){
i = strtoul(fields[1], nil, 0);
if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil)
error(Ebadarg);
e = d->ep[i];
e->maxpkt = strtoul(fields[2], nil, 0);
if(e->maxpkt > 1500)
e->maxpkt = 1500;
}else if(nf > 2 && strcmp(fields[0], "debug") == 0){
i = strtoul(fields[1], nil, 0);
if(i < -1 || i >= nelem(d->ep) || d->ep[i] == nil)
error(Ebadarg);
if (i == -1)
debug = 0;
else {
debug = 1;
e = d->ep[i];
e->debug = strtoul(fields[2], nil, 0);
}
}else if(nf > 1 && strcmp(fields[0], "unstall") == 0){
i = strtoul(fields[1], nil, 0);
if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil)
error(Ebadarg);
e = d->ep[i];
e->err = nil;
}else if(nf == 6 && strcmp(fields[0], "ep") == 0){
/* ep n maxpkt mode poll nbuf */
i = strtoul(fields[1], nil, 0);
if(i < 0 || i >= nelem(d->ep)) {
XPRINT("field 1: 0 <= %d < %d\n", i, nelem(d->ep));
error(Ebadarg);
}
if((e = d->ep[i]) == nil)
e = devendpt(d, i, 1);
if(waserror()){
freept(e);
nexterror();
}
i = strtoul(fields[2], nil, 0);
if(i < 8 || i > 1023)
i = 8;
e->maxpkt = i;
e->mode = strcmp(fields[3],"r") == 0? OREAD :
strcmp(fields[3],"w") == 0? OWRITE : ORDWR;
e->periodic = 0;
e->sched = -1;
if(strcmp(fields[4], "bulk") == 0){
Ctlr *ub;
ub = &ubus;
/* Each bulk device gets a queue head hanging off the
* bulk queue head
*/
if (e->epq == nil) {
e->epq = allocqh(ub);
if(e->epq == nil)
panic("usbwrite: allocqh");
}
queueqh(e->epq);
} else {
e->periodic = 1;
i = strtoul(fields[4], nil, 0);
if(i > 0 && i <= 1000)
e->pollms = i;
else {
XPRINT("field 4: 0 <= %d <= 1000\n", i);
error(Ebadarg);
}
}
i = strtoul(fields[5], nil, 0);
if(i >= 1 && i <= 32)
e->nbuf = i;
poperror();
}else {
XPRINT("command %s, fields %d\n", fields[0], nf);
error(Ebadarg);
}
return n;
case Qsetup: /* SETUP endpoint 0 */
/* should canqlock etc */
if((e = d->ep[0]) == nil)
error(Eio); /* can't happen */
if(n < 8 || n > 1023)
error(Eio);
nw = *(uchar*)a & RD2H;
e->data01 = 0;
n = writeusb(e, a, n, TokSETUP);
if(nw == 0){ /* host to device: use IN[DATA1] to ack */
e->data01 = 1;
nw = readusb(e, cmd, 8);
if(nw != 0)
error(Eio); /* could provide more status */
}else
e->setin = 1; /* two-phase */
break;
default: /* sends DATA[01] */
if((t -= Qep0) < 0 || t >= nelem(d->ep)) {
print("t = %d\n", t);
error(Eio);
}
if((e = d->ep[t]) == nil || e->mode == OREAD) {
error(Eio); /* can't happen */
}
n = writeusb(e, a, n, TokOUT);
break;
}
return n;
}
Dev usbdevtab = {
'U',
"usb",
usbreset,
usbinit,
usbattach,
usbwalk,
usbstat,
usbopen,
devcreate,
usbclose,
usbread,
devbread,
usbwrite,
devbwrite,
devremove,
devwstat,
};
.
568,570c
tp = t;
t = t->next;
continue;
.
553c
XPRINT("cleanq: %8.8lux %8.8lux %8.8lux %8.8lux %8.8lux %8.8lux\n",
t->link, t->status, t->dev, t->buffer, t->flags, t->next);
.
490c
XPRINT("cleanTD: Error %8.8lux %8.8lux %8.8lux %8.8lux\n",
t->link, t->status, t->dev, t->buffer);
.
482c
XPRINT("cleanTD: %8.8lux %8.8lux %8.8lux %8.8lux\n",
t->link, t->status, t->dev, t->buffer);
.
372c
XPRINT("\ts=%s,ep=%ld,d=%ld,D=%ld\n",
buf, (t->dev>>15)&0xF, (t->dev>>8)&0xFF, (t->dev>>19)&1);
.
218,223c
byte mode; /* OREAD, OWRITE, ORDWR */
byte nbuf; /* number of buffers allowed */
byte periodic;
byte iso;
byte debug;
byte active; /* listed for examination by interrupts */
.
216c
byte eof;
.
202c
[Assigned] "Assigned",
.
200c
[Attached] "Attached",
.
## diffname pc/devusb.c 2001/0726
## diff -e /n/emeliedump/2001/0626/sys/src/9/pc/devusb.c /n/emeliedump/2001/0726/sys/src/9/pc/devusb.c
1256c
return;
.
## diffname pc/devusb.c 2001/0916
## diff -e /n/emeliedump/2001/0726/sys/src/9/pc/devusb.c /n/emeliedump/2001/0916/sys/src/9/pc/devusb.c
1904,1906d
1902a
e->mode = strcmp(fields[3],"r") == 0? OREAD :
strcmp(fields[3],"w") == 0? OWRITE : ORDWR;
i = strtoul(fields[4], nil, 0);
if(i >= 1 && i <= 8){
e->samplesz = i;
}else {
XPRINT("field 4: 0 < %d <= 8\n", i);
error(Ebadarg);
}
i = strtoul(fields[5], nil, 0);
if(i >= 1 && i <= 100000){
/* Hz */
e->hz = i;
e->remain = 999/e->pollms;
}else {
XPRINT("field 5: 1 < %d <= 100000 Hz\n", i);
error(Ebadarg);
}
e->maxpkt = (e->hz * e->pollms + 999)/1000 * e->samplesz;
e->iso = 1;
.
1899c
}else {
.
1897c
if(i < 8 || i > 1023)
i = 8;
e->maxpkt = i;
i = strtoul(fields[5], nil, 0);
if(i >= 1 && i <= 32)
e->nbuf = i;
} else {
/* ep n period mode samplesize KHz */
i = strtoul(fields[2], nil, 0);
if(i > 0 && i <= 1000){
.
1894,1895c
e->mode = strcmp(fields[3],"r") == 0? OREAD :
strcmp(fields[3],"w") == 0? OWRITE : ORDWR;
.
1883a
/* ep n `bulk' mode maxpkt nbuf */
.
1873,1881c
if (e->iso && e->sched >= 0){
unschedendpt(e);
}
e->iso = 0;
if(strcmp(fields[2], "bulk") == 0){
.
1861c
/* ep n `bulk' mode maxpkt nbuf OR
* ep n period mode samplesize KHz
*/
.
1786,1789d
1746,1762d
1739,1744c
if (e->iso){
if (e->curbytes == 0){
e->psize = (e->hz + e->remain)*e->pollms/1000;
e->remain = (e->hz + e->remain)*e->pollms%1000;
e->psize *= e->samplesz;
}
if (e->psize > e->maxpkt)
panic("packet size > maximum");
b = e->curblock;
if (b == nil)
panic("writeusb: block");
if((i = n) >= e->psize - e->curbytes)
i = e->psize - e->curbytes;
if (e->curbytes != BLEN(b))
iprint("mismatch: curbytes %d, blen %ld\n", e->curbytes, BLEN(b));
memmove(b->wp, p, i);
b->wp += i;
p += i;
n -= i;
e->curbytes += i;
if (e->curbytes < e->psize){
e->curblock = b;
if (n != 0)
panic("usb iso: can't happen");
break;
}
if (BLEN(b) != e->psize)
panic("usbwrite: length mismatch");
e->curblock = isoxmit(e, b, TokOUT);
e->curbytes = 0;
e->curblock->wp = e->curblock->rp;
e->nbytes += e->psize;
e->nblocks++;
}else{
int j;
if((i = n) >= e->maxpkt)
i = e->maxpkt;
b = allocb(i);
if(waserror()){
freeb(b);
nexterror();
}
XPRINT("out [%ld]", i);
for (j = 0; j < i; j++) XPRINT(" %.2x", p[j]);
XPRINT("\n");
memmove(b->wp, p, i);
b->wp += i;
p += i;
n -= i;
poperror();
qh = qxmit(e, b, tok);
tok = TokOUT;
if(!e->iso)
e->data01 ^= 1;
if(e->ntd >= e->nbuf) {
XPRINT("writeusb: sleep %lux\n", &e->wr);
sleep(&e->wr, qisempty, qh);
XPRINT("writeusb: awake\n");
}
.
1735d
1336,1338d
1330,1334c
ub->frameld = mallocz(FRAMESIZE, 1);
for (i = 0; i < NFRAME; i++){
j = NISOTD*i;
ub->frames[i] = PADDR(&ub->tdpool[j]); /* breadth 1st, is TD, no termin. */
ub->tdpool[j+0].link = PADDR(&ub->tdpool[j+1]);
ub->tdpool[j+0].status = IsoSelect;
ub->tdpool[j+0].dev = 0;
ub->tdpool[j+0].buffer = 0;
ub->tdpool[j+1].link = PADDR(&ub->tdpool[j+2]);
ub->tdpool[j+1].status = IsoSelect;
ub->tdpool[j+1].dev = 0;
ub->tdpool[j+1].buffer = 0;
ub->tdpool[j+2].link = PADDR(&ub->tdpool[j+3]);
ub->tdpool[j+2].status = IsoSelect;
ub->tdpool[j+2].dev = 0;
ub->tdpool[j+2].buffer = 0;
ub->tdpool[j+3].link = PADDR(ub->ctlq) | IsQH;
ub->tdpool[j+3].status = IsoSelect;
ub->tdpool[j+3].dev = 0;
ub->tdpool[j+3].buffer = 0;
.
1306,1307c
* the last entries of the periodic (interrupt & isochronous) scheduling TD entries
* point to the control queue and the bandwidth sop for bulk traffic.
.
1294,1295c
ub->tdpool = xspanalloc((NFRAME*NISOTD+128)*sizeof(TD), 16, 0);
for(i=NFRAME*NISOTD+128; --i>=NFRAME*NISOTD;){
.
1249d
1247c
int i, j;
.
1233c
e->iso = 0;
.
1143a
if(e->iso && e->etd != nil) {
wakeup(&e->wr);
}
}
.
1139,1140c
for(e = activends.f; e != nil; e = e->activef){
if(!e->iso && e->epq != nil) {
.
958d
954c
e->iso = 0;
.
919,930c
ilock(ub);
for (td = e->etd; q < NFRAME; q += e->pollms){
ub->frameld[q] -= e->maxpkt;
next = td->next;
td->ep = nil;
td->next = nil;
freeb(td->bp);
td->bp = nil;
td = next;
}
freeb(e->curblock);
e->sched = -1;
iunlock(ub);
.
917c
if(!e->iso || (q = e->sched) < 0)
.
912,913c
TD *td, *next;
.
898,904c
/* Allocate TDs from the NISOTD entries on each of the
* frames. Link them circularly so they can be walked easily
*/
prev = nil;
first = nil;
td = nil;
for(i = e->sched; i < NFRAME; i += e->pollms){
for(j = 0; j < NISOTD; j++){
td = &ub->tdpool[i*NISOTD + j];
if(td->ep == nil)
break;
}
if(j == NISOTD)
panic("usb: no td entries despite schedulability test");
td->ep = e;
if (td->bp)
panic("usb: schedendpt: block not empty");
td->bp = allocb(e->maxpkt);
if (td->bp == nil)
panic("schedept: allocb");
if (i == e->sched){
first = td;
e->etd = td;
}else{
*prev = td;
}
prev = &td->next;
ub->frameld[i] += e->maxpkt;
}
*prev = first; /* complete circular link */
iunlock(ub);
.
889,896c
ilock(ub);
e->sched = usbsched(ub, e->pollms, e->maxpkt);
if(e->sched < 0)
.
886c
e->curblock = allocb(e->maxpkt);
if (e->curblock == nil)
panic("schedept: allocb");
e->curbytes = 0;
if(!e->iso || e->sched >= 0)
.
883,884c
TD *td, **prev, *first;
int i, j;
.
874,875d
871,872d
847,869c
if (worst < best){
best = worst;
q = d;
.
845c
if (ub->frameld[i] + load > worst)
worst = ub->frameld[i] + load;
.
780,842c
best = 1000000;
q = -1;
for (d = 0; d < pollms; d++){
worst = 0;
for (i = d; i < NFRAME; i++){
for(j = 0; j < NISOTD; j++)
if(ub->tdpool[NISOTD*i + j].ep == nil)
break;
if(j == NISOTD){
worst = 1000000; /* No free TDs */
.
776,778c
int i, j, d, q;
ulong best, worst;
.
769,774c
static int
usbsched( Ctlr *ub, int pollms, ulong load)
.
683a
static int
tdisready(void *arg)
{
return (((TD*)arg)->status & Active) == 0;
}
static Block *
isoxmit(Endpt *e, Block *b, int pid)
{
TD *td;
int n, id;
Block *ob;
static int ioc;
td = e->etd;
if (td == nil)
panic("usb: isoxmit");
while(td->status & Active){
XPRINT("isoxmit: sleep %lux\n", &e->wr);
sleep(&e->wr, tdisready, e->etd);
XPRINT("isoxmit: awake\n");
}
if (td->status & AnyError)
iprint("usbisoerror 0x%lux\n", td->status);
ob = td->bp;
if (ob == nil)
panic("isoxmit: null block");
td->buffer = PADDR(b->rp);
td->bp = b;
n = BLEN(b);
id = (e->x<<7)|(e->dev->x&0x7F);
td->dev = ((n-1)<<21) | ((id&0x7FF)<<8) | pid;
if ((ioc++ & 0xff) == 0)
td->status = ErrLimit3 | Active | IsoSelect | IOC;
else
td->status = ErrLimit3 | Active | IsoSelect;
e->etd = td->next;
return ob;
}
.
260,264c
QH* ctlq; /* queue for control i/o */
QH* bwsop; /* empty bandwidth sop (to PIIX4 errata specifications) */
QH* bulkq; /* queue for bulk i/o (points back to bandwidth sop) */
QH* recvq; /* receive queues for bulk i/o */
.
255,258c
TD* tdpool; /* first NFRAMES*NISOTD entries are preallocated */
TD* freetd;
QH* qhpool;
QH* freeqh;
.
252c
ulong* frameld; /* real time load on each of the frame list entries */
int idgen; /* version number to distinguish new connections */
.
250c
int io;
.
228,229c
int psize; /* size of this packet */
Block *curblock; /* half full block between iso writes */
int curbytes; /* # of bytes in that block */
QH* epq; /* queue of TDs for this endpoint */
TD* etd; /* pointer into circular list of TDs for isochronous ept */
.
226d
223a
int hz;
int remain;
int samplesz;
.
220d
212c
int x; /* index in Udev.ep */
.
179,180c
Endpt* ep[16]; /* active end points */
Udev* ports; /* active ports, if hub */
.
175c
byte port; /* port number on connecting hub */
.
171c
int x; /* index in usbdev[] */
.
151,161d
120a
NISOTD = 4, /* number of TDs for isochronous io per frame */
.
53d
25c
#define XPRINT if(debug)iprint
.
## diffname pc/devusb.c 2001/0917
## diff -e /n/emeliedump/2001/0916/sys/src/9/pc/devusb.c /n/emeliedump/2001/0917/sys/src/9/pc/devusb.c
1730d
1620,1630c
} while (n > 0);
.
1618c
n -= i;
if (l != e->maxpkt)
break;
.
1605,1616c
if (e->iso){
if (e->curbytes == 0){
/* calculate size of next receive buffer */
e->psize = (e->hz + e->remain)*e->pollms/1000;
e->remain = (e->hz + e->remain)*e->pollms%1000;
e->psize *= e->samplesz;
if (e->psize > e->maxpkt)
panic("packet size > maximum");
e->curblock->wp = e->curblock->rp + e->psize;
/* put empty buffer on receive queue and get next full one */
b = isorecv(e, e->curblock, TokIN);
/* bytes to consume in b: */
e->curbytes = BLEN(b);
b->wp = b->rp;
e->nbytes += e->curbytes;
e->nblocks++;
}else{
b = e->curblock;
}
if (b == nil)
panic("readusb: block");
if((i = n) >= e->curbytes)
i = e->curbytes;
memmove(p, b->wp, i);
b->wp += i;
p += i;
n -= i;
e->curbytes -= i;
e->curblock = b;
}else{
qrcv(e);
if(!e->iso)
e->data01 ^= 1;
sleep(&e->rr, eptinput, e);
if(e->err)
error(e->err);
b = qget(e->rq); /* TO DO */
if(b == nil) {
XPRINT("b == nil\n");
break;
}
if(waserror()){
freeb(b);
nexterror();
}
l = BLEN(b);
if((i = l) > n)
i = n;
if(i > 0){
memmove(p, b->rp, i);
p += i;
}
poperror();
.
845a
e->curblock = allocb(e->maxpkt);
if (e->curblock == nil)
panic("schedept: allocb");
e->curbytes = 0;
.
838,841d
716a
static Block*
isorecv(Endpt *e, Block *b, int pid)
{
TD *td;
int n, id;
Block *ob;
static int ioc;
td = e->etd;
if (td == nil)
panic("usb: isoxmit");
while(td->status & Active){
XPRINT("isorecv: sleep %lux\n", &e->wr);
sleep(&e->wr, tdisready, e->etd);
XPRINT("isorecv: awake\n");
}
if (td->status & AnyError)
iprint("usbisoerror 0x%lux\n", td->status);
ob = td->bp;
if (ob == nil)
panic("isorecv: null block");
n = (td->status + 1) & 0x7FF;
if(n > ob->lim - b->wp)
n = 0;
ob->wp = ob->rp + n;
td->buffer = PADDR(b->rp);
td->bp = b;
n = BLEN(b);
id = (e->x<<7)|(e->dev->x&0x7F);
td->dev = ((n-1)<<21) | ((id&0x7FF)<<8) | pid;
if ((ioc++ & 0xff) == 0)
td->status = ErrLimit3 | Active | IsoSelect | IOC;
else
td->status = ErrLimit3 | Active | IsoSelect;
e->etd = td->next;
return ob;
}
.
## diffname pc/devusb.c 2001/0918
## diff -e /n/emeliedump/2001/0917/sys/src/9/pc/devusb.c /n/emeliedump/2001/0918/sys/src/9/pc/devusb.c
2029a
qunlock(&usbstate);
.
2021c
// e->remain = 999/e->pollms;
e->remain = 0;
.
1978a
e->iso = 0;
.
1972,1975c
if (e->isopen)
error(Eperm);
.
1969a
qunlock(&usbstate);
.
1967a
qlock(&usbstate);
.
1646,1651d
1557a
e->isopen = 1;
.
1001a
e->isopen = 0;
.
947,948d
934,935c
.
920d
910c
if (e->mode == OREAD){
int id;
/* enable receive on this entry */
td->buffer = PADDR(td->bp->rp);
id = (e->x<<7)|(e->dev->x&0x7F);
td->dev = ((e->maxpkt-1)<<21) | ((id&0x7FF)<<8) | TokIN;
td->status = ErrLimit3 | Active | IsoSelect | IOC;
}
if(i == e->sched){
.
908c
if(td->bp == nil)
.
905c
if(td->bp)
.
887a
}
.
886c
if(e->sched < 0){
qunlock(&usbstate);
.
884d
879c
if (e->isopen){
return -1;
}
.
746,750c
td->dev = ((e->maxpkt-1)<<21) | ((id&0x7FF)<<8) | pid;
td->status = ErrLimit3 | Active | IsoSelect | IOC;
.
744d
727c
panic("usb: isorecv");
.
206a
byte isopen; /* ep operations forbidde on open endpoints */
.
## diffname pc/devusb.c 2001/0922
## diff -e /n/emeliedump/2001/0918/sys/src/9/pc/devusb.c /n/emeliedump/2001/0922/sys/src/9/pc/devusb.c
1610a
s = QID(c->qid) - Qep0;
if(s >= 0 && s < nelem(d->ep)){
Endpt *e;
if((e = d->ep[s]) == nil) {
XPRINT("usbopen failed (endpoint)\n");
error(Enodev);
}
e->isopen--;
}
.
1601a
int s;
.
1565d
1563a
e->isopen++;
.
1008d
981d
978d
882a
e->curblock = allocb(e->maxpkt);
if (e->curblock == nil)
.
880,881d
207c
byte isopen; /* ep operations forbidden on open endpoints */
.
## diffname pc/devusb.c 2001/0928
## diff -e /n/emeliedump/2001/0922/sys/src/9/pc/devusb.c /n/emeliedump/2001/0928/sys/src/9/pc/devusb.c
1968c
* ep n period mode samplesize hz
.
## diffname pc/devusb.c 2001/1003
## diff -e /n/emeliedump/2001/0928/sys/src/9/pc/devusb.c /n/emeliedump/2001/1003/sys/src/9/pc/devusb.c
710,713c
td->status = ErrLimit1 | Active | IsoSelect | IOC;
.
695c
while(td->status & Active && (td->dev & 0x7ff<21) != (0x7ff<21)){
.
## diffname pc/devusb.c 2001/1004
## diff -e /n/emeliedump/2001/1003/sys/src/9/pc/devusb.c /n/emeliedump/2001/1004/sys/src/9/pc/devusb.c
710c
if ((ioc++ & 0xff) == 0)
td->status = ErrLimit3 | Active | IsoSelect | IOC;
else
td->status = ErrLimit3 | Active | IsoSelect;
.
695c
while(td->status & Active){
.
690a
TD *td;
.
687,688d
## diffname pc/devusb.c 2001/1005
## diff -e /n/emeliedump/2001/1004/sys/src/9/pc/devusb.c /n/emeliedump/2001/1005/sys/src/9/pc/devusb.c
688c
static int ioc, n, id;
.
## diffname pc/devusb.c 2001/1006
## diff -e /n/emeliedump/2001/1005/sys/src/9/pc/devusb.c /n/emeliedump/2001/1006/sys/src/9/pc/devusb.c
1967c
* ep n period mode samplesize KHz
.
1871,1872c
} while(n > 0);
}
.
1854c
XPRINT("out [%d]", i);
.
1846a
if(e->err)
error(e->err);
.
1844c
} while(n > 0);
poperror();
}else{
do {
.
1841c
e->psize = ((e->etd->dev >> 21) & 0x7ff) + 1;
if (e->psize > e->maxpkt)
panic("packet size > maximum");
.
1837,1839c
e->etd = td->next;
.
1832d
1830a
td->flags &= ~IsoLock;
.
1824,1827c
memmove(q, p, i);
.
1817,1821c
td = e->etd;
e->isolock = 0;
td->flags |= IsoLock;
td->flags &= ~IsoClean;
q = (uchar*)td->bp + e->curbytes;
.
1808,1815c
e->td0 = (TD*)(((ulong)e->tdalloc + 0xf) & ~0xf);
if (e->iso){
if(waserror()){
e->isolock = 0;
if (td = e->etd)
td->flags &= ~IsoLock;
nexterror();
}
do {
while (isowready(e) == 0){
XPRINT("writeusb: sleep %lux\n", &e->wr);
sleep(&e->wr, isowready, e);
XPRINT("writeusb: awake\n");
.
1799a
TD *td;
.
1798c
uchar *p, *q;
.
1796c
int i;
.
1792a
static int
isowready(void *arg)
{
int frnum;
Ctlr *ub;
Endpt *e;
e = arg;
e->isolock = 1;
ub = &ubus;
frnum = IN(Frnum) & 0x3ff;
if (e->etd == nil){
/* writer was idle, find a place to start */
e->etd = e->td0 + ((frnum + 4) & 0x3ff);
e->curbytes = 0;
e->psize = ((e->etd->dev >> 21) & 0x7ff) + 1;
if (e->psize > e->maxpkt)
panic("packet size > maximum");
}
if (e->etd != e->xtd && e->etd->next != e->xtd){
/* leave locked if succesful */
return 1;
}
e->isolock = 0;
return 0;
}
.
1708,1709c
} while (n > 0);
}
.
1670,1680c
td = e->etd;
e->isolock = 0;
if (e->psize > 0){
q = (byte*)td->bp + e->curbytes;
if((i = n) >= e->psize)
i = e->psize;
memmove(p, q, i);
p += i;
n -= i;
e->curbytes += i;
e->psize -= i;
if (e->psize > 0){
/* Read done, return */
if (n != 0)
panic("usb iso: can't happen");
break;
}
}
e->isolock = 1;
/* Finished a packet */
id = (e->x<<7)|(e->dev->x&0x7F);
td->dev = ((e->maxpkt-1)<<21) | ((id&0x7FF)<<8) | TokIN;
td->status = ErrLimit3 | Active | IsoSelect | IOC;
if (e->xtd == td)
e->xtd = td->next;
/* Get next buffer, wait for it to become ready */
e->etd = td->next;
} while (n > 0);
}else{
do {
if(e->eof) {
XPRINT("e->eof\n");
break;
}
if(e->err)
error(e->err);
.
1650,1668c
if (e->iso){
do {
if (e->psize == 0){
while(isorready(e) == 0){
XPRINT("readusb: sleep %lux\n", &e->wr);
sleep(&e->wr, isorready, e);
XPRINT("readusb: awake\n");
}
.
1638,1639c
uchar *p, *q;
long l, i, id;
TD *td;
.
1633a
static int
isorready(void *arg)
{
int frnum;
Ctlr *ub;
Endpt *e;
e = arg;
e->isolock = 1;
ub = &ubus;
frnum = IN(Frnum) & 0x3ff;
if (e->etd == nil){
/* reader was idle, find a place to start */
e->etd = e->td0 + (frnum & 0x3ff);
}
if (e->etd->status & Active){
/* not yet ready */
e->isolock = 0;
return 0;
}
/* leave locked if succesful */
e->psize = (e->etd->status + 1) & 0x7FF;
e->curbytes = 0;
e->nbytes += e->psize;
e->nblocks++;
if (e->psize > e->maxpkt)
panic("packet size > maximum");
return 1;
}
.
1615a
if (e->isopen == 0){
eptdeactivate(e);
unschedendpt(e);
}
.
1355,1374c
for (i = 0; i < NFRAME; i++)
ub->frames[i] = PADDR(ub->ctlq) | IsQH;
.
1317,1318c
ub->tdpool = xspanalloc(128*sizeof(TD), 16, 0);
for(i=128; --i>=0;){
.
1271c
int i;
.
1164,1165c
if(e->iso) {
XPRINT("cleaniso(e)\n");
cleaniso(e, frnum);
.
1143c
frnum = IN(Frnum) & 0x3ff;
XPRINT("usbint: #%x f%d\n", s, frnum);
.
1135c
int s, frnum;
.
1130a
cleaniso(Endpt *e, int frnum)
{
TD *td;
int tok, id, size;
if (e->xtd->status & Active){
td = e->td0 + ((frnum -1)&0x3ff);
if (td->status & Active)
return;
iprint("%ld active -> %ld", e->xtd - e->td0, td - e->td0);
e->xtd = td;
}
tok = (e->mode == OREAD)?TokIN:TokOUT;
id = (e->x<<7)|(e->dev->x&0x7F);
for(td = e->xtd; (td->status & Active) == 0; td = td->next){
if (td->flags & IsoLock)
iprint("usbinterrupt: locked?\n");
if (e->isolock == 0 && td == e->etd){
/* if isolock is set, don't touch e->etd */
if (e->xtd == e->etd)
wakeup(&e->wr); /* writer had to wait */
else
e->etd = nil; /* writer was overtaken */
}
if (td->status & AnyError)
iprint("usbisoerror 0x%lux\n", td->status);
size = (e->hz + e->remain)*e->pollms/1000;
e->remain = (e->hz + e->remain)*e->pollms%1000;
size *= e->samplesz;
if ((td->flags & IsoClean) == 0){
memset((byte*)td->bp, 0, e->maxpkt);
td->flags |= IsoClean;
}
td->dev = ((size-1)<<21) | ((id&0x7FF)<<8) | tok;
td->status = ErrLimit3 | Active | IsoSelect | IOC;
}
e->xtd = td;
if (e->etd) wakeup(&e->wr);
}
static void
.
1007d
1004a
eptdeactivate(e);
.
952c
free(e->tdalloc);
free(e->bpalloc);
e->tdalloc = nil;
e->bpalloc = nil;
e->etd = nil;
e->td0 = nil;
.
945,950d
943c
if (e->tdalloc == nil)
panic("tdalloc");
for (q = e->sched; q < NFRAME; q += e->pollms){
td = e->td0++;
addr = &ub->frames[q];
while (*addr != PADDR(td)){
if (*addr & IsQH)
panic("usb: TD expected");
addr = &TFOL(*addr)->link;
}
*addr = td->link;
.
940c
if(!e->iso || e->sched < 0)
.
936c
TD *td;
ulong *addr;
.
928c
e->isolock = 0;
.
919,926c
td->status = ErrLimit3 | Active | IsoSelect | IOC;
td->link = ub->frames[ix];
ub->frames[ix] = PADDR(td);
.
917c
else{
size = (e->hz + e->remain)*e->pollms/1000;
e->remain = (e->hz + e->remain)*e->pollms%1000;
size *= e->samplesz;
memset((byte*)td->bp, 0, size);
td->dev = ((size-1)<<21) | ((id&0x7FF)<<8) | TokOUT;
.
914,915d
906,912c
td->next = &td[1];
ub->frameld[i] += e->maxpkt;
td++;
}
td[-1].next = e->td0;
for(i = e->sched; i < NFRAME; i += e->pollms){
ix = (frnum+i)%NFRAME;
td = &e->td0[ix];
id = (e->x<<7)|(e->dev->x&0x7F);
if (e->mode == OREAD)
.
898,904c
td->bp = (Block *)(bp + e->maxpkt*i/e->pollms);
td->buffer = PADDR(td->bp);
.
890,896c
if (e->tdalloc || e->bpalloc)
panic("usb: tdalloc/bpalloc");
e->tdalloc = mallocz(0x10 + NFRAME*sizeof(TD), 1);
e->bpalloc = mallocz(0x10 + e->maxpkt*NFRAME/e->pollms, 1);
e->td0 = (TD*)(((ulong)e->tdalloc + 0xf) & ~0xf);
bp = (byte *)(((ulong)e->bpalloc + 0xf) & ~0xf);
frnum = (IN(Frnum) + 1) & 0x3ff;
frnum = (frnum & ~(e->pollms - 1)) + e->sched;
e->isolock = 1;
e->xtd = &e->td0[frnum]; /* Next td to finish */
e->etd = nil; /* Writer is idle */
e->remain = 0;
td = e->td0;
.
879,882d
869,870c
TD *td;
uchar *bp;
int i, id, ix, size, frnum;
.
847,853d
839c
int i, d, q;
.
678,750d
227a
int isolock;
.
223c
TD* td0; /* pointer to list of TDs for isochronous ept */
volatile TD* etd; /* reader/writer pointer into list of TDs for isochronous ept */
volatile TD* xtd; /* next td to be cleaned */
.
220d
212a
byte * tdalloc;
byte * bpalloc;
.
210c
volatile byte iso;
.
148a
IsoLock= 1<<1,
IsoClean= 1<<2,
.
119c
NFRAME = (FRAMESIZE/4),
.
## diffname pc/devusb.c 2001/1007
## diff -e /n/emeliedump/2001/1006/sys/src/9/pc/devusb.c /n/emeliedump/2001/1007/sys/src/9/pc/devusb.c
1885a
e->isolock = 0;
.
1878,1880c
e->off = 0;
e->psize = ((e->etd->dev >> 21) + 1) & 0x7ff;
.
1861,1876c
if ((td = e->etd) == nil){
iprint("0");
ub = &ubus;
frnum = (IN(Frnum) + 8) & 0x3ff;
e->etd = e->td0 +frnum;
}else{
td->flags &= ~IsoClean;
q = (uchar*)td->bp + e->off;
if((i = n) >= e->psize)
i = e->psize;
memmove(q, p, i);
p += i;
n -= i;
e->off += i;
e->psize -= i;
if (e->psize){
if (n != 0)
panic("usb iso: can't happen");
break;
}
td->status = ErrLimit3 | Active | IsoSelect | IOC;
e->etd = td->next;
.
1856,1859c
while (isoready(e) == 0){
iprint("i");
sleep(&e->wr, isoready, e);
.
1854a
e->isolock = 1;
.
1851,1852d
1837a
Ctlr *ub;
.
1834c
int i, frnum;
.
1804,1830d
1674,1683c
e->off = 0;
e->psize = (e->etd->status + 1) & 0x7ff;
if (e->psize > e->maxpkt)
panic("packet size > maximum");
e->nbytes += e->psize;
e->nblocks++;
} while(n > 0);
e->isolock = 0;
poperror();
.
1672a
td->status = ErrLimit3 | Active | IsoSelect | IOC;
e->etd = td->next;
.
1667,1668c
if (e->psize){
.
1665c
e->off += i;
.
1655,1659c
if ((td = e->etd) == nil){
ub = &ubus;
frnum = (IN(Frnum) + 8) & 0x3ff;
e->etd = e->td0 +frnum;
}else{
q = (uchar*)td->bp + e->off;
.
1648,1653c
while (isoready(e) == 0){
XPRINT("readusb: sleep %lux\n", &e->wr);
sleep(&e->wr, isoready, e);
XPRINT("readusb: awake\n");
.
1646a
if(waserror()){
e->isolock = 0;
nexterror();
}
e->isolock = 1;
.
1635a
Ctlr *ub;
.
1634c
int l, i, frnum;
.
1607,1626c
return e->etd == nil || (e->etd->status & Active) == 0;
.
1602,1603d
1600c
isoready(void *arg)
.
1103,1104d
1100d
1093,1096c
if (e->mode == OREAD){
td->dev = ((e->maxpkt -1)<<21) | ((id&0x7FF)<<8) | TokIN;
}else{
n = (e->hz + e->remain)*e->pollms/1000;
e->remain = (e->hz + e->remain)*e->pollms%1000;
n *= e->samplesz;
td->dev = ((n -1)<<21) | ((id&0x7FF)<<8) | TokOUT;
}
td = td->next;
if (e->xtd == td){
iprint("@");
break;
}
} while ((td->status & Active) == 0);
e->xtd = td;
if (e->isolock){
wakeup(&e->wr);
return;
}
for (n = 2; n < 6; n++){
td = e->td0 + ((frnum + n)&0x3ff);
if (td->status & Active)
continue;
if (td == e->etd) {
memset((byte*)td->bp+e->off, 0, e->maxpkt-e->off);
if (e->off == 0)
td->flags |= IsoClean;
e->etd = nil;
}else if ((td->flags & IsoClean) == 0){
.
1081,1090c
do{
.
1072,1079c
td = e->xtd;
if (td->status & Active)
return;
.
1070c
int id, n;
.
854d
821,823c
e->xtd = &e->td0[(frnum+8)&0x3ff]; /* Next td to finish */
e->etd = nil;
.
806c
e->off = 0;
.
233d
226,228d
223,224c
int psize; /* (remaining) size of this packet */
int off; /* offset into packet */
int isolock; /* reader/writer interlock with interrupt */
TD* td0; /* first td in array */
TD* etd; /* pointer into circular list of TDs for isochronous ept */
TD* xtd; /* next td to be cleaned */
/* end ISO stuff */
.
221d
218c
int remain; /* for packet size calculations */
.
214a
int setin;
/* ISO related: */
.
149d
## diffname pc/devusb.c 2001/1008
## diff -e /n/emeliedump/2001/1007/sys/src/9/pc/devusb.c /n/emeliedump/2001/1008/sys/src/9/pc/devusb.c
1857a
p = a;
.
1856a
qunlock(&e->wlock);
return n;
.
1815,1855c
n = isoio(e, a, n, 1);
.
1813d
1806d
1803,1804d
1801c
uchar *p;
.
1799c
int i;
.
1676a
p = a;
.
1675a
qunlock(&e->rlock);
return n;
.
1673,1674c
}
td->flags &= ~IsoClean;
q = (uchar*)td->bp + e->off;
if((i = n) >= e->psize)
i = e->psize;
if (w)
memmove(q, p, i);
else
memmove(p, q, i);
p += i;
n -= i;
e->off += i;
e->psize -= i;
if (e->psize){
if (n != 0)
panic("usb iso: can't happen");
break;
}
td->status = ErrLimit3 | Active | IsoSelect | IOC;
e->etd = td->next;
e->off = 0;
} while(n > 0);
e->isolock = 0;
poperror();
return p-(uchar*)a;
}
static long
readusb(Endpt *e, void *a, long n)
{
Block *b;
uchar *p;
int l, i;
XPRINT("qlock(%p)\n", &e->rlock);
qlock(&e->rlock);
XPRINT("got qlock(%p)\n", &e->rlock);
if(waserror()){
qunlock(&e->rlock);
eptcancel(e);
nexterror();
}
if (e->iso){
n = isoio(e, a, n, 0);
.
1667,1668c
if (w)
e->psize = ((td->dev >> 21) + 1) & 0x7ff;
else
e->psize = (e->etd->status + 1) & 0x7ff;
.
1646,1665c
e->isolock = 1;
if (e->etd == nil){
XPRINT("!");
continue;
.
1644d
1642d
1637,1640d
1633,1635c
e->isolock = 1;
do {
td = e->etd;
if (td == nil || e->off == 0){
if (td == nil){
XPRINT("0");
ub = &ubus;
if (w)
frnum = (IN(Frnum) + 8) & 0x3ff;
else
frnum = (IN(Frnum) - 8) & 0x3ff;
td = e->etd = e->td0 +frnum;
e->off = 0;
}
/* New td, make sure it's ready */
.
1629,1630c
e->isolock = 0;
.
1625,1627c
p = a;
.
1623a
TD *td;
.
1621,1622d
1619c
int i, frnum;
.
1617c
isoio(Endpt *e, void *a, long n, int w)
.
1613c
return e->etd == nil || (e->etd != e->xtd && (e->etd->status & Active) == 0);
.
1112a
wakeup(&e->wr);
.
1111c
td->status = ErrLimit1 | Active | IsoSelect | IOC;
.
1107a
XPRINT("-");
.
1102a
XPRINT("*");
.
1096d
1093,1094c
if (e->isolock)
.
1088c
XPRINT("@");
.
851a
td->flags |= IsoClean;
.
850c
td->status = ErrLimit1 | Active | IsoSelect | IOC;
.
847d
836c
ix = (frnum+i) & 0x3ff;
.
## diffname pc/devusb.c 2001/1009
## diff -e /n/emeliedump/2001/1008/sys/src/9/pc/devusb.c /n/emeliedump/2001/1009/sys/src/9/pc/devusb.c
1805,1806c
if((e = d->ep[i]) != nil){ /* TO DO: freeze e */
l += snprint(s+l, READSTR-l, "%2d %#6.6lux %10lud bytes %10lud blocks", i, e->csp, e->nbytes, e->nblocks);
if (e->iso){
l += snprint(s+l, READSTR-l, "iso ");
}
l += snprint(s+l, READSTR-l, "\n");
}
.
## diffname pc/devusb.c 2001/1010
## diff -e /n/emeliedump/2001/1009/sys/src/9/pc/devusb.c /n/emeliedump/2001/1010/sys/src/9/pc/devusb.c
2087c
if (e->iso)
n = isoio(e, a, n, offset, 1);
else
n = writeusb(e, a, n, TokOUT);
.
1893c
usbwrite(Chan *c, void *a, long n, vlong offset)
.
1859,1886c
if(e->err)
error(e->err);
if((i = n) >= e->maxpkt)
i = e->maxpkt;
b = allocb(i);
if(waserror()){
freeb(b);
nexterror();
}
XPRINT("out [%d]", i);
for (j = 0; j < i; j++) XPRINT(" %.2x", p[j]);
XPRINT("\n");
memmove(b->wp, p, i);
b->wp += i;
p += i;
n -= i;
poperror();
qh = qxmit(e, b, tok);
tok = TokOUT;
e->data01 ^= 1;
if(e->ntd >= e->nbuf) {
XPRINT("writeusb: sleep %lux\n", &e->wr);
sleep(&e->wr, qisempty, qh);
XPRINT("writeusb: awake\n");
}
} while(n > 0);
.
1849,1857c
p = a;
do {
int j;
.
1823c
if (e->iso)
n=isoio(e, a, n, offset, 0);
else
n=readusb(e, a, n);
.
1742,1746c
nexterror();
}
l = BLEN(b);
if((i = l) > n)
i = n;
if(i > 0){
memmove(p, b->rp, i);
p += i;
}
poperror();
freeb(b);
n -= i;
if (l != e->maxpkt)
break;
} while (n > 0);
.
1704,1740c
p = a;
do {
if(e->eof) {
XPRINT("e->eof\n");
break;
}
if(e->err)
error(e->err);
qrcv(e);
if(!e->iso)
e->data01 ^= 1;
sleep(&e->rr, eptinput, e);
if(e->err)
error(e->err);
b = qget(e->rq); /* TO DO */
if(b == nil) {
XPRINT("b == nil\n");
break;
}
if(waserror()){
.
1685a
qunlock(&e->rlock);
.
1664c
bp = e->bp0 + (td - e->td0) * e->maxpkt / e->pollms;
q = bp + e->off;
.
1660,1661d
1629a
p = a;
.
1627a
qunlock(&e->rlock);
eptcancel(e);
.
1625c
qlock(&e->rlock);
.
1621c
uchar *p, *q, *bp;
.
1618c
isoio(Endpt *e, void *a, long n, vlong offset, int w)
.
1528,1529c
if(schedendpt(e) < 0){
if (e->isopen)
error("can't schedule USB endpoint, isopen");
else
error("can't schedule USB endpoint");
}
.
1334c
ub->recvq->head = PADDR(ub->bwsop) | IsQH;
ub->bwsop->head = Terminate; /* loop back */
// ub->bwsop->head = PADDR(ub->bwsop) | IsQH; /* loop back */
ub->bwsop->entries = PADDR(t);
.
1330,1332c
ub->ctlq->head = PADDR(ub->bulkq) | IsQH;
.
1289c
if(0){
.
1283a
port = cfg->mem[4].bar & ~0x0F;
.
1279,1281c
if(cfg == nil) {
DPRINT("No USB device found\n");
.
1277a
if((cfg->mem[4].bar & ~0x0F) != 0)
break;
.
1270,1276c
cfg = nil;
while(cfg = pcimatch(cfg, 0, 0)){
/*
* Look for devices with the correct class and
* sub-class code and known device and vendor ID.
*/
if(cfg->ccrb != 0x0C || cfg->ccru != 0x03)
continue;
switch(cfg->vid | cfg->did<<16){
default:
continue;
case 0x8086 | 0x7112<<16: /* 82371[AE]B (PIIX4[E]) */
case 0x8086 | 0x719A<<16: /* 82443MX */
case 0x0586 | 0x1106<<16: /* VIA 82C586 */
break;
.
1156a
XPRINT("cleanq(ub->ctlq, 0, 0)\n");
cleanq(ub->ctlq, 0, 0);
XPRINT("cleanq(ub->bulkq, 0, Vf)\n");
cleanq(ub->bulkq, 0, Vf);
XPRINT("clean recvq\n");
for (q = ub->recvq->next; q; q = q->hlink) {
XPRINT("cleanq(q, 0, Vf)\n");
cleanq(q, 0, Vf);
}
.
1136,1144d
1130c
// iprint("usbint: #%x f%d\n", s, frnum);
.
1128a
sapecount2++;
.
1125a
sapestatus = s;
sapeintenb = IN(Usbintr);
sapeframe = IN(Frnum);
sapeport1 = IN(Portsc0);
sapeport2 = IN(Portsc1);
sapecmd = IN(Cmd);
.
1123a
sapecount1++;
.
1115a
static int sapecount1;
static int sapecount2;
static int sapecmd;
static int sapestatus;
static int sapeintenb;
static int sapeframe;
static int sapeport1;
static int sapeport2;
.
1108c
memset(bp, 0, e->maxpkt);
.
1102c
memset(bp+e->off, 0, e->maxpkt-e->off);
.
1096c
i = ((frnum + n)&0x3ff);
td = e->td0 + i;
bp = e->bp0 + e->maxpkt*i/e->pollms;
.
1091a
e->time = todget(nil);
.
1077a
e->nbytes += (td->status + 1) & 0x3ff;
if ((td->flags & IsoClean) == 0)
e->nblocks++;
.
1075c
do {
.
1069c
int id, n, i;
uchar *bp;
.
827,828c
bp = e->bp0 + e->maxpkt*i/e->pollms;
td->buffer = PADDR(bp);
.
824a
e->nbytes = 0;
.
819c
e->bp0 = (uchar *)(((ulong)e->bpalloc + 0xf) & ~0xf);
.
246a
vlong time;
.
230c
QH * epq; /* queue of TDs for this endpoint */
.
226,228c
uchar* bp0; /* first block in array */
TD * td0; /* first td in array */
TD * etd; /* pointer into circular list of TDs for isochronous ept */
TD * xtd; /* next td to be cleaned */
.
216,217c
uchar* tdalloc;
uchar* bpalloc;
.
208,213c
uchar isopen; /* ep operations forbidden on open endpoints */
uchar mode; /* OREAD, OWRITE, ORDWR */
uchar nbuf; /* number of buffers allowed */
uchar iso;
uchar debug;
uchar active; /* listed for examination by interrupts */
.
206c
uchar eof;
.
165c
uchar port; /* port number on connecting hub */
.
74c
union{
Block* bp; /* non-iso */
ulong offset; /* iso */
};
.
49,50d
## diffname pc/devusb.c 2001/1011
## diff -e /n/emeliedump/2001/1010/sys/src/9/pc/devusb.c /n/emeliedump/2001/1011/sys/src/9/pc/devusb.c
2125c
n = isoio(e, a, n, (ulong)offset, 1);
.
1866c
n=isoio(e, a, n, (ulong)offset, 0);
.
1849,1850c
if (e->iso && e->toffset){
l += snprint(s+l, READSTR-l, " %10lud offset %19lld time", e->toffset, e->time);
.
1735c
return n;
.
1733a
if (isolock)
iunlock(&activends);
.
1732c
n = p-(uchar*)a;
e->foffset += n;
.
1709a
isolock = 0;
iunlock(&activends);
.
1698c
ilock(&activends);
isolock = 1;
.
1694c
isolock = 0;
iunlock(&activends);
.
1685d
1680a
if (isolock == 0){
ilock(&activends);
isolock = 1;
}
.
1679c
ub = &ubus;
if (offset != 0 && offset != e->foffset){
/* Seek to a specific position */
frnum = (IN(Frnum) + 8) & 0x3ff;
td = e->td0 +frnum;
if (offset < td->offset)
error("ancient history");
while (offset > e->toffset){
tsleep(&e->wr, return0, 0, 500);
}
while (offset >= td->offset + ((w?(td->dev >> 21):td->status) + 1) & 0x7ff){
td = td->next;
if (td == e->xtd)
iprint("trouble\n");
}
ilock(&activends);
isolock = 1;
e->off = td->offset - offset;
if (e->off >= e->maxpkt){
iprint("I can't program: %d\n", e->off);
e->off = 0;
}
e->etd = td;
e->foffset = offset;
}
.
1673c
if (isolock){
isolock = 0;
iunlock(&activends);
}
.
1671a
isolock = 0;
.
1666a
volatile int isolock;
.
1664c
isoio(Endpt *e, void *a, long n, ulong offset, int w)
.
1157d
1147,1152d
1144d
1127,1135d
1102,1103d
1092a
td->offset = e->poffset;
e->poffset += n;
.
1088a
e->toffset = td->offset;
.
1087a
e->toffset = td->offset;
.
1086a
e->poffset += (td->status + 1) & 0x3ff;
td->offset = e->poffset;
.
828a
e->foffset = 0;
e->toffset = 0;
e->poffset = 0;
.
249d
230a
/* Real-time iso stuff */
ulong foffset; /* file offset (to detect seeks) */
ulong poffset; /* offset of next packet to be queued */
ulong toffset; /* offset associated with time */
vlong time; /* timeassociated with offset */
.
226d
## diffname pc/devusb.c 2001/1012
## diff -e /n/emeliedump/2001/1011/sys/src/9/pc/devusb.c /n/emeliedump/2001/1012/sys/src/9/pc/devusb.c
1880,1884c
l += epstatus(s+l, READSTR-l, e, i);
.
1825a
int
epstatus(char *s, int n, Endpt *e, int i)
{
int l;
l = 0;
l += snprint(s+l, n-l, "%2d %#6.6lux %10lud bytes %10lud blocks",
i, e->csp, e->nbytes, e->nblocks);
if (e->iso && e->toffset){
l += snprint(s+l, n-l, " %6d buffered %10lud offset %19lld time",
e->buffered, e->toffset, e->time);
}
l += snprint(s+l, n-l, "\n");
return l;
}
.
1747a
e->buffered -= i;
if (e->buffered < 0){
iprint("e->buffered %d?\n", e->buffered);
e->buffered = 0;
}
}
.
1746c
e->buffered += i;
}else{
.
1744c
if (w){
.
1718,1729d
1716a
}else{
/* New td, make sure it's ready */
isolock = 0;
iunlock(&activends);
while (isoready(e) == 0){
sleep(&e->wr, isoready, e);
}
ilock(&activends);
isolock = 1;
if (e->etd == nil){
XPRINT("!");
continue;
}
.
1711,1715c
if (w){
frnum = (IN(Frnum) + 1) & 0x3ff;
td = e->td0 + frnum;
while(td->status & Active)
td = td->next;
}else{
frnum = (IN(Frnum) - 4) & 0x3ff;
td = e->td0 + frnum;
while(td->next != e->xtd)
td = td->next;
}
e->etd = td;
.
1126,1130c
}
} else {
/* Unread bytes are now lost */
e->buffered -= (td->status + 1) & 0x3ff;
.
1121,1124c
if (e->mode == OWRITE){
if (td == e->etd) {
XPRINT("*");
memset(bp+e->off, 0, e->maxpkt-e->off);
if (e->off == 0)
td->flags |= IsoClean;
else
e->buffered += (((td->dev>>21) +1) & 0x3ff) - e->off;
e->etd = nil;
}else if ((td->flags & IsoClean) == 0){
XPRINT("-");
memset(bp, 0, e->maxpkt);
.
1114c
for (n = 2; n < 4; n++){
.
1097a
if ((td->flags & IsoClean) == 0){
e->buffered -= n;
if (e->buffered < 0){
iprint("e->buffered %d?\n", e->buffered);
e->buffered = 0;
}
}
.
1092a
e->buffered += n;
.
1089c
n = (td->status + 1) & 0x3ff;
e->nbytes += n;
.
834a
e->buffered = 0;
.
234a
int buffered; /* bytes captured but unread, or written but unsent */
.
## diffname pc/devusb.c 2001/1013
## diff -e /n/emeliedump/2001/1012/sys/src/9/pc/devusb.c /n/emeliedump/2001/1013/sys/src/9/pc/devusb.c
1516c
len = e->buffered;
devdir(c, q, up->genbuf, len, eve, e->mode==OREAD? 0444: e->mode==OWRITE? 0222: 0666, dp);
.
1491a
len = 0;
.
1442a
vlong len;
.
## diffname pc/devusb.c 2001/1015
## diff -e /n/emeliedump/2001/1013/sys/src/9/pc/devusb.c /n/emeliedump/2001/1015/sys/src/9/pc/devusb.c
1655d
1645,1649d
1641,1643c
if(e = d->ep[s]) {
if (e->isopen == 1){
e->isopen = 0;
eptdeactivate(e);
unschedendpt(e);
}
.
1633,1636d
## diffname pc/devusb.c 2001/1016
## diff -e /n/emeliedump/2001/1015/sys/src/9/pc/devusb.c /n/emeliedump/2001/1016/sys/src/9/pc/devusb.c
2106c
if (e->active)
.
1867d
1864,1865c
l += snprint(s+l, n-l, "bufsize %6d buffered %6d offset %10lud time %19lld\n",
e->maxpkt, e->buffered, e->toffset, e->time);
.
1861c
l += snprint(s+l, n-l, "%2d %#6.6lux %10lud bytes %10lud blocks\n",
.
1648a
}
.
1647c
if(c->flag & COPEN){
XPRINT("usbclose: freedev 0x%p\n", d);
.
1634,1644d
1630c
if(c->qid.type == QTDIR || QID(c->qid) < Q3rd)
.
1628d
1590d
1585,1586c
if (e->active)
error("can't schedule USB endpoint, active");
.
1583a
XPRINT("usbopen: dev 0x%p, ept 0x%p\n", d, e);
.
1577a
XPRINT("usbopen, default 0x%p, %d\n", d, s);
.
1572a
XPRINT("usbopen, Qctl 0x%p\n", d);
.
1557a
}
.
1556c
if(QID(c->qid) < Q3rd){
XPRINT("usbopen, devopen < Q3rd\n");
.
1546a
XPRINT("usbopen, new dev 0x%p\n", d);
.
1448c
if(QID(c->qid) == Qtopdir){
.
811c
if (e->active){
.
663a
XPRINT("deactivate 0x%p\n", e);
.
647a
XPRINT("activate 0x%p\n", e);
.
209c
/* uchar isopen; ep operations forbidden on open endpoints */
.
## diffname pc/devusb.c 2001/1017
## diff -e /n/emeliedump/2001/1016/sys/src/9/pc/devusb.c /n/emeliedump/2001/1017/sys/src/9/pc/devusb.c
1860,1862c
if (e->iso){
l += snprint(s+l, n-l, "bufsize %6d buffered %6d",
e->maxpkt, e->buffered);
if(e->toffset)
l += snprint(s+l, n-l, " offset %10lud time %19lld\n",
e->toffset, e->time);
if (n-l > 0)
s[l++] = '\n';
s[l] = '\0';
.
1689a
iprint("offset %lud, foffset %lud\n", offset, e->foffset);
.
1597a
e->foffset = 0;
e->toffset = 0;
e->poffset = 0;
e->buffered = 0;
.
835,838d
## diffname pc/devusb.c 2001/1019
## diff -e /n/emeliedump/2001/1017/sys/src/9/pc/devusb.c /n/emeliedump/2001/1019/sys/src/9/pc/devusb.c
1783a
if(w)
td->offset = offset + (p-(uchar*)a) - (((td->dev >> 21) + 1) & 0x7ff);
.
1391,1392c
ub->frames = (ulong*)(((ulong)xalloc(2*FRAMESIZE) + FRAMEMASK) & ~FRAMEMASK);
ub->frameld = xallocz(FRAMESIZE, 1);
.
1358c
ub->qhpool = (QH*)(((ulong)xalloc(32*sizeof(QH) + 0x10) + 0xf) & ~0xf);
.
1353c
ub->tdpool = (TD*)(((ulong)xalloc(128*sizeof(TD) + 0x10) + 0xf) & ~0xf);
.
217,218c
void* tdalloc;
void* bpalloc;
.
119,120c
NFRAME = 1024,
FRAMESIZE= NFRAME*sizeof(ulong), /* fixed by hardware; aligned to same */
FRAMEMASK= FRAMESIZE-1, /* fixed by hardware; aligned to same */
.
## diffname pc/devusb.c 2001/1106
## diff -e /n/emeliedump/2001/1019/sys/src/9/pc/devusb.c /n/emeliedump/2001/1106/sys/src/9/pc/devusb.c
1626,1632d
## diffname pc/devusb.c 2001/1117
## diff -e /n/emeliedump/2001/1106/sys/src/9/pc/devusb.c /n/emeliedump/2001/1117/sys/src/9/pc/devusb.c
2041c
nf = tokenize(cmd, fields, nelem(fields));
.
2017c
nf = tokenize(cmd, fields, nelem(fields));
.
## diffname pc/devusb.c 2001/1120
## diff -e /n/emeliedump/2001/1117/sys/src/9/pc/devusb.c /n/emeliedump/2001/1120/sys/src/9/pc/devusb.c
2167a
poperror();
free(cb);
.
2164,2166d
2149c
i = strtoul(cb->f[5], nil, 0);
.
2140,2142c
e->mode = strcmp(cb->f[3],"r") == 0? OREAD :
strcmp(cb->f[3],"w") == 0? OWRITE : ORDWR;
i = strtoul(cb->f[4], nil, 0);
.
2133c
i = strtoul(cb->f[2], nil, 0);
.
2128c
i = strtoul(cb->f[5], nil, 0);
.
2122,2124c
e->mode = strcmp(cb->f[3],"r") == 0? OREAD :
strcmp(cb->f[3],"w") == 0? OWRITE : ORDWR;
i = strtoul(cb->f[4], nil, 0);
.
2107c
if(strcmp(cb->f[2], "bulk") == 0){
.
2092c
i = strtoul(cb->f[1], nil, 0);
.
2088c
break;
case CMep:
.
2085c
error(Ebadusbmsg);
.
2082,2083c
break;
case CMunstall:
i = strtoul(cb->f[1], nil, 0);
.
2080c
e->debug = strtoul(cb->f[2], nil, 0);
.
2074c
error(Ebadusbmsg);
.
2071,2072c
break;
case CMdebug:
i = strtoul(cb->f[1], nil, 0);
.
2068c
e->maxpkt = strtoul(cb->f[2], nil, 0);
.
2066c
error(Ebadusbmsg);
.
2062,2064c
e->data01 = strtoul(cb->f[2], nil, 0) != 0;
break;
case CMmaxpkt:
i = strtoul(cb->f[1], nil, 0);
.
2060c
error(Ebadusbmsg);
.
2056,2058c
d->ep[i]->csp = strtoul(cb->f[3], nil, 0);
break;
case CMdata:
i = strtoul(cb->f[1], nil, 0);
.
2050,2053c
cmderror(cb, Ebadusbmsg);
if (i == 0)
d->csp = strtoul(cb->f[3], nil, 0);
.
2037,2046c
cb = parsecmd(a, n);
if(waserror()){
free(cb);
nexterror();
}
ct = lookupcmd(cb, usbctlmsg, nelem(usbctlmsg));
switch(ct->index){
case CMspeed:
d->ls = strtoul(cb->f[1], nil, 0) == 0;
break;
case CMclass:
i = strtoul(cb->f[2], nil, 0);
d->npt = strtoul(cb->f[1], nil, 0);
.
2029,2030c
break;
case BCMenable:
portenable(id, 1);
break;
case BCMreset:
portreset(id);
break;
}
poperror();
free(cb);
.
2022,2027c
cmderror(cb, "usb port number not 1 or 2 in");
switch(ct->index){
case BCMdisable:
.
2013,2019c
cb = parsecmd(a, n);
if(waserror()){
free(cb);
nexterror();
}
ct = lookupcmd(cb, usbbusctlmsg, nelem(usbbusctlmsg));
.
2006c
Cmdbuf *cb;
Cmdtab *ct;
int id, nw, t, i;
.
289a
enum
{
BCMdisable,
BCMenable,
BCMreset,
};
enum
{
CMclass,
CMdata,
CMdebug,
CMep,
CMmaxpkt,
CMspeed,
CMunstall,
};
static Cmdtab usbbusctlmsg[] =
{
BCMdisable, "disable", 2,
BCMenable, "enable", 2,
BCMreset, "reset", 2,
};
static Cmdtab usbctlmsg[] =
{
CMclass, "class", 4,
CMdata, "data", 3,
CMdebug, "debug", 3,
CMep, "ep", 6,
CMmaxpkt, "maxpkt", 3,
CMspeed, "speed", 2,
CMunstall, "unstall", 2,
};
.
281a
static char Ebadusbmsg[] = "invalid parameters to USB ctl message";
.
## diffname pc/devusb.c 2001/1122
## diff -e /n/emeliedump/2001/1120/sys/src/9/pc/devusb.c /n/emeliedump/2001/1122/sys/src/9/pc/devusb.c
1354,1361c
// switch(cfg->vid | cfg->did<<16){
// default:
// continue;
// case 0x8086 | 0x7112<<16: /* 82371[AE]B (PIIX4[E]) */
// case 0x8086 | 0x719A<<16: /* 82443MX */
// case 0x0586 | 0x1106<<16: /* VIA 82C586 */
// break;
// }
.
## diffname pc/devusb.c 2001/1124
## diff -e /n/emeliedump/2001/1122/sys/src/9/pc/devusb.c /n/emeliedump/2001/1124/sys/src/9/pc/devusb.c
2059c
id = strtol(cb->f[1], nil, 0);
.
2046c
char cmd[50];
.
2027a
XPRINT("qh %s: q=%p first=%p last=%p entries=%.8lux\n",
"writeusb sleep", qh, qh->first, qh->last, qh->entries);
.
1388c
intrenable(cfg->intl, usbinterrupt, ub, cfg->tbdf, "usb");
.
1353a
if(cfg->did == 0x2482 || cfg->did == 0x2487)
continue;
.
1206d
1204a
usbints++;
.
1201a
frameptr = inl(ub->io+Flbaseadd);
framenumber = IN(Frnum) & 0x3ff;
.
1192c
usbinterrupt(Ureg*, void *a)
.
859d
856,857c
if(e->sched < 0)
.
785c
queuetd(ub, qh, t, vf, "qrcv");
.
760c
queuetd(ub, qh, t, vf, "qxmit");
.
648a
if(q->first && q->entries != PADDR(q->first)){
usbbogus++;
q->entries = PADDR(q->first);
}
.
518a
XPRINT(" t=%p q=%p first=%p last=%p entries=%.8lux\n",
t, q, q->first, q->last, q->entries);
.
510a
XPRINT("queuetd %s: t=%p lt=%p q=%p first=%p last=%p entries=%.8lux\n",
why, t, lt, q, q->first, q->last, q->entries);
.
503c
queuetd(Ctlr *ub, QH *q, TD *t, int vf, char *why)
.
279a
static long usbints;
static long framenumber;
static long frameptr;
static long usbbogus;
.
## diffname pc/devusb.c 2001/1127
## diff -e /n/emeliedump/2001/1124/sys/src/9/pc/devusb.c /n/emeliedump/2001/1127/sys/src/9/pc/devusb.c
328c
CMspeed, "speed", 2,
.
323,325c
CMclass, "class", 4,
CMdata, "data", 3,
CMdebug, "debug", 3,
.
143c
BitstuffErr = 1<<17,
.
129,141c
SPD = 1<<29,
ErrLimit0 = 0<<27,
ErrLimit1 = 1<<27,
ErrLimit2 = 2<<27,
ErrLimit3 = 3<<27,
LowSpeed = 1<<26,
IsoSelect = 1<<25,
IOC = 1<<24,
Active = 1<<23,
Stalled = 1<<22,
DataBufferErr = 1<<21,
Babbling = 1<<20,
NAKed = 1<<19,
.
124,126c
Vf = 1<<2, /* TD only */
IsQH = 1<<1,
Terminate = 1<<0,
.
121c
FRAMEMASK= FRAMESIZE-1,
.
110,117c
Suspend = 1<<12,
PortReset = 1<<9,
SlowDevice = 1<<8,
ResumeDetect = 1<<6,
PortChange = 1<<3, /* write 1 to clear */
PortEnable = 1<<2,
StatusChange = 1<<1, /* write 1 to clear */
DevicePresent = 1<<0,
.
## diffname pc/devusb.c 2001/1130
## diff -e /n/emeliedump/2001/1127/sys/src/9/pc/devusb.c /n/emeliedump/2001/1130/sys/src/9/pc/devusb.c
1691a
poperror();
.
1684a
if(waserror()){
qunlock(&usbstate);
return;
}
.
1152c
print("e->buffered %d?\n", e->buffered);
.
## diffname pc/devusb.c 2001/1201
## diff -e /n/emeliedump/2001/1130/sys/src/9/pc/devusb.c /n/emeliedump/2001/1201/sys/src/9/pc/devusb.c
2173a
}
.
2172c
if((e = d->ep[i]) == nil){
XPRINT("calling devendpt in usbwrite (CMep)\n");
.
2125a
}
.
2124c
if(d->ep[i] == nil){
XPRINT("calling devendpt in usbwrite (CMclass)\n");
.
1822c
XPRINT("e->buffered %d?\n", e->buffered);
.
1694c
freedev(d, ept);
.
1692a
ept = (QID(c->qid) != Qctl) ? QID(c->qid) - Qep0 : -1;
.
1691a
XPRINT("usbclose: dev 0x%p\n", d);
.
1680a
int ept;
.
1331a
XPRINT("calling devendpt in usbnewdevice\n");
.
1028c
} else {
if(ept >= 0 && ept < nelem(d->ep)){
e = d->ep[ept];
XPRINT("freedev, freept 0x%p\n", e);
if(e != nil){
eptdeactivate(e);
unschedendpt(e);
}
}
}
.
1022a
XPRINT("freedev 0x%p, 0\n", d);
.
1020a
Endpt *e;
.
1018c
freedev(Udev *d, int ept)
.
1005a
if (e) XPRINT("decref(0x%p) in freept, new value %ld\n", e, e->ref);
return 0;
.
1004a
return 1;
.
994c
static int
.
982a
XPRINT("incref(0x%p) in devendpt, new value %ld\n", *p, (*p)->ref);
.
960a
XPRINT("incref(0x%p) in devendpt, new value %ld\n", e, e->ref);
.
## diffname pc/devusb.c 2001/1204
## diff -e /n/emeliedump/2001/1201/sys/src/9/pc/devusb.c /n/emeliedump/2001/1204/sys/src/9/pc/devusb.c
1705c
nexterror();
.
1703c
if(waserror()){ /* usbdevice can error */
.
## diffname pc/devusb.c 2002/0109
## diff -e /n/emeliedump/2001/1204/sys/src/9/pc/devusb.c /n/emeliedump/2002/0109/sys/src/9/pc/devusb.c
2309a
devshutdown,
.
## diffname pc/devusb.c 2002/0212
## diff -e /n/emeliedump/2002/0109/sys/src/9/pc/devusb.c /n/emeliedump/2002/0212/sys/src/9/pc/devusb.c
2252d
2187c
* ep n period mode samplesize Hz
.
2165a
case CMadjust:
i = strtoul(cb->f[1], nil, 0);
if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil)
error(Ebadusbmsg);
e = d->ep[i];
if (e->iso == 0)
error(Eperm);
i = strtoul(cb->f[2], nil, 0);
/* speed may not result in change of maxpkt */
if (i < (e->maxpkt-1)/e->samplesz * 1000/e->pollms
|| i > e->maxpkt/e->samplesz * 1000/e->pollms){
snprint(cmd, sizeof(cmd), "%d < %d < %d?",
(e->maxpkt-1)/e->samplesz * 1000/e->pollms,
i,
e->maxpkt/e->samplesz * 1000/e->pollms);
error(cmd);
}
e->hz = i;
break;
.
1865a
}
poperror();
.
1863,1864c
if (isolock){
isolock = 0;
.
1828,1829d
327a
CMadjust, "adjust", 3,
.
309a
CMadjust,
.
## diffname pc/devusb.c 2002/0216
## diff -e /n/emeliedump/2002/0212/sys/src/9/pc/devusb.c /n/emeliedump/2002/0216/sys/src/9/pc/devusb.c
1867a
if (isolock)
iunlock(&activends);
.
1863,1866d
1840,1844d
1837d
1833,1834d
1829a
if((i = n) >= e->psize)
i = e->psize;
if (w)
e->buffered += i;
else{
e->buffered -= i;
if (e->buffered < 0)
e->buffered = 0;
}
isolock = 0;
iunlock(&activends);
.
## diffname pc/devusb.c 2002/0219
## diff -e /n/emeliedump/2002/0216/sys/src/9/pc/devusb.c /n/emeliedump/2002/0219/sys/src/9/pc/devusb.c
2309,2319c
t -= Qep0;
if(t < 0 || t >= nelem(d->ep))
error(Egreg);
e = d->ep[t];
if(e == nil || e->mode == OREAD)
error(Egreg);
n = uh->write(uh, e, a, n, offset, TokOUT);
.
2301c
nw = uh->read(uh, e, cmd, 0LL, 8);
.
2298,2299c
n = uh->write(uh, e, a, n, 0LL, TokSETUP);
if(nw == 0) { /* host to device: use IN[DATA1] to ack */
.
2292,2293c
e = d->ep[0];
if(e == nil || e->iso)
error(Egreg);
.
2290c
case Qep0: /* SETUP endpoint 0 */
.
2283c
qunlock(uh);
.
2281a
e->mode = strcmp(cb->f[3],"r") == 0? OREAD :
strcmp(cb->f[3],"w") == 0? OWRITE : ORDWR;
uh->epmode(uh, e);
.
2261,2262d
2253c
/* ep n period mode samplesize Hz */
.
2233,2244c
e->iso = 0;
.
2229,2231d
2226c
if(e->active)
.
2223c
qunlock(uh);
.
2220c
qlock(uh);
.
2122,2125d
2114,2115c
case PMreset:
uh->portreset(uh, id);
.
2111,2112c
case PMenable:
uh->portenable(uh, id, 1);
.
2108,2109c
case PMdisable:
uh->portenable(uh, id, 0);
.
2105,2106d
2103c
ct = lookupcmd(cb, usbportmsg, nelem(usbportmsg));
.
2095,2096c
d = usbdevice(c);
uh = d->uh;
t = TYPE(c->qid);
switch(t){
case Qport:
.
2089a
Cmdbuf *cb;
Usbhost *uh;
.
2088d
2026,2082d
2022a
n = readstr(offset, a, n, s);
poperror();
free(s);
.
2001,2021d
1995,1999c
p = seprint(s, se, "%s %#6.6lux\n", devstates[d->state], d->csp);
for(i=0; i<nelem(d->ep); i++) {
e = d->ep[i];
if(e == nil)
continue;
/* TO DO: freeze e */
p = epstatus(p, se, e, i);
.
1990,1993d
1973,1987c
seprint(s, se, "%11d %11d\n", d->x, d->id);
.
1968,1970c
uh->portinfo(uh, s, se);
break;
.
1963,1966d
1888,1961d
1884,1885c
free(s);
.
1873,1882c
s = smalloc(READSTR);
se = s+READSTR;
.
1830,1871c
return uh->read(uh, e, a, n, offset);
}
.
1823,1828c
return n;
.
1749,1821c
if(t >= Qep0) {
t -= Qep0;
if(t >= nelem(d->ep))
error(Eio);
e = d->ep[t];
if(e == nil || e->mode == OWRITE)
error(Egreg);
if(t == 0) {
if(e->iso)
error(Egreg);
e->data01 = 1;
n = uh->read(uh, e, a, n, 0LL);
if(e->setin){
e->setin = 0;
e->data01 = 1;
uh->write(uh, e, "", 0, 0LL, TokOUT);
.
1740,1747c
d = usbdevice(c);
uh = d->uh;
t = TYPE(c->qid);
.
1736,1738c
if(c->qid.type == QTDIR)
return devdirread(c, a, n, nil, 0, usbgen);
.
1734a
Usbhost *uh;
char *s, *se, *p;
.
1733a
int t, i;
Udev *d;
.
1731,1732c
long
usbread(Chan *c, void *a, long n, vlong offset)
.
1727,1728c
p = seprint(s, se, "%2d %#6.6lux %10lud bytes %10lud blocks\n", i, e->csp, e->nbytes, e->nblocks);
if(e->iso){
p = seprint(p, se, "bufsize %6d buffered %6d", e->maxpkt, e->buffered);
if(e->toffset)
p = seprint(p, se, " offset %10lud time %19lld\n", e->toffset, e->time);
p = seprint(p, se, "\n");
}
return p;
.
1725c
char *p;
.
1722,1723c
static char *
epstatus(char *s, char *se, Endpt *e, int i)
.
1719c
qunlock(uh);
.
1714c
ept = (type != Qctl) ? type - Qep0 : -1;
.
1709,1710c
if(type == Qctl)
.
1704,1706c
d = usbdevice(c);
uh = d->uh;
qlock(uh);
if(waserror()){
qunlock(uh);
.
1702c
type = TYPE(c->qid);
if(c->qid.type == QTDIR || type < Q3rd)
.
1700c
int ept, type;
Usbhost *uh;
.
1684,1696d
1676c
qunlock(uh);
.
1670d
1660,1665c
uh->epopen(uh, e);
.
1654d
1650,1651c
s = type - Qep0;
.
1640d
1638c
switch(type){
.
1634c
qunlock(uh);
.
1632c
d = usbdevice(c);
uh = d->uh;
qlock(uh);
.
1627c
if(type < Q3rd){
.
1622,1623c
type = Qctl;
mkqid(&c->qid, PATH(type, d->x, CTLR(c->qid)), d->id, QTFILE);
.
1615,1616c
type = TYPE(c->qid);
if(type == Qnew){
d = usbdevice(c);
d = usbnewdevice(d->uh);
.
1609c
Endpt *e;
int f, s, type;
Usbhost *uh;
.
1605c
Chan*
.
1592a
static void
usbreset(void)
{
int n, ctlrno;
Usbhost *uh;
char name[64], buf[128], *p, *ebuf, *type;
uh = nil;
for(ctlrno = 0; ctlrno < MaxUsb; ctlrno++){
if(uh == nil)
uh = malloc(sizeof(Usbhost));
memset(uh, 0, sizeof(Usbhost));
uh->tbdf = BUSUNKNOWN;
if(isaconfig("usb", ctlrno, uh) == 0)
continue;
for(n = 0; usbtypes[n].type; n++){
type = uh->type;
if(type == nil || *type == '\0')
type = "uhci";
if(cistrcmp(usbtypes[n].type, type))
continue;
if(usbtypes[n].reset(uh))
break;
/*
* IRQ2 doesn't really exist, it's used to gang the interrupt
* controllers together. A device set to IRQ2 will appear on
* the second interrupt controller as IRQ9.
*/
if(uh->irq == 2)
uh->irq = 9;
snprint(name, sizeof(name), "usb%d", ctlrno);
intrenable(uh->irq, uh->interrupt, uh, uh->tbdf, name);
ebuf = buf + sizeof buf;
p = seprint(buf, ebuf, "#U/usb%d: %s: port 0x%luX irq %lud", ctlrno, type, uh->port, uh->irq);
if(uh->mem)
p = seprint(p, ebuf, " addr 0x%luX", PADDR(uh->mem));
if(uh->size)
seprint(p, ebuf, " size 0x%luX", uh->size);
print("%s\n", buf);
usbhost[ctlrno] = uh;
uh = nil;
break;
}
}
if(uh != nil)
free(uh);
}
void
usbinit(void)
{
Udev *d;
int ctlrno;
Usbhost *uh;
for(ctlrno = 0; ctlrno < MaxUsb; ctlrno++){
uh = usbhost[ctlrno];
if(uh == nil)
continue;
if(uh->init != 0)
uh->init(uh);
/* reserve device for configuration */
d = usbnewdevice(uh);
incref(d);
d->state = Attached;
}
}
Chan *
usbattach(char *spec)
{
return devattach('U', spec);
}
.
1587,1589c
mkqid(&q, PATH(Qep0+s, slot, bus), c->qid.vers, QTFILE);
switch(e->mode) {
case OREAD:
perm = 0444;
break;
case OWRITE:
perm = 0222;
break;
default:
perm = 0666;
break;
}
devdir(c, q, up->genbuf, e->buffered, eve, perm, dp);
.
1581,1583d
1578,1579c
d = usbdeviceofslot(uh, slot);
if(d == nil || s >= nelem(d->ep))
.
1575a
s -= nelem(usbdir3);
.
1570,1572c
if(s < nelem(usbdir3)) {
tab = &usbdir3[s];
mkqid(&q, PATH(tab->qid.path, slot, bus), c->qid.vers, QTFILE);
.
1563,1567c
slot = SLOT(c->qid);
if(s == DEVDOTDOT) {
mkqid(&q, PATH(Q2nd, 0, bus), c->qid.vers, QTDIR);
snprint(up->genbuf, sizeof up->genbuf, "usb%d", bus);
devdir(c, q, up->genbuf, 0, eve, 0555, dp);
.
1553c
mkqid(&q, PATH(Q3rd, s, bus), d->id, QTDIR);
.
1551c
return 0;
.
1548,1549c
if(s >= 0 && s < nelem(uh->dev)) {
d = uh->dev[s];
.
1544c
mkqid(&q, PATH(tab->qid.path, 0, bus), d->id, QTFILE);
devdir(c, q, tab->name, tab->length, eve, tab->perm, dp);
.
1542a
d = uh->dev[0];
if(d == nil)
return -1;
.
1535c
t = TYPE(c->qid);
.
1533c
* Second level contains "new", "port", and a numbered
* directory for each enumerated device on the bus.
.
1530a
bus = CTLR(c->qid);
if(bus >= nelem(usbhost) || (uh = usbhost[bus]) == nil)
return -1;
.
1524,1529c
if(s >= nelem(usbhost) || usbhost[s] == nil)
return -1;
mkqid(&q, PATH(Q2nd, 0, s), 0, QTDIR);
snprint(up->genbuf, sizeof up->genbuf, "usb%d", s);
devdir(c, q, up->genbuf, 0, eve, 0555, dp);
return 1;
.
1518c
if(c->qid.path == Qtopdir){
.
1516c
* Top level directory contains the controller names.
.
1513c
Dirtab *tab;
Usbhost *uh;
int t, bus, slot, perm;
.
1511d
1509d
1507d
1476,1503d
1373,1473c
uh = d->uh;
if(decref(d) == 0){
XPRINT("freedev 0x%p, 0\n", d);
for(i=0; i<nelem(d->ep); i++)
freept(d->ep[i]);
if(d->x >= 0)
uh->dev[d->x] = nil;
free(d);
} else {
if(ept >= 0 && ept < nelem(d->ep)){
e = d->ep[ept];
XPRINT("freedev, freept 0x%p\n", e);
if(e != nil)
uh->epclose(uh, e);
}
}
.
1368,1371c
Endpt *e;
Usbhost *uh;
.
1366d
1364c
freedev(Udev *d, int ept)
.
1359c
qunlock(uh);
.
1355c
uh->dev[i] = d;
.
1348c
d->id = (uh->idgen << 8) | i;
.
1345a
d->uh = uh;
.
1342,1344c
for(i=0; i<nelem(uh->dev); i++)
if(uh->dev[i] == nil){
uh->idgen++;
.
1339c
qunlock(uh);
.
1337c
qlock(uh);
.
1334d
1322,1332d
1028,1320d
1018,1026d
1015,1016c
static Udev*
usbnewdevice(Usbhost *uh)
.
1011,1012d
1009d
1006,1007c
uh->epfree(uh, e);
.
1003,1004c
uh = e->dev->uh;
uh->epclose(uh, e);
.
1000a
Usbhost *uh;
.
998c
static void
.
987a
uh->epfree(uh, e);
.
982a
uh = d->uh;
uh->epalloc(uh, e);
.
979,981d
969a
.
961c
e = *p;
if(e != nil){
.
958d
956d
954a
Usbhost *uh;
.
437,951d
434a
d = usbdeviceofslot(uh, SLOT(c->qid));
if(d == nil || d->id != c->qid.vers || d->state == Disabled)
error(Ehungup);
return d;
.
395,433c
bus = CTLR(c->qid);
if(bus > nelem(usbhost) || (uh = usbhost[bus]) == nil) {
error(Egreg);
return nil; /* for compiler */
.
391,393c
int bus;
Udev *d;
Usbhost *uh;
.
388,389c
static Udev*
usbdevice(Chan *c)
.
378,385c
if(s < 0 || s > nelem(uh->dev))
return nil;
return uh->dev[s];
.
375,376c
static Udev*
usbdeviceofslot(Usbhost *uh, int s)
.
363,372c
if(ntype == MaxUsb)
panic("too many USB host interface types");
usbtypes[ntype].type = t;
usbtypes[ntype].reset = r;
ntype++;
.
361c
static int ntype;
.
342,359c
void
addusbtype(char* t, int (*r)(Usbhost*))
.
340c
char* type;
int (*reset)(Usbhost*);
} usbtypes[MaxUsb+1];
.
334,338c
static struct
.
317,319c
PMdisable, "disable", 2,
PMenable, "enable", 2,
PMreset, "reset", 2,
.
315c
static Cmdtab usbportmsg[] =
.
298,300c
PMdisable,
PMenable,
PMreset,
.
280,295d
277,278d
257,275c
static Dirtab usbdir3[]={
"ctl", {Qctl}, 0, 0666,
"status", {Qstatus}, 0, 0444,
"setup", {Qep0}, 0, 0666,
/* epNdata names are generated on demand */
.
253,254c
static Dirtab usbdir2[] = {
"new", {Qnew}, 0, 0666,
"port", {Qport}, 0, 0666,
.
251c
#define TYPE(q) (((ulong)(q).path)&TYPEMASK)
#define SLOT(q) ((((ulong)(q).path)>>SLOTSHIFT)&SLOTMASK)
#define CTLR(q) ((((ulong)(q).path)>>CTLRSHIFT)&CTLRMASK)
#define PATH(t, s, c) ((t)|((s)<<SLOTSHIFT)|((c)<<CTLRSHIFT))
.
249c
TYPEMASK = (1<<TYPEBITS)-1,
SLOTMASK = (1<<SLOTBITS)-1,
CTLRMASK = (1<<CTLRBITS)-1,
};
.
246,247c
SLOTSHIFT = TYPEBITS,
CTLRSHIFT = SLOTSHIFT+SLOTBITS,
.
201,244c
enum {
TYPEBITS = 8,
SLOTBITS = 8,
CTLRBITS = 4,
.
199c
* Qid path is:
* 8 bits of file type (qids above)
* 8 bits of slot number; default address 0 used for per-controller files
* 4 bits of controller number
.
197a
static char Ebadusbmsg[] = "invalid parameters to USB ctl message";
enum
{
Qtopdir = 0,
Q2nd,
Qnew,
Qport,
Q3rd,
Qctl,
Qstatus,
Qep0,
/* other endpoint files */
};
.
29,189d
27c
Usbhost* usbhost[MaxUsb];
.
22a
#include "usb.h"
static int debug = 0;
.
1,14d
## diffname pc/devusb.c 2002/0221
## diff -e /n/emeliedump/2002/0219/sys/src/9/pc/devusb.c /n/emeliedump/2002/0221/sys/src/9/pc/devusb.c
835c
if(i >= 1 && i*e->samplesz <= 12*1000*1000){
.
## diffname pc/devusb.c 2002/0403
## diff -e /n/emeliedump/2002/0221/sys/src/9/pc/devusb.c /n/emeliedump/2002/0403/sys/src/9/pc/devusb.c
434,435d
432a
if((uh = usbprobe(cardno, ctlrno)) == nil){
cardno++;
continue;
}
usbhost[ctlrno] = uh;
ctlrno++;
.
429,431c
ebuf = buf + sizeof buf;
p = seprint(buf, ebuf, "#U/usb%d: %s: port 0x%luX irq %lud", ctlrno, usbtypes[cardno].type, uh->port, uh->irq);
if(uh->mem)
p = seprint(p, ebuf, " addr 0x%luX", PADDR(uh->mem));
if(uh->size)
seprint(p, ebuf, " size 0x%luX", uh->size);
print("%s\n", buf);
return uh;
}
static void
usbreset(void)
{
int cardno, ctlrno;
Usbhost *uh;
for(ctlrno = 0; ctlrno < MaxUsb; ctlrno++){
if((uh = usbprobe(-1, ctlrno)) == nil)
continue;
usbhost[ctlrno] = uh;
}
cardno = ctlrno = 0;
while(usbtypes[cardno].type != nil && ctlrno < MaxUsb){
if(usbhost[ctlrno] != nil){
ctlrno++;
continue;
.
421,427c
/*
* IRQ2 doesn't really exist, it's used to gang the interrupt
* controllers together. A device set to IRQ2 will appear on
* the second interrupt controller as IRQ9.
*/
if(uh->irq == 2)
uh->irq = 9;
snprint(name, sizeof(name), "usb%d", ctlrno);
intrenable(uh->irq, uh->interrupt, uh, uh->tbdf, name);
.
411,419c
if(cardno >= MaxUsb || usbtypes[cardno].type == nil){
free(uh);
return nil;
}
if(usbtypes[cardno].reset(uh) < 0){
free(uh);
return nil;
}
.
408,409c
break;
}
}
.
406c
if(cistrcmp(usbtypes[cardno].type, type))
.
404c
if(type==nil || *type==0)
.
394,402c
uh = malloc(sizeof(Usbhost));
memset(uh, 0, sizeof(Usbhost));
uh->tbdf = BUSUNKNOWN;
if(cardno < 0){
if(isaconfig("usb", ctlrno, uh) == 0){
free(uh);
return nil;
}
for(cardno = 0; usbtypes[cardno].type; cardno++){
.
392c
char buf[128], *ebuf, name[64], *p, *type;
.
390d
387,388c
static Usbhost*
usbprobe(int cardno, int ctlrno)
.
## diffname pc/devusb.c 2002/0502
## diff -e /n/emeliedump/2002/0403/sys/src/9/pc/devusb.c /n/emeliedump/2002/0502/sys/src/9/pc/devusb.c
453a
if(getconf("*nousbprobe"))
return;
.
## diffname pc/devusb.c 2002/0512
## diff -e /n/emeliedump/2002/0502/sys/src/9/pc/devusb.c /n/emeliedump/2002/0512/sys/src/9/pc/devusb.c
896c
if(n < 8)
.
## diffname pc/devusb.c 2003/0301
## diff -e /n/emeliedump/2002/0512/sys/src/9/pc/devusb.c /n/emeliedump/2003/0301/sys/src/9/pc/devusb.c
432c
p = seprint(buf, ebuf, "#U/usb%d: %s: port 0x%luX irq %d", ctlrno, usbtypes[cardno].type, uh->port, uh->irq);
.
|