@@ 119,6 119,7 @@ udpclose(Conv *c)
{
Udpcb *ucb;
+ c->state = 0;
qclose(c->rq);
qclose(c->wq);
qclose(c->eq);
@@ 234,7 235,7 @@ udpiput(Proto *udp, uchar *ia, Block *bp)
{
int len, olen, ottl;
Udphdr *uh;
- Conv *c, **p, *gc;
+ Conv *c, **p, *spec, *gen;
Udpcb *ucb;
uchar raddr[IPaddrlen], laddr[IPaddrlen];
ushort rport, lport;
@@ 273,11 274,9 @@ udpiput(Proto *udp, uchar *ia, Block *bp)
qlock(udp);
/*
- * Look for a conversation structure for this port. One
- * with headers off wins over one with headers on, i.e.,
- * specific wins over generic.
+ * Look for a conversation structure for this packet
*/
- gc = c = nil;
+ spec = gen = c = nil;
for(p = udp->conv; *p; p++) {
c = *p;
if(c->inuse == 0)
@@ 285,23 284,41 @@ udpiput(Proto *udp, uchar *ia, Block *bp)
if(c->lport == lport){
ucb = (Udpcb*)c->ptcl;
- /* with headers turned on, descriminate only on local port */
- if(ucb->headers && gc == nil){
- gc = c;
- continue;
+ switch(c->state){
+ case Announced:
+ /* headers + Announced is special behaviour */
+ if(ucb->headers)
+ goto found;
+ spec = c;
+ break;
+
+ case Connected:
+ /* exact match */
+ if(c->rport == rport)
+ if(ipcmp(c->raddr, raddr) == 0 || ipisbm(c->raddr))
+ goto found;
}
- /* otherwise discriminate on lport, rport, and raddr */
- if(c->rport == 0 || c->rport == rport)
- if(ipisbm(c->raddr) || ipcmp(c->raddr, IPnoaddr) == 0
- || ipcmp(c->raddr, raddr) == 0)
- break;
+ } else if(c->lport == 0) {
+ /* generic listen for all udp ports */
+ if(c->state == Announced)
+ gen = c;
}
}
+found:
if(*p == nil){
- if(gc != nil)
- c = gc;
- else {
+ if(spec != nil)
+ gen = spec;
+ if(gen != nil){
+ if(ipforme(f, laddr) != Runi)
+ v4tov6(laddr, ia);
+ c = Fsnewcall(gen, raddr, rport, laddr, lport);
+ if(c == nil){
+ freeblist(bp);
+ return;
+ }
+ c->state = Connected;
+ } else {
upriv->ustats.udpNoPorts++;
qunlock(udp);
netlog(f, Logudp, "udp: no conv %I!%d -> %I!%d\n", raddr, rport,
@@ 357,21 374,8 @@ udpiput(Proto *udp, uchar *ia, Block *bp)
hnputs(bp->rp + 2*IPv4addrlen, rport);
hnputs(bp->rp + 2*IPv4addrlen + 2, lport);
break;
- default:
- /* connection oriented udp */
- if(ipcmp(c->raddr, IPnoaddr) == 0){
- /* save the src address in the conversation */
- ipmove(c->raddr, raddr);
- c->rport = rport;
-
- /* reply with the same ip address (if not broadcast) */
- if(ipforme(f, laddr) == Runi)
- ipmove(c->laddr, laddr);
- else
- v4tov6(c->laddr, ia);
- }
- break;
}
+
if(bp->next)
bp = concatblock(bp);
@@ 388,6 392,22 @@ udpiput(Proto *udp, uchar *ia, Block *bp)
}
+/* close any incoming calls waiting on this conversation */
+void
+udpcloseincalls(Conv *c)
+{
+ Conv *nc;
+
+ qlock(c);
+
+ for(nc = c->incall; nc; nc = c->incall){
+ c->incall = nc->next;
+ closeconv(nc);
+ }
+
+ qunlock(c);
+}
+
char*
udpctl(Conv *c, char **f, int n)
{
@@ 397,9 417,13 @@ udpctl(Conv *c, char **f, int n)
if(n == 1){
if(strcmp(f[0], "headers4") == 0){
ucb->headers = 4;
+ /* close any calls that got in twixt announce and headers */
+ udpcloseincalls(c);
return nil;
} else if(strcmp(f[0], "headers") == 0){
ucb->headers = 6;
+ /* close any calls that got in twixt announce and headers */
+ udpcloseincalls(c);
return nil;
}
}
@@ 0,0 1,290 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+
+#define Image IMAGE
+#include <draw.h>
+#include <memdraw.h>
+#include <cursor.h>
+#include "screen.h"
+
+typedef struct {
+ ushort x;
+ ushort y;
+ ushort preset;
+ ushort enable;
+ ushort addr;
+} Cursor546x;
+
+enum {
+ PaletteState = 0xB0,
+ CursorMMIO = 0xE0,
+};
+
+static ulong
+clgd546xlinear(VGAscr* scr, int* size, int* align)
+{
+ ulong aperture, oaperture;
+ int oapsize, wasupamem;
+ Pcidev *p;
+
+ oaperture = scr->aperture;
+ oapsize = scr->apsize;
+ wasupamem = scr->isupamem;
+
+ aperture = 0;
+ if(p = pcimatch(nil, 0x1013, 0)){
+ switch(p->did){
+ case 0xD0:
+ case 0xD4:
+ case 0xD6:
+ aperture = p->mem[0].bar & ~0x0F;
+ *size = p->mem[0].size;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if(wasupamem){
+ if(oaperture == aperture)
+ return oaperture;
+ upafree(oaperture, oapsize);
+ }
+ scr->isupamem = 0;
+
+ aperture = upamalloc(aperture, *size, *align);
+ if(aperture == 0){
+ if(wasupamem && upamalloc(oaperture, oapsize, 0)){
+ aperture = oaperture;
+ scr->isupamem = 1;
+ }
+ else
+ scr->isupamem = 0;
+ }
+ else
+ scr->isupamem = 1;
+
+ return aperture;
+}
+static void
+clgd546xenable(VGAscr* scr)
+{
+ Pcidev *p;
+ Physseg seg;
+ int size, align;
+ ulong aperture;
+
+ /*
+ * Only once, can't be disabled for now.
+ * scr->io holds the virtual address of
+ * the MMIO registers.
+ */
+ if(scr->io)
+ return;
+ if(p = pcimatch(nil, 0x1013, 0)){
+ switch(p->did){
+ case 0xD0:
+ case 0xD4:
+ case 0xD6:
+ break;
+ default:
+ return;
+ }
+ }
+ else
+ return;
+ scr->io = upamalloc(p->mem[1].bar & ~0x0F, p->mem[1].size, 0);
+ if(scr->io == 0)
+ return;
+
+ memset(&seg, 0, sizeof(seg));
+ seg.attr = SG_PHYSICAL;
+ seg.name = smalloc(NAMELEN);
+ snprint(seg.name, NAMELEN, "clgd546xmmio");
+ seg.pa = scr->io;
+ seg.size = p->mem[1].size;
+ addphysseg(&seg);
+
+ scr->io = (ulong)KADDR(scr->io);
+
+ size = p->mem[0].size;
+ align = 0;
+ aperture = clgd546xlinear(scr, &size, &align);
+ if(aperture) {
+ scr->aperture = aperture;
+ scr->apsize = size;
+ memset(&seg, 0, sizeof(seg));
+ seg.attr = SG_PHYSICAL;
+ seg.name = smalloc(NAMELEN);
+ snprint(seg.name, NAMELEN, "clgd546xscreen");
+ seg.pa = aperture;
+ seg.size = size;
+ addphysseg(&seg);
+ }
+}
+
+static void
+clgd546xcurdisable(VGAscr* scr)
+{
+ Cursor546x *cursor546x;
+
+ if(scr->io == 0)
+ return;
+ cursor546x = (Cursor546x*)(scr->io+CursorMMIO);
+ cursor546x->enable = 0;
+}
+
+static void
+clgd546xcurload(VGAscr* scr, Cursor* curs)
+{
+ int c, i, m, y;
+ uchar *p;
+ Cursor546x *cursor546x;
+
+ if(scr->io == 0)
+ return;
+ cursor546x = (Cursor546x*)(scr->io+CursorMMIO);
+
+ /*
+ * Disable the cursor then change only the bits
+ * that need it.
+ */
+ cursor546x->enable = 0;
+ p = (uchar*)(scr->aperture + scr->storage);
+ for(y = 0; y < 16; y++){
+ c = curs->set[2*y];
+ m = 0;
+ for(i = 0; i < 8; i++){
+ if(c & (1<<(7-i)))
+ m |= 1<<i;
+ }
+ *p++ = m;
+ c = curs->set[2*y + 1];
+ m = 0;
+ for(i = 0; i < 8; i++){
+ if(c & (1<<(7-i)))
+ m |= 1<<i;
+ }
+ *p++ = m;
+ p += 6;
+ c = curs->set[2*y]|curs->clr[2*y];
+ m = 0;
+ for(i = 0; i < 8; i++){
+ if(c & (1<<(7-i)))
+ m |= 1<<i;
+ }
+ *p++ = m;
+ c = curs->set[2*y + 1]|curs->clr[2*y + 1];
+ m = 0;
+ for(i = 0; i < 8; i++){
+ if(c & (1<<(7-i)))
+ m |= 1<<i;
+ }
+ *p++ = m;
+ p += 6;
+ }
+
+ /*
+ * Save the cursor hotpoint and enable the cursor.
+ */
+ scr->offset = curs->offset;
+ cursor546x->enable = 1;
+}
+
+static int
+clgd546xcurmove(VGAscr* scr, Point p)
+{
+ int x, xo, y, yo;
+ Cursor546x *cursor546x;
+
+ if(scr->io == 0)
+ return 1;
+ cursor546x = (Cursor546x*)(scr->io+CursorMMIO);
+
+ if((x = p.x+scr->offset.x) < 0){
+ xo = -x;
+ x = 0;
+ }
+ else
+ xo = 0;
+ if((y = p.y+scr->offset.y) < 0){
+ yo = -y;
+ y = 0;
+ }
+ else
+ yo = 0;
+
+ cursor546x->preset = (xo<<8)|yo;
+ cursor546x->x = x;
+ cursor546x->y = y;
+
+ return 0;
+}
+
+static void
+clgd546xcurenable(VGAscr* scr)
+{
+ uchar *p;
+ Cursor546x *cursor546x;
+
+ clgd546xenable(scr);
+ if(scr->io == 0)
+ return;
+ cursor546x = (Cursor546x*)(scr->io+CursorMMIO);
+
+ /*
+ * Cursor colours.
+ * Can't call setcolor here as cursor is already locked.
+ */
+ p = (uchar*)(scr->io+PaletteState);
+ *p |= 0x08;
+ vgao(PaddrW, 0x00);
+ vgao(Pdata, Pwhite);
+ vgao(Pdata, Pwhite);
+ vgao(Pdata, Pwhite);
+ vgao(PaddrW, 0x0F);
+ vgao(Pdata, Pblack);
+ vgao(Pdata, Pblack);
+ vgao(Pdata, Pblack);
+ *p &= ~0x08;
+
+ /*
+ * Find a place for the cursor data in display memory.
+ * 2 cursor images might be needed, 1KB each so use the last
+ * 2KB of the framebuffer and initialise them to be
+ * transparent.
+ */
+ scr->storage = ((vgaxi(Seqx, 0x14) & 0x07)+1)*1024*1022;
+ cursor546x->addr = (scr->storage>>10)<<2;
+ memset((uchar*)(scr->aperture + scr->storage), 0, 2*64*16);
+
+ /*
+ * Load, locate and enable the 64x64 cursor.
+ */
+ clgd546xcurload(scr, &arrow);
+ clgd546xcurmove(scr, ZP);
+ cursor546x->enable = 1;
+}
+
+VGAdev vgaclgd546xdev = {
+ "clgd546x",
+
+ clgd546xenable,
+ nil,
+ nil,
+ clgd546xlinear,
+};
+
+VGAcur vgaclgd546xcur = {
+ "clgd546xhwgc",
+
+ clgd546xcurenable,
+ clgd546xcurdisable,
+ clgd546xcurload,
+ clgd546xcurmove,
+};
@@ 74,7 74,8 @@ enum {
CDial,
CAccept,
COpen,
- CClosing,
+ CLocalClose,
+ CRemoteClose,
CClosed,
};
@@ 86,7 87,7 @@ struct Conv {
int state;
int dataopen;
-
+ int reader; // reader proc has been started
ulong timeout;
int retries;
@@ 95,9 96,11 @@ struct Conv {
ulong dialid;
ulong acceptid;
+ QLock readlk; // protects readproc
Proc *readproc;
- QLock readlk;
+
Chan *chan; // packet channel
+ char *channame;
char owner[NAMELEN]; /* protections */
int perm;
@@ 131,8 134,8 @@ enum {
enum {
ConOpenRequest,
ConOpenAck,
+ ConOpenAckAck,
ConClose,
- ConCloseAck,
ConReset,
};
@@ 186,7 189,8 @@ static char *convstatename[] = {
[CDial] "Dial",
[CAccept] "Accept",
[COpen] "Open",
- [CClosing] "Closing",
+ [CLocalClose] "LocalClose",
+ [CRemoteClose] "RemoteClose",
[CClosed] "Closed",
};
@@ 203,6 207,7 @@ static Block *readcontrol(Conv *c, int n);
static Block *readdata(Conv *c, int n);
static void convoput(Conv *c, int type, Block *b);
static void convoput2(Conv *c, int op, ulong dialid, ulong acceptid);
+static void convreader(void *a);
static void
@@ 328,8 333,12 @@ sdpopen(Chan* ch, int omode)
if(strcmp(up->user, c->owner) != 0 || (perm & c->perm) != perm)
error(Eperm);
c->ref++;
- if(TYPE(ch->qid) == Qdata)
+ if(TYPE(ch->qid) == Qdata) {
+ if(c->dataopen == 0)
+ if(c->readproc != nil)
+ postnote(c->readproc, 1, "interrupt", 0);
c->dataopen++;
+ }
qunlock(c);
poperror();
break;
@@ 377,10 386,10 @@ print("close c->ref = %d\n", c->ref);
break;
case CAccept:
case COpen:
- convsetstate(c, CClosing);
- break;
- case CClosing:
+ convsetstate(c, CLocalClose);
break;
+ case CLocalClose:
+ panic("local close already happened");
}
}
qunlock(c);
@@ 474,20 483,22 @@ print("Qctl write : conv->id = %d\n", c->id);
error("short write");
arg0 = cb->f[0];
print("cmd = %s\n", arg0);
- if(strcmp(arg0, "chan") == 0) {
+ if(strcmp(arg0, "accept") == 0) {
if(cb->nf != 2)
- error("usage: chan file");
+ error("usage: accect file");
if(c->chan != nil)
- error("chan already set");
+ error("already connected");
c->chan = namec(cb->f[1], Aopen, ORDWR, 0);
- } else if(strcmp(arg0, "accept") == 0) {
- if(cb->nf != 2)
- error("usage: accect id");
- c->dialid = atoi(cb->f[1]);
- convsetstate(c, CAccept);
+ c->channame = malloc(strlen(cb->f[1])+1);
+ strcpy(c->channame, cb->f[1]);
} else if(strcmp(arg0, "dial") == 0) {
- if(cb->nf != 1)
- error("usage: dial");
+ if(cb->nf != 2)
+ error("usage: accect file");
+ if(c->chan != nil)
+ error("already connected");
+ c->chan = namec(cb->f[1], Aopen, ORDWR, 0);
+ c->channame = malloc(strlen(cb->f[1])+1);
+ strcpy(c->channame, cb->f[1]);
convsetstate(c, CDial);
} else if(strcmp(arg0, "drop") == 0) {
if(cb->nf != 2)
@@ 584,7 595,7 @@ sdpclone(Sdp *sdp)
break;
}
if(canqlock(c)){
- if(c->state == CClosed)
+ if(c->state == CClosed && c->reader == 0)
break;
qunlock(c);
}
@@ 597,7 608,10 @@ sdpclone(Sdp *sdp)
c->ref++;
c->state = CInit;
-
+ if(!waserror()) {
+ kproc("convreader", convreader, c);
+ c->reader = 1;
+ }
strncpy(c->owner, up->user, sizeof(c->owner));
c->perm = 0660;
qunlock(c);
@@ 647,11 661,13 @@ print("convtimer: %s\n", convstatename[c->state]);
case CAccept:
if(convretry(c))
convoput2(c, ConOpenAck, c->dialid, c->acceptid);
+ else
+ convoput2(c, ConReset, c->dialid, c->acceptid);
break;
case COpen:
// check for control packet and keepalive
break;
- case CClosing:
+ case CLocalClose:
if(convretry(c))
convoput2(c, ConClose, c->dialid, c->acceptid);
break;
@@ 730,15 746,18 @@ print("convsetstate %s -> %s\n", convstatename[c->state], convstatename[state]);
assert(c->state == CDial || c->state == CAccept);
if(c->state == CDial) {
convretryinit(c);
- convoput2(c, ConOpenAck, c->dialid, c->acceptid);
+ convoput2(c, ConOpenAckAck, c->dialid, c->acceptid);
}
// setup initial key and auth method
break;
- case CClosing:
- assert(c->state == COpen);
+ case CLocalClose:
+ assert(c->state == CAccept || c->state == COpen);
convretryinit(c);
convoput2(c, ConClose, c->dialid, c->acceptid);
break;
+ case CRemoteClose:
+ convoput2(c, ConReset, c->dialid, c->acceptid);
+ break;
case CClosed:
if(c->readproc)
postnote(c->readproc, 1, "interrupt", 0);
@@ 748,6 767,10 @@ print("convsetstate %s -> %s\n", convstatename[c->state], convstatename[state]);
cclose(c->chan);
c->chan = nil;
}
+ if(c->channame) {
+ free(c->channame);
+ c->channame = nil;
+ }
strcpy(c->owner, "network");
c->perm = 0660;
c->dialid = 0;
@@ 778,34 801,6 @@ onewaycleanup(OneWay *ow)
}
-static Block *
-convreadblock(Conv *c, int n)
-{
- Block *b;
-
- qlock(&c->readlk);
- if(waserror()) {
- c->readproc = nil;
- qunlock(&c->readlk);
- nexterror();
- }
- qlock(c);
- if(c->state == CClosed) {
- qunlock(c);
- poperror();
- qunlock(&c->readlk);
- return 0;
- }
- c->readproc = up;
- qunlock(c);
-
- b = devtab[c->chan->type]->bread(c->chan, n, 0);
- c->readproc = nil;
- poperror();
- qunlock(&c->readlk);
-
- return b;
-}
// assume we hold lock for c
@@ 853,8 848,6 @@ conviput(Conv *c, Block *b, int control)
c->in.controlseq = cseq;
b->rp += 4;
- if(control)
- return b;
c->in.controlpkt = b;
wakeup(&c->in.controlready);
return nil;
@@ 895,63 888,96 @@ conviput2(Conv *c, Block *b)
dialid = nhgetl(con->dialid);
acceptid = nhgetl(con->acceptid);
-print("conviput2: %d %uld %uld\n", con->op, dialid, acceptid);
+ switch(c->state) {
+ default:
+ panic("unknown state: %d", c->state);
+ case CInit:
+ break;
+ case CDial:
+ if(dialid != c->dialid)
+ goto Reset;
+ break;
+ case CAccept:
+ case COpen:
+ case CLocalClose:
+ case CRemoteClose:
+ if(dialid != c->dialid || acceptid != c->acceptid)
+ goto Reset;
+ break;
+ case CClosed:
+ goto Reset;
+ }
+
+
+print("conviput2: %s: %d %uld %uld\n", convstatename[c->state], con->op, dialid, acceptid);
switch(con->op) {
case ConOpenRequest:
switch(c->state) {
- default:
- convoput2(c, ConReset, dialid, acceptid);
- break;
case CInit:
c->dialid = dialid;
convsetstate(c, CAccept);
- break;
+ return;
case CAccept:
case COpen:
- if(dialid != c->dialid || acceptid != c->acceptid)
- convoput2(c, ConReset, dialid, acceptid);
- break;
+ // duplicate ConOpenRequest that we ignore
+ return;
}
break;
case ConOpenAck:
switch(c->state) {
case CDial:
- if(dialid != c->dialid) {
- convoput2(c, ConReset, dialid, acceptid);
- break;
- }
c->acceptid = acceptid;
convsetstate(c, COpen);
- break;
+ return;
+ case COpen:
+ // duplicate that we have to ack
+ convoput2(c, ConOpenAckAck, acceptid, dialid);
+ return;
+ }
+ break;
+ case ConOpenAckAck:
+ switch(c->state) {
case CAccept:
- if(dialid != c->dialid || acceptid != c->acceptid) {
- convoput2(c, ConReset, dialid, acceptid);
- break;
- }
convsetstate(c, COpen);
+ return;
+ case COpen:
+ // duplicate that we ignore
+ return;
}
break;
case ConClose:
- convoput2(c, ConCloseAck, dialid, acceptid);
- // fall though
- case ConReset:
+ convoput2(c, ConReset, dialid, acceptid);
switch(c->state) {
+ case CInit:
case CDial:
- if(dialid == c->dialid)
- convsetstate(c, CClosed);
- break;
case CAccept:
+ case CLocalClose:
+ convsetstate(c, CClosed);
+ return;
case COpen:
- case CClosing:
- if(dialid == c->dialid && acceptid == c->acceptid)
- convsetstate(c, CClosed);
- break;
+ convsetstate(c, CRemoteClose);
+ return;
+ case CRemoteClose:
+ return;
}
- case ConCloseAck:
- if(c->state == CClosing && dialid == c->dialid && acceptid == c->acceptid)
+ return;
+ case ConReset:
+ switch(c->state) {
+ case CInit:
+ case CDial:
+ case CAccept:
+ case COpen:
+ case CLocalClose:
convsetstate(c, CClosed);
- break;
+ return;
+ case CRemoteClose:
+ return;
+ }
+ return;
}
+Reset:
+ // invalid connection message - reset to sender
+ convoput2(c, ConReset, dialid, acceptid);
}
// assume hold conv lock
@@ 1003,12 1029,45 @@ print("chan = nil\n");
devtab[c->chan->type]->write(c->chan, &con, sizeof(con), 0);
}
+static Block *
+convreadblock(Conv *c, int n)
+{
+ Block *b;
+ Chan *ch = nil;
+
+ qlock(&c->readlk);
+ if(waserror()) {
+ c->readproc = nil;
+ if(ch)
+ cclose(ch);
+ qunlock(&c->readlk);
+ nexterror();
+ }
+ qlock(c);
+ if(c->state == CClosed) {
+ qunlock(c);
+ error("closed");
+ }
+ c->readproc = up;
+ ch = c->chan;
+ incref(ch);
+ qunlock(c);
+
+ b = devtab[ch->type]->bread(ch, n, 0);
+ c->readproc = nil;
+ cclose(ch);
+ poperror();
+ qunlock(&c->readlk);
+
+ return b;
+}
+
static int
readready(void *a)
{
Conv *c = a;
- return (c->state == CClosed) || c->in.controlpkt != nil || c->dataopen == 0;
+ return (c->state == CClosed) || c->in.controlpkt != nil;
}
static Block *
@@ 1016,9 1075,10 @@ readcontrol(Conv *c, int n)
{
Block *b;
+ USED(n);
for(;;) {
qlock(c);
- if(c->state == CClosed || c->state == CInit) {
+ if(c->state == CInit || c->state == CClosed) {
qunlock(c);
return nil;
}
@@ 1031,34 1091,9 @@ readcontrol(Conv *c, int n)
}
qunlock(c);
- // hack - this is to avoid gating onto the
- // read which will in general result in excessive
- // context switches.
- // The assumed behavior is that the client will read
- // from the control channel until the session is authenticated
- // at which point it will open the data channel and
- // start reading on that. After the data channel is opened,
- // read on the channel are required for packets to
- // be delivered to the control channel
-
- if(c->dataopen) {
- sleep(&c->in.controlready, readready, c);
- } else {
- b = convreadblock(c, n);
- if(b == nil)
- return nil;
- qlock(c);
- if(waserror()) {
- qunlock(c);
- return nil;
- }
- b = conviput(c, b, 1);
- poperror();
- qunlock(c);
- if(b != nil)
- return b;
- }
+ sleep(&c->in.controlready, readready, c);
}
+ return 0;
}
static Block *
@@ 1068,8 1103,6 @@ readdata(Conv *c, int n)
for(;;) {
b = convreadblock(c, n);
- if(b == nil)
- return nil;
qlock(c);
if(waserror()) {
qunlock(c);
@@ 1082,3 1115,33 @@ readdata(Conv *c, int n)
return b;
}
}
+
+static void
+convreader(void *a)
+{
+ Conv *c = a;
+ Block *b;
+
+ qlock(c);
+ assert(c->reader == 1);
+ while(c->dataopen == 0) {
+ qunlock(c);
+ b = nil;
+ if(!waserror()) {
+ b = convreadblock(c, 2000);
+ poperror();
+ }
+ qlock(c);
+ if(b == nil) {
+ convsetstate(c, CClosed);
+ break;
+ }
+ if(!waserror()) {
+ conviput(c, b, 1);
+ poperror();
+ }
+ }
+ c->reader = 0;
+ qunlock(c);
+ pexit("hangup", 1);
+}