cec: don't wait 1 second when clearing input when there is no input
[deb_libcec.git] / src / lib / adapter / USBCECAdapterMessageQueue.cpp
CommitLineData
a75e3a5a
LOK
1/*
2 * This file is part of the libCEC(R) library.
3 *
4 * libCEC(R) is Copyright (C) 2011-2012 Pulse-Eight Limited. All rights reserved.
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
33#include "USBCECAdapterMessageQueue.h"
34#include "USBCECAdapterCommunication.h"
35#include "../platform/sockets/socket.h"
36#include "../LibCEC.h"
37
38using namespace CEC;
39using namespace PLATFORM;
40
41CCECAdapterMessageQueueEntry::CCECAdapterMessageQueueEntry(CCECAdapterMessage *message) :
42 m_message(message),
43 m_iPacketsLeft(message->IsTranmission() ? message->Size() / 4 : 1),
44 m_bSucceeded(false),
45 m_bWaiting(true) {}
46
47CCECAdapterMessageQueueEntry::~CCECAdapterMessageQueueEntry(void) { }
48
49void CCECAdapterMessageQueueEntry::Broadcast(void)
50{
51 CLockObject lock(m_mutex);
52 m_condition.Broadcast();
53}
54
55bool CCECAdapterMessageQueueEntry::MessageReceived(const CCECAdapterMessage &message)
56{
57 bool bSendSignal(false);
58 bool bHandled(false);
59
60 PLATFORM::CLockObject lock(m_mutex);
61 if (!IsResponse(message))
62 {
63 /* we received a message from the adapter that's not a response to this command */
64 if (!message.IsTranmission())
65 {
66 /* we received something that's not a transmission while waiting for an ack to this command, so this command failed */
67
68 //TODO verify whether we're not failing too soon
69 CLibCEC::AddLog(CEC_LOG_DEBUG, "%s - %s - not a response %s - failed", __FUNCTION__, ToString(), CCECAdapterMessage::ToString(message.Message()));
70 m_message->state = ADAPTER_MESSAGE_STATE_SENT_NOT_ACKED;
71 bSendSignal = true;
72 }
73 }
74 else
75 {
76 /* we received a response, so this message is handled */
77 bHandled = true;
78 switch (message.Message())
79 {
80 case MSGCODE_COMMAND_ACCEPTED:
81 bSendSignal = MessageReceivedCommandAccepted(message);
82 break;
83 case MSGCODE_TRANSMIT_SUCCEEDED:
84 bSendSignal = MessageReceivedTransmitSucceeded(message);
85 break;
86 default:
87 bSendSignal = MessageReceivedResponse(message);
88 break;
89 }
90 }
91
92 /* signal the waiting thread when we're done */
93 if (bSendSignal)
94 {
95 m_bSucceeded = true;
96 m_condition.Signal();
97 }
98
99 return bHandled;
100}
101
102bool CCECAdapterMessageQueueEntry::Wait(uint32_t iTimeout)
103{
104 bool bReturn(false);
105 /* wait until we receive a signal when the tranmission succeeded */
106 {
107 CLockObject lock(m_mutex);
108 bReturn = m_bSucceeded ? true : m_condition.Wait(m_mutex, m_bSucceeded, iTimeout);
109 m_bWaiting = false;
110 }
111 return bReturn;
112}
113
114bool CCECAdapterMessageQueueEntry::IsWaiting(void)
115{
116 CLockObject lock(m_mutex);
117 return m_bWaiting;
118}
119
120cec_adapter_messagecode CCECAdapterMessageQueueEntry::MessageCode(void)
121{
122 return m_message->Message();
123}
124
125bool CCECAdapterMessageQueueEntry::IsResponse(const CCECAdapterMessage &msg)
126{
127 cec_adapter_messagecode msgCode = msg.Message();
128 return msgCode == MessageCode() ||
129 msgCode == MSGCODE_TIMEOUT_ERROR ||
130 msgCode == MSGCODE_COMMAND_ACCEPTED ||
131 msgCode == MSGCODE_COMMAND_REJECTED ||
132 (m_message->IsTranmission() && msgCode == MSGCODE_HIGH_ERROR) ||
133 (m_message->IsTranmission() && msgCode == MSGCODE_LOW_ERROR) ||
134 (m_message->IsTranmission() && msgCode == MSGCODE_RECEIVE_FAILED) ||
135 (m_message->IsTranmission() && msgCode == MSGCODE_TRANSMIT_FAILED_LINE) ||
136 (m_message->IsTranmission() && msgCode == MSGCODE_TRANSMIT_FAILED_ACK) ||
137 (m_message->IsTranmission() && msgCode == MSGCODE_TRANSMIT_FAILED_TIMEOUT_DATA) ||
138 (m_message->IsTranmission() && msgCode == MSGCODE_TRANSMIT_FAILED_TIMEOUT_LINE) ||
139 (m_message->IsTranmission() && msgCode == MSGCODE_TRANSMIT_SUCCEEDED);
140}
141
142const char *CCECAdapterMessageQueueEntry::ToString(void) const
143{
144 /* CEC transmissions got the 'set ack polarity' msgcode, which doesn't look nice */
145 if (m_message->IsTranmission())
146 return "CEC transmission";
147 else
148 return CCECAdapterMessage::ToString(m_message->Message());
149}
150
151bool CCECAdapterMessageQueueEntry::MessageReceivedCommandAccepted(const CCECAdapterMessage &message)
152{
153 bool bSendSignal(false);
154 if (m_iPacketsLeft == 0)
155 {
156 /* we received a "command accepted", but we're not waiting for one anymore */
157 CLibCEC::AddLog(CEC_LOG_ERROR, "%s - received unexpected 'command accepted' message", ToString());
158 m_message->state = ADAPTER_MESSAGE_STATE_ERROR;
159 bSendSignal = true;
160 }
161 else
162 {
163 /* decrease number of acks we're waiting on by 1 */
164 if (m_iPacketsLeft > 0)
165 m_iPacketsLeft--;
166
167 /* log this message */
168 CStdString strLog;
169 strLog.Format("%s - command accepted", ToString());
170 if (m_iPacketsLeft > 0)
171 strLog.AppendFormat(" - waiting for %d more", m_iPacketsLeft);
172 CLibCEC::AddLog(CEC_LOG_DEBUG, strLog);
173
174 /* no more packets left and not a transmission, so we're done */
175 if (!m_message->IsTranmission() && m_iPacketsLeft == 0)
176 {
177 m_message->state = ADAPTER_MESSAGE_STATE_SENT_ACKED;
178 m_message->response = message.packet;
179 bSendSignal = true;
180 }
181 }
182 return bSendSignal;
183}
184
185bool CCECAdapterMessageQueueEntry::MessageReceivedTransmitSucceeded(const CCECAdapterMessage &message)
186{
187 if (m_iPacketsLeft == 0)
188 {
189 /* transmission succeeded, so we're done */
190 CLibCEC::AddLog(CEC_LOG_DEBUG, "%s - transmit succeeded", ToString());
191 m_message->state = ADAPTER_MESSAGE_STATE_SENT_ACKED;
192 m_message->response = message.packet;
193 }
194 else
195 {
196 /* error, we expected more acks */
197 CLibCEC::AddLog(CEC_LOG_WARNING, "%s - received 'transmit succeeded' but not enough 'command accepted' messages (%d left)", ToString(), m_iPacketsLeft);
198 m_message->state = ADAPTER_MESSAGE_STATE_ERROR;
199 }
200 return true;
201}
202
203bool CCECAdapterMessageQueueEntry::MessageReceivedResponse(const CCECAdapterMessage &message)
204{
205 CLibCEC::AddLog(CEC_LOG_DEBUG, "%s - received response", ToString());
206 m_message->response = message.packet;
207 if (m_message->IsTranmission())
208 m_message->state = message.Message() == MSGCODE_TRANSMIT_SUCCEEDED ? ADAPTER_MESSAGE_STATE_SENT_ACKED : ADAPTER_MESSAGE_STATE_SENT_NOT_ACKED;
209 else
210 m_message->state = ADAPTER_MESSAGE_STATE_SENT_ACKED;
211 return true;
212}
213
214
215CCECAdapterMessageQueue::~CCECAdapterMessageQueue(void)
216{
217 Clear();
218}
219
220void CCECAdapterMessageQueue::Clear(void)
221{
222 CLockObject lock(m_mutex);
223 CCECAdapterMessageQueueEntry *message(NULL);
224 while (m_messages.Pop(message))
225 message->Broadcast();
226}
227
228void CCECAdapterMessageQueue::MessageReceived(const CCECAdapterMessage &msg)
229{
230 CLockObject lock(m_mutex);
231 CCECAdapterMessageQueueEntry *message = GetNextQueuedEntry();
232
233 /* send the received message to the first entry in the queue */
234 bool bHandled = message ? message->MessageReceived(msg) : false;
235
236 if (!message || !bHandled)
237 {
238 /* the message wasn't handled */
239 bool bIsError(m_com->HandlePoll(msg));
240 CLibCEC::AddLog(bIsError ? CEC_LOG_WARNING : CEC_LOG_DEBUG, msg.ToString());
241
242 /* push this message to the current frame */
243 if (!bIsError && msg.PushToCecCommand(m_currentCECFrame))
244 {
245 /* and push the current frame back over the callback method when a full command was received */
246 if (m_com->IsInitialised())
247 m_com->m_callback->OnCommandReceived(m_currentCECFrame);
248
249 /* clear the current frame */
250 m_currentCECFrame.Clear();
251 }
252 }
253}
254
255void CCECAdapterMessageQueue::AddData(uint8_t *data, size_t iLen)
256{
257 for (size_t iPtr = 0; iPtr < iLen; iPtr++)
258 {
259 bool bFullMessage(false);
260 {
261 CLockObject lock(m_mutex);
262 bFullMessage = m_incomingAdapterMessage.PushReceivedByte(data[iPtr]);
263 }
264
265 if (bFullMessage)
266 {
267 /* a full message was received */
268 CCECAdapterMessage newMessage;
269 newMessage.packet = m_incomingAdapterMessage.packet;
270 MessageReceived(newMessage);
271
272 /* clear the current message */
273 CLockObject lock(m_mutex);
274 m_incomingAdapterMessage.Clear();
275 }
276 }
277}
278
279bool CCECAdapterMessageQueue::Write(CCECAdapterMessage *msg)
280{
281 msg->state = ADAPTER_MESSAGE_STATE_WAITING_TO_BE_SENT;
282
283 /* set the correct line timeout */
284 if (msg->IsTranmission())
285 {
286 if (msg->tries == 1)
287 m_com->SetLineTimeout(msg->lineTimeout);
288 else
289 m_com->SetLineTimeout(msg->retryTimeout);
290 }
291
292 CCECAdapterMessageQueueEntry *entry(NULL);
293 /* add to the wait for ack queue */
294 if (msg->Message() != MSGCODE_START_BOOTLOADER)
295 {
296 entry = new CCECAdapterMessageQueueEntry(msg);
297 PLATFORM::CLockObject lock(m_mutex);
298 m_messages.Push(entry);
299 }
300
301 /* TODO write the message async */
302 if (!m_com->WriteToDevice(msg))
303 {
304 /* error! */
305 Clear();
306 return false;
307 }
308
309 if (entry && !entry->Wait(msg->transmit_timeout <= 5 ? CEC_DEFAULT_TRANSMIT_WAIT : msg->transmit_timeout))
310 {
311 CLibCEC::AddLog(CEC_LOG_DEBUG, "command '%s' was not acked by the controller", CCECAdapterMessage::ToString(msg->Message()));
312 msg->state = ADAPTER_MESSAGE_STATE_SENT_NOT_ACKED;
313 return false;
314 }
315
316 return true;
317}
318
319CCECAdapterMessageQueueEntry *CCECAdapterMessageQueue::GetNextQueuedEntry(void)
320{
321 CCECAdapterMessageQueueEntry *message(NULL);
322 while (message == NULL && m_messages.Peek(message))
323 {
324 if (!message->IsWaiting())
325 {
326 /* delete old messages */
327 m_messages.Pop(message);
328 delete message;
329 message = NULL;
330 }
331 }
332 return message;
333}