Index: tools/battor_agent/battor_connection.cc |
diff --git a/tools/battor_agent/battor_connection.cc b/tools/battor_agent/battor_connection.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..5d04689182a59e57f94c2618aea4510c9e7f6b34 |
--- /dev/null |
+++ b/tools/battor_agent/battor_connection.cc |
@@ -0,0 +1,283 @@ |
+// Copyright 2015 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "tools/battor_agent/battor_connection.h" |
+ |
+#include "base/bind.h" |
+#include "base/callback.h" |
+#include "device/serial/buffer.h" |
+#include "device/serial/serial_io_handler.h" |
+#include "net/base/io_buffer.h" |
+ |
+using std::vector; |
+ |
+namespace battor { |
+ |
+namespace { |
+ |
+// Serial configuration parameters for the BattOr. |
+const uint32_t kBattOrBitrate = 2000000; |
+const device::serial::DataBits kBattOrDataBits = |
+ device::serial::DATA_BITS_EIGHT; |
+const device::serial::ParityBit kBattOrParityBit = |
+ device::serial::PARITY_BIT_NONE; |
+const device::serial::StopBits kBattOrStopBit = device::serial::STOP_BITS_ONE; |
+const bool kBattOrCtsFlowControl = true; |
+const bool kBattOrHasCtsFlowControl = true; |
+const uint32_t kMaxMessageSize = 50000; |
+ |
+// MessageHealth describes the possible healthiness states that a partially |
+// received message could be in. |
+enum class MessageHealth { |
+ INVALID, |
+ INCOMPLETE, |
+ COMPLETE, |
+}; |
+ |
+// Parses the specified message. |
+// - message: The incoming message that needs to be parsed. |
+// - parsed_content: Output argument for the message content after removal of |
+// any start, end, type, and escape bytes. |
+// - health: Output argument for the health of the message. |
+// - type: Output argument for the type of message being parsed. |
+// - escape_byte_count: Output argument for the number of escape bytes |
+// removed from the parsed content. |
+void ParseMessage(const vector<char>& message, |
+ vector<char>* parsed_content, |
+ MessageHealth* health, |
+ BattOrMessageType* type, |
+ size_t* escape_byte_count) { |
+ *health = MessageHealth::INCOMPLETE; |
+ *type = BATTOR_MESSAGE_TYPE_CONTROL; |
+ *escape_byte_count = 0; |
+ parsed_content->reserve(message.size()); |
+ |
+ if (message.size() == 0) |
+ return; |
+ |
+ // The first byte is the start byte. |
+ if (message[0] != BATTOR_CONTROL_BYTE_START) { |
+ *health = MessageHealth::INVALID; |
+ return; |
+ } |
+ |
+ if (message.size() == 1) |
+ return; |
+ |
+ // The second byte specifies the message type. |
+ *type = static_cast<BattOrMessageType>(message[1]); |
+ |
+ if (*type < static_cast<uint8_t>(BATTOR_MESSAGE_TYPE_CONTROL) || |
+ *type > static_cast<uint8_t>(BATTOR_MESSAGE_TYPE_PRINT)) { |
+ *health = MessageHealth::INVALID; |
+ return; |
+ } |
+ |
+ // After that comes the message data. |
+ bool escape_next_byte = false; |
+ for (size_t i = 2; i < message.size(); i++) { |
+ if (i >= kMaxMessageSize) { |
+ *health = MessageHealth::INVALID; |
+ return; |
+ } |
+ |
+ char next_byte = message[i]; |
+ |
+ if (escape_next_byte) { |
+ parsed_content->push_back(next_byte); |
+ escape_next_byte = false; |
+ continue; |
+ } |
+ |
+ switch (next_byte) { |
+ case BATTOR_CONTROL_BYTE_START: |
+ // Two start bytes in a message is invalid. |
+ *health = MessageHealth::INVALID; |
+ return; |
+ |
+ case BATTOR_CONTROL_BYTE_END: |
+ if (i != message.size() - 1) { |
+ // We're only parsing a single message here. If we received more bytes |
+ // after the end byte, what we've received so far is *not* valid. |
+ *health = MessageHealth::INVALID; |
+ return; |
+ } |
+ |
+ *health = MessageHealth::COMPLETE; |
+ return; |
+ |
+ case BATTOR_CONTROL_BYTE_ESCAPE: |
+ escape_next_byte = true; |
+ (*escape_byte_count)++; |
+ continue; |
+ |
+ default: |
+ parsed_content->push_back(next_byte); |
+ } |
+ } |
+} |
+ |
+} // namespace |
+ |
+BattOrConnection::BattOrConnection( |
+ const std::string& path, |
+ Listener* listener, |
+ scoped_refptr<base::SingleThreadTaskRunner> file_thread_task_runner, |
+ scoped_refptr<base::SingleThreadTaskRunner> ui_thread_task_runner) |
+ : path_(path), |
+ listener_(listener), |
+ file_thread_task_runner_(file_thread_task_runner), |
+ ui_thread_task_runner_(ui_thread_task_runner) {} |
+ |
+BattOrConnection::~BattOrConnection() {} |
+ |
+void BattOrConnection::Open() { |
+ io_handler_ = CreateIoHandler(); |
+ |
+ device::serial::ConnectionOptions options; |
+ options.bitrate = kBattOrBitrate; |
+ options.data_bits = kBattOrDataBits; |
+ options.parity_bit = kBattOrParityBit; |
+ options.stop_bits = kBattOrStopBit; |
+ options.cts_flow_control = kBattOrCtsFlowControl; |
+ options.has_cts_flow_control = kBattOrHasCtsFlowControl; |
+ |
+ io_handler_->Open(path_, options, |
+ base::Bind(&BattOrConnection::OnOpened, AsWeakPtr())); |
+} |
+ |
+void BattOrConnection::OnOpened(bool success) { |
+ if (!success) |
+ Close(); |
+ |
+ listener_->OnConnectionOpened(success); |
+} |
+ |
+bool BattOrConnection::IsOpen() { |
+ return io_handler_; |
+} |
+ |
+void BattOrConnection::Close() { |
+ io_handler_ = nullptr; |
+} |
+ |
+void BattOrConnection::SendBytes(BattOrMessageType type, |
+ const void* buffer, |
+ size_t bytes_to_send) { |
+ const char* bytes = reinterpret_cast<const char*>(buffer); |
+ |
+ // Reserve a send buffer with 3 extra bytes (start, type, and end byte) and |
+ // twice as many bytes as we're actually sending, because each raw data byte |
+ // might need to be escaped. |
+ vector<char> data; |
+ data.reserve(2 * bytes_to_send + 3); |
+ |
+ data.push_back(BATTOR_CONTROL_BYTE_START); |
+ data.push_back(type); |
+ |
+ for (size_t i = 0; i < bytes_to_send; i++) { |
+ if (bytes[i] == BATTOR_CONTROL_BYTE_START || |
+ bytes[i] == BATTOR_CONTROL_BYTE_END) { |
+ data.push_back(BATTOR_CONTROL_BYTE_ESCAPE); |
+ } |
+ |
+ data.push_back(bytes[i]); |
+ } |
+ |
+ data.push_back(BATTOR_CONTROL_BYTE_END); |
+ |
+ pending_write_length_ = data.size(); |
+ io_handler_->Write(make_scoped_ptr(new device::SendBuffer( |
+ data, base::Bind(&BattOrConnection::OnBytesSent, AsWeakPtr())))); |
+} |
+ |
+void BattOrConnection::ReadBytes(size_t bytes_to_read) { |
+ // Allocate a read buffer and reserve enough space in it to account for the |
+ // start, type, end, and escape bytes. |
+ pending_read_buffer_.reset(new vector<char>()); |
+ pending_read_buffer_->reserve(2 * bytes_to_read + 3); |
+ pending_read_escape_byte_count_ = 0; |
+ |
+ // Add 3 bytes to however many bytes the caller requested because we know |
+ // we'll have to read the start, type, and end bytes. |
+ bytes_to_read += 3; |
+ |
+ ReadMoreBytes(bytes_to_read); |
+} |
+ |
+void BattOrConnection::Flush() { |
+ io_handler_->Flush(); |
+} |
+ |
+scoped_refptr<device::SerialIoHandler> BattOrConnection::CreateIoHandler() { |
+ return device::SerialIoHandler::Create(file_thread_task_runner_, |
+ ui_thread_task_runner_); |
+} |
+ |
+void BattOrConnection::ReadMoreBytes(size_t bytes_to_read) { |
+ last_read_buffer_ = make_scoped_refptr(new net::IOBuffer(bytes_to_read)); |
+ auto on_receive_buffer_filled = |
+ base::Bind(&BattOrConnection::OnBytesRead, AsWeakPtr()); |
+ |
+ pending_read_length_ = bytes_to_read; |
+ io_handler_->Read(make_scoped_ptr(new device::ReceiveBuffer( |
+ last_read_buffer_, bytes_to_read, on_receive_buffer_filled))); |
+} |
+ |
+void BattOrConnection::OnBytesRead(int bytes_read, |
+ device::serial::ReceiveError error) { |
+ if ((static_cast<size_t>(bytes_read) < pending_read_length_) || |
+ (error != device::serial::RECEIVE_ERROR_NONE)) { |
+ listener_->OnBytesRead(false, BATTOR_MESSAGE_TYPE_CONTROL, nullptr); |
+ return; |
+ } |
+ |
+ pending_read_buffer_->insert(pending_read_buffer_->end(), |
+ last_read_buffer_->data(), |
+ last_read_buffer_->data() + bytes_read); |
+ |
+ scoped_ptr<vector<char>> parsed_content(new vector<char>()); |
+ MessageHealth health; |
+ BattOrMessageType type; |
+ size_t escape_byte_count; |
+ |
+ ParseMessage(*pending_read_buffer_, parsed_content.get(), &health, &type, |
+ &escape_byte_count); |
+ |
+ if (health == MessageHealth::INVALID) { |
+ // If we already have an invalid message, there's no sense in continuing to |
+ // process it. |
+ listener_->OnBytesRead(false, BATTOR_MESSAGE_TYPE_CONTROL, nullptr); |
+ return; |
+ } |
+ |
+ size_t new_escape_bytes = escape_byte_count - pending_read_escape_byte_count_; |
+ pending_read_escape_byte_count_ = escape_byte_count; |
+ |
+ if (new_escape_bytes > 0) { |
+ // When the caller requested that we read X additional bytes, they weren't |
+ // taking into account any escape bytes that we received. Because we got |
+ // some escape bytes, we need to fire off another read to get the rest of |
+ // the data. |
+ ReadMoreBytes(new_escape_bytes); |
+ return; |
+ } |
+ |
+ if (health == MessageHealth::INCOMPLETE) |
+ // If everything is valid and we didn't see any escape bytes, then we should |
+ // have the whole message. If we don't, the message was malformed. |
+ listener_->OnBytesRead(false, BATTOR_MESSAGE_TYPE_CONTROL, nullptr); |
+ |
+ // If we've gotten this far, we've received the whole, well-formed message. |
+ listener_->OnBytesRead(true, type, parsed_content.Pass()); |
+} |
+ |
+void BattOrConnection::OnBytesSent(int bytes_sent, |
+ device::serial::SendError error) { |
+ bool success = (error == device::serial::SEND_ERROR_NONE) && |
+ (pending_write_length_ == static_cast<size_t>(bytes_sent)); |
+ listener_->OnBytesSent(success); |
+} |
+ |
+} // namespace battor |