## diffname mpc/devusb.c 2000/0516
## diff -e /dev/null /n/emeliedump/2000/0516/sys/src/9/mpc/devusb.c
0a
#include "u.h"
#include "lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"
typedef struct USBparam USBparam;
struct USBparam {
ushort epptr[4]; /**/
ulong rstate;
ulong rptr;
ushort frame_n; /**/
ushort rbcnt;
ulong rtemp;
};
typedef struct EPparam EPparam;
struct EPparam {
ushort rbase;
ushort tbase;
uchar rfcr;
uchar tfcr;
ushort mrblr;
ushort rbptr;
ushort tbptr;
ulong tstate;
ulong tptr;
ushort tcrc;
ushort tbcnt;
ulong res[2];
};
enum {
Nrdre = 8, /* receive descriptor ring entries */
Ntdre = 8, /* transmit descriptor ring entries */
Rbsize = 8+3, /* ring buffer size (+3 for PID+CRC16) */
Bufsize = (Rbsize+7)&~7, /* aligned */
Nendpt = 4,
};
#define MkPID(x) (((~x&0xF)<<4)|(x&0xF))
enum {
TokIN = MkPID(9),
TokOUT = MkPID(1),
TokSOF = MkPID(5),
TokSETUP = MkPID(0xD),
TokDATA0 = MkPID(3),
TokDATA1 = MkPID(0xB),
TokACK = MkPID(2),
TokNAK = MkPID(0xA),
TokPRE = MkPID(0xC),
};
enum {
BDData0= 1<<7,
BDData1= 1<<6, /* DATA1 if set, DATA0 otherwise */
BDrxerr= 0x3F, /* various error flags */
BDtxcrc= 1<<10,
BDcnf= 1<<9, /* transmit confirmation */
/* BDData0, BDData1 */
BDNak= 1<<4, /* nak received */
BDStall= 1<<3, /* stall received */
BDTmo= 1<<2, /* timeout */
BDUrun= 1<<1, /* underrun */
/* usmod */
EN= 1<<0, /* enable USB */
HOST= 1<<1, /* host mode */
TEST= 1<<2, /* test mode */
RESUME= 1<<6, /* generate resume condition */
LSS= 1<<7, /* low-speed signalling */
/* usber */
Freset= 1<<9,
Fidle= 1<<8,
Ftxe3= 1<<7,
Ftxe2= 1<<6,
Ftxe1= 1<<5,
Ftxe0= 1<<4,
Ftxe= Ftxe0|Ftxe1|Ftxe2|Ftxe3,
Fsof= 1<<3,
Fbsy= 1<<2,
Ftxb= 1<<1,
Frxb= 1<<0,
/* uscom */
FifoFill= 1<<7,
FifoFlush= 1<<6,
/* USB commands (or'd with USBCmd) */
StopTxEndPt = 1<<4,
RestartTxEndPt = 2<<4,
SOFmask= (1<<11)-1,
};
#define USBABITS (SIBIT(14)|SIBIT(15))
#define USBRXCBIT (SIBIT(10)|SIBIT(11))
#define USBTXCBIT (SIBIT(6)|SIBIT(7))
/*
* software structures
*/
typedef struct Ctlr Ctlr;
typedef struct Endpt Endpt;
struct Endpt {
int x; /* index in Ctlr.pts */
int dev; /* remote device (host endpt only) */
int endpt; /* remote endpt (host endpt only) */
uchar addr[2]; /* crc5<<11 | endpt<<7 | dev */
int rtog; /* DATA0 or DATA1 */
int xtog; /* DATA0 or DATA1 */
int rxintr;
Rendez ir;
EPparam* ep;
Ring;
Queue* oq;
Queue* iq;
Ctlr* ctlr;
};
struct Ctlr {
Lock;
int init;
USB* usb;
USBparam *usbp;
Endpt pts[4];
};
static Ctlr usbctlr[1];
static void dumpusb(Block*, char*);
static void interrupt(Ureg*, void*);
static void setupep(int, EPparam*);
static void usbtest(void);
/*
* test driver for USB, host mode
*/
void
usbreset(void)
{
IMM *io;
USB *usb;
USBparam *usbp;
EPparam *ep;
int brg, i;
brg = brgalloc();
if(brg < 0){
print("usb: no baud rate generator is free\n");
return;
}
io = m->iomem;
usb = (USB*)((ulong)m->iomem+0xA00);
usbp = (USBparam*)KADDR(SCC1P);
/* select port pins */
io->padir &= ~USBABITS;
io->papar |= USBABITS;
io->pcpar = (io->pcpar & ~USBRXCBIT) | USBTXCBIT;
io->pcdir = (io->pcdir & ~USBRXCBIT) | USBTXCBIT;
io->pcso |= USBRXCBIT;
eieio();
ep = cpmalloc(Nendpt*sizeof(*ep), 32);
if(ep == nil){
print("can't allocate USB\n");
return;
}
print("ep=#%.8ux\n", PADDR(ep));
usbp->frame_n = 0;
usbp->rstate = 0;
for(i=0; i<Nendpt; i++){
usb->uscom = FifoFlush|i;
usb->usep[i] = 0;
usbp->epptr[i] = PADDR(ep+i) & 0xffff;
ep[i].rbase = 0;
ep[i].tbase = 0;
ep[i].rfcr = 0x10;
ep[i].tfcr = 0x10;
ep[i].mrblr = Bufsize;
ep[i].rbptr = 0;
ep[i].tbptr = 0;
ep[i].tstate = 0;
ep[i].tptr = 0;
}
usbctlr->usb = usb;
usbctlr->usbp = usbp;
usb->usmod = 0;
if(1)
usb->usmod = HOST;
if(0)
usb->usmod |= LSS;
if(1)
usb->usmod |= TEST|HOST;
/* set up baud rate generator for appropriate speed */
if(usb->usmod & LSS){
print("USB: low speed\n");
io->brgc[brg] = baudgen(1000000, 1) | BaudEnable;
}else
io->brgc[brg] = BaudEnable;
eieio();
io->sicr = (io->sicr & ~(7<<3)) | (brg<<3); /* set R1CS */
eieio();
usb->usep[0] = (0<<12) | (2<<8) | (1<<5); /* EPN=0, TM=bulk, multi frame */
setupep(0, ep);
if(usb->usmod & TEST){
print("USB: loop back mode\n");
usb->usep[1] = (1<<12) | (0<<8); /* EPN=1, TM=control*/
setupep(1, ep+1);
}
m->bcsr[4] &= ~(DisableUSB|DisableUSBVcc);
if(usb->usmod & LSS)
m->bcsr[4] &= ~USBFullSpeed;
else
m->bcsr[4] |= USBFullSpeed;
if(usb->usmod & HOST && !(usb->usmod & TEST))
usb->usadr = 0x7f; /* unused in host mode, but just in case */
else
usb->usadr = 0;
usb->usber = ~0; /* clear events */
intrenable(CPICvec+0x1E, interrupt, usbctlr, BUSUNKNOWN);
usb->usbmr = ~0 & ~Fidle; /* enable all events except idle */
eieio();
delay(12);
usb->usmod |= EN;
}
static void
setupep(int n, EPparam *ep)
{
Endpt *e;
e = &usbctlr->pts[n];
e->x = n;
e->ctlr = usbctlr;
e->xtog = TokDATA0;
if(e->oq == nil)
e->oq = qopen(8*1024, 1, 0, 0);
if(e->iq == nil)
e->iq = qopen(8*1024, 1, 0, 0);
e->ep = ep;
if(ioringinit(e, Nrdre, Ntdre, Bufsize) < 0)
panic("usbreset");
ep->rbase = PADDR(e->rdr);
ep->rbptr = ep->rbase;
ep->tbase = PADDR(e->tdr);
ep->tbptr = ep->tbase;
eieio();
}
static void
epprod(Ctlr *ctlr, int epn)
{
ctlr->usb->uscom = FifoFill | (epn&3);
eieio();
}
static void
txstart(Endpt *r)
{
int len, flags;
Block *b;
BD *dre, *rdy;
if(r->ctlr->init)
return;
rdy = nil;
while(r->ntq < Ntdre-1){
b = qget(r->oq);
if(b == 0)
break;
dre = &r->tdr[r->tdrh];
if(dre->status & BDReady)
panic("txstart");
print("Tx%d: ", r->x); dumpusb(b, "TX");
/*
* Give ownership of the descriptor to the chip, increment the
* software ring descriptor pointer and tell the chip to poll.
*/
flags = 0;
switch(b->rp[0]){
case TokDATA1:
flags = BDData1|BDData0;
case TokDATA0:
flags |= BDtxcrc|BDcnf|BDReady;
flags |= BDData0;
/*b->rp++; /* skip DATAn */
}
len = BLEN(b);
dcflush(b->rp, len);
if(r->txb[r->tdrh] != nil)
panic("usb: txstart");
r->txb[r->tdrh] = b;
dre->addr = PADDR(b->rp);
dre->length = len;
eieio();
dre->status = (dre->status & BDWrap) | BDInt|BDLast | flags;
if(rdy){
rdy->status |= BDReady;
rdy = nil;
}
if((flags & BDReady) == 0)
rdy = dre;
eieio();
r->ntq++;
r->tdrh = NEXT(r->tdrh, Ntdre);
}
if(rdy)
rdy->status |= BDReady;
eieio();
}
static void
transmit(Endpt *r)
{
ilock(r->ctlr);
txstart(r);
iunlock(r->ctlr);
}
static void
endptintr(Endpt *r, int events)
{
int len, status;
BD *dre;
Block *b;
if(events & Frxb || 1){
dre = &r->rdr[r->rdrx];
while(((status = dre->status) & BDEmpty) == 0){
if(status & BDrxerr || (status & (BDFirst|BDLast)) != (BDFirst|BDLast)){
print("usb rx%d: %4.4ux %d ", r->x, status, dre->length);
{uchar *p;int i; p=KADDR(dre->addr); for(i=0;i<14&&i<dre->length; i++)print(" %.2ux", p[i]);print("\n");}
}
else{
/*
* We have a packet. Read it into the next
* free ring buffer, if any.
*/
len = dre->length-2; /* discard CRC */
if(len >= 0 && (b = iallocb(len)) != 0){
memmove(b->wp, KADDR(dre->addr), len);
dcflush(KADDR(dre->addr), len);
b->wp += len;
// usbiq(ctlr, b);
print("rx%d: ", r->x); dumpusb(b, "RX");
freeb(b);
}
}
/*
* Finished with this descriptor, reinitialise it,
* give it back to the chip, then on to the next...
*/
dre->length = 0;
dre->status = (dre->status & BDWrap) | BDEmpty | BDInt;
eieio();
r->rdrx = NEXT(r->rdrx, Nrdre);
dre = &r->rdr[r->rdrx];
r->rxintr = 1;
wakeup(&r->ir);
}
}
/*
* Transmitter interrupt: handle anything queued for a free descriptor.
*/
if(events & (Ftxb|Ftxe0|Ftxe1)){
lock(r->ctlr);
while(r->ntq){
dre = &r->tdr[r->tdri];
if(dre->status & BDReady)
break;
print("usbtx%d=#%.4x %8.8lux\n", r->x, dre->status, dre->addr);
/* TO DO: error counting */
b = r->txb[r->tdri];
if(b == nil)
panic("usb/interrupt: bufp");
r->txb[r->tdri] = nil;
freeb(b);
r->ntq--;
r->tdri = NEXT(r->tdri, Ntdre);
}
txstart(r);
unlock(r->ctlr);
if(r->ntq)
epprod(r->ctlr, r->x);
}
}
static void
interrupt(Ureg*, void *arg)
{
int events;
Ctlr *ctlr;
USB *usb;
ctlr = arg;
usb = ctlr->usb;
events = usb->usber;
eieio();
usb->usber = events;
print("USB#%x\n", events);
if(events & Fsof)
print("SOF #%ux\n", ctlr->usbp->frame_n&SOFmask);
endptintr(&ctlr->pts[0], events);
if(usb->usmod & TEST)
endptintr(&ctlr->pts[1], events);
if(events & Ftxe0)
cpmop(USBCmd|RestartTxEndPt, USBID, 0<<1);
if(events & Ftxe1)
cpmop(USBCmd|RestartTxEndPt, USBID, 1<<1);
}
void
usbinit(void)
{
usbreset();
usbtest();
}
static void
dumpusb(Block *b, char *msg)
{
int i;
print("%s: %8.8lux [%d]: ", msg, (ulong)b->rp, BLEN(b));
for(i=0; i<BLEN(b) && i < 16; i++)
print(" %.2x", b->rp[i]);
print("\n");
}
/*
* could generate a table, but since the result can be precomputed
* per connection, it doesn't seem worthwhile.
*/
static ulong
crc5(ushort v)
{
int i;
ulong x, poly;
x = 0x1F;
poly = 0x14;
for(i=0; i<11; i++, v>>= 1){
if((x^v) & 1)
x = ((x>>1)&0x0F)^poly;
else
x = ((x>>1)&0x0F);
}
return ~x&0x1F;
}
static void
setaddr(Endpt *e, int dev, int endpt)
{
ushort v;
e->dev = dev;
e->endpt = endpt;
v = (e->endpt<<7) | e->dev;
v |= crc5(v) << 11;
e->addr[0] = v;
e->addr[1] = v>>8;
}
/*
* Token packets (watch bit/byte order; it's LSB on the wire):
*
* 4 bit PID, 4 bit check
* TokIN, etc.
* 7 bit device, 4 bit end point
* 5 bit CRC5
*/
static void
sendtoken(Endpt *e, int pid)
{
Block *b;
b = allocb(3);
b->wp[0] = pid;
b->wp[1] = e->addr[0];
b->wp[2] = e->addr[1];
b->wp += 3;
if(pid == TokDATA0 || pid == TokDATA1)
e->xtog ^= TokDATA1^TokDATA0;
else
e->xtog = TokDATA0;
qbwrite(e->oq, b);
/* wait for reply? */
// transmit(e);
}
static void
sendSOF(Endpt *e, int frame)
{
Block *b;
b = allocb(3);
b->wp[0] = TokSOF;
frame &= SOFmask;
frame |= crc5(frame) << 11;
b->wp[1] = frame;
b->wp[2] = frame>>8;
b->wp += 3;
qbwrite(e->oq, b);
transmit(e);
}
static void
sendack(Endpt *e, int pid)
{
Block *b;
b = allocb(1);
b->wp[0] = pid;
b->wp += 1;
qbwrite(e->oq, b);
/* wait for reply? */
transmit(e);
}
static void
senddata(Endpt *e, void *buf, long n)
{
Block *b;
b = allocb(n+4);
b->wp += 3; /* skip 3 bytes for word alignment of data */
b->rp = b->wp;
b->wp[0] = e->xtog;
memmove(b->wp+1, buf, n);
b->wp += n+1;
e->xtog ^= TokDATA1^TokDATA0;
qbwrite(e->oq, b);
/* wait for reply? */
transmit(e);
epprod(e->ctlr, e->x);
}
static int
usbinput(void *a)
{
return ((Endpt*)a)->rxintr;
}
static void
usbtest(void)
{
Ctlr *ctlr;
Endpt *e, *e1;
uchar msg[8];
int epn, testing, i;
ctlr = usbctlr;
epn = 0;
testing = ctlr->usb->usmod & TEST;
if(testing)
epn = 1;
e = &ctlr->pts[0];
e1 = &ctlr->pts[1];
if((ctlr->usb->usmod & LSS) == 0)
sendSOF(e, 0);
memset(msg, 0, 8);
msg[0] = 0;
msg[1] = 8; /* set configuration */
msg[2] = 1;
setaddr(e, 0, epn);
sendtoken(e, TokSETUP);
senddata(e, msg, 8);
if(testing){
for(i=0; i<8; i++)
msg[i] = 0x40+i;
senddata(e1, msg, 8);
tsleep(&up->sleep, return0, 0, 3);
}
if(1){
e->rxintr = 0;
sendtoken(e, TokIN);
transmit(e);
epprod(ctlr, 0);
tsleep(&e->ir, usbinput, e, 1000);
if(!usbinput(e))
print("RX: none\n");
}
sendtoken(e, TokSETUP);
msg[0] = 0x23;
msg[1] = 3; /* set feature */
msg[2] = 8; /* port power */
msg[3] = 0;
msg[4] = 1; /* port 1 */
senddata(e, msg, 8);
if(testing){
for(i=0; i<8; i++)
msg[i] = 0x60+i;
senddata(e1, msg, 8);
tsleep(&up->sleep, return0, 0, 3);
}
if(1){
e->rxintr = 0;
sendtoken(e, TokIN);
transmit(e);
epprod(ctlr, 0);
tsleep(&e->ir, usbinput, e, 1000);
if(!usbinput(e))
print("RX: none\n");
}
}
.
## diffname mpc/devusb.c 2001/0527 # deleted
## diff -e /n/emeliedump/2000/0516/sys/src/9/mpc/devusb.c /n/emeliedump/2001/0527/sys/src/9/mpc/devusb.c
1,628d
|