/* * Copyright (c) 2001 Tommy Bohlin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* lap.c * * Limitations: * - Exchange primary/secondary roles not supported * - Point to multipoint not supported * - 2400bps connection procedure not supported * - FRMR and RESET not supported */ #include #include #include static void disconnectLAP(LAPPrivate* lapp); /********************************************************************** * Constants **********************************************************************/ static const char id_lap[]="lap"; #define BROADCAST_ADDRESS 0xffffffff /* Address field */ #define COMMAND_MASK 0x01 #define BROADCAST_HANDLE 0xfe /* Control field */ #define PF_MASK 0x10 #define U_SNRM 0x83 #define U_DISC 0x43 #define U_UI 0x03 #define U_XID_CMD 0x2f #define U_TEST 0xe3 #define U_UA 0x63 #define U_FRMR 0x87 #define U_DM 0x0f #define U_XID_RSP 0xaf #define S_MASK 0x0f #define S_RR 0x01 #define S_RNR 0x05 #define S_REJ 0x09 #define S_SREJ 0x0d #define IS_U(c) (((c)&3)==3) #define IS_S(c) (((c)&3)==1) #define NR_MASK 0xe0 #define NR_SHIFT 5 #define NS_MASK 0x0e #define NS_SHIFT 1 #define NR(c) ((NR_MASK&(c))>>NR_SHIFT) #define NS(c) ((NS_MASK&(c))>>NS_SHIFT) /* Discovery */ #define XID_FORMAT 1 #define XID_VERSION 0 #define END_OF_SLOTS 0xff #define XID_SLOTS_1 0 #define XID_SLOTS_6 1 #define XID_SLOTS_8 2 #define XID_SLOTS_16 3 #define XID_SLOTS_MASK 0x3 #define SLOT_TIME 100 static const int answerSlots[] = { 1, 6, 8, 16 }; /* Negotiation parameters */ #define NEG_BAUD_RATE 0x01 #define ALL_BAUD_RATES 0x3ff static const int baudRateBits[] = { 2400, 9600, 19200, 38400, 57600, 115200, 576000, 1152000, 4000000, 16000000 }; #define NEG_MAX_TA_TIME 0x82 #define MAX_TA_500ms 1 #define RECV_MAX_TA 1 /* Try something shorter? */ #define ALL_MAX_TAS 0xf static const int maxTurnaroundBits[] = { 500, 250, 100, 50 }; #define NEG_DATA_SIZE 0x83 #define DATASIZE_64 1 #define RESET_DATASIZE 384 #define RECV_DATASIZE 512 /* The Palm pilot has a bug that causes it */ #define RECV_DATASIZES 0xf /* to fail if the dataSize is set larger */ #define ALL_DATASIZES 0x3f static const int dataSizeBits[] = { 64, 128, 256, 512, 1024, 2048 }; #define NEG_WINDOW_SIZE 0x84 #define WINDOWSIZE_1 7 #define RECV_WINDOWSIZES 0x7f #define ALL_WINDOWSIZES 0x7f static const int windowSizeBits[] = { 1, 2, 3, 4, 5, 6, 7 }; #define NEG_ADD_BOFS 0x85 #define EXTRA_BOFS_0 0x80 #define RECV_EXTRA_BOFS 0xff #define ALL_EXTRA_BOFS 0xff static const int extraBOFsBits[] = { 48, 24, 12, 5, 3, 2, 1, 0 }; #define NEG_MIN_TA_TIME 0x86 #define ALL_MIN_TAS 0x7f #define MIN_TA_10 1 /* Exact values are: 10ms, 5ms, 1ms, 500us, 100us, 50us, 10us, 0 */ static const int minTurnaroundBits[] = { 10, 5, 1, 1, 1, 1, 1, 0 }; #define NEG_DISC_TIME 0x08 #define RECV_DISCONNECTS 0x1f #define MOST_DISCONNECTS 0x1f #define DISCONNECT_12 0x02 static const int disconnectBits[] = { 3, 8, 12, 16, 20, 25, 30, 40 }; #define PRIMARY_TIMEOUT 500 #define SECONDARY_TIMEOUT 1500 #define HINT_CONTINUATION 0x80808080 #define DISCONNECT_TIME 2000 #define SETUP_TIME 200 #define SETUP_TRIES 15 /********************************************************************** * Frame transmission **********************************************************************/ static void sendXIDSlot(LAPPrivate* lapp) { lapp->ctrlBuf[0]=BROADCAST_HANDLE|COMMAND_MASK; lapp->ctrlBuf[1]=U_XID_CMD|PF_MASK; lapp->ctrlBuf[2]=XID_FORMAT; putBELong(lapp->ctrlBuf+3,lapp->address); putBELong(lapp->ctrlBuf+7,BROADCAST_ADDRESS); lapp->ctrlBuf[11]=lapp->xidFlags; lapp->ctrlBuf[12]=lapp->xidSlot; lapp->ctrlBuf[13]=XID_VERSION; lapp->ctrlLen=14; lapp->mod|=MOD_CONTROL; } static void sendXIDEnd(LAPPrivate* lapp) { lapp->ctrlBuf[0]=BROADCAST_HANDLE|COMMAND_MASK; lapp->ctrlBuf[1]=U_XID_CMD|PF_MASK; lapp->ctrlBuf[2]=XID_FORMAT; putBELong(lapp->ctrlBuf+3,lapp->address); putBELong(lapp->ctrlBuf+7,BROADCAST_ADDRESS); lapp->ctrlBuf[11]=lapp->xidFlags; lapp->ctrlBuf[12]=END_OF_SLOTS; lapp->ctrlBuf[13]=XID_VERSION; lapp->ctrlBuf[14]=lapp->lap.flags|HINT_CONTINUATION; lapp->ctrlBuf[15]=(lapp->lap.flags>>8)&~HINT_CONTINUATION; lapp->ctrlBuf[16]=lapp->info.charSet; memcpy(lapp->ctrlBuf+17,lapp->info.bytes,lapp->info.length); lapp->ctrlLen=17+lapp->info.length; lapp->mod|=MOD_CONTROL; } static void sendXIDResponse(LAPPrivate* lapp, int addr, int flags, int slot) { if(lapp->lap.debug&LAP_DEBUG_FRAMES_OUT) { birda_log("sendXIDResponse: lap-addr=%x addr=%x\n", lapp->address, addr); } lapp->ctrlBuf[0]=BROADCAST_HANDLE; lapp->ctrlBuf[1]=U_XID_RSP|PF_MASK; lapp->ctrlBuf[2]=1; /* format */ putBELong(lapp->ctrlBuf+3,lapp->address); /* src address */ putBELong(lapp->ctrlBuf+7,addr); /* dest address */ lapp->ctrlBuf[11]=flags; lapp->ctrlBuf[12]=slot; lapp->ctrlBuf[13]=0; /* version */ lapp->ctrlBuf[14]=0x80|lapp->lap.flags; lapp->ctrlBuf[15]=0x7f&(lapp->lap.flags>>8); lapp->ctrlBuf[16]=lapp->info.charSet; memcpy(lapp->ctrlBuf+17,lapp->info.bytes,lapp->info.length); lapp->ctrlLen=17+lapp->info.length; lapp->mod|=MOD_CONTROL; } static void sendSNRMConnect(LAPPrivate* lapp) { int i=0; FrameDevice* fd=lapp->fdev; int speedMask=fd->getSpeedMask(fd); lapp->ctrlBuf[i++]=BROADCAST_HANDLE|COMMAND_MASK; lapp->ctrlBuf[i++]=U_SNRM|PF_MASK; putBELong(lapp->ctrlBuf+i,lapp->address); i+=4; putBELong(lapp->ctrlBuf+i,lapp->setupAddress); i+=4; lapp->ctrlBuf[i++]=lapp->handle; lapp->ctrlBuf[i++]=NEG_BAUD_RATE; if(speedMask>0xff) { lapp->ctrlBuf[i++]=2; lapp->ctrlBuf[i++]=speedMask; lapp->ctrlBuf[i++]=speedMask>>2; } else { lapp->ctrlBuf[i++]=1; lapp->ctrlBuf[i++]=speedMask; } lapp->ctrlBuf[i++]=NEG_MAX_TA_TIME; lapp->ctrlBuf[i++]=1; lapp->ctrlBuf[i++]=RECV_MAX_TA; lapp->ctrlBuf[i++]=NEG_DATA_SIZE; lapp->ctrlBuf[i++]=1; lapp->ctrlBuf[i++]=RECV_DATASIZES; lapp->ctrlBuf[i++]=NEG_WINDOW_SIZE; lapp->ctrlBuf[i++]=1; lapp->ctrlBuf[i++]=RECV_WINDOWSIZES; lapp->ctrlBuf[i++]=NEG_ADD_BOFS; lapp->ctrlBuf[i++]=1; lapp->ctrlBuf[i++]=RECV_EXTRA_BOFS; lapp->ctrlBuf[i++]=NEG_MIN_TA_TIME; lapp->ctrlBuf[i++]=1; lapp->ctrlBuf[i++]=fd->getMinTurnaroundMask(fd); lapp->ctrlBuf[i++]=NEG_DISC_TIME; lapp->ctrlBuf[i++]=1; lapp->ctrlBuf[i++]=RECV_DISCONNECTS; lapp->ctrlLen=i; lapp->mod|=MOD_CONTROL; } static void sendUAConnect(LAPPrivate* lapp, int addr, int speedMask, int disconnectMask) { int i=0; FrameDevice* fd=lapp->fdev; lapp->ctrlBuf[i++]=lapp->handle; lapp->ctrlBuf[i++]=U_UA|PF_MASK; putBELong(lapp->ctrlBuf+i,lapp->address); i+=4; putBELong(lapp->ctrlBuf+i,addr); i+=4; lapp->ctrlBuf[i++]=NEG_BAUD_RATE; if(speedMask>0xff) { lapp->ctrlBuf[i++]=2; lapp->ctrlBuf[i++]=speedMask; lapp->ctrlBuf[i++]=speedMask>>2; } else { lapp->ctrlBuf[i++]=1; lapp->ctrlBuf[i++]=speedMask; } lapp->ctrlBuf[i++]=NEG_MAX_TA_TIME; lapp->ctrlBuf[i++]=1; lapp->ctrlBuf[i++]=RECV_MAX_TA; lapp->ctrlBuf[i++]=NEG_DATA_SIZE; lapp->ctrlBuf[i++]=1; lapp->ctrlBuf[i++]=RECV_DATASIZES; lapp->ctrlBuf[i++]=NEG_WINDOW_SIZE; lapp->ctrlBuf[i++]=1; lapp->ctrlBuf[i++]=RECV_WINDOWSIZES; lapp->ctrlBuf[i++]=NEG_ADD_BOFS; lapp->ctrlBuf[i++]=1; lapp->ctrlBuf[i++]=RECV_EXTRA_BOFS; lapp->ctrlBuf[i++]=NEG_MIN_TA_TIME; lapp->ctrlBuf[i++]=1; lapp->ctrlBuf[i++]=fd->getMinTurnaroundMask(fd); lapp->ctrlBuf[i++]=NEG_DISC_TIME; lapp->ctrlBuf[i++]=1; lapp->ctrlBuf[i++]=disconnectMask; lapp->ctrlLen=i; lapp->mod|=MOD_CONTROL; } static void sendDISC(LAPPrivate* lapp) { lapp->ctrlBuf[0]=lapp->handle; if(lapp->state==STATE_PRIMARY) lapp->ctrlBuf[0]|=COMMAND_MASK; lapp->ctrlBuf[1]=U_DISC|PF_MASK; lapp->ctrlLen=2; lapp->mod|=MOD_CONTROL; } static void sendUA(LAPPrivate* lapp) { lapp->ctrlBuf[0]=lapp->handle; lapp->ctrlBuf[1]=U_UA|PF_MASK; lapp->ctrlLen=2; lapp->mod|=MOD_CONTROL; } static void sendDM(LAPPrivate* lapp, int handle) { lapp->ctrlBuf[0]=handle; lapp->ctrlBuf[1]=U_DM|PF_MASK; lapp->ctrlLen=2; lapp->mod|=MOD_CONTROL; } static void sendFirstRR(LAPPrivate* lapp) { lapp->ctrlBuf[0]=lapp->handle|COMMAND_MASK; lapp->ctrlBuf[1]=S_RR|PF_MASK; lapp->ctrlLen=2; lapp->mod|=MOD_CONTROL; } static void sendTEST(LAPPrivate* lapp, const u_char* buf, int len) { if(len>sizeof lapp->ctrlBuf) len=sizeof lapp->ctrlBuf; memcpy(lapp->ctrlBuf,buf,len); lapp->ctrlBuf[0]&=~COMMAND_MASK; lapp->ctrlBuf[1]|=PF_MASK; if(lapp->ctrlBuf[0]==BROADCAST_HANDLE && len>=10) { memcpy(lapp->ctrlBuf+6,lapp->ctrlBuf+2,4); putBELong(lapp->ctrlBuf+2,lapp->address); } lapp->ctrlLen=len; lapp->mod|=MOD_CONTROL; } static void sReply(LAPPrivate* lapp) { FrameDevice* fd=lapp->fdev; lapp->ctrlBuf[0]=lapp->handle; if(lapp->state==STATE_PRIMARY) lapp->ctrlBuf[0]|=COMMAND_MASK; lapp->ctrlBuf[1]=lapp->nextVr<ctrlBuf[1]|=PF_MASK; lapp->ctrlBuf[1]|=lapp->mod&MOD_BUSY_LOCAL ? S_RNR : S_RR; fd->sendFrame(fd,lapp->ctrlBuf,2); if(lapp->lap.debug&LAP_DEBUG_FRAMES_OUT) { birda_log("sReply out: "); showBytes(lapp->ctrlBuf,2); } } static void disconnectReply(LAPPrivate* lapp) { FrameDevice* fd=lapp->fdev; lapp->ctrlBuf[0]=lapp->handle; if(lapp->state==STATE_PRIMARY) lapp->ctrlBuf[0]|=COMMAND_MASK; lapp->ctrlBuf[1]=U_DISC|PF_MASK; fd->sendFrame(fd,lapp->ctrlBuf,2); if(lapp->lap.debug&LAP_DEBUG_FRAMES_OUT) { birda_log("disc out: "); showBytes(lapp->ctrlBuf,2); } } /********************************************************************** * Timers **********************************************************************/ static void xidReplyTimer(void* lap) { LAPPrivate* lapp=(LAPPrivate*)lap; if(lapp->lap.debug&LAP_DEBUG_TIMERS) birda_log("xidReplyTimer\n"); lapp->state=STATE_NDM; } static void speedChangeTimer(void* lap) { LAPPrivate* lapp=(LAPPrivate*)lap; if(lapp->lap.debug&LAP_DEBUG_TIMERS) birda_log("speedChangeTimer %d\n",lapp->sendParams.speed); lapp->fdev->setParams(lapp->fdev,lapp->sendParams.speed,lapp->sendParams.extraBOFs,RECV_DATASIZE+2); if(lapp->lap.debug&LAP_DEBUG_INFO) birda_log("%d baud\n",lapp->sendParams.speed); } static void disconnectTimer(void* lap) { LAPPrivate* lapp=(LAPPrivate*)lap; disconnectLAP(lapp); } static void receiveTimer(void* lap) { LAPPrivate* lapp=(LAPPrivate*)lap; int delay=lapp->state==STATE_PRIMARY ? PRIMARY_TIMEOUT : SECONDARY_TIMEOUT; if(lapp->lap.debug&LAP_DEBUG_TIMERS) birda_log("receiveTimer\n"); if(++lapp->nTimeouts*delay>=lapp->sendParams.disconnect*1000) { disconnectLAP(lapp); } else { if(lapp->state==STATE_PRIMARY) { if(lapp->mod&MOD_DISCONNECT) disconnectReply(lapp); else sReply(lapp); } evtSetTimer(delay,receiveTimer,lapp); } } static void turnaroundTimer(void* lap) { LAPPrivate* lapp=(LAPPrivate*)lap; FrameDevice* fd; int queryEnd=0; fd=lapp->fdev; if(lapp->lap.debug&LAP_DEBUG_TIMERS) birda_log("turnaroundTimer\n"); if(lapp->state==STATE_QUERY) { if(lapp->xidSlotxidFlags&XID_SLOTS_MASK]) { sendXIDSlot(lapp); lapp->xidSlot++; evtSetTimer(SLOT_TIME,turnaroundTimer,lapp); } else { sendXIDEnd(lapp); queryEnd=1; } } else if(lapp->mod&MOD_DISCONNECT) { sendDISC(lapp); } else if(lapp->state==STATE_SETUP) { if(lapp->setupTriessetupTries++; sendSNRMConnect(lapp); } else { lapp->state=STATE_NDM; if(lapp->lap.status) lapp->lap.status(&lapp->lap,LAP_DISCONNECTED,0,0,0,0,0); } } if(lapp->mod&MOD_CONTROL) { fd->sendFrame(fd,lapp->ctrlBuf,lapp->ctrlLen); if(lapp->lap.debug&LAP_DEBUG_FRAMES_OUT) { birda_log("turn1 out: "); showBytes(lapp->ctrlBuf,lapp->ctrlLen); } } else { int answered=0; if(!(lapp->mod&MOD_BUSY_REMOTE) || lapp->mod&MOD_SEL_REJECT) { int nFrames=lapp->mod&MOD_SEL_REJECT ? 1 : lapp->sendParams.windowSize; SendBuf* sb; lmMoveConnectionBuffers(lapp,nFrames); if(nFrames>lapp->sendCount) nFrames=lapp->sendCount; if(nFrames>0) answered=1; sb=lapp->sendHead; while(nFrames-->0) { if(!nFrames && !(lapp->mod&MOD_NEED_S)) { sb->buf[1]|=PF_MASK; } else { sb->buf[1]&=~PF_MASK; } sb->buf[1]=(sb->buf[1]&~NR_MASK)|(lapp->nextVr<sendFrame(fd,sb->buf,sb->length); if(lapp->lap.debug&LAP_DEBUG_FRAMES_OUT) { birda_log("turn2 out: "); showBytes(sb->buf,sb->length); } sb=sb->next; } } if(lapp->mod&MOD_NEED_S || !answered) sReply(lapp); } if(queryEnd) { lapp->state=STATE_NDM; if(lapp->lap.status) lapp->lap.status(&lapp->lap,LAP_DISCOVERY_END,0,0,0,0,0); } if(lapp->mod&MOD_CHANGE_SPEED) { /* NB: Enough time to complete UA response frame * with all negotiation params at 9600 baud */ evtSetTimer(60,speedChangeTimer,lapp); } if(lapp->state&STATE_CONNECTED) { if(lapp->state==STATE_SETUP) { evtSetTimer(SETUP_TIME,turnaroundTimer,lapp); } else { int delay=lapp->state==STATE_PRIMARY ? PRIMARY_TIMEOUT : SECONDARY_TIMEOUT; evtSetTimer(delay,receiveTimer,lapp); } } lapp->mod&=~(MOD_NEED_S|MOD_SEL_REJECT|MOD_CONTROL|MOD_CHANGE_SPEED); } /********************************************************************** * Frame reception **********************************************************************/ static void doXIDCommand(LAPPrivate* lapp, const u_char* buf, int len) { int src,dest,flags,slot; if(len<14 || !(buf[1]&COMMAND_MASK) || buf[2]!=XID_FORMAT || buf[13]!=XID_VERSION) return; src=getBELong(buf+3); if(src==0 || src==BROADCAST_ADDRESS) return; dest=getBELong(buf+7); flags=buf[11]; slot=buf[12]; if(slot==END_OF_SLOTS) { if(lapp->state==STATE_REPLY) lapp->state=STATE_NDM; evtCancelTimer(xidReplyTimer,lapp); if(lapp->lap.status) { int i,hints,charset; if(len>14+32) len=14+32; /* It's not supposed to be longer */ for(i=14;ilap.status(&lapp->lap,LAP_DISCOVERED,src,hints,charset,(char*)buf+i,len-i); } } else if(dest==lapp->address || dest==BROADCAST_ADDRESS) { if(lapp->state==STATE_NDM && slot==0 && lapp->lap.status && lapp->lap.status(&lapp->lap,LAP_DISCOVERING,src,0,0,0,0)) { int nslots=answerSlots[3&flags]; if(flags&4) lapp->address=getRandom(BROADCAST_ADDRESS-2)+1; lapp->xidSlot=getRandom(nslots-1); lapp->state=STATE_REPLY; evtSetTimer(100*(nslots-slot),xidReplyTimer,lapp); } if(lapp->state==STATE_REPLY && slot>=lapp->xidSlot) { lapp->state=STATE_NDM; evtCancelTimer(xidReplyTimer,lapp); sendXIDResponse(lapp,src,flags,slot); } } } static void doXIDResponse(LAPPrivate* lapp, const u_char* buf, int len) { int src; if(len<14 || (buf[0]&COMMAND_MASK) || buf[2]!=XID_FORMAT || buf[13]!=XID_VERSION) return; src=getBELong(buf+3); if(src==0 || src==BROADCAST_ADDRESS) return; if(lapp->lap.status) { int i,hints,charset; if(len>14+32) len=14+32; /* It's not supposed to be longer */ for(i=14;ilap.status(&lapp->lap,LAP_DISCOVERED,src,hints,charset,(char*)buf+i,len-i); } } static bool readParams(LAPPrivate* lapp, const u_char* buf, int len, Params* params) { int bits; bits=ALL_BAUD_RATES&getLEParameter(NEG_BAUD_RATE,SPEED_9600,buf,len); bits&=lapp->fdev->getSpeedMask(lapp->fdev); if(!bits) return FALSE; params->speedMask=bits; params->speed=baudRateBits[highestBit(bits)]; bits=ALL_MAX_TAS&getLEParameter(NEG_MAX_TA_TIME,MAX_TA_500ms,buf,len); if(!bits) return FALSE; params->maxTurnaround=maxTurnaroundBits[highestBit(bits)]; bits=ALL_DATASIZES&getLEParameter(NEG_DATA_SIZE,DATASIZE_64,buf,len); if(!bits) return FALSE; params->dataSize=dataSizeBits[highestBit(bits)]; bits=ALL_WINDOWSIZES&getLEParameter(NEG_WINDOW_SIZE,WINDOWSIZE_1,buf,len); if(!bits) return FALSE; params->windowSize=windowSizeBits[highestBit(bits)]; bits=ALL_EXTRA_BOFS&getLEParameter(NEG_ADD_BOFS,EXTRA_BOFS_0,buf,len); if(!bits) return FALSE; params->extraBOFs=extraBOFsBits[highestBit(bits)]; bits=ALL_MIN_TAS&getLEParameter(NEG_MIN_TA_TIME,MIN_TA_10,buf,len); if(!bits) return FALSE; params->minTurnaround=minTurnaroundBits[highestBit(bits)]; bits=MOST_DISCONNECTS&getLEParameter(NEG_DISC_TIME,DISCONNECT_12,buf,len); if(!bits) return FALSE; params->disconnectMask=bits; params->disconnect=disconnectBits[highestBit(bits)]; #if 0 birda_log("adjust window and data sizes is NYI\n"); /* NB: Don't truncate the window size, turnaroundTimer should * instead estimate the time needed based on actual frame * sizes, and queue up as many as possible within the * allowed window size */ birda_log("params: speed=%d maxta=%d data=%d window=%d bofs=%d minta=%d disc=%d\n", lapp->speed,lapp->maxTurnaround,lapp->dataSize,lapp->windowSize, lapp->extraBOFs,lapp->minTurnaround,lapp->disconnect); #endif return TRUE; } static void initConnection(LAPPrivate* lapp) { lapp->mod=0; lapp->nTimeouts=0; lapp->nextVs=0; lapp->nextVr=0; } static bool checkSNRM(LAPPrivate* lapp, const u_char* buf, int len, int* handle, int* src, Params* params) { *handle=BROADCAST_HANDLE; if(len<11 || !(buf[0]&COMMAND_MASK)) return FALSE; *src=getBELong(buf+2); if(*src==0 || *src==BROADCAST_ADDRESS) return FALSE; if(getBELong(buf+6)!=lapp->address) return FALSE; *handle=buf[10]&~COMMAND_MASK; if(*handle==0 || *handle==BROADCAST_HANDLE) return FALSE; if(!readParams(lapp,buf+11,len-11,params)) return FALSE; return TRUE; } static void connectSNRM(LAPPrivate* lapp, int handle, int src, Params* params) { initConnection(lapp); lapp->handle=handle; memcpy(&lapp->sendParams,params,sizeof(Params)); lapp->mod|=MOD_CHANGE_SPEED; lapp->state=STATE_SECONDARY; sendUAConnect(lapp,src,params->speedMask,params->disconnectMask); } static bool checkUA(LAPPrivate* lapp, const u_char* buf, int len, Params* params) { if(len<10 || (buf[0]&COMMAND_MASK)) return FALSE; if(getBELong(buf+2)!=lapp->setupAddress) return FALSE; if(getBELong(buf+6)!=lapp->address) return FALSE; if(!readParams(lapp,buf+10,len-10,params)) return FALSE; return TRUE; } static void connectUA(LAPPrivate* lapp, Params* params) { int delay; initConnection(lapp); memcpy(&lapp->sendParams,params,sizeof(Params)); delay=lapp->fdev->setParams(lapp->fdev,lapp->sendParams.speed, lapp->sendParams.extraBOFs,RECV_DATASIZE+2); lapp->state=STATE_PRIMARY; sendFirstRR(lapp); if(lapp->lap.debug&LAP_DEBUG_INFO) birda_log("%d baud\n",lapp->sendParams.speed); /* Wait at least 10ms for the speed change to take effect */ if(delay<10) delay=10; evtSetTimer(delay,turnaroundTimer,lapp); } #if 0 static void snoopResetSpeed(void* lap) { LAPPrivate* lapp=(LAPPrivate*)lap; birda_log("snoopResetSpeed\n"); lapp->sendParams.speed=9600; lapp->fdev->resetParams(lapp->fdev); birda_log("reset\n"); } #endif static void snoopFollowSpeed(LAPPrivate* lapp, const u_char* buf, int len) { /* XXX why > 10? in my case the size is exactly 10 */ if(len>10 && buf[1]==(U_UA|PF_MASK)) { if(readParams(lapp,buf+10,len-10,&lapp->sendParams)) { birda_log("snoopFollowSpeed %d\n",lapp->sendParams.speed); lapp->fdev->setParams(lapp->fdev,lapp->sendParams.speed,0,RECV_DATASIZE+2); birda_log("%d baud\n",lapp->sendParams.speed); } } } static void frame(FrameDevice* fd, void* buf0, int len) { u_char* buf=(u_char*)buf0; LAPPrivate* lapp=(LAPPrivate*)fd->handle; u_char handle=buf[0]&~COMMAND_MASK; u_char isCommand=(COMMAND_MASK&buf[0])!=0; u_char control=buf[1]; u_char isFinal=(PF_MASK&buf[1])!=0; if(lapp->lap.debug&LAP_DEBUG_FRAMES_IN) { birda_log("in: "); showBytes(buf,len); } if(lapp->lap.debug&LAP_SNOOP) { snoopFollowSpeed(lapp,buf,len); /* evtSetTimer(5000,snoopResetSpeed,lapp); */ return; } if(IS_U(control)) { switch(lapp->state) { case STATE_NDM: case STATE_QUERY: case STATE_REPLY: if(handle!=BROADCAST_HANDLE) return; switch(control) { case U_XID_CMD: case U_XID_CMD|PF_MASK: doXIDCommand(lapp,buf,len); break; case U_XID_RSP: case U_XID_RSP|PF_MASK: doXIDResponse(lapp,buf,len); break; case U_UI: case U_UI|PF_MASK: if(isCommand) lmUnitData(lapp,buf+2,len-2); break; case U_TEST|PF_MASK: sendTEST(lapp,buf,len); break; case U_SNRM|PF_MASK: { Params params; int handle; int src; if(checkSNRM(lapp,buf,len,&handle,&src,¶ms) && lapp->lap.status && lapp->lap.status(&lapp->lap,LAP_CONNECTING,src,0,0,0,0)) { connectSNRM(lapp,handle,src,¶ms); } else { sendDM(lapp,handle); } } break; } break; case STATE_SETUP: if(handle==lapp->handle) { Params params; switch(control) { case U_DM: case U_DM|PF_MASK: if(!isCommand) { disconnectLAP(lapp); } break; case U_DISC: case U_DISC|PF_MASK: if(isCommand) { disconnectLAP(lapp); } break; case U_UA|PF_MASK: if(checkUA(lapp,buf,len,¶ms)) { connectUA(lapp,¶ms); if(lapp->lap.status) lapp->lap.status(&lapp->lap,LAP_CONNECTED,lapp->setupAddress,0,0,0,0); } break; } } else { Params params; int handle; int src; if(handle==BROADCAST_HANDLE && control==(U_SNRM|PF_MASK) && checkSNRM(lapp,buf,len,&handle,&src,¶ms) && src==lapp->setupAddress && lapp->addresssetupAddress) { connectSNRM(lapp,handle,src,¶ms); } else { return; } } break; case STATE_PRIMARY: case STATE_SECONDARY: if(handle!=lapp->handle) return; if((lapp->state==STATE_PRIMARY) == isCommand) break; switch(control) { case U_UI: case U_UI|PF_MASK: if(!(lapp->mod&(MOD_DISCONNECT|MOD_BUSY_LOCAL))) lmData(lapp,buf+2,len-2,1); break; case U_DM|PF_MASK: if(lapp->mod&MOD_DISCONNECT) { disconnectLAP(lapp); } break; case U_UA|PF_MASK: if(lapp->state==STATE_PRIMARY && (lapp->mod&MOD_DISCONNECT)) { disconnectLAP(lapp); } break; case U_TEST|PF_MASK: if(lapp->state==STATE_SECONDARY) sendTEST(lapp,buf,len); break; case U_DISC|PF_MASK: if(lapp->state==STATE_SECONDARY) { sendUA(lapp); disconnectLAP(lapp); } else if(!(lapp->mod&MOD_DISCONNECT)) { lapp->mod|=MOD_DISCONNECT; evtSetTimer(DISCONNECT_TIME,disconnectTimer,lapp); } break; case U_FRMR|PF_MASK: case U_SNRM|PF_MASK: if(!(lapp->mod&MOD_DISCONNECT)) { lapp->mod|=MOD_DISCONNECT; evtSetTimer(DISCONNECT_TIME,disconnectTimer,lapp); } break; } break; } } else if(lapp->state==STATE_PRIMARY || lapp->state==STATE_SECONDARY) { if(handle!=lapp->handle) return; if((lapp->state==STATE_PRIMARY) == isCommand) { birda_log("Primary/secondary conflict\n"); disconnectLAP(lapp); return; } if(!(lapp->mod&MOD_DISCONNECT)) { int nr=NR(control); while(lapp->sendHead && NS(lapp->sendHead->buf[1])!=nr) { SendBuf* s=lapp->sendHead; lapp->sendHead=s->next; lapp->sendCount--; freeMem(s); } if(!lapp->sendHead && nr!=lapp->nextVs) { birda_log("Sequence error (nr)\n"); } if(IS_S(control)) { switch(S_MASK&control) { case S_RR: lapp->mod&=~MOD_BUSY_REMOTE; break; case S_RNR: lapp->mod|=MOD_BUSY_REMOTE; break; case S_SREJ: lapp->mod|=MOD_SEL_REJECT; break; } } else { int ns=NS(control); if(!(lapp->mod&MOD_BUSY_LOCAL) && lapp->nextVr==ns) { lapp->nextVr=(lapp->nextVr+1)%8; lmData(lapp,buf+2,len-2,0); } } } } lapp->nTimeouts=0; if(isFinal && ((lapp->mod&MOD_CONTROL) || (lapp->state&STATE_CONNECTED) || (lapp->state==STATE_QUERY))) { evtSetTimer(lapp->sendParams.minTurnaround,turnaroundTimer,lapp); } } /********************************************************************** * Cleanup **********************************************************************/ static void resetState(LAPPrivate* lapp) { FrameDevice* fd=lapp->fdev; lapp->state=STATE_NDM; lapp->mod=0; lapp->sendParams.minTurnaround=0; fd->resetParams(fd); if(lapp->lap.debug&LAP_DEBUG_INFO) birda_log("reset\n"); } static void disconnectLAP(LAPPrivate* lapp) { lmDisableConnections(lapp); while(lapp->sendHead) { SendBuf* s=lapp->sendHead->next; freeMem(lapp->sendHead); lapp->sendHead=s; } lapp->sendCount=0; evtCancelTimer(xidReplyTimer,lapp); evtCancelTimer(speedChangeTimer,lapp); evtCancelTimer(receiveTimer,lapp); evtCancelTimer(turnaroundTimer,lapp); evtCancelTimer(disconnectTimer,lapp); resetState(lapp); if(lapp->lap.status) lapp->lap.status(&lapp->lap,LAP_DISCONNECTED,0,0,0,0,0); } /********************************************************************** * Used by mux.c **********************************************************************/ void lapAppendSendBuffer(LAPPrivate* lapp, SendBuf* s) { if(lapp->sendHead) { lapp->sendTail->next=s; } else { lapp->sendHead=s; } lapp->sendTail=s; s->next=0; lapp->sendCount++; s->buf[0]=lapp->handle; if(lapp->state==STATE_PRIMARY) s->buf[0]|=COMMAND_MASK; s->buf[1]=lapp->nextVs<nextVs=(lapp->nextVs+1)%8; } /********************************************************************** * External functions **********************************************************************/ void lapClose(LAP* lap) { LAPPrivate* lapp=(LAPPrivate*)lap; while(lapp->lsaps) { LSAPPrivate* l=lapp->lsaps; lapp->lsaps=l->next; freeMem(l); } disconnectLAP(lapp); lmFreeConnections(lapp); lapp->fdev->handle=0; lapp->fdev->frame=0; freeMem(lapp); } void lapDisconnect(struct LAP* lap) { LAPPrivate* lapp=(LAPPrivate*)lap; if(lapp->state&STATE_CONNECTED) { lapp->mod|=MOD_DISCONNECT; evtSetTimer(DISCONNECT_TIME,disconnectTimer,lapp); } } bool lapConnect(struct LAP* lap, int address) { LAPPrivate* lapp=(LAPPrivate*)lap; /* Media busy ... */ if(lapp->state==STATE_NDM) { lapp->state=STATE_SETUP; lapp->handle=(getRandom(BROADCAST_HANDLE-4)+2)&~1; lapp->setupTries=0; lapp->setupAddress=address; turnaroundTimer(lap); return TRUE; } else { return FALSE; } } bool lapDiscover(struct LAP* lap) { LAPPrivate* lapp=(LAPPrivate*)lap; /* Media busy ... */ if(lapp->state==STATE_NDM) { lapp->state=STATE_QUERY; lapp->xidSlot=0; lapp->xidFlags=XID_SLOTS_6; turnaroundTimer(lap); return TRUE; } else { return FALSE; } } LAP* createLAP(FrameDevice* fdev, int charset, const char* name, int len) { LAPPrivate* lapp=allocMem(id_lap,sizeof(LAPPrivate)); lapp->lap.flags=0; lapp->lap.debug=0; lapp->lap.handle=0; lapp->lap.status=0; lapp->fdev=fdev; lapp->address=getRandom(BROADCAST_ADDRESS-2)+1; lapp->lsaps=0; lapp->sendHead=0; lapp->sendCount=0; lapp->connections=0; lapp->nextConn=0; lapp->recvDataSize=RECV_DATASIZE; if(len>sizeof(lapp->info.bytes)) len=sizeof(lapp->info.bytes); lapp->info.charSet=charset; lapp->info.length=len; memcpy(lapp->info.bytes,name,len); resetState(lapp); fdev->handle=lapp; fdev->frame=frame; return &lapp->lap; }