home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Chip 2001 Mobile
/
Chip_Mobile_2001.iso
/
palm
/
business
/
printcar
/
printcar.exe
/
src
/
IrDA
/
IrConnection.cc
< prev
next >
Wrap
C/C++ Source or Header
|
2000-06-06
|
16KB
|
613 lines
//
// $Id: IrConnection.cc,v 1.3 2000/06/06 09:30:53 sergey Exp $
//
#include <Pilot.h>
#include <irlib.h>
#include "IrConnection.h"
#include "IrCallback.h"
#include "IrIAS.h"
#include "IrError.h"
#include "Util/Assert.h"
#include "Util/System.h"
namespace IrDA
{
//==============================================================================
// Constants
//==============================================================================
const Word NULL_REF_NUM = 0xFFFF;
const Byte DEFAULT_TTP_CREDIT = 126;
const int OPERATION_TIME_OUT = 5; // sec
const int OPERATION_POLLING_PERIOD = 10; // ticks (~0.1 sec?)
const int MIN_CONTROL_CHANNEL_SIZE = 1; // min size in bytes of the control channel (for cooked protocols)
//==============================================================================
// Connection class
//==============================================================================
Connection* Connection::_this = NULL;
Connection::Connection():
_refNum(NULL_REF_NUM),
_bound(false),
_ready(false),
_dataSendReady(false),
_cookedProtocol(false),
_pendingCommand(NoCommand),
_commandSucceed(false),
_callback(NULL)
{
assert(_this == NULL); // ensures only one instance
_this = this;
}
Connection::~Connection()
{
shutdown();
_this = NULL;
}
// operations
bool Connection::init(Word refNum, Callback* callback)
{
_refNum = refNum;
_callback = callback;
if (bind())
{
if (setDeviceInfo())
return true;
unbind();
}
return false;
}
void Connection::shutdown()
{
disconnect();
unbind();
_packetBuffer.free();
}
bool Connection::connect()
{
assert(_bound);
assert(!_ready);
waitUntilMediaReady();
if (discovery())
{
if (connectLap())
{
if (connectComm())
{
_dataSendReady = true;
return true;
}
disconnectLap();
}
}
return false;
}
void Connection::disconnect()
{
if (_bound)
disconnectLap();
}
bool Connection::sendData(const Byte* data, int size)
{
assert(_ready);
if (!_dataSendReady)
return false;
_pendingCommand = SendDataCommand;
_dataSendReady = false;
initDataPacket(data, size);
if (IrDataReq(_refNum, &_connect, &_packet) != IR_STATUS_PENDING)
{
IrError::sendDataError(getMaxTxSize(), size);
_dataSendReady = true; // give it another try
return false;
}
return waitForResult();
}
bool Connection::blockingSendData(const Byte* data, int size)
{
// Watch the ready state too - it could be dropped during the wait.
if (waitUntilDataSendReady() && ready())
return sendData(data, size);
return false;
}
// attributes
int Connection::getMaxTxSize() const
{
assert(_ready);
int size = IrMaxTxSize(_refNum, const_cast<IrConnect*>(&_connect));
return _cookedProtocol? size-MIN_CONTROL_CHANNEL_SIZE : size;
}
int Connection::getMaxRxSize() const
{
assert(_ready);
return IrMaxRxSize(_refNum, const_cast<IrConnect*>(&_connect));
}
// implementation
bool Connection::bind()
{
if (IrBind(_refNum, &_connect, irCallback) != IR_STATUS_SUCCESS)
{
IrError::bindError();
return false;
}
_bound = true;
return true;
}
void Connection::unbind()
{
if (_bound)
{
if (IrUnbind(_refNum, &_connect) == IR_STATUS_SUCCESS)
_bound = false;
else
IrError::unbindError();
}
}
bool Connection::discovery()
{
_pendingCommand = DiscoveryCommand;
if (IrDiscoverReq(_refNum, &_connect) != IR_STATUS_PENDING)
{
IrError::discoveryError();
return false;
}
return waitForResult();
}
bool Connection::connectLap()
{
_pendingCommand = ConnectLapCommand;
// _deviceInfo must be set by discovery command
if (IrConnectIrLap(_refNum, _deviceInfo.hDevice) != IR_STATUS_PENDING)
{
IrError::lapConnectionError();
return false;
}
return waitForResult();
}
bool Connection::disconnectLap()
{
if (IrIsIrLapConnected(_refNum))
{
_pendingCommand = DisconnectLapCommand;
if (IrDisconnectIrLap(_refNum) != IR_STATUS_PENDING)
{
IrError::lapDisconnectionError();
return false;
}
return waitForResult();
}
return false;
}
bool Connection::connectComm()
{
Byte remoteLsap;
IasQuery iasQuery(_refNum);
// Try connect to printer. LPT port uses LMP protocol and has fixed IrLPT class
if (iasQuery.getRemoteLsap(IrLptClass, IrLmpProtocol, remoteLsap))
return connectLmp(remoteLsap);
// Try to make LMP, IrCOMM connection (3-Wire raw)
if (iasQuery.getRemoteLsap(IrCommClass, IrLmpProtocol, remoteLsap))
return connectLmp(remoteLsap);
// Try to make TinyTP IrCOMM connection (3-Wire, 9-Wire)
if (iasQuery.getRemoteLsap(IrCommClass, IrTpProtocol, remoteLsap))
return connectTinyTp(remoteLsap);
IrError::getRemoteLsapError();
return false;
}
bool Connection::connectLmp(Byte remoteLsap)
{
_pendingCommand = ConnectLmCommand;
IrSetConTypeLMP(&_connect);
_cookedProtocol = false; // LMP used only for non-cooked protcols (LPT, 3-Wire raw)
_connect.rLsap = remoteLsap;
_packet.buff = NULL;
_packet.len = 0;
if (IrConnectReq(_refNum, &_connect, &_packet, 0) != IR_STATUS_PENDING)
{
IrError::lmpConnectionError();
return false;
}
return waitForResult();
}
bool Connection::connectTinyTp(Byte remoteLsap)
{
_pendingCommand = ConnectLmCommand;
IrSetConTypeTTP(&_connect);
_cookedProtocol = true; // TTP used only for cooked protcols (3-Wire, 9-Wire, Centronics)
_connect.rLsap = remoteLsap;
initServiceTypePacket();
if (IrConnectReq(_refNum, &_connect, &_packet, DEFAULT_TTP_CREDIT) != IR_STATUS_PENDING)
{
IrError::tinyTpConnectionError();
return false;
}
return waitForResult();
}
bool Connection::setDeviceInfo()
{
// PDA, supports IrCOMM extension
static Byte deviceInfoXID[] = { IR_HINT_PDA, 0, 'P', 'a', 'l', 'm' };
if(IrSetDeviceInfo(_refNum, deviceInfoXID, sizeof(deviceInfoXID)) != IR_STATUS_SUCCESS)
{
IrError::setDeviceInfoError();
return false;
}
return true;
}
void Connection::initServiceTypePacket()
{
struct ServiceTypePacket
{
Byte PI;
Byte PL;
Byte PV;
};
static ServiceTypePacket controlPacket =
{
0x00, // ServiceType packet
0x01, // length
0x02 // 3-Wire service type
};
_packet.buff = (Byte*)&controlPacket;
_packet.len = sizeof(controlPacket);
}
void Connection::initDataPacket(const Byte* data, int size)
{
if (_packetBuffer.isLocked())
_packetBuffer.unlock();
if (_cookedProtocol)
{
_packet.buff = (Byte*)_packetBuffer.lock(size+MIN_CONTROL_CHANNEL_SIZE);
_packet.len = size+MIN_CONTROL_CHANNEL_SIZE;
_packet.buff[0] = 0; // control channel size is 0
MemMove(_packet.buff+MIN_CONTROL_CHANNEL_SIZE, data, size);
}
else
{
_packet.buff = (Byte*)data;
_packet.len = size;
}
}
// utilities
bool Connection::waitUntilMediaReady() const
{
int count = OPERATION_TIME_OUT*SysTicksPerSecond()/OPERATION_POLLING_PERIOD;
for (int i = 0; i < count && IrIsMediaBusy(_refNum); ++i)
Util::YieldControlToSystem(OPERATION_POLLING_PERIOD, false);
return !IrIsMediaBusy(_refNum);
}
bool Connection::waitUntilDataSendReady() const
{
int count = OPERATION_TIME_OUT*SysTicksPerSecond()/OPERATION_POLLING_PERIOD;
for (int i = 0; i < count && !dataSendReady(); ++i)
Util::YieldControlToSystem(OPERATION_POLLING_PERIOD, false);
return dataSendReady();
}
bool Connection::waitForResult() const
{
// wait until the pending command will be executed
int count = OPERATION_TIME_OUT*SysTicksPerSecond()/OPERATION_POLLING_PERIOD;
for (int i = 0; i < count && _pendingCommand != NoCommand; ++i)
Util::YieldControlToSystem(OPERATION_POLLING_PERIOD, false);
return _pendingCommand == NoCommand && _commandSucceed;
}
// callback handling
void Connection::irCallback(IrConnect* conn, IrCallBackParms* params)
{
#ifdef IR_DEBUG
_this->debugPrintEvent(params->event);
#endif
switch (params->event)
{
case LEVENT_DISCOVERY_CNF:
_this->discovered(params->deviceList);
break;
case LEVENT_LAP_CON_CNF:
_this->connectedLap();
break;
case LEVENT_LAP_CON_IND:
_this->requestedLapConnection();
break;
case LEVENT_LAP_DISCON_IND:
_this->disconnectedLap();
break;
case LEVENT_LM_CON_CNF:
_this->connectedLm();
break;
case LEVENT_LM_CON_IND:
_this->requestedLmConnection();
break;
case LEVENT_LM_DISCON_IND:
_this->disconnectedLm();
break;
case LEVENT_PACKET_HANDLED:
_this->packetHandled();
break;
case LEVENT_DATA_IND:
_this->dataReceived(params->rxBuff, params->rxLen);
break;
case LEVENT_STATUS_IND:
_this->statusChanged(params->status);
break;
}
}
void Connection::discovered(IrDeviceList* devices)
{
if (_pendingCommand == DiscoveryCommand)
{
if (devices->nItems > 0)
{
_deviceInfo = devices->dev[0];
_commandSucceed = true;
#ifdef IR_DEBUG
debugPrintDeviceInfo(_deviceInfo);
#endif
}
else
{
_commandSucceed = false;
}
_pendingCommand = NoCommand;
}
}
void Connection::connectedLap()
{
if (_pendingCommand == ConnectLapCommand)
{
_pendingCommand = NoCommand;
_commandSucceed = true;
}
if(_callback != NULL)
_callback->connected();
}
void Connection::requestedLapConnection()
{
if(_callback != NULL)
_callback->connected();
}
void Connection::disconnectedLap()
{
if (_pendingCommand == ConnectLapCommand || _pendingCommand == DisconnectLapCommand)
{
_pendingCommand = NoCommand;
_commandSucceed = _pendingCommand == DisconnectLapCommand;
}
_ready = false;
if (_callback != NULL)
_callback->disconnected();
}
void Connection::connectedLm()
{
if (_pendingCommand == ConnectLmCommand)
{
_pendingCommand = NoCommand;
_commandSucceed = true;
}
_ready = true;
}
void Connection::requestedLmConnection()
{
}
void Connection::disconnectedLm()
{
_ready = false;
if (_callback != NULL)
_callback->disconnected();
}
void Connection::packetHandled()
{
if (_pendingCommand == SendDataCommand)
{
_pendingCommand = NoCommand;
_commandSucceed = true;
_dataSendReady = true;
if (_callback != NULL)
_callback->dataSendReady();
}
}
void Connection::dataReceived(const Byte* data, int size)
{
if (_callback != NULL)
{
// In case of cooked protocol first byte is the size of the control packet. Simply ignore it.
int offset = _cookedProtocol? data[0]+1 : 0;
_callback->dataReceived(data+offset, size-offset);
}
}
void Connection::statusChanged(IrStatus irStatus)
{
#ifdef IR_DEBUG
debugPrintStatus(irStatus);
#endif
if (_callback != NULL)
{
Callback::Status status;
switch(irStatus)
{
case IR_STATUS_MEDIA_NOT_BUSY:
status = Callback::MediaNotBusyStatus;
break;
case IR_STATUS_NO_PROGRESS:
status = Callback::NoProgressStatus;
break;
case IR_STATUS_LINK_OK:
status = Callback::LineOkStatus;
break;
default:
return;
}
_callback->statusChanged(status);
}
}
// debug
#ifdef IR_DEBUG
void Connection::debugPrintStatus(IrStatus status)
{
static const char* statusText[] =
{
"IR_STATUS_SUCCESS",
"IR_STATUS_FAILED",
"IR_STATUS_PENDING",
"IR_STATUS_DISCONNECT",
"IR_STATUS_NO_IRLAP",
"IR_STATUS_MEDIA_BUSY",
"IR_STATUS_MEDIA_NOT_BUSY",
"IR_STATUS_NO_PROGRESS",
"IR_STATUS_LINK_OK"
};
Util::DebugPrintf("Status #%d: %s\n", (int)status, statusText[status]);
}
void Connection::debugPrintEvent(IrEvent event)
{
static const char* eventText[] =
{
"LEVENT_LM_CON_IND",
"LEVENT_LM_DISCON_IND",
"LEVENT_DATA_IND",
"LEVENT_PACKET_HANDLED",
"LEVENT_LAP_CON_IND",
"LEVENT_LAP_DISCON_IND",
"LEVENT_DISCOVERY_CNF",
"LEVENT_LAP_CON_CNF",
"LEVENT_LM_CON_CNF",
"LEVENT_STATUS_IND",
"LEVENT_TEST_IND",
"LEVENT_TEST_CNF"
};
Util::DebugPrintf("Event #%d: %s\n", (int)event, eventText[event]);
}
void Connection::debugPrintDeviceInfo(IrDeviceInfo& deviceInfo)
{
Util::DebugPrintf("DeviceInfo: 0x%x, 0x%x, '%s'\n",
_deviceInfo.xid[0],
_deviceInfo.xid[1],
(char*)_deviceInfo.xid+2);
}
#endif // IR_DEBUG
}
// namespace IrDA