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..4197db84279371fc489923fa3163e18a456ae630 |
--- /dev/null |
+++ b/tools/battor_agent/battor_connection.cc |
@@ -0,0 +1,276 @@ |
+// 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 kBattOrBitrate = 2000000; |
Primiano Tucci (use gerrit)
2015/12/15 11:07:20
nit s/uint32/uint32_t/ :the non stdint versions ar
charliea (OOO until 10-5)
2015/12/15 23:50:04
Done.
|
+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 kMaxMessageSize = 50000; |
Primiano Tucci (use gerrit)
2015/12/15 11:07:20
ditto uint32_t
charliea (OOO until 10-5)
2015/12/15 23:50:04
Done.
|
+ |
+// Parses the specified message, extracting: |
+// |
+// - The message content (the bytes after the start byte and message type but |
+// before the end byte) |
+// - Whether the message is complete |
+// - Whether the message is valid so far |
+// - The type of the message |
+// - The number of escape bytes contained in the message |
Zhen Wang
2015/12/14 23:39:46
Can you make the argument match the explanation? F
charliea (OOO until 10-5)
2015/12/15 23:50:04
Done. I also consolidated is_complete and is_inval
|
+void ParseMessage(const vector<char>* message, |
Primiano Tucci (use gerrit)
2015/12/15 11:07:20
ordering of arguments:
input arguments should come
charliea (OOO until 10-5)
2015/12/15 23:50:04
Done.
|
+ vector<char>* message_content, |
+ bool* is_complete, |
+ bool* is_valid_so_far, |
+ BattOrMessageType* type, |
+ uint16_t* escape_byte_count) { |
+ *is_complete = false; |
+ *is_valid_so_far = true; |
+ *type = BATTOR_MESSAGE_TYPE_CONTROL; |
+ *escape_byte_count = 0; |
+ message_content->reserve(message->size()); |
+ |
+ if (message->size() == 0) |
+ return; |
+ |
+ // The first byte is the start byte. |
+ if ((*message)[0] != BATTOR_SPECIAL_BYTE_START) { |
+ *is_valid_so_far = false; |
+ return; |
+ } |
+ |
+ if (message->size() == 1) |
+ return; |
+ |
+ // The second byte specifies the message type. |
+ *type = static_cast<BattOrMessageType>((*message)[1]); |
Primiano Tucci (use gerrit)
2015/12/15 11:07:19
shouldn't you CHECK that *type isn't > MESSAGE_TYP
charliea (OOO until 10-5)
2015/12/15 23:50:04
Done. I didn't CHECK though, because it's possible
|
+ |
+ // After that comes the message data. |
+ bool escape_next_byte = false; |
+ for (size_t i = 2; i < message->size(); i++) { |
+ if (i >= kMaxMessageSize) { |
+ *is_valid_so_far = false; |
+ return; |
+ } |
+ |
+ char next_byte = (*message)[i]; |
+ |
+ if (escape_next_byte) { |
+ message_content->push_back(next_byte); |
+ escape_next_byte = false; |
+ continue; |
+ } |
+ |
+ switch (next_byte) { |
+ case BATTOR_SPECIAL_BYTE_START: |
+ // Two start bytes in a message is invalid. |
+ *is_valid_so_far = false; |
+ return; |
+ |
+ case BATTOR_SPECIAL_BYTE_END: |
+ // We've found the end of the message. Make sure that we're at the end |
+ // of the message. |
+ *is_complete = true; |
+ |
+ // We're only parsing a single message here. If there are additional |
+ // bytes after this byte, what we've received so far is *not* a valid |
+ // message. |
+ *is_valid_so_far = (i == message->size() - 1); |
+ return; |
+ |
+ case BATTOR_SPECIAL_BYTE_ESCAPE: |
+ escape_next_byte = true; |
+ (*escape_byte_count)++; |
+ continue; |
+ |
+ default: |
+ message_content->push_back(next_byte); |
+ } |
+ } |
+} |
+ |
+} // namespace |
+ |
+BattOrConnection::BattOrConnection( |
+ scoped_refptr<base::SingleThreadTaskRunner> file_thread_task_runner, |
+ scoped_refptr<base::SingleThreadTaskRunner> ui_thread_task_runner) |
+ : file_thread_task_runner_(file_thread_task_runner), |
+ ui_thread_task_runner_(ui_thread_task_runner) {} |
+ |
+BattOrConnection::~BattOrConnection() {} |
+ |
+void BattOrConnection::Connect( |
+ const std::string& path, |
+ const base::Callback<void(bool success)> callback) { |
+ 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, callback); |
+} |
+ |
+void BattOrConnection::SendBytes(const SendCallback& callback, |
+ BattOrMessageType type, |
Zhen Wang
2015/12/14 23:39:46
wrong indent
charliea (OOO until 10-5)
2015/12/15 23:50:04
Doh. Looks like I forgot to rerun git cl format af
|
+ const char* bytes, |
+ uint16_t bytes_to_send) { |
Zhen Wang
2015/12/14 23:39:46
DCHECK the length of bytes == bytes_to_send.
Primiano Tucci (use gerrit)
2015/12/15 11:07:19
well, how if they are not a string (read: can gene
charliea (OOO until 10-5)
2015/12/15 23:50:04
Yea, I don't think that it's possible to determine
|
+ vector<char> data_vector; |
Primiano Tucci (use gerrit)
2015/12/15 11:07:19
just "data" should be enough. It's clear its a vec
charliea (OOO until 10-5)
2015/12/15 23:50:04
Ah, sorry about that. At some point, the incoming
|
+ |
+ // BattOr messages have the following format: |
+ // |
+ // 0x00 (1 byte start marker) |
+ // uint8_t (1 byte header indicating the message type) |
+ // data (message data, with 0x00s and 0x01s escaped with 0x02) |
+ // 0x01 (1 byte end marker) |
+ // |
+ // We have to encode any data we're sending in this format. Because of this, |
+ // we should reserve a send buffer with 3 bytes (start, header, end) and twice |
+ // as many bytes as we're sending, because each raw data byte might need to be |
+ // escaped. |
+ data_vector.reserve(2 * bytes_to_send + 3); |
+ |
+ data_vector.push_back(BATTOR_SPECIAL_BYTE_START); |
+ data_vector.push_back(type); |
+ |
+ for (int i = 0; i < bytes_to_send; i++) { |
+ if (bytes[i] == BATTOR_SPECIAL_BYTE_START || |
+ bytes[i] == BATTOR_SPECIAL_BYTE_END) |
+ data_vector.push_back(BATTOR_SPECIAL_BYTE_ESCAPE); |
Zhen Wang
2015/12/14 23:39:46
Add {...} when if clause takes more than 1 line.
charliea (OOO until 10-5)
2015/12/15 23:50:04
Done.
|
+ |
+ data_vector.push_back(bytes[i]); |
+ } |
+ |
+ data_vector.push_back(BATTOR_SPECIAL_BYTE_END); |
+ |
+ io_handler_->Write(make_scoped_ptr(new device::SendBuffer( |
+ data_vector, base::Bind(&BattOrConnection::OnBytesSent, AsWeakPtr(), |
+ callback, data_vector.size())))); |
+} |
+ |
+void BattOrConnection::ReadBytes(const ReadCallback& callback, |
+ uint16_t bytes_to_read) { |
Zhen Wang
2015/12/14 23:39:44
wrong indent
charliea (OOO until 10-5)
2015/12/15 23:50:04
Doh.
|
+ // Add 3 bytes to however many bytes the caller requested to account for the |
+ // start, type, and end bytes that are included in every BattOr message. |
+ bytes_to_read += 3; |
+ |
+ scoped_ptr<vector<char>> bytes_already_read(new vector<char>()); |
+ ReadMoreBytes(callback, bytes_to_read, bytes_already_read.Pass()); |
+} |
+ |
+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( |
+ const ReadCallback& callback, |
+ uint16_t bytes_to_read, |
+ scoped_ptr<vector<char>> bytes_already_read) { |
Primiano Tucci (use gerrit)
2015/12/15 11:07:19
I wonder if this bytes_alread_read is really an ar
charliea (OOO until 10-5)
2015/12/15 23:50:04
Good point. I added a new private variable named p
|
+ auto io_buffer = make_scoped_refptr(new net::IOBuffer((size_t)bytes_to_read)); |
Primiano Tucci (use gerrit)
2015/12/15 11:07:19
no c-style casts, use static_cast?
(actually why d
charliea (OOO until 10-5)
2015/12/15 23:50:04
Done, although this did involve some casting in or
|
+ auto on_receive_buffer_filled = |
+ base::Bind(&BattOrConnection::OnBytesRead, AsWeakPtr(), callback, |
+ base::Passed(&bytes_already_read), io_buffer, bytes_to_read); |
+ |
+ io_handler_->Read(make_scoped_ptr(new device::ReceiveBuffer( |
+ io_buffer, bytes_to_read, on_receive_buffer_filled))); |
+} |
+ |
+void BattOrConnection::OnBytesSent( |
+ const BattOrConnection::SendCallback& callback, |
+ int expected_bytes_sent, |
+ int bytes_sent, |
+ device::serial::SendError error) { |
+ bool success = (error == device::serial::SEND_ERROR_NONE) && |
+ (expected_bytes_sent == bytes_sent); |
+ callback.Run(success); |
+} |
+ |
+void BattOrConnection::OnBytesRead( |
+ const BattOrConnection::ReadCallback& callback, |
+ scoped_ptr<vector<char>> bytes_already_read, |
+ scoped_refptr<net::IOBuffer> buffer, |
+ int expected_bytes_read, |
+ int bytes_read, |
+ device::serial::ReceiveError error) { |
+ if ((bytes_read < expected_bytes_read) || |
+ (error != device::serial::RECEIVE_ERROR_NONE)) { |
+ callback.Run(false, BATTOR_MESSAGE_TYPE_CONTROL, bytes_already_read.Pass()); |
+ return; |
+ } |
+ |
+ scoped_ptr<vector<char>> message_content(new vector<char>()); |
+ bool is_complete; |
+ bool is_valid_so_far; |
+ BattOrMessageType type; |
+ uint16_t escape_byte_count; |
+ |
+ ParseMessage(bytes_already_read.get(), message_content.get(), &is_complete, |
+ &is_valid_so_far, &type, &escape_byte_count); |
+ |
+ // Parse the piece of the message that we already had in order to determine |
+ // how many escape bytes we've already read. |
Zhen Wang
2015/12/14 23:39:46
Move the comment to above ParseMessage.
Some more
charliea (OOO until 10-5)
2015/12/15 23:50:04
I had thought about that, but I was trying to avoi
|
+ uint16_t escape_byte_already_requested_count = escape_byte_count; |
+ |
+ message_content->clear(); |
+ bytes_already_read->insert(bytes_already_read->end(), buffer->data(), |
+ buffer->data() + bytes_read); |
+ |
+ // Reparse the message with the new bytes added on in order to determine how |
+ // many new escape bytes we've received and whether the message is complete. |
+ ParseMessage(bytes_already_read.get(), message_content.get(), &is_complete, |
+ &is_valid_so_far, &type, &escape_byte_count); |
+ |
+ if (!is_valid_so_far) { |
+ // If we already have an invalid message, there's no sense in continuing to |
+ // process it. |
+ callback.Run(false, BATTOR_MESSAGE_TYPE_CONTROL, bytes_already_read.Pass()); |
+ return; |
+ } |
+ |
+ uint16_t additional_escape_bytes = |
+ escape_byte_count - escape_byte_already_requested_count; |
+ if (additional_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(callback, additional_escape_bytes, bytes_already_read.Pass()); |
+ return; |
+ } |
+ |
+ if (!is_complete) |
+ // 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. |
+ callback.Run(false, BATTOR_MESSAGE_TYPE_CONTROL, bytes_already_read.Pass()); |
+ |
+ // If we've gotten this far, we've received the whole, well-formed message. |
+ callback.Run(true, type, message_content.Pass()); |
+} |
+ |
+} // namespace battor |