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 >
C/C++ Source or Header  |  2000-06-06  |  16KB  |  613 lines

  1. //
  2. //  $Id: IrConnection.cc,v 1.3 2000/06/06 09:30:53 sergey Exp $
  3. //
  4.  
  5. #include <Pilot.h>
  6. #include <irlib.h>
  7. #include "IrConnection.h"
  8. #include "IrCallback.h"
  9. #include "IrIAS.h"
  10. #include "IrError.h"
  11. #include "Util/Assert.h"
  12. #include "Util/System.h"
  13.  
  14.  
  15. namespace IrDA
  16. {
  17.     //==============================================================================
  18.     //  Constants
  19.     //==============================================================================
  20.  
  21.     const Word  NULL_REF_NUM                = 0xFFFF;
  22.     const Byte  DEFAULT_TTP_CREDIT          = 126;
  23.  
  24.     const int   OPERATION_TIME_OUT          = 5;            // sec
  25.     const int   OPERATION_POLLING_PERIOD    = 10;           // ticks (~0.1 sec?)
  26.  
  27.     const int   MIN_CONTROL_CHANNEL_SIZE    = 1;            // min size in bytes of the control channel (for cooked protocols)
  28.  
  29.  
  30.     //==============================================================================
  31.     //  Connection class
  32.     //==============================================================================
  33.  
  34.     Connection* Connection::_this = NULL;
  35.  
  36.     Connection::Connection():
  37.         _refNum(NULL_REF_NUM),
  38.         _bound(false),
  39.         _ready(false),
  40.         _dataSendReady(false),
  41.         _cookedProtocol(false),
  42.         _pendingCommand(NoCommand),
  43.         _commandSucceed(false),
  44.         _callback(NULL)
  45.     {
  46.         assert(_this == NULL);      // ensures only one instance
  47.         _this = this;
  48.     }
  49.  
  50.     Connection::~Connection()
  51.     {
  52.         shutdown();
  53.         _this = NULL;
  54.     }
  55.  
  56.     // operations
  57.  
  58.     bool Connection::init(Word refNum, Callback* callback)
  59.     {
  60.         _refNum = refNum;
  61.         _callback = callback;
  62.  
  63.         if (bind())
  64.         {
  65.             if (setDeviceInfo())
  66.                 return true;
  67.  
  68.             unbind();
  69.         }
  70.  
  71.         return false;
  72.     }
  73.  
  74.     void Connection::shutdown()
  75.     {
  76.         disconnect();
  77.         unbind();
  78.  
  79.         _packetBuffer.free();
  80.     }
  81.  
  82.     bool Connection::connect()
  83.     {
  84.         assert(_bound);
  85.         assert(!_ready);
  86.  
  87.         waitUntilMediaReady();
  88.  
  89.         if (discovery())
  90.         {
  91.             if (connectLap())
  92.             {
  93.                 if (connectComm())
  94.                 {
  95.                     _dataSendReady = true;
  96.                     return true;
  97.                 }
  98.  
  99.                 disconnectLap();
  100.             }
  101.         }
  102.  
  103.         return false;
  104.     }
  105.  
  106.     void Connection::disconnect()
  107.     {
  108.         if (_bound)
  109.             disconnectLap();
  110.     }
  111.  
  112.     bool Connection::sendData(const Byte* data, int size)
  113.     {
  114.         assert(_ready);
  115.  
  116.         if (!_dataSendReady)
  117.             return false;
  118.  
  119.         _pendingCommand = SendDataCommand;
  120.         _dataSendReady = false;
  121.  
  122.         initDataPacket(data, size);
  123.  
  124.         if (IrDataReq(_refNum, &_connect, &_packet) != IR_STATUS_PENDING)
  125.         {
  126.             IrError::sendDataError(getMaxTxSize(), size);
  127.  
  128.             _dataSendReady = true;      // give it another try
  129.             return false;
  130.         }
  131.  
  132.         return waitForResult();
  133.     }
  134.  
  135.     bool Connection::blockingSendData(const Byte* data, int size)
  136.     {
  137.         // Watch the ready state too - it could be dropped during the wait.
  138.         if (waitUntilDataSendReady() && ready())
  139.             return sendData(data, size);
  140.  
  141.         return false;
  142.     }
  143.  
  144.     // attributes
  145.  
  146.     int Connection::getMaxTxSize() const
  147.     {
  148.         assert(_ready);
  149.  
  150.         int size = IrMaxTxSize(_refNum, const_cast<IrConnect*>(&_connect));
  151.         return _cookedProtocol? size-MIN_CONTROL_CHANNEL_SIZE : size;
  152.     }
  153.  
  154.     int Connection::getMaxRxSize() const
  155.     {
  156.         assert(_ready);
  157.         return IrMaxRxSize(_refNum, const_cast<IrConnect*>(&_connect));
  158.     }
  159.  
  160.     // implementation
  161.  
  162.     bool Connection::bind()
  163.     {
  164.         if (IrBind(_refNum, &_connect, irCallback) != IR_STATUS_SUCCESS)
  165.         {
  166.             IrError::bindError();
  167.             return false;
  168.         }
  169.  
  170.         _bound = true;
  171.         return true;
  172.     }
  173.  
  174.     void Connection::unbind()
  175.     {
  176.         if (_bound)
  177.         {
  178.             if (IrUnbind(_refNum, &_connect) == IR_STATUS_SUCCESS)
  179.                _bound = false;
  180.             else
  181.                 IrError::unbindError();
  182.         }
  183.     }
  184.  
  185.     bool Connection::discovery()
  186.     {
  187.         _pendingCommand = DiscoveryCommand;
  188.  
  189.         if (IrDiscoverReq(_refNum, &_connect) != IR_STATUS_PENDING)
  190.         {
  191.             IrError::discoveryError();
  192.             return false;
  193.         }
  194.  
  195.         return waitForResult();
  196.     }
  197.  
  198.     bool Connection::connectLap()
  199.     {
  200.         _pendingCommand = ConnectLapCommand;
  201.  
  202.         // _deviceInfo must be set by discovery command
  203.         if (IrConnectIrLap(_refNum, _deviceInfo.hDevice) != IR_STATUS_PENDING)
  204.         {
  205.             IrError::lapConnectionError();
  206.             return false;
  207.         }
  208.  
  209.         return waitForResult();
  210.     }
  211.  
  212.     bool Connection::disconnectLap()
  213.     {
  214.         if (IrIsIrLapConnected(_refNum))
  215.         {
  216.             _pendingCommand = DisconnectLapCommand;
  217.  
  218.             if (IrDisconnectIrLap(_refNum) != IR_STATUS_PENDING)
  219.             {
  220.                 IrError::lapDisconnectionError();
  221.                 return false;
  222.             }
  223.  
  224.             return waitForResult();
  225.         }
  226.  
  227.         return false;
  228.     }
  229.  
  230.     bool Connection::connectComm()
  231.     {
  232.         Byte remoteLsap;
  233.         IasQuery iasQuery(_refNum);
  234.  
  235.         // Try connect to printer. LPT port uses LMP protocol and has fixed IrLPT class
  236.         if (iasQuery.getRemoteLsap(IrLptClass, IrLmpProtocol, remoteLsap))
  237.             return connectLmp(remoteLsap);
  238.  
  239.         // Try to make LMP, IrCOMM connection (3-Wire raw)
  240.         if (iasQuery.getRemoteLsap(IrCommClass, IrLmpProtocol, remoteLsap))
  241.             return connectLmp(remoteLsap);
  242.  
  243.         // Try to make TinyTP IrCOMM connection (3-Wire, 9-Wire)
  244.         if (iasQuery.getRemoteLsap(IrCommClass, IrTpProtocol, remoteLsap))
  245.             return connectTinyTp(remoteLsap);
  246.  
  247.         IrError::getRemoteLsapError();
  248.         return false;
  249.     }
  250.  
  251.     bool Connection::connectLmp(Byte remoteLsap)
  252.     {
  253.         _pendingCommand = ConnectLmCommand;
  254.  
  255.         IrSetConTypeLMP(&_connect);
  256.         _cookedProtocol = false;        // LMP used only for non-cooked protcols (LPT, 3-Wire raw)
  257.  
  258.         _connect.rLsap = remoteLsap;
  259.         _packet.buff = NULL;
  260.         _packet.len = 0;
  261.  
  262.         if (IrConnectReq(_refNum, &_connect, &_packet, 0) != IR_STATUS_PENDING)
  263.         {
  264.             IrError::lmpConnectionError();
  265.             return false;
  266.         }
  267.         return waitForResult();
  268.     }
  269.  
  270.     bool Connection::connectTinyTp(Byte remoteLsap)
  271.     {
  272.         _pendingCommand = ConnectLmCommand;
  273.  
  274.         IrSetConTypeTTP(&_connect);
  275.         _cookedProtocol = true;      // TTP used only for cooked protcols (3-Wire, 9-Wire, Centronics)
  276.  
  277.         _connect.rLsap = remoteLsap;
  278.         initServiceTypePacket();
  279.  
  280.         if (IrConnectReq(_refNum, &_connect, &_packet, DEFAULT_TTP_CREDIT) != IR_STATUS_PENDING)
  281.         {
  282.             IrError::tinyTpConnectionError();
  283.             return false;
  284.         }
  285.  
  286.         return waitForResult();
  287.     }
  288.  
  289.     bool Connection::setDeviceInfo()
  290.     {
  291.         // PDA, supports IrCOMM extension
  292.         static Byte deviceInfoXID[] = { IR_HINT_PDA, 0, 'P', 'a', 'l', 'm' };
  293.  
  294.         if(IrSetDeviceInfo(_refNum, deviceInfoXID, sizeof(deviceInfoXID)) != IR_STATUS_SUCCESS)
  295.         {
  296.             IrError::setDeviceInfoError();
  297.             return false;
  298.         }
  299.  
  300.         return true;
  301.     }
  302.  
  303.     void Connection::initServiceTypePacket()
  304.     {
  305.         struct ServiceTypePacket
  306.         {
  307.             Byte PI;
  308.             Byte PL;
  309.             Byte PV;
  310.         };
  311.  
  312.         static ServiceTypePacket controlPacket =
  313.         {
  314.             0x00,   // ServiceType packet
  315.             0x01,   // length
  316.             0x02    // 3-Wire service type
  317.         };
  318.  
  319.         _packet.buff = (Byte*)&controlPacket;
  320.         _packet.len = sizeof(controlPacket);
  321.     }
  322.  
  323.     void Connection::initDataPacket(const Byte* data, int size)
  324.     {
  325.         if (_packetBuffer.isLocked())
  326.             _packetBuffer.unlock();
  327.  
  328.         if (_cookedProtocol)
  329.         {
  330.             _packet.buff = (Byte*)_packetBuffer.lock(size+MIN_CONTROL_CHANNEL_SIZE);
  331.             _packet.len = size+MIN_CONTROL_CHANNEL_SIZE;
  332.  
  333.             _packet.buff[0] = 0;    // control channel size is 0
  334.             MemMove(_packet.buff+MIN_CONTROL_CHANNEL_SIZE, data, size);
  335.         }
  336.         else
  337.         {
  338.             _packet.buff = (Byte*)data;
  339.             _packet.len = size;
  340.         }
  341.     }
  342.  
  343.     // utilities
  344.  
  345.     bool Connection::waitUntilMediaReady() const
  346.     {
  347.         int count = OPERATION_TIME_OUT*SysTicksPerSecond()/OPERATION_POLLING_PERIOD;
  348.  
  349.         for (int i = 0; i < count && IrIsMediaBusy(_refNum); ++i)
  350.             Util::YieldControlToSystem(OPERATION_POLLING_PERIOD, false);
  351.  
  352.         return !IrIsMediaBusy(_refNum);
  353.     }
  354.  
  355.     bool Connection::waitUntilDataSendReady() const
  356.     {
  357.         int count = OPERATION_TIME_OUT*SysTicksPerSecond()/OPERATION_POLLING_PERIOD;
  358.  
  359.         for (int i = 0; i < count && !dataSendReady(); ++i)
  360.             Util::YieldControlToSystem(OPERATION_POLLING_PERIOD, false);
  361.  
  362.         return dataSendReady();
  363.     }
  364.  
  365.     bool Connection::waitForResult() const
  366.     {
  367.         // wait until the pending command will be executed
  368.  
  369.         int count = OPERATION_TIME_OUT*SysTicksPerSecond()/OPERATION_POLLING_PERIOD;
  370.  
  371.         for (int i = 0; i < count && _pendingCommand != NoCommand; ++i)
  372.             Util::YieldControlToSystem(OPERATION_POLLING_PERIOD, false);
  373.  
  374.         return _pendingCommand == NoCommand && _commandSucceed;
  375.     }
  376.  
  377.     // callback handling
  378.  
  379.     void Connection::irCallback(IrConnect* conn, IrCallBackParms* params)
  380.     {
  381.         #ifdef IR_DEBUG
  382.         _this->debugPrintEvent(params->event);
  383.         #endif
  384.  
  385.         switch (params->event)
  386.         {
  387.         case LEVENT_DISCOVERY_CNF:
  388.             _this->discovered(params->deviceList);
  389.             break;
  390.  
  391.         case LEVENT_LAP_CON_CNF:
  392.             _this->connectedLap();
  393.             break;
  394.  
  395.         case LEVENT_LAP_CON_IND:
  396.             _this->requestedLapConnection();
  397.             break;
  398.  
  399.         case LEVENT_LAP_DISCON_IND:
  400.             _this->disconnectedLap();
  401.             break;
  402.  
  403.         case LEVENT_LM_CON_CNF:
  404.             _this->connectedLm();
  405.             break;
  406.  
  407.         case LEVENT_LM_CON_IND:
  408.             _this->requestedLmConnection();
  409.             break;
  410.  
  411.         case LEVENT_LM_DISCON_IND:
  412.             _this->disconnectedLm();
  413.             break;
  414.  
  415.         case LEVENT_PACKET_HANDLED:
  416.             _this->packetHandled();
  417.             break;
  418.  
  419.         case LEVENT_DATA_IND:
  420.             _this->dataReceived(params->rxBuff, params->rxLen);
  421.             break;
  422.  
  423.         case LEVENT_STATUS_IND:
  424.             _this->statusChanged(params->status);
  425.             break;
  426.         }
  427.     }
  428.  
  429.     void Connection::discovered(IrDeviceList* devices)
  430.     {
  431.         if (_pendingCommand == DiscoveryCommand)
  432.         {
  433.             if (devices->nItems > 0)
  434.             {
  435.                 _deviceInfo = devices->dev[0];
  436.                 _commandSucceed = true;
  437.  
  438.                 #ifdef IR_DEBUG
  439.                 debugPrintDeviceInfo(_deviceInfo);
  440.                 #endif
  441.             }
  442.             else
  443.             {
  444.                 _commandSucceed = false;
  445.             }
  446.  
  447.             _pendingCommand = NoCommand;
  448.         }
  449.     }
  450.  
  451.     void Connection::connectedLap()
  452.     {
  453.         if (_pendingCommand == ConnectLapCommand)
  454.         {
  455.             _pendingCommand = NoCommand;
  456.             _commandSucceed = true;
  457.         }
  458.  
  459.         if(_callback != NULL)
  460.             _callback->connected();
  461.     }
  462.  
  463.     void Connection::requestedLapConnection()
  464.     {
  465.         if(_callback != NULL)
  466.             _callback->connected();
  467.     }
  468.  
  469.     void Connection::disconnectedLap()
  470.     {
  471.         if (_pendingCommand == ConnectLapCommand || _pendingCommand == DisconnectLapCommand)
  472.         {
  473.             _pendingCommand = NoCommand;
  474.             _commandSucceed = _pendingCommand == DisconnectLapCommand;
  475.         }
  476.  
  477.         _ready = false;
  478.  
  479.         if (_callback != NULL)
  480.             _callback->disconnected();
  481.     }
  482.  
  483.     void Connection::connectedLm()
  484.     {
  485.         if (_pendingCommand == ConnectLmCommand)
  486.         {
  487.             _pendingCommand = NoCommand;
  488.             _commandSucceed = true;
  489.         }
  490.  
  491.         _ready = true;
  492.     }
  493.  
  494.     void Connection::requestedLmConnection()
  495.     {
  496.     }
  497.  
  498.     void Connection::disconnectedLm()
  499.     {
  500.         _ready = false;
  501.  
  502.         if (_callback != NULL)
  503.             _callback->disconnected();
  504.     }
  505.  
  506.     void Connection::packetHandled()
  507.     {
  508.         if (_pendingCommand == SendDataCommand)
  509.         {
  510.             _pendingCommand = NoCommand;
  511.             _commandSucceed = true;
  512.  
  513.             _dataSendReady = true;
  514.             if (_callback != NULL)
  515.                 _callback->dataSendReady();
  516.         }
  517.     }
  518.  
  519.     void Connection::dataReceived(const Byte* data, int size)
  520.     {
  521.         if (_callback != NULL)
  522.         {
  523.             // In case of cooked protocol first byte is the size of the control packet. Simply ignore it.
  524.             int offset = _cookedProtocol? data[0]+1 : 0;
  525.  
  526.            _callback->dataReceived(data+offset, size-offset);
  527.         }
  528.     }
  529.  
  530.     void Connection::statusChanged(IrStatus irStatus)
  531.     {
  532.         #ifdef IR_DEBUG
  533.         debugPrintStatus(irStatus);
  534.         #endif
  535.  
  536.         if (_callback != NULL)
  537.         {
  538.             Callback::Status status;
  539.  
  540.             switch(irStatus)
  541.             {
  542.                 case IR_STATUS_MEDIA_NOT_BUSY:
  543.                     status = Callback::MediaNotBusyStatus;
  544.                     break;
  545.                 case IR_STATUS_NO_PROGRESS:
  546.                     status = Callback::NoProgressStatus;
  547.                     break;
  548.                 case IR_STATUS_LINK_OK:
  549.                     status = Callback::LineOkStatus;
  550.                     break;
  551.                 default:
  552.                     return;
  553.             }
  554.  
  555.             _callback->statusChanged(status);
  556.         }
  557.     }
  558.  
  559.     // debug
  560.  
  561.     #ifdef IR_DEBUG
  562.  
  563.     void Connection::debugPrintStatus(IrStatus status)
  564.     {
  565.         static const char* statusText[] =
  566.         {
  567.             "IR_STATUS_SUCCESS",
  568.             "IR_STATUS_FAILED",
  569.             "IR_STATUS_PENDING",
  570.             "IR_STATUS_DISCONNECT",
  571.             "IR_STATUS_NO_IRLAP",
  572.             "IR_STATUS_MEDIA_BUSY",
  573.             "IR_STATUS_MEDIA_NOT_BUSY",
  574.             "IR_STATUS_NO_PROGRESS",
  575.             "IR_STATUS_LINK_OK"
  576.         };
  577.  
  578.         Util::DebugPrintf("Status #%d: %s\n", (int)status, statusText[status]);
  579.     }
  580.  
  581.     void Connection::debugPrintEvent(IrEvent event)
  582.     {
  583.         static const char* eventText[] =
  584.         {
  585.             "LEVENT_LM_CON_IND",
  586.             "LEVENT_LM_DISCON_IND",
  587.             "LEVENT_DATA_IND",
  588.             "LEVENT_PACKET_HANDLED",
  589.             "LEVENT_LAP_CON_IND",
  590.             "LEVENT_LAP_DISCON_IND",
  591.             "LEVENT_DISCOVERY_CNF",
  592.             "LEVENT_LAP_CON_CNF",
  593.             "LEVENT_LM_CON_CNF",
  594.             "LEVENT_STATUS_IND",
  595.             "LEVENT_TEST_IND",
  596.             "LEVENT_TEST_CNF"
  597.         };
  598.  
  599.         Util::DebugPrintf("Event #%d: %s\n", (int)event, eventText[event]);
  600.     }
  601.  
  602.     void Connection::debugPrintDeviceInfo(IrDeviceInfo& deviceInfo)
  603.     {
  604.         Util::DebugPrintf("DeviceInfo: 0x%x, 0x%x, '%s'\n",
  605.             _deviceInfo.xid[0],
  606.             _deviceInfo.xid[1],
  607.             (char*)_deviceInfo.xid+2);
  608.     }
  609.  
  610.     #endif  // IR_DEBUG
  611. }
  612. // namespace IrDA
  613.