diff options
Diffstat (limited to 'SDL_Core/src/components/transport_manager/src/usb/libusb/usb_connection.cc')
-rw-r--r-- | SDL_Core/src/components/transport_manager/src/usb/libusb/usb_connection.cc | 313 |
1 files changed, 313 insertions, 0 deletions
diff --git a/SDL_Core/src/components/transport_manager/src/usb/libusb/usb_connection.cc b/SDL_Core/src/components/transport_manager/src/usb/libusb/usb_connection.cc new file mode 100644 index 000000000..00c792de8 --- /dev/null +++ b/SDL_Core/src/components/transport_manager/src/usb/libusb/usb_connection.cc @@ -0,0 +1,313 @@ +/** + * \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 <iomanip> + +#include <libusb/libusb.h> + +#include "transport_manager/usb/libusb/usb_connection.h" +#include "transport_manager/transport_adapter/transport_adapter_impl.h" + +namespace transport_manager { +namespace transport_adapter { + +UsbConnection::UsbConnection(const DeviceUID& device_uid, + const ApplicationHandle& app_handle, + TransportAdapterController* controller, + const UsbHandlerSptr& usb_handler, + PlatformUsbDevice* device) + : device_uid_(device_uid), + app_handle_(app_handle), + controller_(controller), + usb_handler_(usb_handler), + libusb_device_(device->GetLibusbDevice()), + device_handle_(device->GetLibusbHandle()), + in_endpoint_(0), + in_endpoint_max_packet_size_(0), + out_endpoint_(0), + out_endpoint_max_packet_size_(0), + in_buffer_(0), + in_transfer_(0), + out_transfer_(0), + out_messages_(), + current_out_message_(), + out_messages_mutex_(), + bytes_sent_(0), + disconnecting_(false), + waiting_in_transfer_cancel_(false), + waiting_out_transfer_cancel_(false) { + pthread_mutex_init(&out_messages_mutex_, 0); +} + +UsbConnection::~UsbConnection() { + Finalise(); + if (in_transfer_) { + libusb_free_transfer(in_transfer_); + } + if (in_buffer_) { + delete[] in_buffer_; + } + pthread_mutex_destroy(&out_messages_mutex_); +} + +void InTransferCallback(libusb_transfer* transfer) { + static_cast<UsbConnection*>(transfer->user_data)->OnInTransfer(transfer); +} + +void OutTransferCallback(libusb_transfer* transfer) { + static_cast<UsbConnection*>(transfer->user_data)->OnOutTransfer(transfer); +} + +bool UsbConnection::PostInTransfer() { + libusb_fill_bulk_transfer(in_transfer_, device_handle_, in_endpoint_, + in_buffer_, in_endpoint_max_packet_size_, + InTransferCallback, this, 0); + const int libusb_ret = libusb_submit_transfer(in_transfer_); + if (LIBUSB_SUCCESS != libusb_ret) { + LOG4CXX_ERROR(logger_, "libusb_submit_transfer failed: " + << libusb_error_name(libusb_ret)); + return false; + } + return true; +} + +void UsbConnection::OnInTransfer(libusb_transfer* transfer) { + if (transfer->status == LIBUSB_TRANSFER_COMPLETED) { + if (logger_->isTraceEnabled()) { + std::ostringstream hexdata; + for (int i = 0; i < transfer->actual_length; ++i) { + hexdata << " " << std::hex << std::setw(2) << std::setfill('0') + << (int)transfer->buffer[i]; + } + LOG4CXX_TRACE(logger_, "USB incoming transfer, size:" + << transfer->actual_length + << ", data:" << hexdata.str()); + } + RawMessageSptr data(new protocol_handler::RawMessage( + 0, 0, in_buffer_, transfer->actual_length)); + controller_->DataReceiveDone(device_uid_, app_handle_, data); + } else { + LOG4CXX_ERROR(logger_, "USB transfer failed: " << transfer->status); + controller_->DataReceiveFailed(device_uid_, app_handle_, + DataReceiveError()); + } + if (disconnecting_) { + waiting_in_transfer_cancel_ = false; + } else { + 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(); + PostOutTransfer(); + } +} + +bool UsbConnection::PostOutTransfer() { + out_transfer_ = libusb_alloc_transfer(0); + if (0 == out_transfer_) { + LOG4CXX_ERROR(logger_, "libusb_alloc_transfer failed"); + return false; + } + libusb_fill_bulk_transfer(out_transfer_, device_handle_, out_endpoint_, + current_out_message_->data() + bytes_sent_, + current_out_message_->data_size() - bytes_sent_, + OutTransferCallback, this, 0); + const int libusb_ret = libusb_submit_transfer(out_transfer_); + if (LIBUSB_SUCCESS != libusb_ret) { + LOG4CXX_ERROR(logger_, "libusb_submit_transfer failed: " + << libusb_error_name(libusb_ret)); + controller_->ConnectionAborted(device_uid_, app_handle_, + CommunicationError()); + Disconnect(); + return false; + } + return true; +} + +void UsbConnection::OnOutTransfer(libusb_transfer* transfer) { + pthread_mutex_lock(&out_messages_mutex_); + if (transfer->status == LIBUSB_TRANSFER_COMPLETED) { + bytes_sent_ += transfer->actual_length; + 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(); + } + } else { + LOG4CXX_ERROR(logger_, "USB transfer failed: " << transfer->status); + controller_->DataSendFailed(device_uid_, app_handle_, current_out_message_, + DataSendError()); + PopOutMessage(); + } + libusb_free_transfer(transfer); + out_transfer_ = 0; + pthread_mutex_unlock(&out_messages_mutex_); + waiting_out_transfer_cancel_ = false; +} + +TransportAdapter::Error UsbConnection::SendData(RawMessageSptr 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_, "Finalise USB connection " << device_uid_); + pthread_mutex_lock(&out_messages_mutex_); + disconnecting_ = true; + if (out_transfer_) { + waiting_out_transfer_cancel_ = true; + if (LIBUSB_SUCCESS != libusb_cancel_transfer(out_transfer_)) { + waiting_out_transfer_cancel_ = false; + } + } + if (in_transfer_) { + waiting_in_transfer_cancel_ = true; + if (LIBUSB_SUCCESS != libusb_cancel_transfer(in_transfer_)) { + waiting_in_transfer_cancel_ = false; + } + } + for (std::list<RawMessageSptr>::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 (waiting_in_transfer_cancel_ || waiting_out_transfer_cancel_) { + pthread_yield(); + } +} + +TransportAdapter::Error UsbConnection::Disconnect() { + Finalise(); + LOG4CXX_INFO(logger_, "USB disconnect done " << device_uid_); + controller_->DisconnectDone(device_uid_, app_handle_); + return TransportAdapter::OK; +} + +bool UsbConnection::Init() { + if (!FindEndpoints()) { + return false; + } + + in_buffer_ = new unsigned char[in_endpoint_max_packet_size_]; + if (0 == in_buffer_) { + LOG4CXX_ERROR(logger_, "in buffer allocation failed (size " + << in_endpoint_max_packet_size_ << ")"); + return false; + } + + in_transfer_ = libusb_alloc_transfer(0); + if (0 == in_transfer_) { + LOG4CXX_ERROR(logger_, "libusb_alloc_transfer failed"); + return false; + } + + controller_->ConnectDone(device_uid_, app_handle_); + if (!PostInTransfer()) { + controller_->ConnectionAborted(device_uid_, app_handle_, + CommunicationError()); + return true; + } + + return true; +} + +bool UsbConnection::FindEndpoints() { + struct libusb_config_descriptor* config; + const int libusb_ret = + libusb_get_active_config_descriptor(libusb_device_, &config); + if (LIBUSB_SUCCESS != libusb_ret) { + LOG4CXX_ERROR(logger_, "libusb_get_active_config_descriptor failed: " + << libusb_error_name(libusb_ret)); + return false; + } + + bool find_in_endpoint = true; + bool find_out_endpoint = true; + + for (int i = 0; i < config->bNumInterfaces; ++i) { + const libusb_interface& interface = config->interface[i]; + for (int i = 0; i < interface.num_altsetting; ++i) { + const libusb_interface_descriptor& iface_desc = interface.altsetting[i]; + for (int i = 0; i < iface_desc.bNumEndpoints; ++i) { + const libusb_endpoint_descriptor& endpoint_desc = + iface_desc.endpoint[i]; + + const uint8_t endpoint_dir = + endpoint_desc.bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK; + if (find_in_endpoint && endpoint_dir == LIBUSB_ENDPOINT_IN) { + in_endpoint_ = endpoint_desc.bEndpointAddress; + in_endpoint_max_packet_size_ = endpoint_desc.wMaxPacketSize; + find_in_endpoint = false; + } else if (find_out_endpoint && endpoint_dir == LIBUSB_ENDPOINT_OUT) { + out_endpoint_ = endpoint_desc.bEndpointAddress; + out_endpoint_max_packet_size_ = endpoint_desc.wMaxPacketSize; + find_out_endpoint = false; + } + } + } + } + libusb_free_config_descriptor(config); + + return !(find_in_endpoint || find_out_endpoint); +} + +} // namespace transport_adapter +} // namespace transport_manager |