copied glibusb in and started making it compile
diff --git a/aos/common/glibusb/CMakeLists.txt b/aos/common/glibusb/CMakeLists.txt
new file mode 100644
index 0000000..11151fe
--- /dev/null
+++ b/aos/common/glibusb/CMakeLists.txt
@@ -0,0 +1,93 @@
+cmake_minimum_required(VERSION 2.8)
+project(glibusb)
+
+enable_testing()
+
+find_package(Boost REQUIRED COMPONENTS thread date_time) # need >= 1.48
+find_package(PkgConfig REQUIRED)
+#pkg_check_modules(LIBUSB REQUIRED libusb-1.0)
+pkg_check_modules(GFLAGS REQUIRED libgflags)
+pkg_check_modules(GLOG REQUIRED libglog)
+
+add_definitions("-std=c++11 -Wall -Wextra")
+
+set(GTEST_INCLUDEDIR "/usr/src/gtest")
+set(GTEST_SRCS
+ /usr/src/gtest/src/gtest_main.cc
+ /usr/src/gtest/src/gtest-all.cc
+)
+
+
+include_directories(
+ ${Boost_INCLUDE_DIRS}
+ /usr/include/libusb-1.0
+ ${GFLAGS_INCLUDE_DIRS}
+ ${GLOG_INCLUDE_DIRS}
+ ${GTEST_INCLUDEDIR}
+)
+
+link_directories(
+ ${Boost_LIBRARY_DIRS}
+ ${GFLAGS_LIBRARY_DIRS}
+ ${GLOG_LIBRARY_DIRS}
+)
+
+set(LIBRARIES
+ ${Boost_LIBRARIES}
+ usb-1.0
+ ${GFLAGS_LIBRARIES}
+ ${GLOG_LIBRARIES}
+ pthread
+)
+
+set(HEADERS
+ gbuffer.h
+ ghexdump.h
+ glibusb_device_internal.h
+ glibusb_endpoint.h
+ glibusb_endpoint_internal.h
+ glibusb.h
+ glibusb_internal.h
+ glibusb_transfer.h
+)
+
+#
+add_library(glibusb
+ gbuffer.cc
+ glibusb.cc
+ glibusb_device.cc
+ glibusb_endpoint.cc
+ glibusb_internal.cc
+ glibusb_transfer.cc
+ ghexdump.cc
+)
+target_link_libraries(glibusb ${LIBRARIES})
+
+#
+add_executable(gbuffer_test
+ testing/gbuffer_test.cc
+ ${GTEST_SRCS}
+)
+target_link_libraries(gbuffer_test glibusb ${LIBRARIES})
+add_test(gbuffer_test gbuffer_test)
+
+#
+add_executable(glibusb_test
+ testing/glibusb_test.cc
+ ${GTEST_SRCS}
+)
+target_link_libraries(glibusb_test glibusb ${LIBRARIES})
+add_test(glibusb_test glibusb_test)
+
+install(TARGETS glibusb
+ LIBRARY DESTINATION lib COMPONENT Runtime
+ ARCHIVE DESTINATION lib COMPONENT Runtime
+)
+install(FILES ${HEADERS}
+ DESTINATION /usr/include/glibusb
+ COMPONENT Development)
+
+set(CPACK_GENERATOR DEB)
+set(CPACK_PACKAGE_VERSION "1.0.14")
+set(CPACK_PACKAGE_CONTACT "charliehotel@google.com")
+include(CPack)
diff --git a/aos/common/glibusb/LICENSE b/aos/common/glibusb/LICENSE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/aos/common/glibusb/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/aos/common/glibusb/NOTICE b/aos/common/glibusb/NOTICE
new file mode 100644
index 0000000..a0af402
--- /dev/null
+++ b/aos/common/glibusb/NOTICE
@@ -0,0 +1 @@
+This code is originally from <https://code.google.com/p/google-libusb/>.
diff --git a/aos/common/glibusb/gbuffer.cc b/aos/common/glibusb/gbuffer.cc
new file mode 100644
index 0000000..b675953
--- /dev/null
+++ b/aos/common/glibusb/gbuffer.cc
@@ -0,0 +1,323 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Modified by FRC Team 971.
+//
+
+#include "gbuffer.h"
+
+#include <stddef.h>
+#include <stdint.h>
+#include <cstring>
+
+#include "aos/common/logging/logging.h"
+
+#include "ghexdump.h"
+
+namespace glibusb {
+
+Buffer::Buffer() {}
+
+Buffer::~Buffer() {}
+
+Buffer::Buffer(const void *src, Buffer::size_type length) {
+ buffer_.resize(length);
+ if (length > 0) {
+ uint8_t *dst = &(buffer_[0]);
+ memcpy(dst, src, length);
+ }
+}
+
+bool Buffer::operator==(const Buffer &other) const {
+ return buffer_ == other.buffer_;
+}
+
+bool Buffer::operator!=(const Buffer &other) const {
+ return !(*this == other);
+}
+
+Buffer *Buffer::MakeSlice(Buffer::size_type offset,
+ Buffer::size_type length) const {
+ CHECK_LE(offset + length, buffer_.size());
+ if (length == 0) {
+ return new Buffer();
+ } else {
+ const uint8_t *p = &(buffer_[offset]);
+ return new Buffer(p, length);
+ }
+}
+
+void Buffer::Clear() {
+ buffer_.clear();
+}
+
+void Buffer::Resize(Buffer::size_type length) {
+ buffer_.resize(length);
+}
+
+void *Buffer::GetBufferPointer(Buffer::size_type length) {
+ return GetBufferPointer(0, length);
+}
+
+const void *Buffer::GetBufferPointer(Buffer::size_type length) const {
+ return GetBufferPointer(0, length);
+}
+
+void *Buffer::GetBufferPointer(Buffer::size_type offset,
+ Buffer::size_type length) {
+ if (length == 0) {
+ return NULL;
+ } else {
+ CHECK_LE(offset + length, buffer_.size());
+ uint8_t *p = &(buffer_[offset]);
+ return static_cast<void *>(p);
+ }
+}
+
+const void *Buffer::GetBufferPointer(Buffer::size_type offset,
+ Buffer::size_type length) const {
+ if (length == 0) {
+ return NULL;
+ } else {
+ CHECK_LE(offset + length, buffer_.size());
+ const uint8_t *p = &(buffer_[offset]);
+ return static_cast<const void *>(p);
+ }
+}
+
+// Specialized template for Get
+template <>
+Buffer::size_type Buffer::Get(Buffer::size_type byte_offset,
+ uint8_t *value_out) const {
+ CHECK_LT(byte_offset, buffer_.size());
+ *CHECK_NOTNULL(value_out) = buffer_[byte_offset];
+ return sizeof(uint8_t);
+}
+
+// Specialized template for Get
+template <>
+Buffer::size_type Buffer::Get(Buffer::size_type byte_offset,
+ int8_t *value_out) const {
+ uint8_t value;
+ Get(byte_offset, &value);
+ *CHECK_NOTNULL(value_out) = static_cast<int8_t>(value);
+ return sizeof(int8_t);
+}
+
+// Specialized template for Get
+template <>
+Buffer::size_type Buffer::Get(Buffer::size_type byte_offset,
+ uint16_t *value_out) const {
+ CHECK_LT(byte_offset + 1, buffer_.size());
+ uint16_t byte0 = static_cast<uint16_t>(buffer_[byte_offset]);
+ uint16_t byte1 = static_cast<uint16_t>(buffer_[byte_offset + 1]);
+ uint16_t value = byte0 | (byte1 << 8);
+ *CHECK_NOTNULL(value_out) = value;
+ return sizeof(uint16_t);
+}
+
+// Specialized template for Get
+template <>
+Buffer::size_type Buffer::Get(Buffer::size_type byte_offset,
+ int16_t *value_out) const {
+ uint16_t value;
+ Get(byte_offset, &value);
+ *CHECK_NOTNULL(value_out) = static_cast<int16_t>(value);
+ return sizeof(int16_t);
+}
+
+// Specialized template for Get
+template <>
+Buffer::size_type Buffer::Get(Buffer::size_type byte_offset,
+ uint32_t *value_out) const {
+ CHECK_LT(byte_offset + 3, buffer_.size());
+ uint32_t byte0 = static_cast<uint32_t>(buffer_[byte_offset]);
+ uint32_t byte1 = static_cast<uint32_t>(buffer_[byte_offset + 1]);
+ uint32_t byte2 = static_cast<uint32_t>(buffer_[byte_offset + 2]);
+ uint32_t byte3 = static_cast<uint32_t>(buffer_[byte_offset + 3]);
+ uint32_t value = byte0 | (byte1 << 8) | (byte2 << 16) | (byte3 << 24);
+ *CHECK_NOTNULL(value_out) = value;
+ return sizeof(uint32_t);
+}
+
+// Specialized template for Get
+template <>
+Buffer::size_type Buffer::Get(Buffer::size_type byte_offset,
+ int32_t *value_out) const {
+ uint32_t value;
+ Get(byte_offset, &value);
+ *CHECK_NOTNULL(value_out) = static_cast<int32_t>(value);
+ return sizeof(int32_t);
+}
+
+// Specialized template for Get
+template <>
+Buffer::size_type Buffer::Get(Buffer::size_type byte_offset,
+ uint64_t *value_out) const {
+ CHECK_LT(byte_offset + 7, buffer_.size());
+ uint64_t byte0 = static_cast<uint64_t>(buffer_[byte_offset]);
+ uint64_t byte1 = static_cast<uint64_t>(buffer_[byte_offset + 1]);
+ uint64_t byte2 = static_cast<uint64_t>(buffer_[byte_offset + 2]);
+ uint64_t byte3 = static_cast<uint64_t>(buffer_[byte_offset + 3]);
+ uint64_t byte4 = static_cast<uint64_t>(buffer_[byte_offset + 4]);
+ uint64_t byte5 = static_cast<uint64_t>(buffer_[byte_offset + 5]);
+ uint64_t byte6 = static_cast<uint64_t>(buffer_[byte_offset + 6]);
+ uint64_t byte7 = static_cast<uint64_t>(buffer_[byte_offset + 7]);
+ uint64_t value =
+ byte0 | (byte1 << 8) | (byte2 << 16) | (byte3 << 24) |
+ (byte4 << 32) | (byte5 << 40) | (byte6 << 48) | (byte7 << 56);
+ *CHECK_NOTNULL(value_out) = value;
+ return sizeof(uint64_t);
+}
+
+// Specialized template for Get
+template <>
+Buffer::size_type Buffer::Get(Buffer::size_type byte_offset,
+ int64_t *value_out) const {
+ uint64_t value;
+ Get(byte_offset, &value);
+ *CHECK_NOTNULL(value_out) = static_cast<int64_t>(value);
+ return sizeof(int64_t);
+}
+
+Buffer::size_type Buffer::Get(Buffer::size_type byte_offset,
+ std::string *out) const {
+ CHECK_NOTNULL(out);
+ out->clear();
+ size_type n = 0;
+ for (size_t i = byte_offset; /**/; ++i, ++n) {
+ uint8_t p;
+ Get(i, &p);
+ if (!p) {
+ break;
+ }
+ out->push_back(static_cast<char>(p));
+ }
+
+ // strings are always padded out to 4 bytes
+ n = (n + 3) & ~3;
+ return n;
+}
+
+// Specialized template for Put
+template <>
+Buffer::size_type Buffer::Put(Buffer::size_type byte_offset, uint8_t value) {
+ CHECK_LT(byte_offset, buffer_.size());
+ buffer_[byte_offset] = value;
+ return sizeof(uint8_t);
+}
+
+// Specialized template for Put
+template <>
+Buffer::size_type Buffer::Put(Buffer::size_type byte_offset, int8_t value) {
+ return Put(byte_offset, static_cast<uint8_t>(value));
+}
+
+// Specialized template for Put
+template <>
+Buffer::size_type Buffer::Put(Buffer::size_type byte_offset, uint16_t value) {
+ CHECK_LT(byte_offset + 1, buffer_.size());
+ uint8_t byte_0 = static_cast<uint8_t>(value & 0xff);
+ uint8_t byte_1 = static_cast<uint8_t>((value >> 8) & 0xff);
+ buffer_[byte_offset] = byte_0;
+ buffer_[byte_offset + 1] = byte_1;
+ return sizeof(uint16_t);
+}
+
+// Specialized template for Put
+template <>
+Buffer::size_type Buffer::Put(Buffer::size_type byte_offset, int16_t value) {
+ return Put(byte_offset, static_cast<uint16_t>(value));
+}
+
+// Specialized template for Put
+template <>
+Buffer::size_type Buffer::Put(Buffer::size_type byte_offset, uint32_t value) {
+ CHECK_LT(byte_offset + 3, buffer_.size());
+ uint8_t byte_0 = static_cast<uint8_t>(value & 0xff);
+ uint8_t byte_1 = static_cast<uint8_t>((value >> 8) & 0xff);
+ uint8_t byte_2 = static_cast<uint8_t>((value >> 16) & 0xff);
+ uint8_t byte_3 = static_cast<uint8_t>((value >> 24) & 0xff);
+ buffer_[byte_offset] = byte_0;
+ buffer_[byte_offset + 1] = byte_1;
+ buffer_[byte_offset + 2] = byte_2;
+ buffer_[byte_offset + 3] = byte_3;
+ return sizeof(uint32_t);
+}
+
+// Specialized template for Put
+template <>
+Buffer::size_type Buffer::Put(Buffer::size_type byte_offset, int32_t value) {
+ return Put(byte_offset, static_cast<uint32_t>(value));
+}
+
+// Specialized template for Put
+template <>
+Buffer::size_type Buffer::Put(Buffer::size_type byte_offset, uint64_t value) {
+ CHECK_LT(byte_offset + 7, buffer_.size());
+ uint8_t byte_0 = static_cast<uint8_t>(value & 0xff);
+ uint8_t byte_1 = static_cast<uint8_t>((value >> 8) & 0xff);
+ uint8_t byte_2 = static_cast<uint8_t>((value >> 16) & 0xff);
+ uint8_t byte_3 = static_cast<uint8_t>((value >> 24) & 0xff);
+ uint8_t byte_4 = static_cast<uint8_t>((value >> 32) & 0xff);
+ uint8_t byte_5 = static_cast<uint8_t>((value >> 40) & 0xff);
+ uint8_t byte_6 = static_cast<uint8_t>((value >> 48) & 0xff);
+ uint8_t byte_7 = static_cast<uint8_t>((value >> 56) & 0xff);
+ buffer_[byte_offset] = byte_0;
+ buffer_[byte_offset + 1] = byte_1;
+ buffer_[byte_offset + 2] = byte_2;
+ buffer_[byte_offset + 3] = byte_3;
+ buffer_[byte_offset + 4] = byte_4;
+ buffer_[byte_offset + 5] = byte_5;
+ buffer_[byte_offset + 6] = byte_6;
+ buffer_[byte_offset + 7] = byte_7;
+ return sizeof(uint64_t);
+}
+
+// Specialized template for Put
+template <>
+Buffer::size_type Buffer::Put(Buffer::size_type byte_offset, int64_t value) {
+ return Put(byte_offset, static_cast<uint64_t>(value));
+}
+
+void Buffer::Append(const Buffer &source) {
+ buffer_.insert(buffer_.end(), source.buffer_.begin(), source.buffer_.end());
+}
+
+void Buffer::AddHeader(Buffer::size_type length) {
+ buffer_.insert(buffer_.begin(), length, 0);
+}
+
+void Buffer::RemoveHeader(Buffer::size_type length) {
+ if (length > 0) {
+ CHECK_LE(length, buffer_.size());
+ buffer_.erase(buffer_.begin(), buffer_.begin() + length);
+ }
+}
+
+void Buffer::Copy(const Buffer &source) {
+ buffer_.assign(source.buffer_.begin(), source.buffer_.end());
+}
+
+#if 0
+void Buffer::WriteOrDie(File *fp) const {
+ size_type n = Length();
+ const void *p = GetBufferPointer(n);
+ fp->WriteOrDie(p, n);
+}
+
+void Buffer::WriteToPathOrDie(const char *name) const {
+ FileCloser file(File::OpenOrDie(name, "w"));
+ WriteOrDie(file.get());
+}
+#endif
+
+std::string Buffer::Dump() const {
+ size_type n = Length();
+ if (n == 0) {
+ return "";
+ } else {
+ return glibusb::Dump(&(buffer_[0]), n);
+ }
+}
+
+} // namespace glibusb
diff --git a/aos/common/glibusb/gbuffer.h b/aos/common/glibusb/gbuffer.h
new file mode 100644
index 0000000..5c1d8c6
--- /dev/null
+++ b/aos/common/glibusb/gbuffer.h
@@ -0,0 +1,130 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// A buffer for dealing with data. Some special support for PTP is
+// available.
+
+#ifndef _GLIBUSB_GBUFFER_H_
+#define _GLIBUSB_GBUFFER_H_
+
+#include <stdint.h>
+#include <string>
+#include <vector>
+
+namespace glibusb {
+
+// Buffer of bytes.
+class Buffer {
+ public:
+ // Underlying byte store type.
+ typedef std::vector<uint8_t> ByteBuffer;
+ typedef ByteBuffer::size_type size_type;
+
+ // Destructor.
+ ~Buffer();
+
+ // Constructs an empty buffer.
+ Buffer();
+
+ // Constructs a buffer from memory of given length. Data is copied.
+ // (This is an interface to C functions, chiefly, libusb, and should
+ // only be used for such purposes.)
+ Buffer(const void *src, size_type length);
+
+ // Returns true iff the buffers contain the same data.
+ bool operator==(const Buffer &other) const;
+
+ // Returns true iff the buffer does not contain the same data.
+ bool operator!=(const Buffer &other) const;
+
+ // Returns a new allocated slice of the buffer.
+ Buffer *MakeSlice(size_type offset, size_type length) const;
+
+ // Returns the length.
+ size_type Length() const { return buffer_.size(); }
+
+ // Clears (as in std::vector) the buffer.
+ void Clear();
+
+ // Resizes (as in std::vector) the buffer.
+ void Resize(size_type length);
+
+ // Returns a pointer to the underlying store that of a guaranteed
+ // length (or CHECK), possibly beginning at a given offset. (These
+ // are interfaces to C functions, chiefly, libusb, and should only
+ // be used for such purposes.)
+ void *GetBufferPointer(size_type length);
+ const void *GetBufferPointer(size_type length) const;
+ void *GetBufferPointer(size_type offset, size_type length);
+ const void *GetBufferPointer(size_type offset, size_type length) const;
+
+ // Gets the value of integral type T from the buffer at the offset
+ // byte_offset, places it in value_out, and returns the length of
+ // the marshalled data. The integer is expected to be in little
+ // endian format. This template is specialized for
+ // {int,uint}{8,16,32,64}_t.
+ template <class T>
+ size_type Get(size_type byte_offset, T *value_out) const;
+
+ // Gets an ASCII string from the buffer at the offset byte_offset,
+ // places it in value_out, and returns the length of the marshalled
+ // data. The string data in the buffer is expected to be
+ // null-terminated.
+ size_type Get(size_type byte_offset, std::string *value_out) const;
+
+ // Puts the value of integral type T into the buffer offset
+ // byte_offset and returns the length of the marshalled data. Data
+ // are put in little endian format. This template is specialized for
+ // {int,uint}{8,16,32,64}_t.
+ template <class T>
+ size_type Put(size_type byte_offset, T value);
+
+ // Appends the value of type T into the buffer offset byte_offset
+ // and returns the length of the marshalled data. Data are appended
+ // in little endian format. This template is available for
+ // {int,uint}{8,16,32,64}_t.
+ template <class T> void Append(const T value);
+
+ // Append a buffer.
+ void Append(const Buffer &buffer);
+
+ // Inserts length bytes of (uninitialized) space at the beginning of
+ // the buffer.
+ void AddHeader(size_type length);
+
+ // Removes length bytes of space from the beginning of the buffer.
+ void RemoveHeader(size_type length);
+
+ // Copies the source buffer.
+ void Copy(const Buffer &source);
+
+#if 0
+ // Writes the contents of the buffer to the file, or dies.
+ void WriteOrDie(File *fp) const;
+
+ // Writes the contents of the buffer to the path, or dies.
+ void WriteToPathOrDie(const char *name) const;
+#endif
+
+ // Returns a hex dump of the buffer.
+ std::string Dump() const;
+
+ private:
+ // The underlying byte store.
+ ByteBuffer buffer_;
+
+ Buffer(const Buffer &) = delete;
+ void operator=(const Buffer &) = delete;
+};
+
+
+// Template for Buffer::Append for integral values.
+template <typename T>
+void Buffer::Append(const T value) {
+ size_type offset = Length();
+ Resize(offset + sizeof(T));
+ Put(offset, value);
+}
+
+} // namespace glibusb
+
+#endif // _GLIBUSB_GBUFFER_H_
diff --git a/aos/common/glibusb/gbuffer_test.cc b/aos/common/glibusb/gbuffer_test.cc
new file mode 100644
index 0000000..0e6733f
--- /dev/null
+++ b/aos/common/glibusb/gbuffer_test.cc
@@ -0,0 +1,570 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+// Author: charliehotel@google.com (Christopher Hoover)
+//
+// Tests for Buffer.
+
+#include "../gbuffer.h"
+
+#include <stdint.h>
+#include <limits>
+
+#include <boost/scoped_ptr.hpp>
+#include <gtest/gtest.h>
+
+#define GG_LONGLONG(x) x##LL
+#define GG_ULONGLONG(x) x##ULL
+#define ARRAYSIZE(x) (static_cast<int>(sizeof(x)/sizeof(x[0])))
+
+namespace glibusb {
+namespace {
+
+typedef ::testing::Types<int8_t, int16_t, int32_t, int64_t,
+ uint8_t, uint16_t, uint32_t, uint64_t> AllIntegerTypes;
+
+// Tests that a newly constructed buffer is empty.
+TEST(BufferTest, EmptyBufferLength) {
+ Buffer buffer;
+ EXPECT_EQ(0, buffer.Length());
+}
+
+// Tests clearing.
+TEST(BufferTest, EmptyBufferClear) {
+ Buffer buffer;
+ buffer.Append(uint8_t(1));
+ buffer.Clear();
+ EXPECT_EQ(0, buffer.Length());
+}
+
+// Tests resizing.
+TEST(BufferTest, EmptyBufferResize) {
+ Buffer buffer;
+ const int kSize = 100;
+ buffer.Resize(kSize);
+ EXPECT_EQ(kSize, buffer.Length());
+}
+
+// Tests getting a pointer on an empty buffer.
+TEST(BufferTest, EmptyBufferGetPointer) {
+ Buffer buffer;
+ void *p;
+ const void *cp;
+ p = buffer.GetBufferPointer(0);
+ EXPECT_EQ(NULL, p);
+ cp = buffer.GetBufferPointer(0);
+ EXPECT_EQ(NULL, cp);
+ p = buffer.GetBufferPointer(0, 0);
+ EXPECT_EQ(NULL, p);
+ cp = buffer.GetBufferPointer(0, 0);
+ EXPECT_EQ(NULL, cp);
+}
+
+// Tests getting a pointer on an empty buffer.
+TEST(BufferTest, ConstEmptyBufferGetPointer) {
+ const Buffer buffer;
+ const void *cp;
+ cp = buffer.GetBufferPointer(0);
+ EXPECT_EQ(NULL, cp);
+ cp = buffer.GetBufferPointer(0, 0);
+ EXPECT_EQ(NULL, cp);
+}
+
+
+// Tests Get on an empty buffer.
+template <typename T>
+class EmptyBufferGetDeathTest : public ::testing::Test {
+ public:
+ void Check() {
+ Buffer buffer;
+ T value;
+ EXPECT_DEATH(buffer.Get(0, &value), "Check failed");
+ }
+};
+
+// Tests Get for all types on an empty bufer.
+TYPED_TEST_CASE(EmptyBufferGetDeathTest, AllIntegerTypes);
+TYPED_TEST(EmptyBufferGetDeathTest, Check) {
+ this->Check();
+}
+
+
+// Tests Put on an empty buffer.
+template <typename T>
+class EmptyBufferPutDeathTest : public ::testing::Test {
+ public:
+ void Check() {
+ Buffer buffer;
+ T value(0);
+ EXPECT_DEATH(buffer.Put(0, value), "Check failed");
+ }
+};
+
+// Tests Put for all types on an empty bufer.
+TYPED_TEST_CASE(EmptyBufferPutDeathTest, AllIntegerTypes);
+TYPED_TEST(EmptyBufferPutDeathTest, Check) {
+ this->Check();
+}
+
+
+// Tests getting a string on an empty buffer.
+TEST(BufferDeathTest, EmptyBufferGetString) {
+ Buffer buffer;
+ std::string s;
+ EXPECT_DEATH(buffer.Get(0, &s), "Check failed");
+}
+
+
+// Tests removing the header from an empty buffer.
+TEST(BufferDeathTest, EmptyBufferRemoveHeader) {
+ Buffer buffer;
+ buffer.RemoveHeader(0);
+ EXPECT_EQ(0, buffer.Length());
+ EXPECT_DEATH(buffer.RemoveHeader(1), "Check failed");
+}
+
+
+// Tests adding a header of size 0.
+TEST(BufferTest, EmptyBufferAddHeader) {
+ Buffer buffer;
+ buffer.AddHeader(0);
+ EXPECT_EQ(0, buffer.Length());
+}
+
+
+// Tests adding a header of size > 0.
+TEST(BufferTest, EmptyBufferAddHeader2) {
+ Buffer buffer;
+ const int kSize = 100;
+ buffer.AddHeader(kSize);
+ EXPECT_EQ(kSize, buffer.Length());
+}
+
+
+// Tests copying an empty buffer.
+TEST(BufferTest, EmptyBufferCopy) {
+ Buffer buffer;
+ Buffer buffer2;
+ buffer2.Append(uint8_t(1));
+ buffer2.Copy(buffer);
+ EXPECT_EQ(0, buffer2.Length());
+}
+
+// Tests dumping an empty buffer.
+TEST(BufferTest, EmptyBufferDump) {
+ Buffer buffer;
+ std::string s = buffer.Dump();
+ EXPECT_EQ("", s);
+}
+
+
+// Tests slicing an empty buffer.
+TEST(BufferTest, EmptyBufferSlice) {
+ Buffer buffer;
+ boost::scoped_ptr<Buffer> slice(buffer.MakeSlice(0, 0));
+ EXPECT_EQ(slice->Length(), 0);
+}
+
+
+// Tests Get, Put and Append for signed and unsigned integers.
+template <typename T>
+class PutGetAppendIntegerTest : public ::testing::Test {
+ public:
+ void Check() {
+ static const T kValues[] = {
+ std::numeric_limits<T>::max(),
+ T(0),
+ std::numeric_limits<T>::min()
+ };
+
+ for (int i = 0; i < ARRAYSIZE(kValues); ++i) {
+ const T kValue = kValues[i];
+
+ // Tests Put - Get
+ {
+ Buffer buffer;
+ buffer.Resize(sizeof(T));
+ buffer.Put(0, kValue);
+ T check;
+ buffer.Get(0, &check);
+ EXPECT_EQ(kValue, check);
+ }
+
+ // Tests Append - Get
+ {
+ Buffer buffer;
+ buffer.Append(kValue);
+ T check;
+ buffer.Get(0, &check);
+ EXPECT_EQ(kValue, check);
+ }
+ }
+ }
+};
+
+// Tests Get, Put and Append for all signed integers.
+TYPED_TEST_CASE(PutGetAppendIntegerTest, AllIntegerTypes);
+TYPED_TEST(PutGetAppendIntegerTest, Check) {
+ this->Check();
+}
+
+const uint8_t kDeadBeef[] = {
+ 0xef, 0xbe, 0xad, 0xde
+};
+
+// Test harness for a buffer construct from "C" data.
+class ConstructedFromDataBufferTest : public testing::Test {
+ protected:
+ void SetUp() {
+ buffer.reset(new Buffer(kDeadBeef, sizeof(kDeadBeef)));
+ }
+
+ boost::scoped_ptr<Buffer> buffer;
+};
+
+typedef ConstructedFromDataBufferTest ConstructedFromDataBufferDeathTest;
+
+// Tests constructing a buffer from "C" data.
+TEST_F(ConstructedFromDataBufferTest, ConstructedFromDataBufferLength) {
+ EXPECT_EQ(sizeof(kDeadBeef), buffer->Length());
+}
+
+// Tests that a buffer constructed from "C" data contains the right
+// data.
+TEST_F(ConstructedFromDataBufferTest, ConstructedFromDataByteAccess) {
+ for (int i = 0; i < ARRAYSIZE(kDeadBeef); ++i) {
+ uint8_t u8;
+ buffer->Get(i, &u8);
+ EXPECT_EQ(kDeadBeef[i], u8);
+ }
+}
+
+// Tests clearing.
+TEST_F(ConstructedFromDataBufferTest, ConstructedFromDataClear) {
+ buffer->Clear();
+ EXPECT_EQ(0, buffer->Length());
+}
+
+// Tests resizing.
+TEST_F(ConstructedFromDataBufferTest, ConstructedFromDataResize) {
+ const int kSize = 100;
+ buffer->Resize(kSize);
+ EXPECT_EQ(kSize, buffer->Length());
+}
+
+// Tests that getting a pointer works.
+TEST_F(ConstructedFromDataBufferTest, ConstructedFromDataGetPointer) {
+ void *p;
+ const void *cp;
+ p = buffer->GetBufferPointer(0);
+ EXPECT_EQ(NULL, p);
+ cp = buffer->GetBufferPointer(0);
+ EXPECT_EQ(NULL, cp);
+ p = buffer->GetBufferPointer(0, 0);
+ EXPECT_EQ(NULL, p);
+ cp = buffer->GetBufferPointer(0, 0);
+ EXPECT_EQ(NULL, cp);
+
+ p = buffer->GetBufferPointer(2);
+ EXPECT_TRUE(p != NULL);
+ EXPECT_EQ(kDeadBeef[0], *static_cast<uint8_t *>(p));
+ EXPECT_EQ(kDeadBeef[1], *(static_cast<uint8_t *>(p) + 1));
+ cp = buffer->GetBufferPointer(2);
+ EXPECT_TRUE(p != NULL);
+ EXPECT_EQ(kDeadBeef[0], *static_cast<uint8_t *>(p));
+ EXPECT_EQ(kDeadBeef[1], *(static_cast<uint8_t *>(p) + 1));
+
+ p = buffer->GetBufferPointer(1, 2);
+ EXPECT_TRUE(p != NULL);
+ EXPECT_EQ(kDeadBeef[1], *static_cast<uint8_t *>(p));
+ EXPECT_EQ(kDeadBeef[2], *(static_cast<uint8_t *>(p) + 1));
+ cp = buffer->GetBufferPointer(1, 2);
+ EXPECT_TRUE(p != NULL);
+ EXPECT_EQ(kDeadBeef[1], *static_cast<uint8_t *>(p));
+ EXPECT_EQ(kDeadBeef[2], *(static_cast<uint8_t *>(p) + 1));
+
+ const Buffer &const_buffer(*buffer);
+ cp = const_buffer.GetBufferPointer(1, 2);
+ EXPECT_TRUE(p != NULL);
+ EXPECT_EQ(kDeadBeef[1], *static_cast<uint8_t *>(p));
+ EXPECT_EQ(kDeadBeef[2], *(static_cast<uint8_t *>(p) + 1));
+}
+
+// Tests that Get{S,U}{8,16,32,64} work on zero.
+TEST(BufferTest, GetZero) {
+ boost::scoped_ptr<Buffer> buffer(new Buffer());
+ for (int i = 0; i < 8; ++i) {
+ buffer->Append(uint8_t(0));
+ }
+ int8_t s8;
+ uint8_t u8;
+ int16_t s16;
+ uint16_t u16;
+ int32_t s32;
+ uint32_t u32;
+ int64_t s64;
+ uint64_t u64;
+ buffer->Get(0, &s8);
+ EXPECT_EQ(0, s8);
+ buffer->Get(0, &u8);
+ EXPECT_EQ(0, u8);
+ buffer->Get(0, &s16);
+ EXPECT_EQ(0, s16);
+ buffer->Get(0, &u16);
+ EXPECT_EQ(0, u16);
+ buffer->Get(0, &s32);
+ EXPECT_EQ(0, s32);
+ buffer->Get(0, &u32);
+ EXPECT_EQ(0, u32);
+ buffer->Get(0, &s64);
+ EXPECT_EQ(0, s64);
+ buffer->Get(0, &u64);
+ EXPECT_EQ(0, u64);
+}
+
+// Tests that GetU{8,16,32,64} work.
+TEST(BufferTest, GetUXX) {
+ boost::scoped_ptr<Buffer> buffer(new Buffer());
+ buffer->Append(uint8_t(0x88));
+ buffer->Append(uint8_t(0x77));
+ buffer->Append(uint8_t(0x66));
+ buffer->Append(uint8_t(0x55));
+ buffer->Append(uint8_t(0x44));
+ buffer->Append(uint8_t(0x33));
+ buffer->Append(uint8_t(0x22));
+ buffer->Append(uint8_t(0x11));
+ uint8_t u8;
+ uint16_t u16;
+ uint32_t u32;
+ uint64_t u64;
+ buffer->Get(0, &u8);
+ EXPECT_EQ(0x88, u8);
+ buffer->Get(0, &u16);
+ EXPECT_EQ(0x7788, u16);
+ buffer->Get(0, &u32);
+ EXPECT_EQ(0x55667788, u32);
+ buffer->Get(0, &u64);
+ EXPECT_EQ(GG_ULONGLONG(0x1122334455667788), u64);
+}
+
+// Tests that GetS{8,16,32,64} work for positive values.
+TEST(BufferTest, GetSXXPositive) {
+ boost::scoped_ptr<Buffer> buffer(new Buffer());
+ buffer->Append(uint8_t(0x08));
+ buffer->Append(uint8_t(0x07));
+ buffer->Append(uint8_t(0x06));
+ buffer->Append(uint8_t(0x05));
+ buffer->Append(uint8_t(0x04));
+ buffer->Append(uint8_t(0x03));
+ buffer->Append(uint8_t(0x02));
+ buffer->Append(uint8_t(0x01));
+ int8_t s8;
+ int16_t s16;
+ int32_t s32;
+ int64_t s64;
+ buffer->Get(0, &s8);
+ EXPECT_EQ(0x08, s8);
+ buffer->Get(0, &s16);
+ EXPECT_EQ(0x0708, s16);
+ buffer->Get(0, &s32);
+ EXPECT_EQ(0x05060708, s32);
+ buffer->Get(0, &s64);
+ EXPECT_EQ(GG_ULONGLONG(0x0102030405060708), s64);
+}
+
+// Tests that GetS{8,16,32,64} work for negative values.
+TEST(BufferTest, GetSXXNegative) {
+ boost::scoped_ptr<Buffer> buffer(new Buffer());
+ buffer->Append(uint8_t(0xF8));
+ buffer->Append(uint8_t(0xF7));
+ buffer->Append(uint8_t(0x06));
+ buffer->Append(uint8_t(0xF5));
+ buffer->Append(uint8_t(0x04));
+ buffer->Append(uint8_t(0x03));
+ buffer->Append(uint8_t(0x02));
+ buffer->Append(uint8_t(0xF1));
+
+ // Calculate directly the the signed (2's complement) value that we
+ // should expect.
+ const int8_t kExpected8 = 0xF8 - 0xFF - 1;
+ int8_t s8;
+ buffer->Get(0, &s8);
+ EXPECT_EQ(kExpected8, s8);
+
+ const int16_t kExpected16 = 0xF7F8 - 0xFFFF - 1;
+ int16_t s16;
+ buffer->Get(0, &s16);
+ EXPECT_EQ(kExpected16, s16);
+
+ const int32_t kExpected32 = 0xF506F7F8 - 0xFFFFFFFF - 1;
+ int32_t s32;
+ buffer->Get(0, &s32);
+ EXPECT_EQ(kExpected32, s32);
+
+ const int64_t kExpected64 = (GG_LONGLONG(0xF1020304F506F7F8) -
+ GG_LONGLONG(0xFFFFFFFFFFFFFFFF) -
+ GG_LONGLONG(1));
+ int64_t s64;
+ buffer->Get(0, &s64);
+ EXPECT_EQ(kExpected64, s64);
+}
+
+// Tests that getting a string works.
+TEST_F(ConstructedFromDataBufferDeathTest, ConstructedFromDataGetString) {
+ std::string s;
+ EXPECT_DEATH(buffer->Get(0, &s), "Check failed");
+ buffer->Append(uint8_t(0));
+ Buffer::size_type n = buffer->Get(0, &s);
+ EXPECT_STREQ("\xEF\xBE\xAD\xDE", s.c_str());
+ EXPECT_EQ(4, n);
+ n = buffer->Get(1, &s);
+ EXPECT_STREQ("\xBE\xAD\xDE", s.c_str());
+ EXPECT_EQ(4, n);
+}
+
+// Tests removing a header.
+TEST_F(ConstructedFromDataBufferTest, ConstructedFromDataRemoveHeader) {
+ buffer->RemoveHeader(0);
+ EXPECT_EQ(sizeof(kDeadBeef), buffer->Length());
+ buffer->RemoveHeader(2);
+ EXPECT_EQ(sizeof(kDeadBeef) - 2, buffer->Length());
+ uint16_t u16;
+ buffer->Get(0, &u16);
+ EXPECT_EQ(0xdead, u16);
+}
+
+// Tests adding a zero-length header.
+TEST_F(ConstructedFromDataBufferTest, ConstructedFromDataAddHeader) {
+ buffer->AddHeader(0);
+ EXPECT_EQ(sizeof(kDeadBeef), buffer->Length());
+}
+
+// Tests adding a header of size > 0.
+TEST_F(ConstructedFromDataBufferTest, ConstructedFromDataAddHeader2) {
+ const int kSize = 100;
+ buffer->AddHeader(kSize);
+ EXPECT_EQ(sizeof(kDeadBeef) + kSize, buffer->Length());
+ uint32_t u32;
+ buffer->Get(kSize, &u32);
+ EXPECT_EQ(0xdeadbeef, u32);
+}
+
+// Tests copying.
+TEST_F(ConstructedFromDataBufferTest, ConstructedFromDataCopy) {
+ buffer->Append(uint8_t(0));
+ std::string s;
+ buffer->Get(0, &s);
+
+ Buffer buffer2;
+ buffer2.Copy(*buffer);
+ std::string s2;
+ buffer2.Get(0, &s2);
+
+ EXPECT_STREQ(s.c_str(), s2.c_str());
+}
+
+// Tests dumping.
+TEST_F(ConstructedFromDataBufferTest, ConstructedFromDataDump) {
+ const char kExpected[] =
+ "0x00000000: ef be ad de | ....\n";
+ std::string s = buffer->Dump();
+ EXPECT_STREQ(kExpected, s.c_str());
+}
+
+// Tests emtpy slicing.
+TEST_F(ConstructedFromDataBufferTest, ConstructedFromDataSlice) {
+ boost::scoped_ptr<Buffer> slice(buffer->MakeSlice(0, 0));
+ EXPECT_EQ(slice->Length(), 0);
+}
+
+// Tests slicing.
+TEST_F(ConstructedFromDataBufferTest, ConstructedFromDataSlice2) {
+ boost::scoped_ptr<Buffer> slice(buffer->MakeSlice(0, 2));
+ EXPECT_EQ(slice->Length(), 2);
+ uint16_t u16;
+ slice->Get(0, &u16);
+ EXPECT_EQ(0xbeef, u16);
+}
+
+// Tests dumping.
+TEST(BufferTest, DumpTest) {
+ const char kData[] = "Hello";
+ const char kExpected[] =
+ "0x00000000: 48 65 6c 6c 6f 00 "
+ " | Hello.\n";
+ Buffer buffer(kData, sizeof(kData));
+ std::string s = buffer.Dump();
+ EXPECT_STREQ(kExpected, s.c_str());
+}
+
+#if 0
+// Tests writing to a file.
+TEST(BufferTest, FileTest) {
+ const char kData[] = "Hello";
+ Buffer buffer(kData, sizeof(kData));
+ string out;
+ FileCloser file(MutableStringFile("file", &out,
+ DO_NOT_TAKE_OWNERSHIP,
+ DO_NOT_ALLOW_MMAP));
+ buffer.WriteOrDie(file.get());
+ EXPECT_STREQ(kData, out.c_str());
+}
+
+TEST(BufferTest, WritePathOrDieTest) {
+ const char kData[] = "Hello";
+ Buffer buffer(kData, sizeof(kData));
+ buffer.WriteToPathOrDie("/dev/null");
+}
+#endif
+
+// Tests appending.
+TEST(BufferTest, AppendBuffer) {
+ const char kData1[] = "Hello ";
+ const char kData2[] = "World";
+ Buffer buffer1(kData1, sizeof(kData1) - 1);
+ EXPECT_EQ(sizeof(kData1) - 1, buffer1.Length());
+ Buffer buffer2(kData2, sizeof(kData2));
+ EXPECT_EQ(sizeof(kData2), buffer2.Length());
+ Buffer buffer;
+ EXPECT_EQ(0, buffer.Length());
+ buffer.Append(Buffer());
+ buffer.Append(buffer1);
+ buffer.Append(buffer2);
+ std::string s;
+ buffer.Get(0, &s);
+ EXPECT_STREQ("Hello World", s.c_str());
+}
+
+// Tests operator==
+TEST(Buffer, OpEqualTrue) {
+ Buffer b1;
+ Buffer b2;
+ EXPECT_EQ(b1, b2);
+ b1.Append(uint8_t(1));
+ b2.Append(uint8_t(1));
+ EXPECT_EQ(b1, b2);
+}
+
+// Tests operator==
+TEST(Buffer, OpEqualFalse) {
+ Buffer empty;
+ Buffer b1;
+ Buffer b2;
+ Buffer b12;
+ b1.Append(uint8_t(1));
+ b2.Append(uint8_t(2));
+ b12.Append(uint8_t(1));
+ b12.Append(uint8_t(2));
+ EXPECT_NE(empty, b1);
+ EXPECT_NE(b1, empty);
+ EXPECT_NE(b1, b2);
+ EXPECT_NE(b1, b12);
+ EXPECT_NE(b12, b1);
+}
+
+TEST(Buffer, Dump) {
+ Buffer b;
+ b.Append(uint8_t(1));
+ std::string s = b.Dump();
+}
+
+} // namespace
+} // namespace glibusb
diff --git a/aos/common/glibusb/ghexdump.cc b/aos/common/glibusb/ghexdump.cc
new file mode 100644
index 0000000..66b22a3
--- /dev/null
+++ b/aos/common/glibusb/ghexdump.cc
@@ -0,0 +1,53 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Modified by FRC Team 971.
+//
+
+#include "ghexdump.h"
+
+#include <sstream>
+#include <iomanip>
+#include <stddef.h>
+#include <stdint.h>
+#include <cstring>
+
+namespace glibusb {
+
+std::string Dump(const void *buf, size_t n) {
+ const int kBytesPerRow = 16;
+ const uint8_t *p = static_cast<const uint8_t *>(CHECK_NOTNULL(buf));
+ std::stringstream dump;
+ for (uint32_t i = 0; i < n;) {
+ dump << "0x"
+ << std::hex << std::setw(8) << std::setfill('0') << i
+ << ": ";
+ for (int j = 0; j < kBytesPerRow; ++j) {
+ auto o = i + j;
+ if (o < n) {
+ auto b = p[o];
+ dump << std::hex << std::setw(2) << std::setfill('0') << int(b) << ' ';
+ } else {
+ dump << " ";
+ }
+ }
+ dump << " | ";
+ for (int j = 0; j < kBytesPerRow; ++j) {
+ auto o = i + j;
+ if (o == n) {
+ break;
+ }
+ auto b = p[o];
+ if (b > ' ' && b <= '~') {
+ dump << char(b);
+ } else {
+ dump << '.';
+ }
+ }
+ dump << std::endl;
+ i += kBytesPerRow;
+ }
+ return dump.str();
+}
+
+} // namespace glibusb
+
diff --git a/aos/common/glibusb/ghexdump.h b/aos/common/glibusb/ghexdump.h
new file mode 100644
index 0000000..e775ce8
--- /dev/null
+++ b/aos/common/glibusb/ghexdump.h
@@ -0,0 +1,15 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+
+#ifndef _GLIBUSB_GHEXDUMP_H_
+#define _GLIBUSB_GHEXDUMP_H_
+
+#include <stddef.h>
+#include <string>
+
+namespace glibusb {
+
+std::string Dump(const void *buf, size_t n);
+
+} // namespace glibusb
+
+#endif // _GLIBUSB_GHEXDUMP_H_
diff --git a/aos/common/glibusb/glibusb.cc b/aos/common/glibusb/glibusb.cc
new file mode 100644
index 0000000..576e35c
--- /dev/null
+++ b/aos/common/glibusb/glibusb.cc
@@ -0,0 +1,316 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+
+#include "glibusb.h"
+
+#include <cstdio>
+#include <sstream>
+#include <iomanip>
+#include <string>
+#include <glog/logging.h>
+#include <libusb.h>
+
+#include "glibusb_device_internal.h"
+
+namespace glibusb {
+
+namespace {
+bool safe_strtou32(const std::string &s, uint32_t *value) {
+ CHECK_NOTNULL(value);
+ std::stringstream stream(s);
+ stream >> std::dec;
+ return stream >> *value;
+}
+
+bool safe_strtou32_hex(const std::string &s, uint32_t *value) {
+ CHECK_NOTNULL(value);
+ std::stringstream stream(s);
+ stream >> std::hex;
+ return stream >> *value;
+}
+}
+
+std::string DeviceLocation::ToString() const
+{
+ return DeviceLocationToString(*this);
+}
+
+std::string DeviceLocationToString(const DeviceLocation &location) {
+ std::stringstream stream;
+ stream
+ << std::dec << std::setw(3) << std::setfill('0')
+ << static_cast<int>(location.bus_number)
+ << ":"
+ << std::dec << std::setw(3) << std::setfill('0')
+ << static_cast<int>(location.device_address);
+ return stream.str();
+}
+
+std::ostream &operator <<(std::ostream &out,
+ const DeviceLocation &location) {
+ out << DeviceLocationToString(location);
+ return out;
+}
+
+std::string VendorProductIdToString(const VendorProductId &vendor_product_id) {
+ std::stringstream stream;
+ stream
+ << std::hex << std::setw(4) << std::setfill('0')
+ << static_cast<int>(vendor_product_id.vendor_id)
+ << ":"
+ << std::hex << std::setw(4) << std::setfill('0')
+ << static_cast<int>(vendor_product_id.product_id);
+ return stream.str();
+}
+
+std::string DeviceLocationAndId::ToString() const
+{
+ return DeviceLocationAndIdToString(*this);
+}
+
+std::string DeviceLocationAndIdToString(const DeviceLocationAndId &location_and_id) {
+ return DeviceLocationToString(location_and_id.location) + " " +
+ VendorProductIdToString(location_and_id.id);
+}
+
+std::ostream &operator <<(std::ostream &out,
+ const DeviceLocationAndId &location_and_id) {
+ out << DeviceLocationAndIdToString(location_and_id);
+ return out;
+}
+
+//////////////////////////////////////////////////////////////////////
+
+Libusb::Libusb() {
+ CHECK_EQ(libusb_init(&libusb_context_), 0);
+}
+
+Libusb::~Libusb() {
+ libusb_exit(libusb_context_);
+}
+
+void Libusb::SetDebug(int level) {
+ libusb_set_debug(libusb_context_, level);
+}
+
+void Libusb::FindDeviceLocationAndId(std::vector<DeviceLocationAndId> *result) {
+ CHECK_NOTNULL(result);
+ struct libusb_device **devices_head;
+ CHECK_GE(libusb_get_device_list(libusb_context_, &devices_head), 0);
+ CHECK_NOTNULL(devices_head);
+
+ for (struct libusb_device **devices = devices_head;
+ *devices != NULL; ++devices) {
+ struct libusb_device_descriptor descriptor;
+ CHECK_GE(libusb_get_device_descriptor(*devices, &descriptor), 0);
+ VLOG(2) << "idVendor = 0x" << std::hex << descriptor.idVendor
+ << " idProduct = 0x" << std::hex << descriptor.idProduct;
+ DeviceLocationAndId dev_location_id;
+ dev_location_id.location.bus_number = libusb_get_bus_number(*devices);
+ dev_location_id.location.device_address =
+ libusb_get_device_address(*devices);
+ dev_location_id.id.vendor_id = descriptor.idVendor;
+ dev_location_id.id.product_id = descriptor.idProduct;
+ result->push_back(dev_location_id);
+ }
+ libusb_free_device_list(devices_head, /*unref_devices=*/ 1);
+}
+
+// Find a single device that matches the vendor_id and product_id, and
+// optionally bus_number and device_address. CHECK if more than one device is
+// found or no devices are found.
+//
+// This is the implementation behind FindSingleMatchingDeviceAtLocationOrLose()
+// and FindSingleMatchingDeviceOrLose().
+static libusb_device_handle *FindSingleDevice(
+ struct libusb_context *context,
+ bool match_location,
+ const DeviceLocationAndId &dev_location_id) {
+ struct libusb_device **devices_head;
+ CHECK_GE(libusb_get_device_list(context, &devices_head), 0);
+ CHECK_NOTNULL(devices_head);
+
+ struct libusb_device *matching_device = NULL;
+ for (struct libusb_device **devices = devices_head;
+ *devices != NULL; ++devices) {
+ if (match_location) {
+ uint8_t device_bus_number = libusb_get_bus_number(*devices);
+ uint8_t device_device_address = libusb_get_device_address(*devices);
+ if (device_bus_number != dev_location_id.location.bus_number ||
+ device_device_address != dev_location_id.location.device_address)
+ continue;
+ }
+
+ struct libusb_device_descriptor descriptor;
+ CHECK_GE(libusb_get_device_descriptor(*devices, &descriptor), 0);
+ VLOG(2) << "idVendor = 0x" << std::hex << descriptor.idVendor
+ << " idProduct = 0x" << std::hex << descriptor.idProduct;
+ if (descriptor.idVendor == dev_location_id.id.vendor_id &&
+ descriptor.idProduct == dev_location_id.id.product_id) {
+ CHECK(matching_device == NULL) << ": found multiple matching devices";
+ matching_device = *devices;
+ }
+ }
+ if (match_location) {
+ int bus_number = static_cast<int>(dev_location_id.location.bus_number);
+ int device_address =
+ static_cast<int>(dev_location_id.location.device_address);
+ CHECK(matching_device != NULL)
+ << ": no matching device found for "
+ << "vid=" << std::hex << dev_location_id.id.vendor_id << ", "
+ << "pid=" << std::hex << dev_location_id.id.product_id << ", "
+ << "bus_number=" << std::dec << bus_number << ", "
+ << "device_address=" << std::dec << device_address;
+ } else {
+ const int vendor_id = dev_location_id.id.vendor_id;
+ const int product_id = dev_location_id.id.product_id;
+ CHECK(matching_device != NULL) << ": no matching device found for "
+ << "vid=" << std::hex << vendor_id << ", "
+ << "pid=" << std::hex << product_id;
+ }
+
+ struct libusb_device_handle *handle = NULL;
+ if (matching_device != NULL) {
+ int return_value = libusb_open(matching_device, &handle);
+ if (return_value < 0) {
+ // TODO(charliehotel): this must not be FATAL.
+ LOG(FATAL) << "Failed to open device: "
+ << libusb_error_name(return_value);
+ }
+ CHECK_NOTNULL(handle); // should never happen
+ }
+ libusb_free_device_list(devices_head, /*unref_devices=*/ 1);
+ return handle;
+}
+
+UsbDevice *Libusb::FindSingleMatchingDeviceAtLocationOrLose(
+ const DeviceLocationAndId &dev_location_id) {
+ return CHECK_NOTNULL(FindSingleMatchingDeviceAtLocation(dev_location_id));
+}
+
+UsbDevice *Libusb::FindSingleMatchingDeviceAtLocation(
+ const DeviceLocationAndId &dev_location_id) {
+ auto handle = FindSingleDevice(libusb_context_,
+ /* match_location= */ true,
+ dev_location_id);
+ if (handle == NULL) {
+ return NULL;
+ } else {
+ return new PhysicalUsbDevice(libusb_context_, handle);
+ }
+}
+
+UsbDevice *Libusb::FindSingleMatchingDeviceOrLose(
+ const VendorProductId &id) {
+ return CHECK_NOTNULL(FindSingleMatchingDeviceOrLose(id));
+}
+
+UsbDevice *Libusb::FindSingleMatchingDevice(
+ const VendorProductId &id) {
+ DeviceLocationAndId dev_location_id;
+ dev_location_id.id = id;
+ auto handle = FindSingleDevice(libusb_context_,
+ /* match_location= */ false,
+ dev_location_id);
+ if (handle == NULL) {
+ return NULL;
+ } else {
+ return new PhysicalUsbDevice(libusb_context_, handle);
+ }
+}
+
+void Libusb::FindDeviceBySpecification(
+ const std::string &target_vendor_product_id,
+ const std::string &target_device_location,
+ DeviceLocationAndId *dev_location_id) {
+ std::vector<VendorProductId> target_ids;
+ ParseProductVendorString(target_vendor_product_id, &target_ids);
+ FindSingleDeviceMatchingTargetIds(target_ids, target_device_location,
+ dev_location_id);
+}
+
+/*static*/ void Libusb::ParseProductVendorString(
+ const std::string &target_vendor_product_id,
+ std::vector<VendorProductId> *target_ids) {
+ CHECK_NE(target_vendor_product_id, "");
+ CHECK_EQ(target_vendor_product_id.size(), 9);
+ CHECK_EQ(target_vendor_product_id[4], ':');
+ uint32_t vendor_id;
+ CHECK(safe_strtou32_hex(
+ target_vendor_product_id.substr(0, 4), &vendor_id));
+ uint32_t product_id;
+ CHECK(safe_strtou32_hex(
+ target_vendor_product_id.substr(5, 4), &product_id));
+ VendorProductId temp;
+ temp.vendor_id = vendor_id;
+ temp.product_id = product_id;
+ target_ids->push_back(temp);
+}
+
+/*static*/ void Libusb::ParseDeviceLocationString(
+ const std::string &target_device_location, DeviceLocation *location) {
+ CHECK_EQ(target_device_location.size(), 7);
+ CHECK_EQ(target_device_location[3], ':');
+ uint32_t parsed_bus_number;
+ CHECK(safe_strtou32(
+ target_device_location.substr(0, 3), &parsed_bus_number));
+ uint32_t parsed_device_address;
+ CHECK(safe_strtou32(
+ target_device_location.substr(4, 3), &parsed_device_address));
+ location->bus_number = parsed_bus_number;
+ location->device_address = parsed_device_address;
+}
+
+/*static*/ void Libusb::FindSingleDeviceMatchingTargetId(
+ const VendorProductId &target_id,
+ const std::string &target_device_location,
+ DeviceLocationAndId *dev_location_id) {
+ std::vector<VendorProductId> target_ids;
+ target_ids.push_back(target_id);
+ FindSingleDeviceMatchingTargetIds(
+ target_ids, target_device_location, dev_location_id);
+}
+
+void Libusb::FindSingleDeviceMatchingTargetIds(
+ const std::vector<VendorProductId> &target_ids,
+ const std::string &target_device_location,
+ DeviceLocationAndId *result_dev_location_id) {
+
+ bool have_target_device_location = (target_device_location != "");
+ DeviceLocation location;
+ if (have_target_device_location) {
+ ParseDeviceLocationString(target_device_location, &location);
+ }
+
+ // Get the location and vendor/product IDs for all attached devices.
+ std::vector<DeviceLocationAndId> dev_location_ids;
+ FindDeviceLocationAndId(&dev_location_ids);
+
+ // Filter the list by target parameters. Make sure that exactly one device
+ // is found.
+ bool found_exactly_one_device = false;
+ for (const auto &dev_location_id : dev_location_ids) {
+ if (have_target_device_location) {
+ if (dev_location_id.location.bus_number != location.bus_number ||
+ dev_location_id.location.device_address != location.device_address)
+ continue;
+ }
+
+ bool found_matching_product_vendor_id = false;
+ for (const auto &target : target_ids) {
+ if (target.vendor_id == dev_location_id.id.vendor_id &&
+ target.product_id == dev_location_id.id.product_id) {
+ found_matching_product_vendor_id = true;
+ break;
+ }
+ }
+ if (!found_matching_product_vendor_id)
+ continue;
+
+ CHECK(!found_exactly_one_device);
+ found_exactly_one_device = true;
+ *result_dev_location_id = dev_location_id;
+ }
+ CHECK(found_exactly_one_device);
+}
+
+} // namespace glibusb
diff --git a/aos/common/glibusb/glibusb.gyp b/aos/common/glibusb/glibusb.gyp
new file mode 100644
index 0000000..e081d67
--- /dev/null
+++ b/aos/common/glibusb/glibusb.gyp
@@ -0,0 +1,26 @@
+{
+ 'targets': [
+ {
+ 'target_name': 'gbuffer',
+ 'type': 'static_library',
+ 'sources': [
+ 'gbuffer.cc',
+ 'ghexdump.cc',
+ ],
+ 'dependencies': [
+ '<(AOS)/build/aos.gyp:logging',
+ ],
+ },
+ {
+ 'target_name': 'gbuffer_test',
+ 'type': 'executable',
+ 'sources': [
+ 'gbuffer_test.cc',
+ ],
+ 'dependencies': [
+ 'gbuffer',
+ '<(EXTERNALS):gtest',
+ ],
+ },
+ ],
+}
diff --git a/aos/common/glibusb/glibusb.h b/aos/common/glibusb/glibusb.h
new file mode 100644
index 0000000..e2afb0f
--- /dev/null
+++ b/aos/common/glibusb/glibusb.h
@@ -0,0 +1,229 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Wrapper for libusb.
+
+#ifndef _GLIBUSB_GLIBUSB_H_
+#define _GLIBUSB_GLIBUSB_H_
+
+#include <stdint.h>
+#include <iosfwd>
+#include <string>
+#include <vector>
+
+#include "glibusb_endpoint.h"
+
+extern "C" {
+struct libusb_context;
+}
+
+namespace glibusb {
+
+// Structure to hold the physical location on the USB bus of the device.
+struct DeviceLocation {
+ DeviceLocation() : bus_number(0), device_address(0) {}
+ DeviceLocation(uint8_t new_bus_number, uint8_t new_device_address)
+ : bus_number(new_bus_number), device_address(new_device_address) {}
+
+ bool operator==(const struct DeviceLocation &rhs) const {
+ return ((bus_number == rhs.bus_number) &&
+ (device_address == rhs.device_address));
+ }
+
+ std::string ToString() const;
+
+ uint8_t bus_number;
+ uint8_t device_address;
+};
+
+// Returns a string representing the DeviceLocation.
+std::string DeviceLocationToString(const DeviceLocation &location);
+std::ostream &operator <<(std::ostream &out,
+ const DeviceLocation &location);
+
+
+// Structure to hold the USB vendor and product ids for a device.
+struct VendorProductId {
+ VendorProductId() : vendor_id(0), product_id(0) {}
+ VendorProductId(uint16_t new_vendor_id, uint16_t new_product_id)
+ : vendor_id(new_vendor_id), product_id(new_product_id) {}
+
+ bool operator==(const struct VendorProductId &rhs) const {
+ return ((vendor_id == rhs.vendor_id) &&
+ (product_id == rhs.product_id));
+ }
+
+ std::string ToString() const;
+
+ uint16_t vendor_id;
+ uint16_t product_id;
+};
+
+// Returns a string representing the VendorProductId.
+std::string VendorProductIdToString(const VendorProductId &vendor_product_id);
+
+// Structure to hold the location and id of a device. This is enough to
+// uniquely identify the device redundantly.
+struct DeviceLocationAndId {
+ DeviceLocationAndId() {}
+ DeviceLocationAndId(const DeviceLocation &new_location,
+ const VendorProductId &new_id)
+ : location(new_location), id(new_id)
+ {}
+
+ bool operator==(const struct DeviceLocationAndId &rhs) const {
+ return ((location == rhs.location) &&
+ (id == rhs.id));
+ }
+
+ std::string ToString() const;
+
+ DeviceLocation location;
+ VendorProductId id;
+};
+
+// Returns a string representing the DeviceLocation and provides a stream
+// operator for logging.
+std::string DeviceLocationAndIdToString(const DeviceLocationAndId &location_and_id);
+std::ostream &operator <<(std::ostream &out,
+ const DeviceLocationAndId &location_and_id);
+
+
+
+// Provides an interface to an individual USB device.
+class UsbDevice {
+ public:
+ explicit UsbDevice(VendorProductId vendor_product_id)
+ : vendor_product_id_(vendor_product_id) {}
+ virtual ~UsbDevice() {}
+
+ // Activates an alternate setting; returns true on success.
+ bool SetAlternateSetting(int setting) {
+ return DoSetAlternateSetting(setting);
+ }
+
+ // Returns the first endpoint to match the direction and transfer types.
+ // Caller is responsible for freeing the endpoint.
+ UsbInEndpoint *FindInEndpoint(UsbEndpoint::TransferType endpoint) {
+ return DoFindInEndpoint(endpoint);
+ }
+ UsbOutEndpoint *FindOutEndpoint(UsbEndpoint::TransferType endpoint) {
+ return DoFindOutEndpoint(endpoint);
+ }
+
+ // Returns the endpoint at the specified address.
+ // Caller is responsible for freeing the endpoint.
+ // Virtual for testing.
+ UsbInEndpoint *InEndpoint(int number) { return DoInEndpoint(number); }
+ UsbOutEndpoint *OutEndpoint(int number) { return DoOutEndpoint(number); }
+
+ // Returns the vendor and product ids.
+ VendorProductId GetVendorAndProductId() { return vendor_product_id_; }
+
+ struct DeviceLocationAndId Id() { return DoDeviceLocationAndId(); }
+
+ protected:
+ VendorProductId vendor_product_id_;
+
+ private:
+ friend class Libusb; // For private constructor.
+
+ virtual bool DoSetAlternateSetting(int setting) = 0;
+ virtual UsbInEndpoint *DoFindInEndpoint(
+ UsbEndpoint::TransferType endpoint) = 0;
+ virtual UsbOutEndpoint *DoFindOutEndpoint(
+ UsbEndpoint::TransferType endpoint) = 0;
+ virtual UsbInEndpoint *DoInEndpoint(int number) = 0;
+ virtual UsbOutEndpoint *DoOutEndpoint(int number) = 0;
+ virtual struct DeviceLocationAndId DoDeviceLocationAndId() = 0;
+
+ UsbDevice(const UsbDevice &) = delete;
+ void operator=(const UsbDevice &) = delete;
+};
+
+
+// Provides RAII-style libusb initialization.
+class Libusb {
+ public:
+ Libusb();
+ ~Libusb();
+
+ // Sets the debug level.
+ void SetDebug(int level);
+
+ // Returns the locations, vendor ids, and product ids of all attached devices.
+ void FindDeviceLocationAndId(std::vector<DeviceLocationAndId> *result);
+
+ // Finds and returns exactly one device with the matching vendor and
+ // product ID or CHECKs.
+ UsbDevice *FindSingleMatchingDeviceOrLose(const VendorProductId &id);
+ // Does the same, but returns NULL on failure rather than CHECKing.
+ UsbDevice *FindSingleMatchingDevice(const VendorProductId &id);
+
+ // Finds and returns exactly one device with the matching vendor and
+ // product ID, at the specified bus number and device address, or CHECKs.
+ UsbDevice *FindSingleMatchingDeviceAtLocationOrLose(
+ const DeviceLocationAndId &dev_location_id);
+ // Does the same, but returns NULL on failure rather than CHECKing.
+ UsbDevice *FindSingleMatchingDeviceAtLocation(
+ const DeviceLocationAndId &dev_location_id);
+
+ // Finds exactly one device that matches whatever parameters were specified,
+ // or CHECKs.
+ //
+ // Args:
+ // target_vendor_product_id: a vendor/product ID pair to search for,
+ // or empty string.
+ // target_device_location: a device location to search for, or empty string.
+ // [out] dev_location_id: The location and Ids of the chosen device.
+ void FindDeviceBySpecification(
+ const std::string &target_vendor_product_id,
+ const std::string &target_device_location,
+ DeviceLocationAndId *dev_location_id);
+
+ // Finds exactly one device matching the specified parameters or checks.
+ //
+ // Args:
+ // target_ids: a list of product and vendor ids that match.
+ // target_device_location: a string with the device location, or an empty
+ // string to search all locations.
+ // [out] dev_location_id: The location and Ids of the chosen device.
+ void FindSingleDeviceMatchingTargetId(
+ const VendorProductId &target_id,
+ const std::string &target_device_location,
+ DeviceLocationAndId *dev_location_id);
+
+ // Finds exactly one device matching the specified parameters or checks.
+ //
+ // Args:
+ // target_ids: a list of product and vendor ids that match.
+ // target_device_location: a string with the device location, or an empty
+ // string to search all locations.
+ // [out] dev_location_id: The location and Ids of the chosen device.
+ void FindSingleDeviceMatchingTargetIds(
+ const std::vector<VendorProductId> &target_ids,
+ const std::string &target_device_location,
+ DeviceLocationAndId *dev_location_id);
+
+ // Parses a vendor id and product id string, and appends the result on
+ // target_ids.
+ // The string must be in the form VVVV:PPPP, where VVVV is a 4-digit hex
+ // vendor id and PPPP is a 4-digit hex product id.
+ static void ParseProductVendorString(const std::string &target_vendor_product_id,
+ std::vector<VendorProductId> *target_ids);
+
+ static void ParseDeviceLocationString(const std::string &target_device_location,
+ DeviceLocation *location);
+
+ // TODO(charliehotel): add richer ways to retrieve device handles.
+
+ private:
+ // Libusb handle
+ struct libusb_context *libusb_context_;
+
+ Libusb(const Libusb &) = delete;
+ void operator=(const Libusb &) = delete;
+};
+
+} // namespace glibusb
+
+#endif // _GLIBUSB_GLIBUSB_H_
diff --git a/aos/common/glibusb/glibusb_device.cc b/aos/common/glibusb/glibusb_device.cc
new file mode 100644
index 0000000..9fc8df3
--- /dev/null
+++ b/aos/common/glibusb/glibusb_device.cc
@@ -0,0 +1,227 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+
+#include <stddef.h>
+#include <glog/logging.h>
+#include <boost/function.hpp>
+#include <boost/scoped_ptr.hpp>
+#include <libusb.h>
+
+#include "glibusb.h"
+#include "glibusb_device_internal.h"
+#include "glibusb_endpoint.h"
+#include "glibusb_endpoint_internal.h"
+#include "glibusb_internal.h"
+
+namespace glibusb {
+
+namespace {
+VendorProductId GetVendorAndProductIdInternal(
+ struct libusb_device_handle *device_handle) {
+ CHECK_NOTNULL(device_handle);
+ libusb_device_descriptor desc;
+ libusb_device *dev = libusb_get_device(device_handle);
+ libusb_get_device_descriptor(dev, &desc);
+ return VendorProductId(desc.idVendor, desc.idProduct);
+}
+} // namespace
+
+PhysicalUsbDevice::PhysicalUsbDevice(struct libusb_context *context,
+ struct libusb_device_handle *handle)
+ : UsbDevice(GetVendorAndProductIdInternal(handle)),
+ libusb_context_(CHECK_NOTNULL(context)),
+ device_handle_(CHECK_NOTNULL(handle)) {
+ int r = libusb_claim_interface(device_handle_, 0);
+ // TODO(charliehotel): this must not be FATAL.
+ CHECK_GE(r, 0) << ": libusb_claim_interface failed, r=" << std::dec << r;
+
+ struct libusb_device *dev = libusb_get_device(device_handle_);
+ // TODO(charliehotel): this must not be FATAL.
+ CHECK(dev != NULL) << ": libusb_get_device failed";
+
+ struct libusb_device_descriptor desc;
+ r = libusb_get_device_descriptor(dev, &desc);
+ // TODO(charliehotel): this must not be FATAL.
+ CHECK_GE(r, 0) << ": libusb_get_device_descriptor failed";
+
+ VLOG(2) << "vid=0x" << std::hex << desc.idVendor
+ << ", pid=0x" << desc.idProduct;
+ VLOG(2) << " # of configurations = "
+ << static_cast<int>(desc.bNumConfigurations);
+
+ struct libusb_config_descriptor *config;
+ r = libusb_get_active_config_descriptor(dev, &config);
+ // TODO(charliehotel): this must not be FATAL.
+ CHECK_GE(r, 0) << ": libusb_get_active_config_descriptor failed";
+ // TODO(charliehotel): this must not be FATAL.
+ CHECK_NOTNULL(config);
+
+ if (config->bNumInterfaces != 1) {
+ VLOG(2) << "config->bNumInterfaces="
+ << static_cast<int>(config->bNumInterfaces)
+ << ", expected ony one";
+ }
+
+ if (VLOG_IS_ON(2)) {
+ for (int i = 0; i < config->bNumInterfaces; ++i) {
+ const struct libusb_interface *interface = config->interface + i;
+ const struct libusb_interface_descriptor *setting = interface->altsetting;
+
+ VLOG(2) << "bInterfaceNumber="
+ << static_cast<int>(setting->bInterfaceNumber);
+ VLOG(2) << "bAlternateSetting="
+ << static_cast<int>(setting->bAlternateSetting);
+ VLOG(2) << "bNumEndpoints="
+ << static_cast<int>(setting->bNumEndpoints);
+
+ for (int j = 0; j < setting->bNumEndpoints; ++j) {
+ const struct libusb_endpoint_descriptor *endpoint =
+ setting->endpoint + j;
+ switch (endpoint->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) {
+ case LIBUSB_TRANSFER_TYPE_CONTROL:
+ VLOG(2) << "control";
+ break;
+ case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
+ VLOG(2) << "iso";
+ break;
+ case LIBUSB_TRANSFER_TYPE_BULK:
+ VLOG(2) << "bulk";
+ break;
+ case LIBUSB_TRANSFER_TYPE_INTERRUPT:
+ VLOG(2) << "interrupt";
+ break;
+ default:
+ LOG(FATAL) << "unknown transfer type";
+ }
+ if ((endpoint->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) ==
+ LIBUSB_ENDPOINT_IN) {
+ VLOG(2) << " ep: 0x"
+ << std::hex << static_cast<int>(endpoint->bEndpointAddress)
+ << " (in)";
+ } else {
+ VLOG(2) << " ep: 0x"
+ << std::hex << static_cast<int>(endpoint->bEndpointAddress)
+ << " (out)";
+ }
+ VLOG(2) << " packet size="
+ << std::dec << static_cast<int>(endpoint->wMaxPacketSize);
+ VLOG(2) << " interval="
+ << std::dec << static_cast<int>(endpoint->bInterval);
+ }
+ }
+ }
+ libusb_free_config_descriptor(config);
+}
+
+PhysicalUsbDevice::~PhysicalUsbDevice() {
+ CHECK_NOTNULL(device_handle_);
+ libusb_close(device_handle_);
+ device_handle_ = nullptr;
+}
+
+bool PhysicalUsbDevice::DoSetAlternateSetting(int setting) {
+ CHECK_NOTNULL(device_handle_);
+ int r = libusb_set_interface_alt_setting(device_handle_, 0, setting);
+ return r == 0;
+}
+
+template <class UsbEndpointType>
+UsbEndpointType *PhysicalUsbDevice::MatchEndpoint(EndpointMatcher matcher) {
+ CHECK_NOTNULL(device_handle_);
+ struct libusb_config_descriptor *config;
+ libusb_device *dev = libusb_get_device(device_handle_);
+ const int r = libusb_get_active_config_descriptor(dev, &config);
+ // TODO(charliehotel): this must not be FATAL.
+ CHECK_GE(r, 0) << ": libusb_get_active_config_descriptor failed";
+ // TODO(charliehotel): this must not be FATAL.
+ CHECK_NOTNULL(config);
+ const struct libusb_interface *interface = config->interface;
+ const struct libusb_interface_descriptor *setting = interface->altsetting;
+ for (int j = 0; j < setting->bNumEndpoints; ++j) {
+ const struct libusb_endpoint_descriptor *descriptor = setting->endpoint + j;
+ if (matcher(descriptor)) {
+ UsbEndpointType *ans = new UsbEndpointType(
+ libusb_context_, device_handle_, descriptor);
+ libusb_free_config_descriptor(config);
+ return ans;
+ }
+ }
+ libusb_free_config_descriptor(config);
+ return NULL;
+}
+
+namespace {
+
+struct DescriptorHasAddressAndDirection {
+ DescriptorHasAddressAndDirection(int address, UsbEndpoint::DirectionType direction)
+ : address_(address), direction_(direction) {}
+
+ // Returns true if the descriptor provided has an address equal to the number
+ bool operator()(const struct libusb_endpoint_descriptor *descriptor) {
+ return (DescriptorToAddress(descriptor) == address_ &&
+ DescriptorToDirection(descriptor) == direction_);
+ }
+
+ int address_;
+ UsbEndpoint::DirectionType direction_;
+};
+
+// Returns true if the descriptor has a transfer type and direction equal to the
+// provided type and direction.
+struct DescriptorIsOfTypeAndDirection {
+ DescriptorIsOfTypeAndDirection(UsbEndpoint::TransferType transfer_type,
+ UsbEndpoint::DirectionType direction)
+ : transfer_type_(transfer_type), direction_(direction) {}
+
+ bool operator()(const struct libusb_endpoint_descriptor *descriptor) {
+ return (DescriptorToTransfer(descriptor) == transfer_type_ &&
+ DescriptorToDirection(descriptor) == direction_);
+ }
+
+ UsbEndpoint::TransferType transfer_type_;
+ UsbEndpoint::DirectionType direction_;
+};
+
+} // namespace
+
+UsbInEndpoint *PhysicalUsbDevice::DoInEndpoint(int number) {
+ CHECK_EQ(number & LIBUSB_ENDPOINT_ADDRESS_MASK, number)
+ << ": Endpoint out of range.";
+
+ DescriptorHasAddressAndDirection matcher(number, UsbEndpoint::kIn);
+ return MatchEndpoint<PhysicalUsbInEndpoint>(matcher);
+}
+
+UsbOutEndpoint *PhysicalUsbDevice::DoOutEndpoint(int number) {
+ CHECK_EQ(number & LIBUSB_ENDPOINT_ADDRESS_MASK, number)
+ << ": Endpoint out of range.";
+
+ DescriptorHasAddressAndDirection matcher(number, UsbEndpoint::kOut);
+ return MatchEndpoint<PhysicalUsbOutEndpoint>(matcher);
+}
+
+UsbInEndpoint *PhysicalUsbDevice::DoFindInEndpoint(
+ UsbEndpoint::TransferType endpoint) {
+
+ DescriptorIsOfTypeAndDirection matcher(endpoint, UsbEndpoint::kIn);
+ return MatchEndpoint<PhysicalUsbInEndpoint>(matcher);
+}
+
+UsbOutEndpoint *PhysicalUsbDevice::DoFindOutEndpoint(
+ UsbEndpoint::TransferType endpoint) {
+
+ DescriptorIsOfTypeAndDirection matcher(endpoint, UsbEndpoint::kOut);
+ return MatchEndpoint<PhysicalUsbOutEndpoint>(matcher);
+}
+
+struct DeviceLocationAndId PhysicalUsbDevice::DoDeviceLocationAndId() {
+ CHECK_NOTNULL(device_handle_);
+ libusb_device *device = ::libusb_get_device(device_handle_);
+ CHECK_NOTNULL(device);
+ struct DeviceLocationAndId dlid;
+ dlid.location.bus_number = ::libusb_get_bus_number(device);
+ dlid.location.device_address = ::libusb_get_device_address(device);
+ dlid.id = vendor_product_id_;
+ return dlid;
+}
+
+} // namespace glibusb
diff --git a/aos/common/glibusb/glibusb_device_internal.h b/aos/common/glibusb/glibusb_device_internal.h
new file mode 100644
index 0000000..4f2ad7f
--- /dev/null
+++ b/aos/common/glibusb/glibusb_device_internal.h
@@ -0,0 +1,55 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Wrapper for libusb's device that implements the UsbDevice interface.
+
+#ifndef _GLIBUSB_GLIBUSB_DEVICE_INTERNAL_H_
+#define _GLIBUSB_GLIBUSB_DEVICE_INTERNAL_H_
+
+#include <stdint.h>
+#include <utility>
+#include <boost/function.hpp>
+
+#include "glibusb.h"
+#include "glibusb_endpoint.h"
+
+namespace glibusb {
+
+// Provides an interface to an individual USB device.
+class PhysicalUsbDevice : public UsbDevice {
+ public:
+ virtual ~PhysicalUsbDevice();
+
+ private:
+ friend class Libusb; // For private constructor.
+ // Constructs a device given the context and handle.
+ // Frees the handle on destruction.
+ PhysicalUsbDevice(struct libusb_context *context,
+ struct libusb_device_handle *handle);
+
+ typedef boost::function<bool(const struct libusb_endpoint_descriptor *)>
+ EndpointMatcher;
+
+ // Iterates through all the endpoint descriptors for this device
+ // and allocates and allocates a UsbEndpointType for the first
+ // endpoint for which the matcher returns true.
+ template <class UsbEndpointType>
+ UsbEndpointType *MatchEndpoint(EndpointMatcher matcher);
+
+ virtual bool DoSetAlternateSetting(int setting);
+ virtual UsbInEndpoint *DoFindInEndpoint(UsbEndpoint::TransferType endpoint);
+ virtual UsbOutEndpoint *DoFindOutEndpoint(UsbEndpoint::TransferType endpoint);
+ virtual UsbInEndpoint *DoInEndpoint(int number);
+ virtual UsbOutEndpoint *DoOutEndpoint(int number);
+ virtual struct DeviceLocationAndId DoDeviceLocationAndId();
+
+ // Libusb context and handle used to interact with libusb.
+ struct libusb_context *libusb_context_;
+ struct libusb_device_handle *device_handle_;
+
+ PhysicalUsbDevice(const PhysicalUsbDevice &) = delete;
+ void operator=(const PhysicalUsbDevice &) = delete;
+};
+
+} // namespace glibusb
+
+#endif // _GLIBUSB_GLIBUSB_DEVICE_INTERNAL_H_
diff --git a/aos/common/glibusb/glibusb_endpoint.cc b/aos/common/glibusb/glibusb_endpoint.cc
new file mode 100644
index 0000000..30c8bd7
--- /dev/null
+++ b/aos/common/glibusb/glibusb_endpoint.cc
@@ -0,0 +1,362 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+
+#include "glibusb_endpoint.h"
+
+#include <stddef.h>
+#include <glog/logging.h>
+#include <boost/scoped_ptr.hpp>
+#include <boost/date_time/posix_time/posix_time.hpp>
+
+#include "gbuffer.h"
+#include "glibusb_endpoint_internal.h"
+#include "glibusb_internal.h"
+#include "glibusb_transfer.h"
+
+namespace glibusb {
+
+namespace {
+int LibusbGetMaxPacketSize(libusb_device_handle *handle,
+ unsigned char endpoint)
+{
+ libusb_device *device = CHECK_NOTNULL(libusb_get_device(handle));
+ return libusb_get_max_packet_size(device, endpoint);
+}
+
+int LibusbGetMaxIsoPacketSize(libusb_device_handle *handle,
+ unsigned char endpoint)
+{
+ libusb_device *device = CHECK_NOTNULL(libusb_get_device(handle));
+ return libusb_get_max_iso_packet_size(device, endpoint);
+}
+} // namespace
+
+Notification::Notification(bool prenotify)
+ : notified_(prenotify) {}
+
+bool Notification::HasBeenNotified() const {
+ boost::lock_guard<boost::mutex> lock(mutex_);
+ return notified_;
+}
+
+void Notification::WaitForNotification() const {
+ boost::unique_lock<boost::mutex> lock(mutex_);
+ notified_changed_.wait(
+ lock,
+ boost::bind(&Notification::HasBeenNotifiedUnlocked, this));
+}
+
+bool Notification::WaitForNotificationWithTimeout(int64_t milliseconds) const {
+ boost::unique_lock<boost::mutex> lock(mutex_);
+ auto timeout = boost::posix_time::milliseconds(milliseconds);
+ notified_changed_.timed_wait(
+ lock, timeout,
+ boost::bind(&Notification::HasBeenNotifiedUnlocked, this));
+ return notified_;
+}
+
+void Notification::Notify() {
+ boost::lock_guard<boost::mutex> lock(mutex_);
+ CHECK(!notified_) << ": already notified";
+ notified_ = true;
+ notified_changed_.notify_all();
+}
+
+bool Notification::HasBeenNotifiedUnlocked() const {
+ return notified_;
+}
+
+////////////////////////////////////////////////////////////////////////
+
+const int32_t UsbEndpoint::kMaxBulkTransferBytes;
+
+UsbEndpoint::UsbEndpoint(DirectionType direction, TransferType transfer,
+ int endpoint_address)
+ : direction_(direction),
+ transfer_(transfer),
+ endpoint_address_and_direction_(endpoint_address | direction) {
+ CHECK_EQ(endpoint_address_and_direction_ & 0x80, direction)
+ << ": Direction in address doesn't match specified direction.";
+ CHECK_EQ(endpoint_address_and_direction_ & 0x8f,
+ endpoint_address_and_direction_)
+ << ": Invalid endpoint address.";
+}
+
+////////////////////////////////////////////////////////////////////////
+
+UsbInEndpoint::UsbInEndpoint(TransferType transfer, int endpoint_address)
+ : UsbEndpoint(UsbEndpoint::kIn, transfer, endpoint_address) {
+}
+
+bool UsbInEndpoint::Read(Buffer *out) {
+ IoStatus status = ReadAtMostWithTimeout(kMaxBulkTransferBytes, 0, out);
+ return status == kSuccess;
+}
+
+UsbEndpoint::IoStatus
+UsbInEndpoint::ReadWithTimeout(int32_t timeout_milliseconds,
+ Buffer *out) {
+ return ReadAtMostWithTimeout(kMaxBulkTransferBytes,
+ timeout_milliseconds, out);
+}
+
+bool UsbInEndpoint::ReadAtMost(uint32_t length, Buffer *out) {
+ IoStatus status = ReadAtMostWithTimeout(length, 0, out);
+ return status == kSuccess;
+}
+
+UsbEndpoint::IoStatus UsbInEndpoint::ReadAtMostWithTimeout(
+ uint32_t length, int32_t timeout_milliseconds, Buffer *out) {
+ return DoRead(length, timeout_milliseconds, out, NULL);
+}
+
+UsbEndpoint::IoStatus UsbInEndpoint::ReadAtMostWithTimeoutAndNotification(
+ uint32_t length, int32_t timeout_milliseconds, Buffer *out,
+ Notification *quit) {
+ CHECK_NOTNULL(quit);
+ return DoRead(length, timeout_milliseconds, out, quit);
+}
+
+////////////////////////////////////////////////////////////////////////
+
+UsbOutEndpoint::UsbOutEndpoint(TransferType transfer, int endpoint_address)
+ : UsbEndpoint(UsbEndpoint::kOut, transfer, endpoint_address) {
+}
+
+bool UsbOutEndpoint::Write(const Buffer &buffer) {
+ IoStatus status = DoWrite(buffer, 0);
+ return status == kSuccess;
+}
+
+UsbEndpoint::IoStatus UsbOutEndpoint::WriteWithTimeout(const Buffer &buffer,
+ int32_t timeout_milliseconds) {
+ return DoWrite(buffer, timeout_milliseconds);
+}
+
+////////////////////////////////////////////////////////////////////////
+
+PhysicalUsbInEndpoint::PhysicalUsbInEndpoint(
+ struct libusb_context *context,
+ struct libusb_device_handle *handle,
+ const struct libusb_endpoint_descriptor *descriptor)
+ : UsbInEndpoint(DescriptorToTransfer(descriptor),
+ DescriptorToAddress(descriptor)),
+ libusb_context_(CHECK_NOTNULL(context)),
+ handle_(CHECK_NOTNULL(handle)) {
+ VLOG(1) << "0x" << std::hex << static_cast<int>(endpoint_address_and_direction())
+ << ", max_packet_size " << std::dec << descriptor->wMaxPacketSize;
+ CHECK_EQ(DescriptorToDirection(descriptor), UsbEndpoint::kIn);
+}
+
+PhysicalUsbInEndpoint::~PhysicalUsbInEndpoint() {
+ CHECK_NOTNULL(handle_);
+ handle_ = nullptr;
+ libusb_context_ = nullptr;
+}
+
+int PhysicalUsbInEndpoint::DoGetMaxPacketSize() {
+ CHECK_NOTNULL(handle_);
+ return LibusbGetMaxPacketSize(handle_, endpoint_address());
+}
+
+int PhysicalUsbInEndpoint::DoGetMaxIsoPacketSize() {
+ CHECK_NOTNULL(handle_);
+ return LibusbGetMaxIsoPacketSize(handle_, endpoint_address());
+}
+
+namespace {
+unsigned char LibUsbTransferType(int transfer_type) {
+ switch (transfer_type) {
+ case UsbEndpoint::kControl:
+ return LIBUSB_TRANSFER_TYPE_CONTROL;
+ case UsbEndpoint::kBulk:
+ return LIBUSB_TRANSFER_TYPE_BULK;
+ case UsbEndpoint::kInterrupt:
+ return LIBUSB_TRANSFER_TYPE_INTERRUPT;
+ case UsbEndpoint::kIsochronous:
+ return LIBUSB_TRANSFER_TYPE_ISOCHRONOUS;
+ default:
+ LOG(FATAL) << "transfer_type " << transfer_type << " is bogus";
+ }
+}
+
+const char kTransferTypeNameControl[] = "control";
+const char kTransferTypeNameBulk[] = "bulk";
+const char kTransferTypeNameInterrupt[] = "interrupt";
+const char kTransferTypeNameIsochronous[] = "isochronous";
+
+const char *TransferTypeName(int transfer_type) {
+ switch (transfer_type) {
+ case UsbEndpoint::kControl:
+ return kTransferTypeNameControl;
+ case UsbEndpoint::kBulk:
+ return kTransferTypeNameBulk;
+ case UsbEndpoint::kInterrupt:
+ return kTransferTypeNameInterrupt;
+ case UsbEndpoint::kIsochronous:
+ return kTransferTypeNameIsochronous;
+ default:
+ LOG(FATAL) << "transfer_type " << transfer_type << " is bogus";
+ }
+}
+} // namespace
+
+UsbEndpoint::IoStatus PhysicalUsbInEndpoint::DoRead(
+ uint32_t length, int32_t timeout_milliseconds, Buffer *out,
+ Notification *quit) {
+ CHECK_NOTNULL(handle_);
+ CHECK_GE(timeout_milliseconds, 0);
+ CHECK_NOTNULL(out);
+
+ VLOG(2) << "read on 0x" << std::hex << endpoint_address_and_direction()
+ << ", size 0x" << std::hex << length
+ << ", timeout " << std::dec << timeout_milliseconds << " [ms]";
+
+ boost::scoped_ptr<Buffer> whole_buffer(new Buffer());
+ whole_buffer->Resize(length);
+ void *p = whole_buffer->GetBufferPointer(length);
+ int transferred;
+ const unsigned int timeout = static_cast<unsigned int>(timeout_milliseconds);
+ int r;
+
+ unsigned char transfer_type = LibUsbTransferType(transfer());
+ r = do_sync_bulk_transfer(libusb_context_,
+ handle_, endpoint_address_and_direction(),
+ static_cast<unsigned char *>(p), length,
+ &transferred, timeout, transfer_type,
+ quit);
+
+ switch (r) {
+ case LIBUSB_SUCCESS:
+ {
+ size_t size_transferred = static_cast<size_t>(transferred);
+ whole_buffer->Resize(size_transferred);
+
+ VLOG(2)
+ << "read on 0x"
+ << std::hex << static_cast<int>(endpoint_address_and_direction())
+ << ", size_transferred=0x" << std::hex << size_transferred;
+ out->Copy(*whole_buffer);
+ return kSuccess;
+ }
+ case LIBUSB_ERROR_TIMEOUT:
+ VLOG(2) << "libusb_" << TransferTypeName(transfer())
+ << "_transfer timeout";
+ return kTimeout;
+ case LIBUSB_ERROR_IO:
+ VLOG(1) << "Device i/o error.";
+ return kFail;
+ case LIBUSB_ERROR_NO_DEVICE:
+ VLOG(1) << "Device disconnected.";
+ return kNoDevice;
+ case LIBUSB_ERROR_OTHER:
+ LOG(INFO) << "libusb_" << TransferTypeName(transfer())
+ << "_transfer failed with r=" << std::dec << r;
+ return kUnknown;
+ default:
+ // Most of these are more esoteric.
+ LOG(INFO) << "libusb_" << TransferTypeName(transfer())
+ << "_transfer failed with r=" << std::dec << r;
+ return kFail;
+ }
+}
+
+////////////////////////////////////////////////////////////////////////
+
+PhysicalUsbOutEndpoint::PhysicalUsbOutEndpoint(
+ struct libusb_context *context,
+ struct libusb_device_handle *handle,
+ const struct libusb_endpoint_descriptor *descriptor)
+ : UsbOutEndpoint(DescriptorToTransfer(descriptor),
+ DescriptorToAddress(descriptor)),
+ libusb_context_(CHECK_NOTNULL(context)),
+ handle_(CHECK_NOTNULL(handle)) {
+ VLOG(1) << "0x" << std::hex << static_cast<int>(endpoint_address_and_direction())
+ << ", max_packet_size " << std::dec << descriptor->wMaxPacketSize;
+ CHECK_EQ(DescriptorToDirection(descriptor), UsbEndpoint::kOut);
+}
+
+PhysicalUsbOutEndpoint::~PhysicalUsbOutEndpoint() {
+ CHECK_NOTNULL(handle_);
+ handle_ = nullptr;
+ libusb_context_ = nullptr;
+}
+
+int PhysicalUsbOutEndpoint::DoGetMaxPacketSize() {
+ CHECK_NOTNULL(handle_);
+ return LibusbGetMaxPacketSize(handle_, endpoint_address());
+}
+
+int PhysicalUsbOutEndpoint::DoGetMaxIsoPacketSize() {
+ CHECK_NOTNULL(handle_);
+ return LibusbGetMaxIsoPacketSize(handle_, endpoint_address());
+}
+
+UsbEndpoint::IoStatus PhysicalUsbOutEndpoint::DoWrite(
+ const Buffer &buffer, int32_t timeout_milliseconds) {
+ CHECK_NOTNULL(handle_);
+ CHECK_EQ(direction(), kOut);
+
+ VLOG(2) << "writing on 0x" << std::hex << endpoint_address_and_direction()
+ << ", length=" << std::dec << buffer.Length()
+ << ", timeout " << std::dec << timeout_milliseconds << " [ms]";
+
+ size_t length = buffer.Length();
+ const unsigned char *p =
+ static_cast<const unsigned char *>(buffer.GetBufferPointer(length));
+ const unsigned int timeout = static_cast<unsigned int>(timeout_milliseconds);
+
+ int transferred;
+ int r;
+
+ switch (transfer()) {
+ case kBulk:
+ VLOG(2) << "libusb_bulk_transfer, length=" << std::dec << length;
+ r = libusb_bulk_transfer(handle_, endpoint_address_and_direction(),
+ const_cast<unsigned char *>(p),
+ length, &transferred,
+ timeout);
+ VLOG(2) << "libusb_bulk_transfer, r=" << std::dec << r
+ << ", transferred=" << std::dec << transferred;
+ break;
+ case kInterrupt:
+ VLOG(2) << "libusb_interrupt_transfer, length="
+ << std::dec << length;
+ r = libusb_interrupt_transfer(handle_, endpoint_address_and_direction(),
+ const_cast<unsigned char *>(p),
+ length, &transferred,
+ timeout);
+ VLOG(2) << "libusb_interrupt_transfer, r=" << std::dec << r
+ << ", transferred=" << std::dec << transferred;
+ break;
+ default:
+ LOG(FATAL) << "bogus transfer() value";
+ }
+
+ size_t size_transferred;
+
+ switch (r) {
+ case LIBUSB_SUCCESS:
+ size_transferred = static_cast<size_t>(transferred);
+ CHECK_EQ(size_transferred, length);
+ return kSuccess;
+ case LIBUSB_ERROR_TIMEOUT:
+ VLOG(2) << "libusb_" << TransferTypeName(transfer()) << "_transfer timeout";
+ return kTimeout;
+ case LIBUSB_ERROR_IO:
+ VLOG(1) << "Device i/o error.";
+ return kFail;
+ case LIBUSB_ERROR_NO_DEVICE:
+ VLOG(1) << "Device disconnected.";
+ return kNoDevice;
+ case LIBUSB_ERROR_OTHER:
+ LOG(INFO) << "libusb_" << TransferTypeName(transfer())
+ << "_transfer failed with r=" << std::dec << r;
+ return kUnknown;
+ default:
+ VLOG(1) << "libusb_" << TransferTypeName(transfer())
+ << "_transfer failed, r=" << std::dec << r;
+ return kFail;
+ }
+}
+
+} // namespace glibusb
diff --git a/aos/common/glibusb/glibusb_endpoint.h b/aos/common/glibusb/glibusb_endpoint.h
new file mode 100644
index 0000000..6b1b1a7
--- /dev/null
+++ b/aos/common/glibusb/glibusb_endpoint.h
@@ -0,0 +1,181 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Usb Endpoint Interfaces.
+
+#ifndef _GLIBUSB_GLIBUSB_ENDPOINT_H_
+#define _GLIBUSB_GLIBUSB_ENDPOINT_H_
+
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <libusb.h>
+#include <boost/bind.hpp>
+#include <boost/thread.hpp>
+#include <boost/thread/condition.hpp>
+
+#include "gbuffer.h"
+
+#ifndef MAX_ISO_BUFFER_LENGTH
+#define MAX_ISO_BUFFER_LENGTH 32768
+#endif
+
+#ifndef MAX_BULK_BUFFER_LENGTH
+#define MAX_BULK_BUFFER_LENGTH 16384
+#endif
+
+#ifndef MAX_CTRL_BUFFER_LENGTH
+#define MAX_CTRL_BUFFER_LENGTH 4096
+#endif
+
+namespace glibusb {
+
+// Provides a base-class for all endpoints.
+class Buffer;
+
+class Notification {
+ public:
+ explicit Notification(bool prenotify = false);
+ bool HasBeenNotified() const;
+ void WaitForNotification() const;
+ bool WaitForNotificationWithTimeout(int64_t milliseconds) const;
+ void Notify();
+
+ private:
+ bool HasBeenNotifiedUnlocked() const;
+
+ mutable boost::mutex mutex_;
+ mutable boost::condition notified_changed_;
+ bool notified_;
+
+ Notification(const Notification &) = delete;
+ void operator=(const Notification &) = delete;
+};
+
+
+class UsbEndpoint {
+ public:
+ // The values for Direction are defined by the USB spec and must not be
+ // modified.
+ enum DirectionType { kIn = 0x80, kOut = 0x00 };
+ enum TransferType { kControl, kBulk, kInterrupt, kIsochronous };
+ enum IoStatus { kSuccess, kFail, kTimeout, kAbort, kNoDevice, kUnknown };
+
+ // The max transfer (in bytes) that the kernel supports.
+ static const int32_t kMaxBulkTransferBytes = MAX_BULK_BUFFER_LENGTH;
+
+ virtual ~UsbEndpoint() {}
+
+ // Returns the endpoint number.
+ int endpoint_address() const {
+ return endpoint_address_and_direction_ & LIBUSB_ENDPOINT_ADDRESS_MASK;
+ }
+
+ // Returns the direction.
+ DirectionType direction() const { return direction_; }
+
+ // Returns the transfer type.
+ TransferType transfer() const { return transfer_; }
+
+ // Returns the wMaxPacketSize value for this endpoint in the
+ // active device configuration.
+ int GetMaxPacketSize() { return DoGetMaxPacketSize(); }
+
+ // Returns the maximum packet size for this endpoint that
+ // can be sent or received during one microframe.
+ int GetMaxIsoPacketSize() { return DoGetMaxIsoPacketSize(); }
+
+ protected:
+ // Constructs an endpoint with the provided tranfser type and address.
+ UsbEndpoint(DirectionType direction, TransferType transfer,
+ int endpoint_address);
+
+ // Returns the address that libusb uses to refer to the endpoint. This
+ // includes the direction as the highest bit.
+ int endpoint_address_and_direction() const {
+ return endpoint_address_and_direction_;
+ }
+
+ private:
+ virtual int DoGetMaxPacketSize() = 0;
+ virtual int DoGetMaxIsoPacketSize() = 0;
+
+ // Endpoint type and direction.
+ DirectionType direction_;
+ TransferType transfer_;
+
+ // Endpoint address.
+ int endpoint_address_and_direction_;
+
+ UsbEndpoint(const UsbEndpoint &) = delete;
+ void operator=(const UsbEndpoint &) = delete;
+};
+
+// Provides an interface to allow reading from a USB endpoint.
+class UsbInEndpoint : public UsbEndpoint {
+ public:
+ // Constructs an endpoint with the provided transfer type and address.
+ UsbInEndpoint(TransferType transfer, int endpoint_address);
+ virtual ~UsbInEndpoint() {}
+
+ // Reads into the buffer from the endpoint until the transfer limit is
+ // reached, or a zero length packet/non full packet is received.
+ // Returns true on success. Waits forever.
+ bool Read(Buffer *out);
+
+ // Reads into the buffer from the endpoint until the transfer limit is
+ // reached, or a zero length packet/non full packet is received.
+ IoStatus ReadWithTimeout(int32_t timeout_milliseconds, Buffer *out);
+
+ // Reads into the buffer from the endpoint until the length is
+ // reached, or a zero length packet/non full packet is received.
+ // Returns true on success.
+ bool ReadAtMost(uint32_t length, Buffer *out);
+
+ // Reads into the buffer from the endpoint until the length is
+ // reached, or a zero length packet/non full packet is received.
+ IoStatus ReadAtMostWithTimeout(
+ uint32_t length, int32_t timeout_milliseconds, Buffer *out);
+
+ // Reads into the buffer from the endpoint until the length is
+ // reached, or a zero length packet/non full packet is received.
+ // Cancels the request when the notification is notified.
+ IoStatus ReadAtMostWithTimeoutAndNotification(
+ uint32_t length, int32_t timeout_milliseconds, Buffer *out,
+ Notification *quit);
+
+ private:
+ // Actually executes the read, with the length, timeout, buffer, and
+ // notification.
+ virtual IoStatus DoRead(
+ uint32_t length, int32_t timeout_milliseconds, Buffer *out,
+ Notification *quit) = 0;
+
+ UsbInEndpoint(const UsbInEndpoint &) = delete;
+ void operator=(const UsbInEndpoint &) = delete;
+};
+
+// Provides an interface to allow writing to a USB endpoint.
+class UsbOutEndpoint : public UsbEndpoint {
+ public:
+ // Constructs an endpoint with the provided tranfser type and address.
+ UsbOutEndpoint(TransferType transfer, int endpoint_address);
+ virtual ~UsbOutEndpoint() {}
+
+ // Writes the buffer to the endpoint. Returns true on success.
+ bool Write(const Buffer &buffer);
+
+ // Writes the buffer to the endpoint with timeout.
+ IoStatus WriteWithTimeout(const Buffer &buffer, int timeout_milliseconds);
+
+ private:
+ // Implements the actual write.
+ virtual IoStatus DoWrite(const Buffer &buffer,
+ int32_t timeout_milliseconds) = 0;
+
+ UsbOutEndpoint(const UsbOutEndpoint &) = delete;
+ void operator=(const UsbOutEndpoint &) = delete;
+};
+
+} // namespace glibusb
+
+#endif // _GLIBUSB_GLIBUSB_ENDPOINT_H_
diff --git a/aos/common/glibusb/glibusb_endpoint_internal.h b/aos/common/glibusb/glibusb_endpoint_internal.h
new file mode 100644
index 0000000..f19899e
--- /dev/null
+++ b/aos/common/glibusb/glibusb_endpoint_internal.h
@@ -0,0 +1,83 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Wrapper for libusb's endpoints.
+
+#ifndef _GLIBUSB_GLIBUSB_ENDPOINT_INTERNAL_H_
+#define _GLIBUSB_GLIBUSB_ENDPOINT_INTERNAL_H_
+
+#include <stddef.h>
+#include <stdint.h>
+#include <libusb.h>
+
+#include "glibusb_endpoint.h"
+
+class Notification;
+
+namespace glibusb {
+
+class Buffer;
+
+
+// Provides an interface to allow reading from a USB endpoint.
+class PhysicalUsbInEndpoint : public UsbInEndpoint {
+ public:
+ virtual ~PhysicalUsbInEndpoint();
+
+ private:
+ friend class PhysicalUsbDevice; // For constructor
+ // Constructs an endpoint given the context, handle, and a descriptor of the
+ // endpoint. The context and handle must remain valid throughout the
+ // lifetime of this object.
+ PhysicalUsbInEndpoint(struct libusb_context *context,
+ struct libusb_device_handle *handle,
+ const struct libusb_endpoint_descriptor *descriptor);
+
+ virtual int DoGetMaxPacketSize();
+ virtual int DoGetMaxIsoPacketSize();
+
+ // Actually executes the read, with the length, timeout, buffer, and
+ // notification.
+ virtual IoStatus DoRead(
+ uint32_t length, int32_t timeout_milliseconds, Buffer *out,
+ Notification *quit);
+
+ // Libusb handles and endpoint information.
+ struct libusb_context *libusb_context_;
+ struct libusb_device_handle *handle_;
+
+ PhysicalUsbInEndpoint(const PhysicalUsbInEndpoint &) = delete;
+ void operator=(const PhysicalUsbInEndpoint &) = delete;
+};
+
+// Provides an interface to allow writing to a USB endpoint.
+class PhysicalUsbOutEndpoint : public UsbOutEndpoint {
+ public:
+ virtual ~PhysicalUsbOutEndpoint();
+
+ private:
+ friend class PhysicalUsbDevice; // For constructor
+ // Constructs an endpoint given the context, handle, and a descriptor of the
+ // endpoint. The context and handle must remain valid throughout the
+ // lifetime of this object.
+ PhysicalUsbOutEndpoint(struct libusb_context *context,
+ struct libusb_device_handle *handle,
+ const struct libusb_endpoint_descriptor *descriptor);
+
+ virtual int DoGetMaxPacketSize();
+ virtual int DoGetMaxIsoPacketSize();
+
+ // Implements the actual write.
+ virtual IoStatus DoWrite(const Buffer &buffer,
+ int32_t timeout_milliseconds);
+
+ // Libusb handles and endpoint information.
+ struct libusb_context *libusb_context_;
+ struct libusb_device_handle *handle_;
+
+ PhysicalUsbOutEndpoint(const PhysicalUsbOutEndpoint &) = delete;
+ void operator=(const PhysicalUsbOutEndpoint &) = delete;
+};
+
+} // namespace glibusb
+
+#endif // _GLIBUSB_GLIBUSB_ENDPOINT_INTERNAL_H_
diff --git a/aos/common/glibusb/glibusb_internal.cc b/aos/common/glibusb/glibusb_internal.cc
new file mode 100644
index 0000000..f55dc2e
--- /dev/null
+++ b/aos/common/glibusb/glibusb_internal.cc
@@ -0,0 +1,49 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+
+#include "glibusb_internal.h"
+
+#include <libusb.h>
+
+#include "glog/logging.h"
+#include "glibusb_endpoint.h"
+
+namespace glibusb {
+
+// Converts libusb endpoint address to integer
+int DescriptorToAddress(const struct libusb_endpoint_descriptor *descriptor) {
+ return descriptor->bEndpointAddress & LIBUSB_ENDPOINT_ADDRESS_MASK;
+}
+
+// Converts libusb direction to UsbEndpoint direction.
+UsbEndpoint::DirectionType DescriptorToDirection(
+ const struct libusb_endpoint_descriptor *descriptor) {
+ if ((descriptor->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) ==
+ LIBUSB_ENDPOINT_IN) {
+ return UsbEndpoint::kIn;
+ } else {
+ return UsbEndpoint::kOut;
+ }
+}
+
+// Converts libusb transfer type to UsbEndpoint transfer type.
+UsbEndpoint::TransferType DescriptorToTransfer(
+ const struct libusb_endpoint_descriptor *descriptor) {
+ switch (descriptor->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) {
+ case LIBUSB_TRANSFER_TYPE_CONTROL:
+ return UsbEndpoint::kControl;
+ break;
+ case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
+ return UsbEndpoint::kIsochronous;
+ break;
+ case LIBUSB_TRANSFER_TYPE_BULK:
+ return UsbEndpoint::kBulk;
+ break;
+ case LIBUSB_TRANSFER_TYPE_INTERRUPT:
+ return UsbEndpoint::kInterrupt;
+ break;
+ default:
+ LOG(FATAL) << "bogus transfer type";
+ }
+}
+
+} // namespace glibusb
diff --git a/aos/common/glibusb/glibusb_internal.h b/aos/common/glibusb/glibusb_internal.h
new file mode 100644
index 0000000..8c34e5d
--- /dev/null
+++ b/aos/common/glibusb/glibusb_internal.h
@@ -0,0 +1,25 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Internal conversions between libusb descriptors and enums.
+
+#ifndef _GLIBUSB_GLIBUSB_INTERNAL_H_
+#define _GLIBUSB_GLIBUSB_INTERNAL_H_
+
+#include "glibusb_endpoint.h"
+
+namespace glibusb {
+
+// Converts libusb endpoint address to integer
+int DescriptorToAddress(const struct libusb_endpoint_descriptor *descriptor);
+
+// Converts libusb direction to UsbEndpoint direction.
+UsbEndpoint::DirectionType DescriptorToDirection(
+ const struct libusb_endpoint_descriptor *descriptor);
+
+// Converts libusb transfer type to UsbEndpoint transfer type.
+UsbEndpoint::TransferType DescriptorToTransfer(
+ const struct libusb_endpoint_descriptor *descriptor);
+
+} // namespace glibusb
+
+#endif // _GLIBUSB_GLIBUSB_INTERNAL_H_
diff --git a/aos/common/glibusb/glibusb_test.cc b/aos/common/glibusb/glibusb_test.cc
new file mode 100644
index 0000000..7a569cf
--- /dev/null
+++ b/aos/common/glibusb/glibusb_test.cc
@@ -0,0 +1,37 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+// Author: charliehotel@google.com (Christopher Hoover)
+
+#include "../glibusb.h"
+
+#include <sstream>
+#include <gtest/gtest.h>
+
+namespace glibusb {
+namespace testing {
+
+// Tests that DeviceLocationAndId gets streamed correctly.
+TEST(LibusbTest, LogsLocationAndID) {
+ DeviceLocationAndId location_and_id;
+ location_and_id.location.bus_number = 10;
+ location_and_id.location.device_address = 20;
+ location_and_id.id.vendor_id = 0x30;
+ location_and_id.id.product_id = 0x40;
+
+ std::stringstream out;
+ out << location_and_id;
+ EXPECT_EQ("010:020 0030:0040", out.str());
+}
+
+// Tests setup and teardown.
+TEST(LibusbTest, SetupTeardown) {
+ Libusb libusb;
+}
+
+// Tests SetDebug
+TEST(LibusbTest, SetDebug) {
+ Libusb libusb;
+ libusb.SetDebug(1);
+}
+
+} // namespace testing
+} // namespace glibusb
diff --git a/aos/common/glibusb/glibusb_transfer.cc b/aos/common/glibusb/glibusb_transfer.cc
new file mode 100644
index 0000000..a700cf2
--- /dev/null
+++ b/aos/common/glibusb/glibusb_transfer.cc
@@ -0,0 +1,136 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Alternative libusb call to do transfers that quits when
+// a notification is notified.
+//
+// This code was originally from third_party/libusb/libusb1/sync.c
+// and has been slightly modified to poll the notification.
+
+#include "glibusb_transfer.h"
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <sys/time.h>
+#include <gflags/gflags.h>
+#include <glog/logging.h>
+#include <libusb.h>
+
+#include "glibusb_endpoint.h"
+
+
+DEFINE_int32(notification_poll,
+ 60,
+ "Time in seconds to wait between checking the quit notification "
+ "when waiting for USB messages.");
+
+namespace {
+
+static bool GEQZero(const char* flagname, int32_t value) {
+ if (value < 0) {
+ printf("Invalid value for --%s: %d\n", flagname, value);
+ return false;
+ }
+ return true;
+}
+
+static const bool notification_poll_dummy = google::RegisterFlagValidator(
+ &FLAGS_notification_poll, &GEQZero);
+
+} // namespace
+
+
+namespace glibusb {
+
+namespace {
+// Static code from libusb1/sync.c
+void bulk_transfer_cb(struct libusb_transfer *transfer) {
+ int *completed = static_cast<int*>(transfer->user_data);
+ *completed = 1;
+ VLOG(3) << "actual_length=" << transfer->actual_length;
+ /* caller interprets results and frees transfer */
+}
+} // namespace
+
+int do_sync_bulk_transfer(
+ struct libusb_context *context,
+ struct libusb_device_handle *dev_handle,
+ unsigned char endpoint, unsigned char *buffer, int length,
+ int *transferred, unsigned int timeout, unsigned char type,
+ Notification *quit) {
+ struct libusb_transfer *transfer = libusb_alloc_transfer(0);
+ int completed = 0;
+ int r;
+
+ if (!transfer)
+ return LIBUSB_ERROR_NO_MEM;
+
+ libusb_fill_bulk_transfer(transfer, dev_handle, endpoint, buffer, length,
+ bulk_transfer_cb, &completed, timeout);
+ transfer->type = type;
+
+ r = libusb_submit_transfer(transfer);
+ if (r < 0) {
+ libusb_free_transfer(transfer);
+ return r;
+ }
+
+ while (!completed) {
+ struct timeval tv;
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+ r = libusb_handle_events_timeout_completed(context, &tv, &completed);
+ if (quit != NULL && quit->HasBeenNotified()) {
+ LOG(WARNING) << "Caught quit notification. Canceling transfer.";
+ r = LIBUSB_ERROR_TIMEOUT;
+ }
+ if (r < 0) {
+ if (r == LIBUSB_ERROR_INTERRUPTED) {
+ continue;
+ }
+ libusb_cancel_transfer(transfer);
+ while (!completed) {
+ struct timeval cancel_tv;
+ cancel_tv.tv_sec = (quit == NULL ? FLAGS_notification_poll : 60);
+ cancel_tv.tv_usec = 0;
+ if (libusb_handle_events_timeout_completed(context, &cancel_tv,
+ &completed) < 0) {
+ break;
+ }
+ }
+ libusb_free_transfer(transfer);
+ return r;
+ }
+ }
+
+ *transferred = transfer->actual_length;
+ switch (transfer->status) {
+ case LIBUSB_TRANSFER_COMPLETED:
+ r = 0;
+ break;
+ case LIBUSB_TRANSFER_TIMED_OUT:
+ r = LIBUSB_ERROR_TIMEOUT;
+ break;
+ case LIBUSB_TRANSFER_STALL:
+ r = LIBUSB_ERROR_PIPE;
+ break;
+ case LIBUSB_TRANSFER_OVERFLOW:
+ r = LIBUSB_ERROR_OVERFLOW;
+ break;
+ case LIBUSB_TRANSFER_NO_DEVICE:
+ r = LIBUSB_ERROR_NO_DEVICE;
+ break;
+ case LIBUSB_TRANSFER_ERROR:
+ case LIBUSB_TRANSFER_CANCELLED:
+ r = LIBUSB_ERROR_IO;
+ break;
+ default:
+ LOG(WARNING) << "unrecognised status code " << transfer->status;
+ r = LIBUSB_ERROR_OTHER;
+ }
+
+ libusb_free_transfer(transfer);
+ return r;
+}
+
+} // namespace glibusb
diff --git a/aos/common/glibusb/glibusb_transfer.h b/aos/common/glibusb/glibusb_transfer.h
new file mode 100644
index 0000000..9218049
--- /dev/null
+++ b/aos/common/glibusb/glibusb_transfer.h
@@ -0,0 +1,33 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Alternative libusb call to do transfers that quits when
+// a notification is notified.
+
+#ifndef _GLIBUSB_GLIBUSB_TRANSFER_H_
+#define _GLIBUSB_GLIBUSB_TRANSFER_H_
+
+extern "C" {
+struct libusb_context;
+struct libusb_device_handle;
+}
+
+class Notification;
+
+namespace glibusb {
+
+// Bulk transfer code cribbed from libusb1/sync.c
+// The difference between this and the original code is that the
+// transfer now accepts a notification to poll for the quit message.
+// When it receives a quit message on the notification, it cancels the transfer.
+// The provided API's don't support better reuse of the existing code from what
+// I can tell.
+int do_sync_bulk_transfer(
+ struct libusb_context *context,
+ struct libusb_device_handle *dev_handle,
+ unsigned char endpoint, unsigned char *buffer, int length,
+ int *transferred, unsigned int timeout, unsigned char type,
+ Notification *quit);
+
+} // namespace glibusb
+
+#endif // _GLIBUSB_GLIBUSB_TRANSFER_H_