summaryrefslogtreecommitdiff
path: root/src/components/transport_manager/src/usb/qnx/usb_connection.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/components/transport_manager/src/usb/qnx/usb_connection.cc')
-rw-r--r--src/components/transport_manager/src/usb/qnx/usb_connection.cc404
1 files changed, 404 insertions, 0 deletions
diff --git a/src/components/transport_manager/src/usb/qnx/usb_connection.cc b/src/components/transport_manager/src/usb/qnx/usb_connection.cc
new file mode 100644
index 0000000000..da5d30136b
--- /dev/null
+++ b/src/components/transport_manager/src/usb/qnx/usb_connection.cc
@@ -0,0 +1,404 @@
+/**
+ * \file usb_connection.cc
+ * \brief UsbConnection class source file.
+ *
+ * Copyright (c) 2013, Ford Motor Company
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of the Ford Motor Company nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <errno.h>
+#include <sched.h>
+#include <cstring>
+
+#include "transport_manager/usb/qnx/usb_connection.h"
+#include "transport_manager/transport_adapter/transport_adapter_impl.h"
+
+#include "utils/logger.h"
+
+namespace transport_manager {
+namespace transport_adapter {
+
+CREATE_LOGGERPTR_GLOBAL(logger_, "TransportManager")
+
+UsbConnection::UsbConnection(const DeviceUID& device_uid,
+ const ApplicationHandle& app_handle,
+ TransportAdapterController* controller,
+ const UsbHandlerSptr& libusb_handler,
+ PlatformUsbDevice* device)
+ : device_uid_(device_uid),
+ app_handle_(app_handle),
+ controller_(controller),
+ libusb_handler_(libusb_handler),
+ usbd_device_(device->GetUsbdDevice()),
+ in_pipe_(NULL),
+ out_pipe_(NULL),
+ in_buffer_(NULL),
+ in_urb_(NULL),
+ out_urb_(NULL),
+ out_messages_(),
+ current_out_message_(),
+ out_messages_mutex_(),
+ bytes_sent_(0),
+ disconnecting_(false),
+ pending_in_transfer_(false),
+ pending_out_transfer_(false) {
+ pthread_mutex_init(&out_messages_mutex_, 0);
+}
+
+UsbConnection::~UsbConnection() {
+ Finalise();
+ if (in_urb_) usbd_free_urb(in_urb_);
+ if (out_urb_) usbd_free_urb(out_urb_);
+
+ if (in_pipe_) {
+ const int close_pipe_rc = usbd_close_pipe(in_pipe_);
+ if (EOK != close_pipe_rc) {
+ LOG4CXX_ERROR(logger_, "Failed to close pipe: " << close_pipe_rc);
+ }
+ }
+ if (out_pipe_) {
+ const int close_pipe_rc = usbd_close_pipe(out_pipe_);
+ if (EOK != close_pipe_rc) {
+ LOG4CXX_ERROR(logger_, "Failed to close pipe: " << close_pipe_rc);
+ }
+ }
+
+ pthread_mutex_destroy(&out_messages_mutex_);
+}
+
+void InTransferCallback(usbd_urb* urb, usbd_pipe*, void* data) {
+ static_cast<UsbConnection*>(data)->OnInTransfer(urb);
+}
+
+void OutTransferCallback(usbd_urb* urb, usbd_pipe*, void* data) {
+ static_cast<UsbConnection*>(data)->OnOutTransfer(urb);
+}
+
+bool UsbConnection::PostInTransfer() {
+ usbd_setup_bulk(in_urb_, URB_DIR_IN, in_buffer_, kInBufferSize);
+ pending_in_transfer_ = true;
+ const int io_rc =
+ usbd_io(in_urb_, in_pipe_, InTransferCallback, this, USBD_TIME_INFINITY);
+ if (EOK != io_rc) {
+ pending_in_transfer_ = false;
+ LOG4CXX_ERROR(logger_, "Failed to post in transfer: " << io_rc);
+ return false;
+ }
+ return true;
+}
+
+void UsbConnection::OnInTransfer(usbd_urb* urb) {
+ uint32_t status = 0;
+ uint32_t len = 0;
+ bool error = false;
+ const int urb_status_rc = usbd_urb_status(urb, &status, &len);
+ if (EOK != urb_status_rc && EIO != urb_status_rc) { // EIO is OK
+ LOG4CXX_ERROR(logger_, "Get in urb status failed: " << urb_status_rc);
+ error = true;
+ }
+ LOG4CXX_INFO(logger_, "USB in transfer, status " << std::hex << status
+ << ", length " << std::dec
+ << len);
+
+ if (!error) {
+ switch (status) {
+ case USBD_STATUS_CMP:
+ case USBD_STATUS_CMP_ERR | USBD_STATUS_DATA_UNDERRUN:
+ error = false;
+ break;
+ default:
+ error = true;
+ break;
+ }
+ }
+
+ if (error) {
+ LOG4CXX_ERROR(logger_, "USB in transfer failed");
+ controller_->DataReceiveFailed(device_uid_, app_handle_,
+ DataReceiveError());
+ } else {
+ RawMessagePtr msg(new protocol_handler::RawMessage(0, 0, in_buffer_, len));
+ controller_->DataReceiveDone(device_uid_, app_handle_, msg);
+ }
+
+ pending_in_transfer_ = false;
+ if (!disconnecting_) {
+ if (!PostInTransfer()) {
+ controller_->ConnectionAborted(device_uid_, app_handle_,
+ CommunicationError());
+ Disconnect();
+ }
+ }
+}
+
+void UsbConnection::PopOutMessage() {
+ bytes_sent_ = 0;
+ if (out_messages_.empty()) {
+ current_out_message_.reset();
+ } else {
+ current_out_message_ = out_messages_.front();
+ out_messages_.pop_front();
+ }
+}
+
+bool UsbConnection::PostOutTransfer() {
+ const int len = current_out_message_->data_size() - bytes_sent_;
+ out_buffer_ = usbd_alloc(len);
+ memmove(out_buffer_, current_out_message_->data() + bytes_sent_, len);
+ usbd_setup_bulk(out_urb_, URB_DIR_OUT, out_buffer_, len);
+ LOG4CXX_INFO(logger_, "out transfer :" << len);
+ pending_out_transfer_ = true;
+ const int io_rc = usbd_io(out_urb_, out_pipe_, OutTransferCallback, this,
+ USBD_TIME_INFINITY);
+ if (EOK != io_rc) {
+ pending_out_transfer_ = false;
+ usbd_free(out_buffer_);
+ LOG4CXX_ERROR(logger_, "Failed to post out transfer: " << io_rc);
+ return false;
+ }
+ return true;
+}
+
+void UsbConnection::OnOutTransfer(usbd_urb* urb) {
+ usbd_free(out_buffer_);
+ uint32_t status = 0;
+ uint32_t len = 0;
+ bool error = false;
+ const int urb_status_rc = usbd_urb_status(urb, &status, &len);
+ if (EOK != urb_status_rc && EIO != urb_status_rc) { // EIO is OK
+ LOG4CXX_ERROR(logger_, "Get out urb status failed: " << urb_status_rc);
+ error = true;
+ }
+ LOG4CXX_INFO(logger_, "USB out transfer, status " << std::hex << status
+ << ", length " << std::dec
+ << len);
+
+ if (!error) {
+ switch (status) {
+ case USBD_STATUS_CMP:
+ case USBD_STATUS_CMP_ERR | USBD_STATUS_DATA_UNDERRUN:
+ error = false;
+ break;
+ default:
+ error = true;
+ break;
+ }
+ }
+
+ pthread_mutex_lock(&out_messages_mutex_);
+
+ if (error) {
+ LOG4CXX_ERROR(logger_, "USB out transfer failed");
+ controller_->DataSendFailed(device_uid_, app_handle_, current_out_message_,
+ DataSendError());
+ PopOutMessage();
+ } else {
+ bytes_sent_ += len;
+ if (bytes_sent_ == current_out_message_->data_size()) {
+ LOG4CXX_INFO(logger_, "USB out transfer, data sent: "
+ << current_out_message_.get());
+ controller_->DataSendDone(device_uid_, app_handle_, current_out_message_);
+ PopOutMessage();
+ }
+ }
+
+ if ((!disconnecting_) && current_out_message_.valid()) {
+ PostOutTransfer();
+ } else {
+ pending_out_transfer_ = false;
+ }
+ pthread_mutex_unlock(&out_messages_mutex_);
+}
+
+TransportAdapter::Error UsbConnection::SendData(RawMessagePtr message) {
+ if (disconnecting_) {
+ return TransportAdapter::BAD_STATE;
+ }
+ pthread_mutex_lock(&out_messages_mutex_);
+ if (current_out_message_.valid()) {
+ out_messages_.push_back(message);
+ } else {
+ current_out_message_ = message;
+ if (!PostOutTransfer()) {
+ controller_->DataSendFailed(device_uid_, app_handle_, message,
+ DataSendError());
+ }
+ }
+ pthread_mutex_unlock(&out_messages_mutex_);
+ return TransportAdapter::OK;
+}
+
+void UsbConnection::Finalise() {
+ LOG4CXX_INFO(logger_, "Finalising");
+ pthread_mutex_lock(&out_messages_mutex_);
+ disconnecting_ = true;
+ usbd_abort_pipe(in_pipe_);
+ usbd_abort_pipe(out_pipe_);
+ for (std::list<RawMessagePtr>::iterator it = out_messages_.begin();
+ it != out_messages_.end(); it = out_messages_.erase(it)) {
+ controller_->DataSendFailed(device_uid_, app_handle_, *it, DataSendError());
+ }
+ pthread_mutex_unlock(&out_messages_mutex_);
+ while (pending_in_transfer_ || pending_out_transfer_) sched_yield();
+}
+
+TransportAdapter::Error UsbConnection::Disconnect() {
+ LOG4CXX_INFO(logger_, "Disconnecting");
+ Finalise();
+ controller_->DisconnectDone(device_uid_, app_handle_);
+ return TransportAdapter::OK;
+}
+
+bool UsbConnection::Init() {
+ if (!OpenEndpoints()) return false;
+
+ in_urb_ = usbd_alloc_urb(NULL);
+ out_urb_ = usbd_alloc_urb(NULL);
+ if (NULL == in_urb_ || NULL == out_urb_) {
+ LOG4CXX_ERROR(logger_, "usbd_alloc_urb failed");
+ return false;
+ }
+
+ in_buffer_ = static_cast<unsigned char*>(usbd_alloc(kInBufferSize));
+ if (NULL == in_buffer_) {
+ LOG4CXX_ERROR(logger_, "usbd_alloc failed");
+ return false;
+ }
+
+ controller_->ConnectDone(device_uid_, app_handle_);
+
+ if (!PostInTransfer()) {
+ controller_->ConnectionAborted(device_uid_, app_handle_,
+ CommunicationError());
+ return true;
+ }
+
+ return true;
+}
+
+bool UsbConnection::OpenEndpoints() {
+ usbd_descriptors_t* in_endpoint_desc = NULL;
+ usbd_descriptors_t* out_endpoint_desc = NULL;
+
+ usbd_desc_node* device_desc_node = NULL;
+ usbd_device_descriptor_t* device_desc =
+ usbd_device_descriptor(usbd_device_, &device_desc_node);
+ if (0 == device_desc) {
+ LOG4CXX_ERROR(logger_, "Device descriptor not found");
+ return false;
+ }
+ usbd_desc_node* cfg_desc_node = NULL;
+ usbd_descriptors_t* config_desc = NULL;
+ usbd_descriptors_t* iface_desc = NULL;
+ int cfg = 0;
+ bool found = false;
+ while (!found) {
+ config_desc =
+ usbd_parse_descriptors(usbd_device_, device_desc_node,
+ USB_DESC_CONFIGURATION, cfg++, &cfg_desc_node);
+ if (config_desc == NULL) {
+ break;
+ }
+ LOG4CXX_INFO(logger_, "USB configuration "
+ << static_cast<int>(config_desc->configuration
+ .bConfigurationValue));
+ int iface = 0;
+ usbd_desc_node* iface_desc_node;
+ while (!found) {
+ iface_desc =
+ usbd_parse_descriptors(usbd_device_, cfg_desc_node,
+ USB_DESC_INTERFACE, iface++, &iface_desc_node);
+ if (iface_desc == NULL) {
+ break;
+ }
+#if ENABLE_LOG
+ const uint8_t interface_number = iface_desc->interface.bInterfaceNumber;
+#endif
+ const uint8_t interface_subclass =
+ iface_desc->interface.bInterfaceSubClass;
+ LOG4CXX_INFO(logger_, "USB interface number "
+ << static_cast<int>(interface_number)
+ << ", subclass " << std::hex
+ << static_cast<int>(interface_subclass)
+ << std::dec);
+ if (interface_subclass != 0xff) {
+ continue;
+ }
+ int endpoint = 0;
+ in_endpoint_desc = NULL;
+ out_endpoint_desc = NULL;
+ while (true) {
+ usbd_descriptors_t* endpoint_desc = usbd_parse_descriptors(
+ usbd_device_, iface_desc_node, USB_DESC_ENDPOINT, endpoint++, NULL);
+ if (NULL == endpoint_desc) break;
+ const uint8_t attributes = endpoint_desc->endpoint.bmAttributes;
+ if ((attributes & 0x03) == USB_ATTRIB_BULK) {
+ const uint8_t endpoint_address =
+ endpoint_desc->endpoint.bEndpointAddress;
+ LOG4CXX_INFO(logger_, "Endpoint with address "
+ << std::hex
+ << static_cast<int>(endpoint_address)
+ << std::dec << " found");
+ if (endpoint_address & USB_ENDPOINT_IN) {
+ if (NULL == in_endpoint_desc) {
+ in_endpoint_desc = endpoint_desc;
+ }
+ } else {
+ if (NULL == out_endpoint_desc) {
+ out_endpoint_desc = endpoint_desc;
+ }
+ }
+ }
+ }
+ if (in_endpoint_desc != NULL && out_endpoint_desc != NULL) {
+ found = true;
+ }
+ }
+ }
+
+ if (!found) return false;
+
+ int open_pipe_rc = usbd_open_pipe(usbd_device_, in_endpoint_desc, &in_pipe_);
+ if (EOK != open_pipe_rc) {
+ LOG4CXX_ERROR(logger_, "Cannot open input pipe, error " << open_pipe_rc);
+ return false;
+ }
+
+ open_pipe_rc = usbd_open_pipe(usbd_device_, out_endpoint_desc, &out_pipe_);
+ if (EOK != open_pipe_rc) {
+ LOG4CXX_ERROR(logger_, "Cannot open output pipe, error " << open_pipe_rc);
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace transport_adapter
+} // namespace transport_manager