| plan 9 kernel history: overview | file list | diff list |
1998/0109/pc/devastar.c (diff list | history)
| pc/devastar.c on 1994/1106 | ||
| 1994/1106 | #include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" #include "../port/error.h" /* | |
| 1994/1107 | * Stargate's Avanstar serial board. There are ISA, EISA, microchannel * versions. We only handle the ISA one. | |
| 1994/1113 | * * At the expense of performance, I've tried to be careful about * endian-ness to make this convertable to other ISA bus machines. | |
| 1998/0109 | * However, xchgw() is in assembler and will have to be translated. | |
| 1994/1106 | */ | |
| 1994/1113 | #define LENDIAN 1 /* unsigned short little endian representation */ #ifdef LENDIAN #define LEUS(x) (x) #else #define LEUS(x) ( (((x)<<8)&0xff00) | (((x)>>8)&0xff) ) #endif LENDIAN | |
| 1994/1106 | typedef struct Astar Astar; | |
| 1994/1107 | typedef struct Astarchan Astarchan; | |
| 1994/1106 | enum { /* ISA control ports */ ISAid= 0, /* Id port and its values */ ISAid0= 0xEC, ISAid1= 0x13, | |
| 1996/0807 | ISAid0x= 0x69, ISAid1x= 0x96, | |
| 1994/1106 | ISActl1= 1, /* board control */ ISAien= 1<<7, /* interrupt enable */ | |
| 1994/1107 | ISAirq= 7<<4, /* mask for irq code */ | |
| 1994/1115 | ISAnotdl= 1<<1, /* download bit (0 == download) */ | |
| 1994/1106 | ISApr= 1<<0, /* program ready */ ISActl2= 2, /* board control */ ISA186ien= 1<<7, /* I186 irq enable bit state */ ISA186idata= 1<<6, /* I186 irq data bit state */ | |
| 1994/1107 | ISAmen= 1<<4, /* enable memory to respond to ISA cycles */ | |
| 1994/1121 | ISAmbank= 0xf<<0, /* shift for 4 bit memory bank */ | |
| 1994/1107 | ISAmaddr= 3, /* bits 14-19 of the boards mem address */ | |
| 1994/1106 | ISAstat1= 4, /* board status (1 bit per channel) */ ISAstat2= 5, /* board status (1 bit per channel) */ | |
| 1994/1107 | ||
| 1994/1113 | Pageshift= 14, /* footprint of card mem in ISA space */ Pagesize= 1<<Pageshift, Pagemask= Pagesize-1, | |
| 1998/0109 | PCIrange= 0x00, PCIremap= 0x04, PCIregion= 0x18, PCImailbox= 0x40, PCIdoorbell0= 0x60, PCIdoorbell1= 0x64, PCIcontrol= 0x68, /* write */ PCIstatus= 0x68, /* read */ PCIcommand= 0x6C, Maxcard= 8, Pramsize= 64*1024, /* size of program ram */ | |
| 1994/1106 | }; | |
| 1994/1113 | #define APAGE(x) ((x)>>Pageshift) | |
| 1994/1116 | #define LOCKPAGE(a, o) if((a)->needpage){ilock(&(a)->pagelock);setpage(a, o);} #define UNLOCKPAGE(a) if((a)->needpage)iunlock(&(a)->pagelock) | |
| 1994/1107 | /* IRQ codes */ static int isairqcode[16] = { -1, -1, -1, 0<<4, 1<<4, 2<<4, -1, -1, -1, 3<<4, 4<<4, 5<<4, 6<<4, -1, -1, 7<<4, }; | |
| 1994/1106 | /* control program global control block */ typedef struct GCB GCB; struct GCB { ushort cmd; /* command word */ ushort status; /* status word */ ushort serv; /* service request, must be accessed via exchange 'X' */ ushort avail; /* available buffer space */ ushort type; /* board type */ ushort cpvers; /* control program version */ | |
| 1994/1113 | ushort ccbn; /* control channel block count */ ushort ccboff; /* control channel block offset */ ushort ccbsz; /* control channel block size */ | |
| 1994/1106 | ushort cmd2; /* command word 2 */ ushort status2; /* status word 2 */ ushort errserv; /* comm error service request 'X' */ ushort inserv; /* input buffer service request 'X' */ ushort outserv; /* output buffer service request 'X' */ ushort modemserv; /* modem change service request 'X' */ ushort cmdserv; /* channel command service request 'X' */ }; | |
| 1994/1107 | enum { /* GCB.cmd commands/codes */ Greadycmd= 0, Gdiagcmd= 1, Gresetcmd= 2, /* GCB.status values */ Gready= 0, Gstopped= 1, Gramerr= 2, Gbadcmd= 3, Gbusy= 4, /* GCB.type values */ Gx00m= 0x6, G100e= 0xA, Gx00i= 0xC, | |
| 1994/1114 | /* GCB.cmd2 bit */ Gintack= 0x1, | |
| 1994/1107 | /* GCB.status2 bits */ Ghas232= (1<<0), Ghas422= (1<<1), Ghasmodems= (1<<2), Ghasrj11s= (1<<7), Ghasring= (1<<8), Ghasdcd= (1<<9), Ghasdtr= (1<<10), Ghasdsr= (1<<11), Ghascts= (1<<12), Ghasrts= (1<<13), }; | |
| 1994/1106 | /* control program channel control block */ typedef struct CCB CCB; struct CCB { ushort baud; /* baud rate */ ushort format; /* data format */ | |
| 1994/1107 | ushort proto; /* line protocol */ | |
| 1994/1106 | ushort insize; /* input buffer size */ ushort outsize; /* output buffer size */ ushort intrigger; /* input buffer trigger rate */ ushort outlow; /* output buffer low water mark */ | |
| 1994/1107 | char xon[2]; /* xon characters */ | |
| 1994/1106 | ushort inhigh; /* input buffer high water mark */ ushort inlow; /* input buffer low water mark */ ushort cmd; /* channel command */ ushort status; /* channel status */ ushort inbase; /* input buffer start addr */ ushort inlim; /* input buffer ending addr */ ushort outbase; /* output buffer start addr */ ushort outlim; /* output buffer ending addr */ ushort inwp; /* input read and write pointers */ ushort inrp; ushort outwp; /* output read and write pointers */ ushort outrp; ushort errstat; /* error status */ ushort badp; /* bad character pointer */ ushort mctl; /* modem control */ ushort mstat; /* modem status */ ushort bstat; /* blocking status */ ushort rflag; /* character received flag */ | |
| 1994/1107 | char xoff[2]; /* xoff characters */ | |
| 1994/1106 | ushort status2; | |
| 1994/1107 | char strip[2]; /* strip/error characters */ | |
| 1994/1106 | }; | |
| 1994/1107 | enum { /* special baud rate codes for CCB.baud */ Cb76800= 0xff00, Cb115200= 0xff01, /* CCB.format fields */ | |
| 1994/1113 | Clenmask= 3<<0, /* data bits */ | |
| 1994/1107 | C1stop= 0<<2, /* stop bits */ C2stop= 1<<2, Cnopar= 0<<3, /* parity */ Coddpar= 1<<3, Cevenpar= 3<<3, Cmarkpar= 5<<3, Cspacepar= 7<<3, | |
| 1994/1113 | Cparmask= 7<<3, | |
| 1994/1107 | Cnormal= 0<<6, /* normal mode */ Cecho= 1<<6, /* echo mode */ Clloop= 2<<6, /* local loopback */ Crloop= 3<<6, /* remote loopback */ /* CCB.proto fields */ Cobeyxon= 1<<0, /* obey received xoff/xon controls */ Canyxon= 1<<1, /* any rcvd character restarts xmit */ Cgenxon= 1<<2, /* generate xoff/xon controls */ Cobeycts= 1<<3, /* obey hardware flow ctl */ Cgendtr= 1<<4, /* dtr off when uart rcvr full */ C½duplex= 1<<5, /* rts off while xmitting */ Cgenrts= 1<<6, /* generate hardware flow ctl */ Cmctl= 1<<7, /* direct modem control via CCB.mctl */ Cstrip= 1<<12, /* to strip out characters */ Ceia422= 1<<13, /* to select eia 422 lines */ /* CCB.cmd fields */ Cconfall= 1<<0, /* configure channel and UART */ Cconfchan= 1<<1, /* configure just channel */ Cflushin= 1<<2, /* flush input buffer */ Cflushout= 1<<3, /* flush output buffer */ Crcvena= 1<<4, /* enable receiver */ Crcvdis= 1<<5, /* disable receiver */ Cxmtena= 1<<6, /* enable transmitter */ Cxmtdis= 1<<7, /* disable transmitter */ Cmreset= 1<<9, /* reset modem */ /* CCB.errstat fields */ Coverrun= 1<<0, Cparity= 1<<1, Cframing= 1<<2, Cbreak= 1<<3, /* CCB.mctl fields */ Cdtrctl= 1<<0, Crtsctl= 1<<1, | |
| 1994/1112 | Cbreakctl= 1<<4, | |
| 1994/1107 | /* CCB.mstat fields */ Cctsstat= 1<<0, Cdsrstat= 1<<1, Cristat= 1<<2, Cdcdstat= 1<<3, /* CCB.bstat fields */ Cbrcvoff= 1<<0, Cbxmtoff= 1<<1, Clbxoff= 1<<2, /* transmitter blocked by XOFF */ Clbcts= 1<<3, /* transmitter blocked by CTS */ Crbxoff= 1<<4, /* remote blocked by xoff */ Crbrts= 1<<4, /* remote blocked by rts */ }; | |
| 1994/1106 | /* host per controller info */ struct Astar { | |
| 1994/1114 | QLock; /* lock for rendez */ Rendez r; /* waiting for command completion */ | |
| 1994/1112 | ||
| 1994/1107 | ISAConf; | |
| 1998/0109 | Pcidev* pci; | |
| 1994/1114 | Lock pagelock; /* lock for setting page */ int page; /* page currently mapped */ | |
| 1994/1107 | int id; /* from plan9.ini */ | |
| 1994/1111 | int nchan; /* number of channels */ Astarchan *c; /* channels */ | |
| 1994/1107 | int ramsize; /* 16k or 256k */ | |
| 1994/1113 | int memsize; /* size of memory currently mapped */ | |
| 1994/1116 | int needpage; | |
| 1998/0109 | int pagebase; /* pci */ | |
| 1994/1115 | GCB *gcb; /* global board comm area */ | |
| 1994/1113 | uchar *addr; /* base of memory area */ int running; | |
| 1994/1106 | }; /* host per channel info */ struct Astarchan { | |
| 1994/1121 | QLock; /* lock for rendez */ Rendez r; /* waiting for command completion */ | |
| 1994/1106 | ||
| 1994/1121 | Astar *a; /* controller */ CCB *ccb; /* channel control block */ | |
| 1994/1111 | int perm; | |
| 1994/1112 | int opens; | |
| 1994/1121 | int baud; /* baud rate */ int framing; /* framing errors */ int overrun; /* overruns */ int dtr; /* non-zero means dtr on */ | |
| 1994/1122 | int rts; /* non-zero means rts on */ | |
| 1994/1107 | ||
| 1994/1106 | Queue *iq; Queue *oq; }; | |
| 1994/1109 | Astar *astar[Maxcard]; | |
| 1994/1111 | static int nastar; | |
| 1994/1107 | ||
| 1994/1111 | enum { | |
| 1997/0710 | Qmem= 1, | |
| 1994/1112 | Qbctl, | |
| 1994/1111 | Qdata, Qctl, Qstat, }; #define TYPE(x) ((x)&0xff) | |
| 1994/1122 | #define BOARD(x) (((x)>>16)&0xff) #define CHAN(x) (((x)>>8)&0xff) | |
| 1994/1111 | #define QID(b,c,t) (((b)<<16)|((c)<<8)|(t)) | |
| 1994/1114 | static int astarsetup(Astar*); static void astarintr(Ureg*, void*); static void astarkick(Astarchan*); | |
| 1994/1116 | static void astarkickin(Astarchan*); | |
| 1994/1114 | static void enable(Astarchan*); static void disable(Astarchan*); | |
| 1994/1115 | static void astarctl(Astarchan*, char*); | |
| 1994/1107 | ||
| 1994/1113 | /* * Only 16k maps into ISA space */ | |
| 1994/1114 | static void | |
| 1994/1113 | setpage(Astar *a, ulong offset) { | |
| 1994/1121 | int i; | |
| 1994/1113 | ||
| 1998/0109 | if(a->pci){ print("#G%d: setpage caller pc %uX\n", a->id, getcallerpc(a)); return; } | |
| 1994/1113 | i = APAGE(offset); if(i == a->page) return; | |
| 1994/1120 | ||
| 1994/1121 | outb(a->port+ISActl2, ISAmen|i); | |
| 1994/1113 | a->page = i; } /* * generate the astar directory entries */ | |
| 1997/0327 | static int | |
| 1994/1112 | astargen(Chan *c, Dirtab *tab, int ntab, int i, Dir *db) | |
| 1994/1111 | { int dev, sofar, ch, t; | |
| 1994/1112 | extern ulong kerndate; | |
| 1994/1111 | ||
| 1994/1115 | memset(db, 0, sizeof(Dir)); | |
| 1994/1111 | USED(tab, ntab); sofar = 0; | |
| 1994/1112 | for(dev = 0; dev < nastar; dev++){ | |
| 1994/1111 | if(sofar == i){ | |
| 1994/1115 | sprint(db->name, "astar%dmem", astar[dev]->id); | |
| 1994/1112 | db->qid.path = QID(dev, 0, Qmem); | |
| 1994/1111 | db->mode = 0660; | |
| 1994/1115 | db->length = astar[dev]->memsize; | |
| 1994/1111 | break; } | |
| 1994/1112 | sofar++; | |
| 1994/1111 | ||
| 1994/1112 | if(sofar == i){ | |
| 1994/1115 | sprint(db->name, "astar%dctl", astar[dev]->id); | |
| 1994/1112 | db->qid.path = QID(dev, 0, Qbctl); db->mode = 0660; break; } sofar++; if(i - sofar < 3*astar[dev]->nchan){ | |
| 1994/1111 | i -= sofar; ch = i/3; t = i%3; switch(t){ case 0: | |
| 1994/1121 | sprint(db->name, "eia%d%2.2d", astar[dev]->id, ch); | |
| 1994/1111 | db->mode = astar[dev]->c[ch].perm; | |
| 1994/1115 | db->qid.path = QID(dev, ch, Qdata); | |
| 1994/1111 | break; case 1: | |
| 1994/1121 | sprint(db->name, "eia%d%2.2dctl", astar[dev]->id, ch); | |
| 1994/1111 | db->mode = astar[dev]->c[ch].perm; | |
| 1994/1115 | db->qid.path = QID(dev, ch, Qctl); | |
| 1994/1111 | break; case 2: | |
| 1994/1121 | sprint(db->name, "eia%d%2.2dstat", astar[dev]->id, ch); | |
| 1994/1111 | db->mode = 0444; | |
| 1994/1115 | db->qid.path = QID(dev, ch, Qstat); | |
| 1994/1111 | break; | |
| 1994/1112 | } | |
| 1994/1111 | break; } | |
| 1994/1112 | sofar += 3*astar[dev]->nchan; | |
| 1994/1111 | } | |
| 1994/1112 | if(dev == nastar) | |
| 1994/1111 | return -1; | |
| 1994/1115 | db->qid.vers = 0; | |
| 1994/1111 | db->atime = seconds(); db->mtime = kerndate; memmove(db->uid, eve, NAMELEN); memmove(db->gid, eve, NAMELEN); | |
| 1997/0408 | db->type = devtab[c->type]->dc; | |
| 1994/1111 | db->dev = c->dev; if(c->flag&CMSG) db->mode |= CHMOUNT; return 1; } | |
| 1997/0327 | static void | |
| 1994/1107 | astarreset(void) | |
| 1994/1106 | { | |
| 1998/0109 | int i, x; | |
| 1994/1107 | Astar *a; | |
| 1998/0109 | Pcidev *p; | |
| 1994/1106 | ||
| 1998/0109 | p = nil; | |
| 1994/1106 | for(i = 0; i < Maxcard; i++){ | |
| 1994/1109 | a = astar[nastar] = xalloc(sizeof(Astar)); | |
| 1994/1112 | if(isaconfig("serial", i, a) == 0){ | |
| 1994/1107 | xfree(a); | |
| 1994/1109 | astar[nastar] = 0; | |
| 1994/1118 | continue; | |
| 1994/1106 | } | |
| 1998/0109 | a->ramsize = 0; | |
| 1994/1120 | /* check all possible names */ | |
| 1998/0109 | if(cistrcmp(a->type, "a100i") == 0 || cistrcmp(a->type,"A100I") == 0) | |
| 1994/1107 | a->ramsize = 16*1024; | |
| 1998/0109 | else if(cistrcmp(a->type, "a200i") == 0 || cistrcmp(a->type,"A200I") == 0 || cistrcmp(a->type, "a16i") == 0) | |
| 1994/1107 | a->ramsize = 256*1024; | |
| 1998/0109 | else if(cistrcmp(a->type, "AvanstarXp") == 0){ if(p = pcimatch(p, 0x114F, 0x6001)){ a->pci = p; /* * It's really 128KB, but split into * two 64KB chunks. */ a->ramsize = 64*1024; } } if(a->ramsize == 0){ | |
| 1994/1118 | xfree(a); astar[nastar] = 0; | |
| 1994/1107 | continue; | |
| 1994/1118 | } | |
| 1994/1108 | ||
| 1994/1107 | a->id = i; | |
| 1998/0109 | if(a->pci){ a->irq = p->intl; a->port = p->mem[1].bar & ~0x03; a->mem = upamalloc(p->mem[2].bar & ~0x0F, p->mem[2].size, 0); a->addr = (uchar*)a->mem; a->gcb = (GCB*)(a->mem+0x10000); | |
| 1994/1107 | ||
| 1998/0109 | /* * Toggle the software reset and wait for * the adapter local init status to indicate done. */ outl(a->port+PCIremap, 0xA0000001); x = inl(a->port+PCIcommand); outl(a->port+PCIcommand, 0x40000000|x); microdelay(1); outl(a->port+PCIcommand, x); delay(100); for(x = 0; x < 10000; x++){ if(inl(a->port+PCIcommand) & 0x80000000) break; } if(!(inl(a->port+PCIcommand) & 0x80000000)) print("#G: didn't reset\n", a->id); | |
| 1994/1210 | ||
| 1998/0109 | /* * So the memory can be read before any other * initialisation takes place. */ a->memsize = a->ramsize; | |
| 1994/1107 | } | |
| 1998/0109 | else{ /* defaults */ if(a->irq == 0) a->irq = 15; a->mem = umbmalloc(a->mem, Pagesize, Pagesize); if(a->mem == 0) panic("astarreset: %lux", a->mem); a->mem = PADDR(a->mem); if(astarsetup(a) < 0){ xfree(a); astar[nastar] = 0; continue; } print("\tctl1 %ux ctl2 %ux maddr %ux stat1 %ux stat2 %ux\n", inb(a->port+ISActl1), inb(a->port+ISActl2), inb(a->port+ISAmaddr), inb(a->port+ISAstat1), inb(a->port+ISAstat2)); } print("#G%d: %s port 0x%luX addr 0x%luX irq %d\n", a->id, a->type, a->port, a->addr, a->irq); | |
| 1994/1109 | nastar++; | |
| 1994/1107 | } } | |
| 1994/1109 | /* isa ports an ax00i can appear at */ | |
| 1997/0327 | static int isaport[] = { 0x200, 0x208, 0x300, 0x308, 0x600, 0x608, 0x700, 0x708, 0 }; | |
| 1994/1107 | ||
| 1994/1109 | static int astarprobe(int port) | |
| 1994/1107 | { uchar c, c1; if(port < 0) return 0; | |
| 1994/1113 | c = inb(port+ISAid); c1 = inb(port+ISAid); | |
| 1994/1107 | return (c == ISAid0 && c1 == ISAid1) | |
| 1996/0807 | || (c == ISAid1 && c1 == ISAid0) || (c == ISAid0x && c1 == ISAid1x) || (c == ISAid1x && c1 == ISAid0x); | |
| 1994/1107 | } | |
| 1994/1109 | static int | |
| 1994/1107 | astarsetup(Astar *a) { | |
| 1994/1121 | int i, found; | |
| 1994/1107 | /* see if the card exists */ found = 0; if(a->port == 0) for(i = 0; isaport[i]; i++){ a->port = isaport[i]; | |
| 1994/1109 | found = astarprobe(isaport[i]); | |
| 1994/1107 | if(found){ isaport[i] = -1; break; } } else | |
| 1994/1112 | found = astarprobe(a->port); | |
| 1994/1107 | if(!found){ | |
| 1998/0109 | print("#G%d: not found\n", a->id); | |
| 1994/1107 | return -1; } | |
| 1994/1120 | /* check interrupt level */ | |
| 1994/1112 | if(isairqcode[a->irq] == -1){ | |
| 1998/0109 | print("#G%d: bad irq %d\n", a->id, a->irq); | |
| 1994/1107 | return -1; } | |
| 1994/1111 | ||
| 1994/1120 | /* set ISA memory address */ outb(a->port+ISAmaddr, (a->mem>>12) & 0xfc); | |
| 1998/0109 | a->gcb = KADDR(a->mem); a->addr = KADDR(a->mem); | |
| 1994/1120 | /* disable ISA memory response */ | |
| 1994/1121 | outb(a->port+ISActl2, 0); | |
| 1994/1120 | a->memsize = 0; a->page = -1; | |
| 1994/1121 | /* reset processor */ outb(a->port+ISActl1, 0); | |
| 1994/1111 | return 0; } | |
| 1997/0327 | static Chan* | |
| 1994/1111 | astarattach(char *spec) { | |
| 1994/1115 | return devattach('G', spec); | |
| 1994/1111 | } | |
| 1997/0327 | static int | |
| 1994/1111 | astarwalk(Chan *c, char *name) { return devwalk(c, name, 0, 0, astargen); } | |
| 1997/0327 | static void | |
| 1994/1111 | astarstat(Chan *c, char *dp) { devstat(c, dp, 0, 0, astargen); } | |
| 1997/0327 | static Chan* | |
| 1994/1111 | astaropen(Chan *c, int omode) { | |
| 1994/1112 | Astar *a; | |
| 1994/1115 | Astarchan *ac; | |
| 1994/1111 | c = devopen(c, omode, 0, 0, astargen); | |
| 1994/1115 | a = astar[BOARD(c->qid.path)]; | |
| 1994/1111 | ||
| 1994/1112 | switch(TYPE(c->qid.path)){ case Qmem: case Qbctl: if(!iseve()) error(Eperm); break; | |
| 1994/1114 | case Qdata: case Qctl: | |
| 1994/1115 | ac = a->c + CHAN(c->qid.path); | |
| 1994/1114 | qlock(ac); if(waserror()){ | |
| 1994/1115 | qunlock(ac); | |
| 1995/0207 | ac->opens--; | |
| 1994/1114 | nexterror(); } | |
| 1994/1116 | if(ac->opens++ == 0){ | |
| 1994/1114 | enable(ac); | |
| 1994/1116 | qreopen(ac->iq); qreopen(ac->oq); } | |
| 1994/1114 | qunlock(ac); poperror(); break; | |
| 1994/1112 | } | |
| 1994/1111 | return c; } | |
| 1997/0327 | static void | |
| 1994/1111 | astarclose(Chan *c) { | |
| 1994/1112 | Astar *a; | |
| 1994/1115 | Astarchan *ac; | |
| 1994/1112 | ||
| 1997/0710 | if((c->flag & COPEN) == 0) return; | |
| 1994/1115 | a = astar[BOARD(c->qid.path)]; | |
| 1994/1112 | switch(TYPE(c->qid.path)){ | |
| 1994/1114 | case Qdata: case Qctl: | |
| 1994/1115 | ac = a->c + CHAN(c->qid.path); | |
| 1994/1114 | qlock(ac); if(waserror()){ | |
| 1994/1115 | qunlock(ac); | |
| 1994/1114 | nexterror(); | |
| 1994/1112 | } | |
| 1994/1122 | if(--(ac->opens) == 0){ | |
| 1994/1114 | disable(ac); | |
| 1994/1115 | qclose(ac->iq); qclose(ac->oq); } | |
| 1994/1114 | qunlock(ac); poperror(); | |
| 1994/1112 | break; } | |
| 1994/1111 | } | |
| 1994/1114 | /* | |
| 1998/0109 | * read PCI mapped memory */ static long pcimemread(Astar *a, uchar *to, long n, ulong offset) { uchar *from; int rem; if(offset+n > a->memsize){ if(offset >= a->memsize) return 0; n = a->memsize - offset; } from = a->addr+offset; for(rem = n; rem > 0; rem--) *to++ = *from++; return n; } /* | |
| 1994/1114 | * read ISA mapped memory */ | |
| 1994/1113 | static long | |
| 1998/0109 | isamemread(Astar *a, uchar *to, long n, ulong offset) | |
| 1994/1113 | { | |
| 1994/1114 | uchar *from, *e, *tp; | |
| 1994/1113 | int i, rem; | |
| 1994/1114 | uchar tmp[256]; | |
| 1994/1113 | if(offset+n > a->memsize){ if(offset >= a->memsize) return 0; n = a->memsize - offset; } for(rem = n; rem > 0; rem -= i){ /* map in right piece of memory */ i = offset&Pagemask; | |
| 1994/1114 | from = a->addr + i; | |
| 1994/1113 | i = Pagesize - i; if(i > rem) i = rem; | |
| 1994/1115 | if(i > sizeof(tmp)) i = sizeof(tmp); | |
| 1994/1114 | /* * byte at a time so endian doesn't matter, * go via tmp to avoid pagefaults while ilock'd */ tp = tmp; | |
| 1994/1116 | LOCKPAGE(a, offset); | |
| 1994/1115 | for(e = tp + i; tp < e;) | |
| 1994/1114 | *tp++ = *from++; | |
| 1994/1116 | UNLOCKPAGE(a); | |
| 1994/1114 | memmove(to, tmp, i); to += i; | |
| 1994/1115 | offset += i; | |
| 1994/1113 | } return n; } | |
| 1994/1114 | /* * read ISA status */ | |
| 1994/1113 | static long | |
| 1994/1115 | bctlread(Astar *a, void *buf, long n, ulong offset) | |
| 1994/1113 | { char s[128]; | |
| 1998/0109 | if(a->pci) sprint(s, "range %uX remap %uX region %uX mailbox %uX doorbell0 %uX doorbell1 %uX control %uX command %uX", inl(a->port+PCIrange), inl(a->port+PCIremap), inl(a->port+PCIregion), inl(a->port+PCImailbox), inl(a->port+PCIdoorbell0), inl(a->port+PCIdoorbell1), inl(a->port+PCIcontrol), inl(a->port+PCIcommand)); else sprint(s, "id %4.4ux ctl1 %2.2ux ctl2 %2.2ux maddr %2.2ux stat %4.4ux", (inb(a->port+ISAid)<<8)|inb(a->port+ISAid), inb(a->port+ISActl1), inb(a->port+ISActl2), inb(a->port+ISAmaddr), (inb(a->port+ISAstat2)<<8)|inb(a->port+ISAstat1)); | |
| 1994/1113 | return readstr(offset, buf, n, s); } | |
| 1994/1121 | static long statread(Astarchan *ac, void *buf, long n, ulong offset) { char s[128]; int mstat, bstat; LOCKPAGE(ac->a, 0); mstat = LEUS(ac->ccb->mstat); bstat = LEUS(ac->ccb->bstat); UNLOCKPAGE(ac->a); /* CCB.mstat fields */ | |
| 1994/1122 | sprint(s, "opens %d ferr %d oerr %d baud %d", ac->opens, ac->framing, ac->overrun, ac->baud); | |
| 1994/1121 | if(mstat & Cctsstat) strcat(s, " cts"); if(mstat & Cdsrstat) strcat(s, " dsr"); if(mstat & Cristat) strcat(s, " ring"); if(mstat & Cdcdstat) strcat(s, " dcd"); | |
| 1994/1122 | if(ac->dtr) | |
| 1994/1121 | strcat(s, " dtr"); | |
| 1994/1122 | if(ac->rts && (bstat & Crbrts) == 0) | |
| 1994/1121 | strcat(s, " rts"); | |
| 1994/1122 | strcat(s, "\n"); | |
| 1994/1121 | return readstr(offset, buf, n, s); } | |
| 1997/0327 | static long | |
| 1994/1111 | astarread(Chan *c, void *buf, long n, ulong offset) { | |
| 1994/1115 | Astar *a; Astarchan *ac; | |
| 1994/1111 | if(c->qid.path & CHDIR) return devdirread(c, buf, n, 0, 0, astargen); switch(TYPE(c->qid.path)){ | |
| 1994/1121 | case Qstat: a = astar[BOARD(c->qid.path)]; return statread(a->c + CHAN(c->qid.path), buf, n, offset); | |
| 1994/1111 | case Qmem: | |
| 1998/0109 | a = astar[BOARD(c->qid.path)]; if(a->pci) return pcimemread(a, buf, n, offset); return isamemread(a, buf, n, offset); | |
| 1994/1113 | case Qbctl: return bctlread(astar[BOARD(c->qid.path)], buf, n, offset); | |
| 1994/1115 | case Qdata: a = astar[BOARD(c->qid.path)]; ac = a->c + CHAN(c->qid.path); | |
| 1994/1116 | return qread(ac->iq, buf, n); | |
| 1994/1113 | } | |
| 1994/1112 | ||
| 1994/1113 | return 0; } | |
| 1994/1112 | ||
| 1994/1114 | /* | |
| 1998/0109 | * write PCI mapped memory */ static long pcimemwrite(Astar *a, uchar *from, long n, ulong offset) { uchar *to; int rem; ulong limit; /* * Disallow writes above 0xD000 where the i960 * data structures live if writing in the lower bank. */ if(a->addr == (uchar*)a->mem) limit = 0xD000; else limit = a->memsize; if(offset+n > limit){ if(offset >= limit) return 0; n = limit - offset; } to = a->addr+offset; for(rem = n; rem > 0; rem--) *to++ = *from++; return n; } /* | |
| 1994/1114 | * write ISA mapped memory */ | |
| 1994/1113 | static long | |
| 1998/0109 | isamemwrite(Astar *a, uchar *from, long n, ulong offset) | |
| 1994/1113 | { | |
| 1994/1114 | uchar *to, *e, *tp; | |
| 1994/1113 | int i, rem; | |
| 1994/1114 | uchar tmp[256]; | |
| 1994/1113 | if(offset+n > a->memsize){ if(offset >= a->memsize) return 0; n = a->memsize - offset; } for(rem = n; rem > 0; rem -= i){ /* map in right piece of memory */ i = offset&Pagemask; | |
| 1994/1114 | to = a->addr + i; | |
| 1994/1113 | i = Pagesize - i; if(i > rem) i = rem; | |
| 1994/1115 | if(i > sizeof(tmp)) i = sizeof(tmp); | |
| 1994/1113 | ||
| 1994/1114 | /* * byte at a time so endian doesn't matter, * go via tmp to avoid pagefaults while ilock'd */ memmove(tmp, from, i); tp = tmp; | |
| 1994/1116 | LOCKPAGE(a, offset); | |
| 1994/1115 | for(e = tp + i; tp < e;) | |
| 1994/1114 | *to++ = *tp++; | |
| 1994/1116 | UNLOCKPAGE(a); | |
| 1994/1114 | from += i; | |
| 1994/1115 | offset += i; | |
| 1994/1113 | } return n; } /* | |
| 1996/0808 | * put board into download mode */ static void downloadmode(Astar *a) { int c, i; /* put board in download mode */ | |
| 1998/0109 | if(a->pci){ /* * Don't let the download write over the * i960 data structures. */ a->memsize = 0xD000; a->addr = (uchar*)a->mem; | |
| 1996/0808 | } | |
| 1998/0109 | else{ a->memsize = Pramsize; a->needpage = 1; c = inb(a->port+ISActl1); outb(a->port+ISActl1, c & ~ISAnotdl); | |
| 1996/0808 | ||
| 1998/0109 | /* give it up to 5 seconds to reset */ for(i = 0; i < 21; i++){ if(!(inb(a->port+ISActl1) & ISAnotdl)) break; tsleep(&a->r, return0, 0, 500); } if(inb(a->port+ISActl1) & ISAnotdl){ print("#G%d: did not reset\n", a->id); error(Eio); } /* enable ISA access to first 16k */ a->page = -1; setpage(a, 0); } | |
| 1996/0808 | } /* | |
| 1995/0505 | * start control program | |
| 1994/1113 | */ static void startcp(Astar *a) { | |
| 1994/1120 | int c, n, i, sz; | |
| 1994/1113 | uchar *x; CCB *ccb; | |
| 1994/1114 | Astarchan *ac; | |
| 1994/1113 | if(a->running) error(Eio); /* take board out of download mode and enable IRQ */ | |
| 1998/0109 | if(a->pci){ outl(a->port+PCImailbox, 1); /* wait for control program to signal life */ delay(100); for(i = 0; i < 10; i++){ if(inl(a->port+PCImailbox) & 0x80000000) break; tsleep(&a->r, return0, 0, 100); } if(!(inl(a->port+PCImailbox) & 0x80000000)){ print("#G%d: program not ready\n", a->id); //error(Eio); } a->addr = (uchar*)(a->mem+0x10000); } else{ c = inb(a->port+ISActl1); outb(a->port+ISActl1, c|ISAien|ISAnotdl); /* wait for control program to signal life */ for(i = 0; i < 21; i++){ if(inb(a->port+ISActl1) & ISApr) break; tsleep(&a->r, return0, 0, 500); } if((inb(a->port+ISActl1) & ISApr) == 0){ print("#G%d: program not ready\n", a->id); error(Eio); } } | |
| 1994/1113 | a->memsize = a->ramsize; | |
| 1998/0109 | if(a->pci || a->memsize <= Pagesize) | |
| 1994/1116 | a->needpage = 0; | |
| 1998/0109 | else{ a->page = -1; setpage(a, 0); | |
| 1994/1116 | a->needpage = 1; | |
| 1994/1113 | } | |
| 1994/1114 | if(waserror()){ | |
| 1994/1116 | UNLOCKPAGE(a); | |
| 1994/1114 | poperror(); } | |
| 1994/1119 | ||
| 1994/1116 | LOCKPAGE(a, 0); | |
| 1994/1113 | i = LEUS(a->gcb->type); switch(i){ default: | |
| 1998/0109 | print("#G%d: wrong board type %uX\n", a->id, i); | |
| 1994/1113 | error(Eio); | |
| 1998/0109 | case 0x0C: case 0x12: /* AvanstarXp */ | |
| 1994/1113 | break; } /* check assumptions */ n = LEUS(a->gcb->ccbn); if(n != 8 && n != 16){ | |
| 1998/0109 | print("#G%d: has %d channels?\n", a->id, i); | |
| 1994/1113 | error(Eio); } x = a->addr + LEUS(a->gcb->ccboff); sz = LEUS(a->gcb->ccbsz); if(x+n*sz > a->addr+Pagesize){ | |
| 1998/0109 | print("#G%d: ccb's not in 1st page\n", a->id); | |
| 1994/1113 | error(Eio); } for(i = 0; i < n; i++){ ccb = (CCB*)(x + i*sz); if(APAGE(LEUS(ccb->inbase)) != APAGE(LEUS(ccb->inlim)) || APAGE(LEUS(ccb->outbase)) != APAGE(LEUS(ccb->outlim))){ | |
| 1998/0109 | print("#G%d: chan buffer spans pages\n", a->id); | |
| 1994/1113 | error(Eio); | |
| 1994/1112 | } | |
| 1994/1113 | } | |
| 1994/1116 | UNLOCKPAGE(a); | |
| 1994/1114 | poperror(); | |
| 1994/1113 | /* setup the channels */ a->running = 1; a->nchan = i; | |
| 1994/1121 | a->c = smalloc(a->nchan * sizeof(Astarchan)); | |
| 1994/1113 | for(i = 0; i < a->nchan; i++){ | |
| 1994/1114 | ac = &a->c[i]; ac->a = a; ac->ccb = (CCB*)x; | |
| 1994/1122 | ac->baud = 9600; /* a100i default */ | |
| 1994/1115 | ac->perm = 0660; | |
| 1994/1116 | ac->iq = qopen(4*1024, 0, astarkickin, ac); | |
| 1994/1114 | ac->oq = qopen(4*1024, 0, astarkick, ac); | |
| 1994/1113 | x += sz; } | |
| 1994/1121 | /* set up interrupt level, enable interrupts */ | |
| 1998/0109 | if(a->pci){ /* * Which bits in the interrupt control register should be set? */ outl(a->port+PCIcontrol, 0x00031F00); intrenable(VectorPIC + a->irq, astarintr, a, a->pci->tbdf); } else{ c = inb(a->port+ISActl1); c &= ~ISAirq; c |= ISAien|isairqcode[a->irq]; outb(a->port+ISActl1, c); intrenable(VectorPIC + a->irq, astarintr, a, BUSUNKNOWN); } | |
| 1994/1121 | /* enable control program interrupt generation */ LOCKPAGE(a, 0); a->gcb->cmd2 = LEUS(Gintack); UNLOCKPAGE(a); | |
| 1994/1113 | } | |
| 1994/1115 | static void bctlwrite(Astar *a, char *cmsg) | |
| 1994/1113 | { if(waserror()){ | |
| 1994/1112 | qunlock(a); | |
| 1994/1113 | nexterror(); } qlock(a); | |
| 1994/1115 | if(a->running) error(Eio); | |
| 1994/1113 | ||
| 1994/1115 | if(strncmp(cmsg, "download", 8) == 0){ | |
| 1994/1113 | /* put board in download mode */ | |
| 1996/0808 | downloadmode(a); | |
| 1994/1113 | } else if(strncmp(cmsg, "run", 3) == 0){ | |
| 1994/1115 | /* start up downloaded program */ | |
| 1994/1113 | startcp(a); | |
| 1998/0109 | } else if(strncmp(cmsg, "test", 4) == 0){ uchar *p; #ifdef notdef p = a->addr; a->page = -1; setpage(a, 16*1024); *p = 'X'; p = a->addr; print("page0: %8.8uX %2.2uX\n", inl(a->port+PCIremap), *p); setpage(a, 2*16*1024); print("page1: %8.8uX %2.2uX\n", inl(a->port+PCIremap), *p); p = a->addr; *p = 'Y'; p = a->addr; print("page1: %8.8uX %2.2uX\n", inl(a->port+PCIremap), *p); setpage(a, 16*1024); p = a->addr; print("page0: %8.8uX %2.2uX\n", inl(a->port+PCIremap), *p); #else p = a->addr; *p = 'A'; print(" 0K: %8.8uX %2.2uX\n", inl(a->port+PCIremap), *p); p = a->addr+(16*1024); *p = 'B'; print("16K: %8.8uX %2.2uX\n", inl(a->port+PCIremap), *p); p = a->addr; print(" 0K: %8.8uX %2.2uX\n", inl(a->port+PCIremap), *p); *p = 'C'; print(" 0K: %8.8uX %2.2uX\n", inl(a->port+PCIremap), *p); p = a->addr+(64*1024); *p = 'D'; print("64K: %8.8uX %2.2uX\n", inl(a->port+PCIremap), *p); p = a->addr; print(" 0K: %8.8uX %2.2uX\n", inl(a->port+PCIremap), *p); p = a->addr+(16*1024); print("16K: %8.8uX %2.2uX\n", inl(a->port+PCIremap), *p); #endif /* notdef */ | |
| 1994/1113 | } else error(Ebadarg); qunlock(a); poperror(); } /* | |
| 1994/1114 | * Send a command to a channel * (must be called with ac qlocked) */ static int chancmddone(void *arg) { | |
| 1994/1115 | Astarchan *ac = arg; | |
| 1994/1114 | int x; | |
| 1994/1116 | LOCKPAGE(ac->a, 0); | |
| 1994/1115 | x = ac->ccb->cmd; | |
| 1994/1116 | UNLOCKPAGE(ac->a); | |
| 1994/1114 | ||
| 1994/1115 | return !x; | |
| 1994/1114 | } static void chancmd(Astarchan *ac, int cmd) { CCB *ccb; | |
| 1994/1115 | int status; | |
| 1994/1114 | ccb = ac->ccb; | |
| 1994/1116 | LOCKPAGE(ac->a, 0); | |
| 1994/1114 | ccb->cmd = cmd; | |
| 1994/1116 | UNLOCKPAGE(ac->a); | |
| 1994/1114 | /* wait outside of lock */ | |
| 1994/1115 | tsleep(&ac->r, chancmddone, ac, 1000); | |
| 1994/1114 | ||
| 1994/1116 | LOCKPAGE(ac->a, 0); | |
| 1994/1115 | status = ccb->status; cmd = ccb->cmd; | |
| 1994/1116 | UNLOCKPAGE(ac->a); | |
| 1994/1115 | if(cmd){ | |
| 1998/0109 | print("#G%d: cmd didn't terminate\n", ac->a->id); | |
| 1994/1114 | error(Eio); } | |
| 1994/1115 | if(status){ | |
| 1998/0109 | print("#G%d: cmd status %ux\n", ac->a->id, status); | |
| 1994/1114 | error(Eio); } } /* * enable a channel for IO, set standard params. * (must be called with ac qlocked) */ static void enable(Astarchan *ac) { | |
| 1994/1115 | Astar *a = ac->a; int n; | |
| 1994/1114 | /* make sure we control RTS, DTR and break */ | |
| 1994/1116 | LOCKPAGE(a, 0); | |
| 1994/1114 | n = LEUS(ac->ccb->proto) | Cmctl; ac->ccb->proto = LEUS(n); | |
| 1994/1115 | ac->ccb->outlow = 64; | |
| 1994/1116 | UNLOCKPAGE(a); | |
| 1994/1115 | chancmd(ac, Cconfall); | |
| 1994/1114 | astarctl(ac, "l8"); astarctl(ac, "p0"); astarctl(ac, "d1"); astarctl(ac, "r1"); | |
| 1994/1115 | chancmd(ac, Crcvena|Cxmtena|Cconfall); | |
| 1994/1114 | } /* * disable a channel for IO * (must be called with ac qlocked) */ static void disable(Astarchan *ac) { | |
| 1994/1117 | int n; | |
| 1994/1114 | astarctl(ac, "d0"); astarctl(ac, "r0"); | |
| 1994/1117 | LOCKPAGE(ac->a, 0); n = LEUS(ac->ccb->proto) | Cmctl; ac->ccb->proto = LEUS(n); UNLOCKPAGE(ac->a); | |
| 1994/1114 | chancmd(ac, Crcvdis|Cxmtdis|Cflushin|Cflushout|Cconfall); } /* | |
| 1994/1113 | * change channel parameters | |
| 1994/1114 | * (must be called with ac qlocked) | |
| 1994/1113 | */ | |
| 1994/1114 | static void | |
| 1994/1113 | astarctl(Astarchan *ac, char *cmd) { int i, n; int command; | |
| 1994/1114 | CCB *ccb; Astar *a; | |
| 1994/1113 | /* let output drain for a while */ for(i = 0; i < 16 && qlen(ac->oq); i++) tsleep(&ac->r, qlen, ac->oq, 125); if(strncmp(cmd, "break", 5) == 0) cmd = "k"; | |
| 1994/1114 | ccb = ac->ccb; | |
| 1994/1113 | command = 0; | |
| 1994/1114 | i = atoi(cmd+1); | |
| 1994/1115 | ||
| 1994/1114 | a = ac->a; | |
| 1994/1116 | LOCKPAGE(a, 0); | |
| 1994/1115 | ||
| 1994/1113 | switch(*cmd){ case 'B': case 'b': | |
| 1994/1116 | /* set baud rate (high rates are special - only 16 bits) */ | |
| 1994/1114 | switch(i){ | |
| 1994/1113 | case 76800: | |
| 1994/1114 | ccb->baud = LEUS(Cb76800); | |
| 1994/1113 | break; case 115200: | |
| 1994/1114 | ccb->baud = LEUS(Cb115200); | |
| 1994/1113 | break; default: | |
| 1994/1115 | ccb->baud = LEUS(i); | |
| 1994/1113 | break; } | |
| 1994/1116 | ac->baud = i; /* set trigger level to about 50 per second */ n = i/500; i = (LEUS(ccb->inlim) - LEUS(ccb->inbase))/2; if(n > i) n = i; ccb->intrigger = LEUS(n); | |
| 1994/1113 | command = Cconfall; | |
| 1994/1112 | break; | |
| 1994/1113 | case 'D': case 'd': | |
| 1994/1114 | n = LEUS(ccb->mctl); if(i) n |= Cdtrctl; else n &= ~Cdtrctl; | |
| 1994/1122 | ac->dtr = i; | |
| 1994/1114 | ccb->mctl = LEUS(n); | |
| 1994/1112 | break; | |
| 1994/1113 | case 'f': case 'F': qflush(ac->oq); break; case 'H': case 'h': | |
| 1997/0327 | qhangup(ac->iq, nil); qhangup(ac->oq, nil); | |
| 1994/1113 | break; case 'L': case 'l': | |
| 1994/1115 | n = i - 5; | |
| 1994/1113 | if(n < 0 || n > 3) error(Ebadarg); | |
| 1994/1114 | n |= LEUS(ccb->format) & ~Clenmask; ccb->format = LEUS(n); | |
| 1994/1113 | command = Cconfall; break; case 'm': case 'M': | |
| 1994/1114 | /* turn on cts */ n = LEUS(ccb->proto); | |
| 1994/1116 | if(i){ n |= Cobeycts|Cgenrts; n &= ~Cmctl; } else { n &= ~(Cobeycts|Cgenrts); n |= Cmctl; } | |
| 1994/1114 | ccb->proto = LEUS(n); | |
| 1994/1113 | command = Cconfall; break; case 'n': case 'N': | |
| 1994/1115 | qnoblock(ac->oq, i); | |
| 1994/1113 | break; case 'P': case 'p': switch(*(cmd+1)){ case 'e': n = Cevenpar; break; case 'o': n = Coddpar; break; default: n = Cnopar; break; } | |
| 1994/1114 | n |= LEUS(ccb->format) & ~Cparmask; ccb->format = LEUS(n); | |
| 1994/1113 | command = Cconfall; break; case 'K': case 'k': | |
| 1994/1114 | if(i <= 0) i = 250; n = LEUS(ccb->mctl) | Cbreakctl; ccb->mctl = LEUS(n); | |
| 1994/1116 | UNLOCKPAGE(a); | |
| 1994/1114 | tsleep(&ac->r, return0, 0, i); | |
| 1994/1116 | LOCKPAGE(a, 0); | |
| 1994/1114 | n &= ~Cbreakctl; ccb->mctl = LEUS(n); | |
| 1994/1113 | break; case 'R': case 'r': | |
| 1994/1114 | n = LEUS(ccb->mctl); if(i) n |= Crtsctl; else n &= ~Crtsctl; ccb->mctl = LEUS(n); | |
| 1994/1122 | ac->rts = i; | |
| 1994/1113 | break; case 'Q': case 'q': | |
| 1994/1115 | qsetlimit(ac->iq, i); qsetlimit(ac->oq, i); | |
| 1994/1113 | break; case 'X': case 'x': | |
| 1994/1114 | n = LEUS(ccb->proto); if(i) n |= Cobeyxon; else n &= ~Cobeyxon; ccb->proto = LEUS(n); | |
| 1994/1113 | command = Cconfall; break; | |
| 1994/1111 | } | |
| 1994/1116 | UNLOCKPAGE(a); | |
| 1994/1114 | if(command) chancmd(ac, command); | |
| 1994/1111 | } | |
| 1997/0327 | static long | |
| 1994/1111 | astarwrite(Chan *c, void *buf, long n, ulong offset) { | |
| 1994/1112 | Astar *a; | |
| 1994/1113 | Astarchan *ac; | |
| 1994/1112 | char cmsg[32]; | |
| 1994/1111 | if(c->qid.path & CHDIR) error(Eperm); | |
| 1994/1113 | a = astar[BOARD(c->qid.path)]; | |
| 1994/1111 | switch(TYPE(c->qid.path)){ case Qmem: | |
| 1998/0109 | if(a->pci) return pcimemwrite(a, buf, n, offset); return isamemwrite(a, buf, n, offset); | |
| 1994/1112 | case Qbctl: if(n > sizeof cmsg) n = sizeof(cmsg) - 1; memmove(cmsg, buf, n); cmsg[n] = 0; | |
| 1994/1115 | bctlwrite(a, cmsg); return n; | |
| 1994/1113 | case Qdata: ac = a->c + CHAN(c->qid.path); return qwrite(ac->oq, buf, n); case Qctl: | |
| 1994/1114 | ac = a->c + CHAN(c->qid.path); | |
| 1994/1113 | if(n > sizeof cmsg) n = sizeof(cmsg) - 1; memmove(cmsg, buf, n); cmsg[n] = 0; | |
| 1994/1114 | if(waserror()){ qunlock(ac); nexterror(); } qlock(ac); | |
| 1994/1115 | astarctl(ac, cmsg); | |
| 1994/1114 | qunlock(ac); poperror(); | |
| 1994/1111 | } | |
| 1994/1109 | return 0; | |
| 1995/0108 | } | |
| 1997/0327 | static void | |
| 1994/1112 | astarwstat(Chan *c, char *dp) { | |
| 1994/1115 | Dir d; Astarchan *ac; if(!iseve()) error(Eperm); if(CHDIR & c->qid.path) error(Eperm); if(TYPE(c->qid.path) != Qdata && TYPE(c->qid.path) != Qctl) error(Eperm); ac = astar[BOARD(c->qid.path)]->c + CHAN(c->qid.path); convM2D(dp, &d); d.mode &= 0666; ac->perm = d.mode; | |
| 1994/1106 | } | |
| 1994/1113 | ||
| 1997/0327 | Dev astardevtab = { | |
| 1997/0408 | 'G', "astar", | |
| 1997/0327 | astarreset, devinit, astarattach, devclone, astarwalk, astarstat, astaropen, devcreate, astarclose, astarread, devbread, astarwrite, devbwrite, devremove, astarwstat, }; | |
| 1994/1114 | /* * get output going */ static void | |
| 1994/1117 | astaroutput(Astarchan *ac) | |
| 1994/1114 | { Astar *a = ac->a; | |
| 1994/1115 | CCB *ccb = ac->ccb; uchar buf[256]; uchar *rp, *wp, *bp, *ep, *p, *e; | |
| 1994/1114 | int n; | |
| 1994/1116 | if(a->needpage) setpage(a, 0); | |
| 1994/1115 | ep = a->addr; rp = ep + LEUS(ccb->outrp); wp = ep + LEUS(ccb->outwp); bp = ep + LEUS(ccb->outbase); ep = ep + LEUS(ccb->outlim); | |
| 1994/1114 | for(;;){ | |
| 1994/1115 | n = rp - wp - 1; if(n < 0) n += ep - bp + 1; if(n == 0) break; if(n > sizeof(buf)) n = sizeof(buf); n = qconsume(ac->oq, buf, n); if(n <= 0) break; | |
| 1994/1116 | if(a->needpage) setpage(a, bp - a->addr); | |
| 1994/1115 | e = buf + n; for(p = buf; p < e;){ *wp++ = *p++; if(wp > ep) wp = bp; } | |
| 1994/1116 | if(a->needpage) setpage(a, 0); | |
| 1994/1115 | ccb->outwp = LEUS(wp - a->addr); | |
| 1994/1114 | } } | |
| 1997/0327 | ||
| 1994/1114 | static void astarkick(Astarchan *ac) { ilock(&ac->a->pagelock); | |
| 1994/1117 | astaroutput(ac); | |
| 1994/1114 | iunlock(&ac->a->pagelock); } /* | |
| 1994/1115 | * process input */ static void astarinput(Astarchan *ac) { Astar *a = ac->a; CCB *ccb = ac->ccb; uchar buf[256]; uchar *rp, *wp, *bp, *ep, *p, *e; int n; | |
| 1994/1116 | if(a->needpage) setpage(a, 0); | |
| 1994/1115 | ep = a->addr; rp = ep + LEUS(ccb->inrp); wp = ep + LEUS(ccb->inwp); bp = ep + LEUS(ccb->inbase); ep = ep + LEUS(ccb->inlim); for(;;){ n = wp - rp; if(n == 0) break; if(n < 0) n += ep - bp + 1; if(n > sizeof(buf)) n = sizeof(buf); | |
| 1994/1116 | if(a->needpage) setpage(a, bp - a->addr); | |
| 1994/1115 | e = buf + n; for(p = buf; p < e;){ *p++ = *rp++; if(rp > ep) rp = bp; } | |
| 1997/0919 | if(ac->opens == 0 || qproduce(ac->iq, buf, n) < 0) break; /* flow controlled or not open */ | |
| 1994/1116 | if(a->needpage) setpage(a, 0); ccb->inrp = LEUS(rp - a->addr); | |
| 1994/1115 | } | |
| 1994/1116 | if(a->needpage) setpage(a, 0); | |
| 1994/1115 | } /* | |
| 1994/1116 | * get flow controlled input going again */ static void | |
| 1994/1117 | astarkickin(Astarchan *ac) | |
| 1994/1116 | { ilock(&ac->a->pagelock); astarinput(ac); iunlock(&ac->a->pagelock); } /* | |
| 1994/1114 | * handle an interrupt */ static void astarintr(Ureg *ur, void *arg) { Astar *a = arg; Astarchan *ac; | |
| 1997/0917 | ulong globvec, vec, invec, outvec, errvec, mvec, cmdvec; | |
| 1998/0109 | int c, status; | |
| 1994/1114 | ||
| 1998/0109 | ||
| 1994/1114 | USED(ur); | |
| 1994/1121 | if(a->running == 0) | |
| 1998/0109 | panic("#G%d: interrupt but cp not running\n", a->id); | |
| 1994/1121 | ||
| 1994/1114 | lock(&a->pagelock); | |
| 1994/1116 | if(a->needpage) setpage(a, 0); | |
| 1994/1114 | /* get causes */ | |
| 1997/0917 | globvec = LEUS(xchgw(&a->gcb->serv, 0)); USED(globvec); | |
| 1994/1114 | invec = LEUS(xchgw(&a->gcb->inserv, 0)); outvec = LEUS(xchgw(&a->gcb->outserv, 0)); errvec = LEUS(xchgw(&a->gcb->errserv, 0)); mvec = LEUS(xchgw(&a->gcb->modemserv, 0)); cmdvec = LEUS(xchgw(&a->gcb->cmdserv, 0)); | |
| 1994/1115 | USED(mvec); | |
| 1994/1114 | /* reenable interrupts */ | |
| 1998/0109 | if(a->pci){ /* * Only the PCI doorbell interrupt is expected. */ status = inl(a->port+PCIstatus); if((status & 0x0810E000) != 0x00002000) print("#G%d: unexpected interrupt %uX\n", a->id, status); if(status & 0x00002000) outl(a->port+PCIdoorbell1, 1); } | |
| 1994/1115 | a->gcb->cmd2 = LEUS(Gintack); | |
| 1994/1114 | /* service interrupts */ ac = a->c; | |
| 1994/1115 | for(vec = invec; vec; vec >>= 1){ if(vec&1) astarinput(ac); | |
| 1994/1114 | ac++; } ac = a->c; | |
| 1994/1115 | for(vec = outvec; vec; vec >>= 1){ | |
| 1994/1114 | if(vec&1) | |
| 1994/1117 | astaroutput(ac); | |
| 1994/1114 | ac++; } ac = a->c; | |
| 1994/1115 | for(vec = cmdvec; vec; vec >>= 1){ | |
| 1994/1114 | if(vec&1) wakeup(&ac->r); | |
| 1994/1121 | ac++; } ac = a->c; for(vec = errvec; vec; vec >>= 1){ c = LEUS(ac->ccb->errstat); if(c & Cframing) ac->framing++; if(c & Coverrun) ac->overrun++; | |
| 1994/1114 | ac++; } unlock(&a->pagelock); } | |