xwords/xwords4/palm/palmir.c
2005-04-03 16:53:32 +00:00

720 lines
21 KiB
C

/* -*-mode: C; fill-column: 77; c-basic-offset: 4; -*- */
/*
* Copyright 2001 by Eric House (fixin@peak.org). All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifdef IR_SUPPORT
#include <TimeMgr.h>
#include "palmir.h"
#include "callback.h"
#include "xwords4defines.h"
#include "comms.h"
#include "memstream.h"
#include "palmutil.h"
#include "LocalizedStrIncludes.h"
# ifndef IR_EXCHMGR
#define IR_NO_TIMEOUT 0xFFFFFFFF
#if 1
# define IR_TIMEOUT (15*60) /* 10 seconds during debugging */
#else
# define IR_TIMEOUT (150*60) /* 100 seconds during debugging */
#endif
#define RESET_TIMER(g) (g)->ir_timeout = TimGetTicks() + IR_TIMEOUT
struct MyIrPacket {
IrPacket packet;
struct MyIrPacket* next;
Boolean in_use;
};
/***************************************************************************/
static void receiveData( PalmAppGlobals* globals, UInt8* buf, UInt16 len );
#define addFreeQueue( g,p ) (--(g)->irSendQueueLen)
static void addToSendQueue( PalmAppGlobals* globals, MyIrPacket* packet );
static void clearSendQueue( PalmAppGlobals* globals );
static MyIrPacket* getFreeSendPacket( PalmAppGlobals* globals );
#ifdef DEBUG
static void printStateTransition( PalmAppGlobals* globals );
static void assert_state1( PalmAppGlobals* globals, short line,
IR_STATE assertState );
static void assert_state2( PalmAppGlobals* globals, short line,
IR_STATE assertState1,IR_STATE assertState2 );
#else
#define printStateTransition( globals )
#define assert_state1( globals, line, assertState )
#define assert_state2( globals, line, assertState1, assertState2 )
#endif
/***************************************************************************/
static Boolean
storeDiscovery( PalmAppGlobals* globals, IrDeviceList* deviceList,
IrConnect* con )
{
short i;
XP_ASSERT( deviceList->nItems <= 1 );
for ( i = 0; i < deviceList->nItems; ++i ) {
globals->irDev = deviceList->dev[i].hDevice;
XP_ASSERT( &globals->irC_out.irCon == con );
con->rLsap = deviceList->dev[i].xid[0];
#ifdef DEBUG
globals->save_rLsap = con->rLsap;
#endif
}
return deviceList->nItems > 0;
} /* storeDiscovery */
void
ir_callback_out( IrConnect* con, IrCallBackParms* parms )
{
PalmAppGlobals* globals;
IrStatus status;
CALLBACK_PROLOGUE();
globals = ((MyIrConnect*)con)->globals;
switch ( parms->event ) {
case LEVENT_LAP_DISCON_IND: /* IrLAP connection has gone down, or
IrConnectIrLap failed */
XP_STATUSF( "LAP_DISCON_IND received" );
if ( !!globals->rcvBuffSize ) {
/* we've received a buffer and now need to do something with it */
assert_state1( globals, __LINE__, IR_STATE_NONE );
/* globals->ir_state = IR_STATE_MESSAGE_RECD; */
} else {
globals->ir_state = IR_STATE_NONE; /* was IR_STATE_DOLAP */
}
break;
case LEVENT_LM_CON_IND:
XP_ASSERT( !globals->conPacketInUse );
XP_STATUSF( "responding to incomming connection" );
assert_state2( globals, __LINE__, IR_STATE_CONN_RECD,
IR_STATE_LAP_RCV );
globals->conPacket.buff = globals->conBuff;
globals->conPacket.len = sizeof(globals->conBuff);
XP_ASSERT( globals->conPacket.len <= IR_MAX_CON_PACKET );
status = IrConnectRsp( globals->irLibRefNum, con,
&globals->conPacket, 0 /* credit: ignored */);
XP_ASSERT( status == IR_STATUS_PENDING );
if ( status == IR_STATUS_PENDING ) {
globals->conPacketInUse = true;
globals->ir_state = IR_STATE_CONN_INCOMMING;
} else {
XP_STATUSF( "IrConnectRsp call failed with %d", status );
}
break;
case LEVENT_LM_DISCON_IND:
XP_WARNF( "LEVENT_LM_DISCON_IND received; failure???" );
break;
case LEVENT_PACKET_HANDLED: {
IrPacket* packetPtr = parms->packet;
packetPtr->buff = NULL;
packetPtr->len = 0;
if ( packetPtr == &globals->conPacket ) {
/* don't change the state here. This is just telling us the
packet's free */
/* assert_state2( globals, __LINE__, IR_STATE_LMPREQ_SENT, */
/* IR_STATE_LMPRCV_REQ_SENT ); */
XP_ASSERT( globals->conPacketInUse );
/* not true if an incomming connection */
/* XP_ASSERT( !!getSendQueueHead(globals) ); */
globals->conPacketInUse = false;
} else {
assert_state1( globals, __LINE__, IR_STATE_SEND_DONE );
((MyIrPacket*)packetPtr)->in_use = false;
addFreeQueue( globals, packetPtr );
/* if we've received notification that a send was successful, and
if we've no further sends to do, shut down the connection.*/
if ( !!getSendQueueHead(globals) ) { /* another message? */
globals->ir_state = IR_STATE_LMP_ESTAB;
} else {
globals->ir_state = IR_STATE_CAN_DISCONNECT;
XP_STATUSF( "state:IR_STATE_CAN_DISCONNECT" );
}
}
}
break;
case LEVENT_DATA_IND:
assert_state1( globals, __LINE__, IR_STATE_CONN_INCOMMING );
receiveData( globals, parms->rxBuff, parms->rxLen );
globals->ir_state = IR_STATE_MESSAGE_RECD;
break;
case LEVENT_STATUS_IND:
break;
case LEVENT_DISCOVERY_CNF: /* both sides must do this */
assert_state1( globals, __LINE__, IR_STATE_DISCOVERY_SENT );
if ( storeDiscovery( globals, parms->deviceList, con ) ) {
if ( !!getSendQueueHead( globals ) ) {
globals->ir_state = IR_STATE_DOLAP;
} else {
globals->ir_state = IR_STATE_DISCOVERY_COMPLETE;
}
} else { /* discovery failed */
globals->ir_state = IR_STATE_REDO_DISCOVERY;
}
break;
case LEVENT_LAP_CON_CNF:
XP_STATUSF( "irlap established" );
assert_state1( globals, __LINE__, IR_STATE_LAP_SENT );
globals->ir_state = IR_STATE_LAP_ESTAB;
break;
case LEVENT_LM_CON_CNF: /* requested IrLMP connection successful */
XP_STATUSF( "IrLMP connection is up" );
assert_state1( globals, __LINE__, IR_STATE_LMPREQ_SENT );
XP_ASSERT( ir_work_exists(globals) );
/* I'm not sure whether we get this event before or after the one
releasing the packet passed to IrConnectReq. Both need to happen
before we can do a send -- though I guess since we're using a
different packet that's not strictly true. */
globals->ir_state = IR_STATE_LMP_ESTAB;
break;
case LEVENT_LAP_CON_IND:
/* indicates that the other side's opened up a connection */
XP_STATUSF( "other side opened up a LAP connection" );
globals->ir_state = IR_STATE_LAP_RCV;
break;
/* case LEVENT_TEST_CNF: */
/* XP_ASSERT( globals->packet_in_use ); */
/* globals->packet_in_use = false; */
/* XP_DEBUGF( "LEVENT_TEST_CNF: returned %d", parms->status ); */
/* break; */
/* case LEVENT_TEST_IND: */
/* XP_DEBUGF( "LEVENT_TEST_IND received" ); */
/* receiveData( globals, parms->rxBuff, parms->rxLen ); */
/* break; */
default:
}
CALLBACK_EPILOGUE();
} /* ir_callback_out */
#endif
# ifndef IR_EXCHMGR
Boolean
ir_do_work( PalmAppGlobals* globals )
{
IrStatus status;
Boolean result = false;
XWStreamCtxt* instream;
MyIrPacket* packetPtr;
printStateTransition( globals );
if ( !!getSendQueueHead(globals) /* we're here to send something */
&& globals->ir_state > IR_STATE_NOTHING_TO_DO
&& globals->ir_timeout < TimGetTicks() ) {
Boolean retry;
retry = palmaskFromStrId( globals, STR_RESEND_IR, -1, -1 );
/* why did I do this? */
if ( IrIsIrLapConnected( globals->irLibRefNum ) ) {
status = IrDisconnectIrLap( globals->irLibRefNum );
XP_ASSERT( status == IR_STATUS_PENDING );
}
if ( retry ) {
RESET_TIMER(globals);
} else {
clearSendQueue( globals );
}
globals->ir_state = retry? IR_STATE_DO_DISCOVERY : IR_STATE_NONE;
} else {
switch( globals->ir_state ) {
case IR_STATE_NONE: /* do we need this state anymore? */
XP_ASSERT( !!getSendQueueHead( globals ) );
if ( IrIsIrLapConnected(globals->irLibRefNum) ) {
globals->ir_state = IR_STATE_LAP_ESTAB;
} else {
globals->ir_state = IR_STATE_DO_DISCOVERY;
}
break;
case IR_STATE_DO_DISCOVERY:
/* might a well set it up here */
globals->conPacket.buff = globals->conBuff;
globals->conPacket.len = IR_MAX_CON_PACKET;
RESET_TIMER(globals);
case IR_STATE_REDO_DISCOVERY:
if ( IrIsIrLapConnected(globals->irLibRefNum) ) {
globals->ir_state = IR_STATE_LAP_ESTAB;
} else {
status = IrDiscoverReq( globals->irLibRefNum,
&globals->irC_out.irCon );
if (status == IR_STATUS_SUCCESS ||
status == IR_STATUS_PENDING) {
globals->ir_state = IR_STATE_DISCOVERY_SENT;
} else {
XP_STATUSF( "discov failed: %d", status );
globals->ir_state = IR_STATE_REDO_DISCOVERY;
}
}
break;
case IR_STATE_DISCOVERY_SENT:
break;
case IR_STATE_DOLAP:
/* better be a message to send! */
XP_ASSERT( !!getSendQueueHead( globals ) );
XP_STATUSF( "calling IrConnectIrLap" );
status = IrConnectIrLap( globals->irLibRefNum, globals->irDev );
if (status != IR_STATUS_SUCCESS && status != IR_STATUS_PENDING) {
XP_STATUSF( "IrConnectIrLap call failed: %d", status );
} else {
globals->ir_state = IR_STATE_LAP_SENT;
}
break;
case IR_STATE_LAP_SENT:
/* XP_DEBUGF( "state still IR_STATE_LAP_SENT" ); */
break;
case IR_STATE_LAP_ESTAB:
if ( !globals->conPacketInUse ) {
/* not true if from other side */
/* XP_ASSERT( !!globals->conPacket.buff ); */
XP_ASSERT( IrIsIrLapConnected(globals->irLibRefNum) );
/* not sure what this means anyway.... */
/* XP_ASSERT(globals->irC_out.irCon.rLsap== */
/* globals->save_rLsap); */
status = IrConnectReq( globals->irLibRefNum,
&globals->irC_out.irCon,
&globals->conPacket, 0 );
if ( status == IR_STATUS_PENDING ) {
if ( globals->ir_state == IR_STATE_LAP_ESTAB ) {
globals->ir_state = IR_STATE_LMPREQ_SENT;
} else {
globals->ir_state = IR_STATE_LMPRCV_REQ_SENT;
}
globals->conPacketInUse = true;
XP_STATUSF( "IrConnectReq succeeded" );
} else {
XP_STATUSF( "IrConnectReq returned %d; will try again",
status );
}
} else {
XP_WARNF( "Can't call IrConnectReq b/c packet_in_use" );
}
break;
case IR_STATE_LMP_ESTAB:
packetPtr = getSendQueueHead( globals );
XP_ASSERT( !!packetPtr );
if ( !!packetPtr ) {
XP_ASSERT( !!packetPtr->packet.buff );
XP_ASSERT( packetPtr->packet.len > 0 );
status = IrDataReq( globals->irLibRefNum,
&globals->irC_out.irCon,
&packetPtr->packet );
if ( status == IR_STATUS_PENDING ) {
packetPtr->in_use = true;
globals->ir_state = IR_STATE_SEND_DONE;
} else {
XP_WARNF( "IrDataReq returned %d", status );
}
}
break;
case IR_STATE_MESSAGE_RECD:
XP_ASSERT( !!globals->rcvBuffSize );
instream = mem_stream_make( MEMPOOL globals, 0, NULL );
stream_open( instream );
stream_putBytes( instream, globals->rcvBuff,
globals->rcvBuffSize );
globals->rcvBuffSize = 0;
if ( comms_checkIncommingStream( globals->game.comms, instream,
&instream, 1 ) ) { /* FIXME!!! */
result = server_receiveMessage( globals->game.server,
instream );
}
stream_destroy( instream );
palm_util_requestTime( &globals->util );
globals->ir_state = IR_STATE_CAN_DISCONNECT;
break; /* comment this out? */
#if 1
case IR_STATE_CAN_DISCONNECT:
/* send the disconnect message so receiver will know the
message is finished */
/* if the other side disconnects, it'll already be down?? */
if ( IrIsIrLapConnected( globals->irLibRefNum ) ) {
status = IrDisconnectIrLap( globals->irLibRefNum );
XP_ASSERT( status == IR_STATUS_PENDING );
}
globals->ir_state = IR_STATE_NONE;
break;
#endif
default:
break;
}
}
return result;
} /* ir_do_work */
void
ir_show_status( PalmAppGlobals* globals )
{
if ( !!globals->mainForm ) {
XP_U16 x, y;
WinHandle oldHand = WinSetDrawWindow( (WinHandle)globals->mainForm );
x = globals->isLefty?1:154;
y = 160 - TRAY_HEIGHT - IR_STATUS_HEIGHT;
if ( globals->ir_state > IR_STATE_NOTHING_TO_DO ) {
char buf[2] = { 0, 0 };
if ( globals->ir_state <= 9 ) {
buf[0] = '0' + globals->ir_state;
} else {
buf[0] = 'A' + globals->ir_state-10;
}
WinDrawChars( buf, 1, x, y );
} else {
RectangleType r = { {x, y}, {8, 10} };
WinEraseRectangle( &r, 0);
}
WinSetDrawWindow( oldHand );
}
} /* ir_show_status */
/* Free any memory associated with message queues, etc.
*/
void
ir_cleanup( PalmAppGlobals* globals )
{
MyIrPacket* packet;
MyIrPacket* next;
for ( packet = globals->packetListHead; !!packet; packet = next ) {
next = packet->next;
XP_FREE( globals->mpool, packet );
}
globals->packetListHead = NULL;
} /* ir_cleanup */
#endif
/* We're passed an address as we've previously defined it and a buffer
* containing a message to send. Prepend any palm/ir specific headers to the
* message, save the buffer somewhere, and fire up the state machine that
* will eventually get it sent to the address.
*
* Note that the caller will queue the message for possible resend, but
* won't automatically schedule that resend whatever results we return.
*
* NOTE also that simply stuffing the buf ptr into the packet won't work
* if there's any ir-specific packet header I need to prepend to what's
* outgoing.
*/
XP_S16
palm_ir_send( XP_U8* buf, XP_U16 len, PalmAppGlobals* globals )
{
#ifdef IR_EXCHMGR
UInt32 sent = 0;
Err err;
ExgSocketType exgSocket;
XP_MEMSET( &exgSocket, 0, sizeof(exgSocket) );
exgSocket.description = "Crosswords data";
exgSocket.length = len;
exgSocket.target = APPID;
if ( globals->romVersion >= 40 ) {
exgSocket.name = exgBeamPrefix;
}
err = ExgPut( &exgSocket );
while ( !err && sent < len ) {
sent += ExgSend( &exgSocket, buf+sent, len-sent, &err );
XP_ASSERT( sent < 0x7FFF );
}
err = ExgDisconnect( &exgSocket, err );
return err==0? sent : 0;
#else
MyIrPacket* packet = getFreeSendPacket( globals );
packet->packet.buff = buf;
packet->packet.len = len;
XP_ASSERT( !packet->in_use );
addToSendQueue( globals, packet );
return len;
#endif
} /* palm_ir_send */
#ifdef IR_EXCHMGR
void
palm_ir_receiveMove( PalmAppGlobals* globals, ExgSocketPtr socket )
{
UInt32 nBytesReceived = -1;
Err err;
err = ExgAccept( socket );
if ( err == 0 ) {
XWStreamCtxt* instream;
instream = mem_stream_make( MEMPOOL globals->vtMgr, globals,
CHANNEL_NONE, NULL );
stream_open( instream );
for ( ; ; ) {
UInt8 buf[128];
nBytesReceived = ExgReceive( socket, buf, sizeof(buf), &err );
if ( nBytesReceived == 0 || err != 0 ) {
break;
}
stream_putBytes( instream, buf, nBytesReceived );
}
(void)ExgDisconnect( socket, err );
if ( nBytesReceived == 0 ) { /* successful loop exit */
checkAndDeliver( globals, instream );
}
}
} /* palm_ir_receiveMove */
#else
static void
receiveData( PalmAppGlobals* globals, UInt8* buf, UInt16 len )
{
XP_ASSERT( !!len );
XP_ASSERT( !globals->conPacketInUse );
XP_ASSERT( !globals->rcvBuffSize ); /* else messages coming in several
parts; old code handled this */
XP_MEMCPY( globals->rcvBuff, buf, len );
globals->rcvBuffSize = len;
globals->ir_timeout = IR_NO_TIMEOUT;
} /* receiveData */
/* return the first packet ready to be sent, i.e. whose buf ptr is non-null
* and whose in_use flag is not set. To make searching faster, keep track of
* whether there are actually any on the queue. */
MyIrPacket*
getSendQueueHead( PalmAppGlobals* globals )
{
MyIrPacket* packet = NULL;
if ( globals->irSendQueueLen > 0 ) {
packet = (MyIrPacket*)globals->packetListHead;
for ( ; !!packet; packet = packet->next ) {
if ( !!packet->packet.buff && !packet->in_use ) {
break;
}
}
}
return packet;
} /* getSendQueueHead */
#endif
/* The ptr's already on the list, but we need to move it to the end, behind
* anything that's already there waiting to be sent. That's because messages
* need to get sent in order.
*/
#ifndef IR_EXCHMGR
static void
addToSendQueue( PalmAppGlobals* globals, MyIrPacket* packet )
{
MyIrPacket* end = globals->packetListHead;
packet->next = NULL;
if ( !end ) {
globals->packetListHead = packet;
} else {
for ( ; !!end->next; end = end->next ) {
}
end->next = packet;
}
++globals->irSendQueueLen;
RESET_TIMER(globals);
} /* addToSendQueue */
#endif /* ifndef IR_EXCHMGR */
#ifndef IR_EXCHMGR
static void
clearSendQueue( PalmAppGlobals* globals )
{
MyIrPacket* packet;
MyIrPacket* next;
for ( packet = globals->packetListHead; !!packet; packet = next ) {
next = packet->next;
if ( packet->packet.buff != NULL ) {
packet->packet.buff = NULL;
packet->packet.len = 0;
--globals->irSendQueueLen;
}
}
XP_ASSERT( globals->irSendQueueLen == 0 ) ;
} /* clearSendQueue */
static MyIrPacket*
getFreeSendPacket( PalmAppGlobals* globals )
{
MyIrPacket* packet = globals->packetListHead;
MyIrPacket* prev;
for ( prev = NULL; !!packet; prev = packet, packet = packet->next ) {
if ( !packet->packet.buff ) {
XP_ASSERT( packet->packet.len == 0 );
/* cut out of list before returning */
if ( !!prev ) {
prev->next = packet->next;
} else {
XP_ASSERT( globals->packetListHead == packet );
globals->packetListHead = NULL;
}
return packet;
}
}
packet = XP_MALLOC( globals->mpool, sizeof(*packet) );
XP_MEMSET( packet, 0, sizeof(*packet) );
return packet;
} /* getFreeSendPacket */
#endif
#ifdef DEBUG
#ifndef IR_EXCHMGR
static char*
getStateName( IR_STATE state )
{
switch ( state ) {
case IR_STATE_NONE: return "NONE";
case IR_STATE_DISCOVERY_COMPLETE: return "DISCOVERY_COMPLETE";
case IR_STATE_NOTHING_TO_DO: return "NOTHING_TO_DO";
case IR_STATE_NO_OTHER_FOUND: return "NO_OTHER_FOUND";
case IR_STATE_DO_DISCOVERY: return "DO_DISCOVERY";
case IR_STATE_REDO_DISCOVERY: return "REDO_DISCOVERY";
case IR_STATE_DISCOVERY_SENT: return "DISCOVERY_SENT";
case IR_STATE_DOLAP: return "DOLAP";
case IR_STATE_LAP_SENT: return "LAP_SENT";
case IR_STATE_LAP_ESTAB: return "LAP_ESTAB";
case IR_STATE_LMPREQ_SENT: return "LMPREQ_SENT";
case IR_STATE_LMP_ESTAB: return "LMP_ESTAB";
case IR_STATE_SEND_DONE: return "SEND_DONE";
case IR_STATE_CAN_DISCONNECT: return "CAN_DISCONNECT";
case IR_STATE_CONN_RECD: return "CONN_RECD";
case IR_STATE_LAP_RCV: return "LAP_RCV";
case IR_STATE_LMPRCV_REQ_SENT: return "LMPRCV_REQ_SENT";
case IR_STATE_CONN_INCOMMING: return "CONN_INCOMMING";
case IR_STATE_MESSAGE_RECD: return "MESSAGE_RECD";
default:
return "unknown";
}
} /* getStateName */
static void
assert_state1( PalmAppGlobals* globals, short line, IR_STATE assertState )
{
if ( globals->ir_state != assertState ) {
XP_WARNF( "Line %d: sought %s; found %s", line,
getStateName(assertState), getStateName(globals->ir_state));
}
} /* assert_state1 */
static void
assert_state2( PalmAppGlobals* globals, short line, IR_STATE assertState1,
IR_STATE assertState2 )
{
if ( globals->ir_state != assertState1
&& globals->ir_state != assertState2){
XP_WARNF( "Line %d: sought %s or %s; found %s", line,
getStateName(assertState1), getStateName(assertState2),
getStateName( globals->ir_state ) );
}
} /* assertState2 */
static void
printStateTransition( PalmAppGlobals* globals )
{
if ( globals->ir_state != globals->ir_state_prev ) {
char* oldState = getStateName( globals->ir_state_prev );
char* newState = getStateName( globals->ir_state );
XP_STATUSF( "ir_st:%s->%s", oldState, newState );
globals->ir_state_prev = globals->ir_state;
}
} /* printStateTransition */
# endif /* IR_EXCHMGR */
#endif /* DEBUG */
#endif /* IR_SUPPORT */