/* * 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. */ /* commclt.c */ #include #include /********************************************************************** * Constants **********************************************************************/ static const char id_client[]="comm client"; /********************************************************************** * Data structures **********************************************************************/ typedef struct Port { int id; int lsap; int st; int pt; } Port; typedef struct COMMClientPrivate { COMMClient comm; LAP* lap; int state; #define STATE_GET_LSAPS 1 #define STATE_GET_PARAMETERS 2 #define STATE_LIVE 3 #define STATE_CLOSED 0 IASClient* ias; Connection* con; int stMask; int ptMask; #define NPORTS 10 Port ports[NPORTS]; /* Communication state */ int serviceType; bool isDCE; int dce; int dte; } COMMClientPrivate; /********************************************************************** * Internal functions **********************************************************************/ static int parseControl(COMMClientPrivate* commp, const u_char* buf, int len) { int i,len1; if(len<1) return 0; len1=buf[0]+1; if(len1>len) return -1; i=1; while(i+1len1) return -1; switch(pi) { case PI_SERVICE_TYPE: if(commp->comm.debug&COMM_DEBUG_INFO) birda_log("service type %x\n",pv); break; case PI_PORT_TYPE: if(commp->comm.debug&COMM_DEBUG_INFO) birda_log("port type %x\n",pv); break; case PI_FIXED_PORT_NAME: if(commp->comm.debug&COMM_DEBUG_INFO) birda_log("fixed port name %.*s\n",pl,buf+i+2); break; case PI_DATA_RATE: if(commp->comm.debug&COMM_DEBUG_INFO) birda_log("data rate %d\n",pv); break; case PI_DATA_FORMAT: if(commp->comm.debug&COMM_DEBUG_INFO) birda_log("data format %x\n",pv); break; case PI_FLOW_CONTROL: if(commp->comm.debug&COMM_DEBUG_INFO) birda_log("flow control %x\n",pv); break; case PI_XON_XOFF: if(commp->comm.debug&COMM_DEBUG_INFO) birda_log("xon/xoff %x\n",pv); break; case PI_ENQ_ACK: if(commp->comm.debug&COMM_DEBUG_INFO) birda_log("enq/ack %x\n",pv); break; case PI_DTE: if(commp->comm.debug&COMM_DEBUG_INFO) birda_log("DTE %x\n",pv); commp->dte=pv&(COMM_DTR|COMM_RTS); break; case PI_DCE: if(commp->comm.debug&COMM_DEBUG_INFO) birda_log("DCE %x\n",pv); commp->dce&=pv&(COMM_CTS|COMM_DSR|COMM_RI|COMM_CD); break; case PI_POLL_LINE_SETTINGS: birda_log("poll line settings is NYI\n"); default: if(commp->comm.debug&COMM_DEBUG_INFO) birda_log("pi=%x pl=%d\n",pi,pl); break; } i+=2+pl; } if(i!=len1) return -1; return len1; } static void checkParameters(COMMClientPrivate* commp) { for(;;) { int i,id,type=iasCltResType(commp->ias); if(type==IAS_NONE) break; if(type!=IAS_OCTETS) continue; id=iasCltResId(commp->ias); for(i=0;iports[i].id==id) { u_char* p=iasCltResPtr(commp->ias); int n=iasCltResLength(commp->ias); commp->ports[i].st=getBEParameter(PI_SERVICE_TYPE,0,p,n); commp->ports[i].pt=getBEParameter(PI_PORT_TYPE,COMM_PT_SERIAL|COMM_PT_PARALLEL,p,n); break; } } iasCltResNext(commp->ias); } } static void checkLsaps(COMMClientPrivate* commp) { int i=0; while(iias); if(type==IAS_NONE) break; if(type!=IAS_INT) continue; commp->ports[i].id=iasCltResId(commp->ias); commp->ports[i].lsap=iasCltResInt(commp->ias); i++; iasCltResNext(commp->ias); } while(iports[i++].lsap=0; } static void connData(Connection* con, void* buf0, int len) { u_char* buf=(u_char*)buf0; COMMClientPrivate* commp=(COMMClientPrivate*)con->handle; COMMClient* comm=&commp->comm; int len1=parseControl(commp,buf,len); if(len1>=0 && len1data) comm->data(comm,buf+len1,len-len1); } static void connStatus(Connection* con, int event, void* buf, int len) { COMMClient* comm=(COMMClient*)con->handle; switch(event) { case CONN_OPENED: if(comm->status) comm->status(comm,COMM_CONNECTED); if(len>0) connData(con,buf,len); break; case CONN_CLOSED: if(comm->status) comm->status(comm,COMM_DISCONNECTED); break; } } static void choosePort(COMMClientPrivate* commp) { u_char buf[32]; int i,len; for(i=0;iports[i].lsap; int pt=commp->ports[i].pt&commp->ptMask; int st=commp->ports[i].st&commp->stMask; if(!lsap || !pt || !st) continue; if (st&COMM_ST_CENTRONICS) { birda_log("centronics protocol is NYI\n"); } else if(st&COMM_ST_9WIRE) { if(commp->comm.debug&COMM_DEBUG_INFO) birda_log("using 9 wire protocol\n"); commp->serviceType=COMM_ST_9WIRE; buf[0]=9; buf[1]=PI_SERVICE_TYPE; buf[2]=1; buf[3]=COMM_ST_9WIRE; buf[4]=PI_FLOW_CONTROL; buf[5]=1; buf[6]=0; buf[7]=commp->isDCE ? PI_DCE : PI_DTE; buf[8]=1; buf[9]=commp->isDCE ? commp->dce : commp->dte; len=10; /* Send later: PI_DATA_RATE, PI_DATA_FORMAT * Ignore: PI_XON_XOFF, PI_ENQ_ACK */ } else if(st&COMM_ST_3WIRE) { birda_log("3 wire protocol is NYI\n"); /* Send: PI_SERVICE_TYPE, PI_FLOW_CONTROL, PI_XON_XOFF, PI_ENQ_ACK */ } else if(st&COMM_ST_3WIRE_RAW) { birda_log("3 wire raw protocol is NYI\n"); } commp->state=STATE_LIVE; commp->con=lapNewConnection(commp->lap,lsap,LM_TINY_TP,buf,len); commp->con->handle=commp; commp->con->status=connStatus; commp->con->data=connData; return; } birda_log("failed to match ports, NYI\n"); } static void iasStatus(IASClient* ic, int event) { COMMClientPrivate* commp=(COMMClientPrivate*)ic->handle; switch(event) { case IAS_QUERY_COMPLETE: switch(commp->state) { case STATE_GET_LSAPS: checkLsaps(commp); if(!iasCltGetValueByClass(commp->ias,"IrDA:IrCOMM","Parameters")) { birda_log("fail2\n"); } commp->state=STATE_GET_PARAMETERS; break; case STATE_GET_PARAMETERS: checkParameters(commp); iasCltClose(commp->ias); commp->ias=0; choosePort(commp); break; } break; case IAS_QUERY_FAILED: birda_log("ias query failed is NYI\n"); break; } } /********************************************************************** * External functions **********************************************************************/ void commCltWrite(COMMClient* comm, const void* buf0, int len) { u_char* buf=(u_char*)buf0; COMMClientPrivate* commp=(COMMClientPrivate*)comm; int k=connGetSendDataSize(commp->con); int n,i=0; u_char cbuf[1]; cbuf[0]=0; while((n=len-i)>0) { if(commp->serviceType!=COMM_ST_3WIRE_RAW) { if(n>=k) n=k-1; connWrite2(commp->con,cbuf,1,buf+i,n); } else { if(n>k) n=k; connWrite(commp->con,buf+i,n); } i+=n; } } void commCltSetParams(COMMClient* comm, int dataRate, int dataFormat) { COMMClientPrivate* commp=(COMMClientPrivate*)comm; if(commp->serviceType!=COMM_ST_3WIRE_RAW) { u_char cbuf[10]; cbuf[0]=9; cbuf[1]=PI_DATA_RATE; cbuf[2]=4; putBELong(cbuf+3,dataRate); cbuf[7]=PI_DATA_FORMAT; cbuf[8]=1; cbuf[9]=dataFormat; connWrite(commp->con,cbuf,10); } } void commCltSetDTE(COMMClient* comm, int dte) { COMMClientPrivate* commp=(COMMClientPrivate*)comm; int delta; dte&=COMM_DTR|COMM_RTS; delta=(commp->dte^dte)>>2; if(commp->serviceType!=COMM_ST_3WIRE_RAW && delta) { u_char cbuf[4]; cbuf[0]=3; cbuf[1]=PI_DTE; cbuf[2]=1; cbuf[3]=delta|dte; connWrite(commp->con,cbuf,4); commp->dte=dte; } } void commCltSetDCE(COMMClient* comm, int dce) { COMMClientPrivate* commp=(COMMClientPrivate*)comm; int delta; dce&=COMM_CTS|COMM_DSR|COMM_RI|COMM_CD; delta=(commp->dce^dce)>>4; if(commp->serviceType!=COMM_ST_3WIRE_RAW && delta) { u_char cbuf[4]; cbuf[0]=3; cbuf[1]=PI_DCE; cbuf[2]=1; cbuf[3]=delta|dce; connWrite(commp->con,cbuf,4); commp->dce=dce; } } void commCltClose(COMMClient* comm) { COMMClientPrivate* commp=(COMMClientPrivate*)comm; if(commp->ias) iasCltClose(commp->ias); if(commp->con) connClose(commp->con); freeMem(commp); } COMMClient* createCOMMClient(LAP* lap, int serviceMask, int portMask, bool isDCE) { COMMClientPrivate* commp; IASClient* ias=createIASClient(lap); if(!ias) return 0; commp=allocMem(id_client,sizeof(COMMClientPrivate)); commp->comm.handle=0; commp->comm.status=0; commp->lap=lap; commp->stMask=serviceMask; commp->ptMask=portMask; commp->state=STATE_GET_LSAPS; commp->ias=ias; commp->ias->handle=commp; commp->ias->status=iasStatus; commp->con=0; commp->isDCE=isDCE; commp->dce=COMM_CTS|COMM_DSR|COMM_RI|COMM_CD; commp->dte=COMM_DTR|COMM_RTS; iasCltGetValueByClass(commp->ias,"IrDA:IrCOMM","IrDA:TinyTP:LsapSel"); return &commp->comm; }