cec: delay the 'active source' command for panasonic, until the vendor command that...
[deb_libcec.git] / src / lib / adapter / USBCECAdapterCommunication.cpp
CommitLineData
a8f0bd18
LOK
1/*
2 * This file is part of the libCEC(R) library.
3 *
b492c10e 4 * libCEC(R) is Copyright (C) 2011-2012 Pulse-Eight Limited. All rights reserved.
a8f0bd18
LOK
5 * libCEC(R) is an original work, containing original code.
6 *
7 * libCEC(R) is a trademark of Pulse-Eight Limited.
8 *
9 * This program is dual-licensed; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 *
23 *
24 * Alternatively, you can license this library under a commercial license,
25 * please contact Pulse-Eight Licensing for more information.
26 *
27 * For more information contact:
28 * Pulse-Eight Licensing <license@pulse-eight.com>
29 * http://www.pulse-eight.com/
30 * http://www.pulse-eight.net/
31 */
32
7bb4ed43 33#include "USBCECAdapterCommunication.h"
a75e3a5a
LOK
34#include "USBCECAdapterCommands.h"
35#include "USBCECAdapterMessageQueue.h"
ba65909d
LOK
36#include "../platform/sockets/serialport.h"
37#include "../platform/util/timeutils.h"
5477a250 38#include "../LibCEC.h"
7bb4ed43 39#include "../CECProcessor.h"
a8f0bd18
LOK
40
41using namespace std;
42using namespace CEC;
f00ff009 43using namespace PLATFORM;
a8f0bd18 44
ae54110f
LOK
45#define CEC_ADAPTER_PING_TIMEOUT 15000
46
a75e3a5a
LOK
47CUSBCECAdapterCommunication::CUSBCECAdapterCommunication(IAdapterCommunicationCallback *callback, const char *strPort, uint16_t iBaudRate /* = 38400 */) :
48 IAdapterCommunication(callback),
12027dbe 49 m_port(NULL),
1fc16cfd 50 m_iLineTimeout(0),
a75e3a5a 51 m_lastPollDestination(CECDEVICE_UNKNOWN),
56e53c14 52 m_bInitialised(false),
a75e3a5a
LOK
53 m_pingThread(NULL),
54 m_commands(NULL),
55 m_adapterMessageQueue(NULL)
a8f0bd18 56{
4164923b
LOK
57 for (unsigned int iPtr = 0; iPtr < 15; iPtr++)
58 m_bWaitingForAck[iPtr] = false;
089f0e9d 59 m_port = new CSerialPort(strPort, iBaudRate);
a8f0bd18
LOK
60}
61
a75e3a5a 62CUSBCECAdapterCommunication::~CUSBCECAdapterCommunication(void)
a8f0bd18 63{
a75e3a5a
LOK
64 Close();
65 delete m_commands;
66 delete m_adapterMessageQueue;
e4a53f8e 67 delete m_port;
efed01e1 68}
a8f0bd18 69
a75e3a5a 70bool CUSBCECAdapterCommunication::Open(uint32_t iTimeoutMs /* = 10000 */, bool bSkipChecks /* = false */, bool bStartListening /* = true */)
efed01e1 71{
a75e3a5a 72 bool bConnectionOpened(false);
2c780401 73 {
efed01e1
LOK
74 CLockObject lock(m_mutex);
75
a75e3a5a 76 /* we need the port settings here */
efed01e1
LOK
77 if (!m_port)
78 {
79 CLibCEC::AddLog(CEC_LOG_ERROR, "port is NULL");
a75e3a5a 80 return bConnectionOpened;
efed01e1
LOK
81 }
82
a75e3a5a 83 /* return true when the port is already open */
efed01e1
LOK
84 if (IsOpen())
85 {
a75e3a5a 86 CLibCEC::AddLog(CEC_LOG_WARNING, "port is already open");
efed01e1
LOK
87 return true;
88 }
89
a75e3a5a
LOK
90 /* adapter commands */
91 if (!m_commands)
92 m_commands = new CUSBCECAdapterCommands(this);
93
94 if (!m_adapterMessageQueue)
95 m_adapterMessageQueue = new CCECAdapterMessageQueue(this);
96
97 /* try to open the connection */
efed01e1 98 CStdString strError;
a75e3a5a
LOK
99 CTimeout timeout(iTimeoutMs);
100 while (!bConnectionOpened && timeout.TimeLeft() > 0)
efed01e1 101 {
a75e3a5a 102 if ((bConnectionOpened = m_port->Open(timeout.TimeLeft())) == false)
efed01e1
LOK
103 {
104 strError.Format("error opening serial port '%s': %s", m_port->GetName().c_str(), m_port->GetError().c_str());
105 Sleep(250);
efed01e1 106 }
a75e3a5a 107 /* and retry every 250ms until the timeout passed */
efed01e1
LOK
108 }
109
a75e3a5a
LOK
110 /* return false when we couldn't connect */
111 if (!bConnectionOpened)
efed01e1
LOK
112 {
113 CLibCEC::AddLog(CEC_LOG_ERROR, strError);
114 return false;
115 }
116
117 CLibCEC::AddLog(CEC_LOG_DEBUG, "connection opened, clearing any previous input and waiting for active transmissions to end before starting");
a75e3a5a 118 ClearInputBytes();
2c780401 119 }
a8f0bd18 120
a75e3a5a 121 if (!CreateThread())
a8f0bd18 122 {
a75e3a5a
LOK
123 bConnectionOpened = false;
124 CLibCEC::AddLog(CEC_LOG_ERROR, "could not create a communication thread");
125 }
126 else if (!bSkipChecks && !CheckAdapter())
127 {
128 bConnectionOpened = false;
befa3a23 129 CLibCEC::AddLog(CEC_LOG_ERROR, "the adapter failed to pass basic checks");
befa3a23 130 }
f80cd208 131 else if (bStartListening)
befa3a23 132 {
a75e3a5a
LOK
133 /* start a ping thread, that will ping the adapter every 15 seconds
134 if it doesn't receive any ping for 30 seconds, it'll switch to auto mode */
56e53c14 135 m_pingThread = new CAdapterPingThread(this, CEC_ADAPTER_PING_TIMEOUT);
a75e3a5a 136 if (m_pingThread->CreateThread())
efed01e1 137 {
a75e3a5a 138 bConnectionOpened = true;
efed01e1
LOK
139 }
140 else
141 {
a75e3a5a
LOK
142 bConnectionOpened = false;
143 CLibCEC::AddLog(CEC_LOG_ERROR, "could not create a ping thread");
efed01e1 144 }
a8f0bd18 145 }
a75e3a5a
LOK
146
147 if (!bConnectionOpened || !bStartListening)
148 StopThread(0);
a8f0bd18 149
a75e3a5a 150 return bConnectionOpened;
a8f0bd18
LOK
151}
152
7bb4ed43 153void CUSBCECAdapterCommunication::Close(void)
a8f0bd18 154{
0b714871
LOK
155 /* stop the reader thread */
156 StopThread(0);
157
158 CLockObject lock(m_mutex);
159
a75e3a5a 160 /* set the ackmask to 0 before closing the connection */
0b714871 161 if (IsRunning() && m_port->IsOpen() && m_port->GetErrorNumber() == 0)
a75e3a5a 162 {
0b714871 163 CLibCEC::AddLog(CEC_LOG_DEBUG, "%s - closing the connection", __FUNCTION__);
a75e3a5a
LOK
164 SetAckMask(0);
165 if (m_commands->GetFirmwareVersion() >= 2)
166 SetControlledMode(false);
167 }
168
169 /* stop and delete the ping thread */
56e53c14
LOK
170 if (m_pingThread)
171 m_pingThread->StopThread(0);
172 delete m_pingThread;
173 m_pingThread = NULL;
a8f0bd18 174
a75e3a5a 175 /* close and delete the com port connection */
e4a53f8e
LOK
176 if (m_port)
177 m_port->Close();
0b714871
LOK
178
179 libcec_parameter param;
180 CLibCEC::Alert(CEC_ALERT_CONNECTION_LOST, param);
a8f0bd18
LOK
181}
182
33dd87a9 183cec_adapter_message_state CUSBCECAdapterCommunication::Write(const cec_command &data, bool &bRetry, uint8_t iLineTimeout)
7bb4ed43
LOK
184{
185 cec_adapter_message_state retVal(ADAPTER_MESSAGE_STATE_UNKNOWN);
9f68cc28
LOK
186 if (!IsRunning())
187 return retVal;
7bb4ed43 188
33dd87a9 189 CCECAdapterMessage *output = new CCECAdapterMessage(data, iLineTimeout);
7bb4ed43 190
a75e3a5a
LOK
191 /* mark as waiting for an ack from the destination */
192 MarkAsWaiting(data.destination);
4164923b 193
a75e3a5a 194 /* send the message */
33dd87a9 195 bRetry = (!m_adapterMessageQueue->Write(output) || output->NeedsRetry()) && output->transmit_timeout > 0;
0b714871
LOK
196 if (output->state == ADAPTER_MESSAGE_STATE_ERROR)
197 Close();
198 else if (bRetry)
33dd87a9 199 Sleep(CEC_DEFAULT_TRANSMIT_RETRY_WAIT);
7bb4ed43
LOK
200 retVal = output->state;
201
202 delete output;
203 return retVal;
204}
205
a75e3a5a 206void *CUSBCECAdapterCommunication::Process(void)
3c53ac93 207{
a75e3a5a
LOK
208 CCECAdapterMessage msg;
209 CLibCEC::AddLog(CEC_LOG_DEBUG, "communication thread started");
5dcf9f25 210
a75e3a5a 211 while (!IsStopped())
5dcf9f25 212 {
a75e3a5a
LOK
213 /* read from the serial port */
214 if (!ReadFromDevice(50, 5))
215 break;
216
217 /* TODO sleep 5 ms so other threads can get a lock */
218 Sleep(5);
5dcf9f25
LOK
219 }
220
a75e3a5a
LOK
221 m_adapterMessageQueue->Clear();
222 CLibCEC::AddLog(CEC_LOG_DEBUG, "communication thread ended");
223 return NULL;
a8f0bd18
LOK
224}
225
a75e3a5a 226bool CUSBCECAdapterCommunication::HandlePoll(const CCECAdapterMessage &msg)
a8f0bd18 227{
a75e3a5a
LOK
228 bool bIsError(msg.IsError());
229 cec_adapter_messagecode messageCode(msg.Message());
230 CLockObject lock(m_mutex);
9f68cc28 231
a75e3a5a 232 if (messageCode == MSGCODE_FRAME_START && msg.IsACK())
a8f0bd18 233 {
a75e3a5a
LOK
234 m_lastPollDestination = msg.Destination();
235 if (msg.Destination() < CECDEVICE_BROADCAST)
a8f0bd18 236 {
a75e3a5a
LOK
237 if (!m_bWaitingForAck[msg.Destination()] && !msg.IsEOM())
238 {
239 if (m_callback)
240 m_callback->HandlePoll(msg.Initiator(), msg.Destination());
241 }
242 else
243 m_bWaitingForAck[msg.Destination()] = false;
a8f0bd18 244 }
7bb4ed43 245 }
a75e3a5a 246 else if (messageCode == MSGCODE_RECEIVE_FAILED)
7bb4ed43 247 {
a75e3a5a
LOK
248 /* hack to suppress warnings when an LG is polling */
249 if (m_lastPollDestination != CECDEVICE_UNKNOWN)
250 bIsError = m_callback->HandleReceiveFailed(m_lastPollDestination);
7bb4ed43 251 }
a8f0bd18 252
a75e3a5a 253 return bIsError;
a8f0bd18 254}
2abe74eb 255
a75e3a5a 256void CUSBCECAdapterCommunication::MarkAsWaiting(const cec_logical_address dest)
2abe74eb 257{
a75e3a5a
LOK
258 /* mark as waiting for an ack from the destination */
259 if (dest < CECDEVICE_BROADCAST)
7bb4ed43 260 {
a75e3a5a
LOK
261 CLockObject lock(m_mutex);
262 m_bWaitingForAck[dest] = true;
7bb4ed43 263 }
7bb4ed43
LOK
264}
265
a75e3a5a 266void CUSBCECAdapterCommunication::ClearInputBytes(uint32_t iTimeout /* = 1000 */)
1fc16cfd 267{
a75e3a5a
LOK
268 CTimeout timeout(iTimeout);
269 uint8_t buff[1024];
270 ssize_t iBytesRead(0);
a4d657c7 271 bool bGotMsgEnd(true);
1fc16cfd 272
a75e3a5a 273 while (timeout.TimeLeft() > 0 && ((iBytesRead = m_port->Read(buff, 1024, 5)) > 0 || !bGotMsgEnd))
1fc16cfd 274 {
a4d657c7 275 bGotMsgEnd = false;
a75e3a5a
LOK
276 /* if something was received, wait for MSGEND */
277 for (ssize_t iPtr = 0; iPtr < iBytesRead; iPtr++)
278 bGotMsgEnd = buff[iPtr] == MSGEND;
1fc16cfd 279 }
1fc16cfd
LOK
280}
281
7bb4ed43 282bool CUSBCECAdapterCommunication::SetLineTimeout(uint8_t iTimeout)
a171d2fd 283{
16459df9 284 bool bReturn(true);
a75e3a5a 285 bool bChanged(false);
089f0e9d 286
a75e3a5a 287 /* only send the command if the timeout changed */
089f0e9d 288 {
a75e3a5a
LOK
289 CLockObject lock(m_mutex);
290 bChanged = (m_iLineTimeout != iTimeout);
291 m_iLineTimeout = iTimeout;
089f0e9d
LOK
292 }
293
a75e3a5a
LOK
294 if (bChanged)
295 bReturn = m_commands->SetLineTimeout(iTimeout);
a171d2fd 296
a75e3a5a 297 return bReturn;
f9e01dac
LOK
298}
299
a75e3a5a 300bool CUSBCECAdapterCommunication::WriteToDevice(CCECAdapterMessage *message)
5dcf9f25 301{
a75e3a5a
LOK
302 CLockObject adapterLock(m_mutex);
303 if (!m_port->IsOpen())
304 {
0b714871 305 CLibCEC::AddLog(CEC_LOG_DEBUG, "error writing command '%s' to serial port '%s': the connection is closed", CCECAdapterMessage::ToString(message->Message()), m_port->GetName().c_str());
a75e3a5a
LOK
306 message->state = ADAPTER_MESSAGE_STATE_ERROR;
307 return false;
308 }
5dcf9f25 309
a75e3a5a
LOK
310 /* write the message */
311 if (m_port->Write(message->packet.data, message->Size()) != (ssize_t) message->Size())
312 {
0b714871 313 CLibCEC::AddLog(CEC_LOG_DEBUG, "error writing command '%s' to serial port '%s': %s", CCECAdapterMessage::ToString(message->Message()), m_port->GetName().c_str(), m_port->GetError().c_str());
a75e3a5a 314 message->state = ADAPTER_MESSAGE_STATE_ERROR;
cb4ee028 315 return false;
a75e3a5a 316 }
cb4ee028 317
a75e3a5a
LOK
318 CLibCEC::AddLog(CEC_LOG_DEBUG, "command '%s' sent", message->IsTranmission() ? "CEC transmission" : CCECAdapterMessage::ToString(message->Message()));
319 message->state = ADAPTER_MESSAGE_STATE_SENT;
320 return true;
c214d197 321}
b057edad 322
a75e3a5a 323bool CUSBCECAdapterCommunication::ReadFromDevice(uint32_t iTimeout, size_t iSize /* = 256 */)
12a36be9 324{
a75e3a5a
LOK
325 ssize_t iBytesRead(0);
326 uint8_t buff[256];
327 if (iSize > 256)
328 iSize = 256;
12a36be9 329
a75e3a5a 330 /* read from the serial port */
12a36be9 331 {
a75e3a5a 332 CLockObject lock(m_mutex);
0b714871 333 if (!m_port || !m_port->IsOpen())
a75e3a5a
LOK
334 return false;
335 iBytesRead = m_port->Read(buff, sizeof(uint8_t) * iSize, iTimeout);
12a36be9
LOK
336 }
337
a75e3a5a 338 if (iBytesRead < 0 || iBytesRead > 256)
12a36be9 339 {
a75e3a5a 340 CLibCEC::AddLog(CEC_LOG_ERROR, "error reading from serial port: %s", m_port->GetError().c_str());
0b714871 341 Close();
a75e3a5a 342 return false;
12a36be9 343 }
a75e3a5a 344 else if (iBytesRead > 0)
12a36be9 345 {
a75e3a5a
LOK
346 /* add the data to the current frame */
347 m_adapterMessageQueue->AddData(buff, iBytesRead);
12a36be9
LOK
348 }
349
a75e3a5a 350 return true;
b057edad
BL
351}
352
a75e3a5a 353CCECAdapterMessage *CUSBCECAdapterCommunication::SendCommand(cec_adapter_messagecode msgCode, CCECAdapterMessage &params, bool bIsRetry /* = false */)
c214d197 354{
a75e3a5a
LOK
355 if (!m_port || !m_port->IsOpen() ||
356 !m_adapterMessageQueue)
357 return NULL;
c214d197 358
a75e3a5a
LOK
359 /* create the adapter message for this command */
360 CCECAdapterMessage *output = new CCECAdapterMessage;
361 output->PushBack(MSGSTART);
362 output->PushEscaped((uint8_t)msgCode);
363 output->Append(params);
364 output->PushBack(MSGEND);
12a36be9 365
a75e3a5a
LOK
366 /* write the command */
367 if (!m_adapterMessageQueue->Write(output))
12a36be9 368 {
0b714871
LOK
369 if (output->state == ADAPTER_MESSAGE_STATE_ERROR)
370 Close();
a75e3a5a 371 return output;
12a36be9 372 }
a75e3a5a 373 else
12a36be9 374 {
a75e3a5a
LOK
375 if (!bIsRetry && output->Reply() == MSGCODE_COMMAND_REJECTED && msgCode != MSGCODE_SET_CONTROLLED)
376 {
377 /* if the controller reported that the command was rejected, and we didn't send the command
378 to set controlled mode, then the controller probably switched to auto mode. set controlled
379 mode and retry */
380 CLibCEC::AddLog(CEC_LOG_DEBUG, "setting controlled mode and retrying");
381 delete output;
382 if (SetControlledMode(true))
383 return SendCommand(msgCode, params, true);
384 }
12a36be9 385 }
12a36be9 386
a75e3a5a 387 return output;
c214d197
LOK
388}
389
a75e3a5a 390bool CUSBCECAdapterCommunication::CheckAdapter(uint32_t iTimeoutMs /* = 10000 */)
12a36be9 391{
a75e3a5a
LOK
392 bool bReturn(false);
393 CTimeout timeout(iTimeoutMs > 0 ? iTimeoutMs : CEC_DEFAULT_TRANSMIT_WAIT);
12a36be9 394
a75e3a5a
LOK
395 /* try to ping the adapter */
396 bool bPinged(false);
397 unsigned iPingTry(0);
398 while (timeout.TimeLeft() > 0 && (bPinged = PingAdapter()) == false)
12a36be9 399 {
a75e3a5a
LOK
400 CLibCEC::AddLog(CEC_LOG_ERROR, "the adapter did not respond correctly to a ping (try %d)", ++iPingTry);
401 CEvent::Sleep(500);
12a36be9 402 }
c214d197 403
a75e3a5a
LOK
404 /* try to read the firmware version */
405 if (bPinged && timeout.TimeLeft() > 0 && m_commands->RequestFirmwareVersion() >= 2)
12a36be9 406 {
a75e3a5a
LOK
407 /* try to set controlled mode for v2+ firmwares */
408 unsigned iControlledTry(0);
409 bool bControlled(false);
410 while (timeout.TimeLeft() > 0 && (bControlled = SetControlledMode(true)) == false)
411 {
412 CLibCEC::AddLog(CEC_LOG_ERROR, "the adapter did not respond correctly to setting controlled mode (try %d)", ++iControlledTry);
413 CEvent::Sleep(500);
414 }
415 bReturn = bControlled;
12a36be9 416 }
a75e3a5a
LOK
417 else
418 bReturn = true;
c214d197 419
a75e3a5a
LOK
420 SetInitialised(bReturn);
421 return bReturn;
c214d197
LOK
422}
423
a75e3a5a 424bool CUSBCECAdapterCommunication::IsOpen(void)
12a36be9 425{
a75e3a5a
LOK
426 /* thread is not being stopped, the port is open and the thread is running */
427 return !IsStopped() && m_port->IsOpen() && IsRunning();
12a36be9
LOK
428}
429
a75e3a5a 430CStdString CUSBCECAdapterCommunication::GetError(void) const
c214d197 431{
a75e3a5a 432 return m_port->GetError();
c214d197
LOK
433}
434
a75e3a5a 435void CUSBCECAdapterCommunication::SetInitialised(bool bSetTo /* = true */)
12a36be9
LOK
436{
437 CLockObject lock(m_mutex);
a75e3a5a 438 m_bInitialised = bSetTo;
12a36be9
LOK
439}
440
a75e3a5a 441bool CUSBCECAdapterCommunication::IsInitialised(void)
c214d197
LOK
442{
443 CLockObject lock(m_mutex);
a75e3a5a 444 return m_bInitialised;
c214d197
LOK
445}
446
a75e3a5a 447bool CUSBCECAdapterCommunication::StartBootloader(void)
12a36be9 448{
0b714871 449 return m_port->IsOpen() ? m_commands->StartBootloader() : false;
12a36be9
LOK
450}
451
a75e3a5a 452bool CUSBCECAdapterCommunication::SetAckMask(uint16_t iMask)
13fd6a66 453{
0b714871 454 return m_port->IsOpen() ? m_commands->SetAckMask(iMask) : false;
13fd6a66 455}
ef7696f5 456
a75e3a5a 457bool CUSBCECAdapterCommunication::PingAdapter(void)
6729ac71 458{
0b714871 459 return m_port->IsOpen() ? m_commands->PingAdapter() : false;
6729ac71
LOK
460}
461
a75e3a5a 462uint16_t CUSBCECAdapterCommunication::GetFirmwareVersion(void)
ef7696f5 463{
a75e3a5a 464 return m_commands->GetFirmwareVersion();
ef7696f5
LOK
465}
466
a75e3a5a 467bool CUSBCECAdapterCommunication::PersistConfiguration(libcec_configuration *configuration)
ef7696f5 468{
0b714871 469 return m_port->IsOpen() ? m_commands->PersistConfiguration(configuration) : false;
ef7696f5
LOK
470}
471
a75e3a5a 472bool CUSBCECAdapterCommunication::GetConfiguration(libcec_configuration *configuration)
ef7696f5 473{
0b714871 474 return m_port->IsOpen() ? m_commands->GetConfiguration(configuration) : false;
ef7696f5
LOK
475}
476
cba904a6
LOK
477CStdString CUSBCECAdapterCommunication::GetPortName(void)
478{
a75e3a5a 479 return m_port->GetName();
c9c282a4 480}
d4db0c6f 481
a75e3a5a 482bool CUSBCECAdapterCommunication::SetControlledMode(bool controlled)
d4db0c6f 483{
0b714871 484 return m_port->IsOpen() ? m_commands->SetControlledMode(controlled) : false;
d4db0c6f 485}
56e53c14
LOK
486
487void *CAdapterPingThread::Process(void)
488{
489 while (!IsStopped())
490 {
491 if (m_timeout.TimeLeft() == 0)
492 {
a75e3a5a 493 /* reinit the timeout */
56e53c14 494 m_timeout.Init(CEC_ADAPTER_PING_TIMEOUT);
a75e3a5a
LOK
495
496 /* send a ping to the adapter */
497 bool bPinged(false);
498 int iFailedCounter(0);
499 while (!bPinged && iFailedCounter < 3)
500 {
501 if (!m_com->PingAdapter())
502 {
503 /* sleep 1 second and retry */
504 Sleep(1000);
505 ++iFailedCounter;
506 }
507 else
508 {
509 bPinged = true;
510 }
511 }
512
513 if (iFailedCounter == 3)
514 {
515 /* failed to ping the adapter 3 times in a row. something must be wrong with the connection */
516 CLibCEC::AddLog(CEC_LOG_ERROR, "failed to ping the adapter 3 times in a row. closing the connection.");
517 m_com->StopThread(false);
518 break;
519 }
56e53c14
LOK
520 }
521
522 Sleep(500);
523 }
524 return NULL;
525}