diff options
Diffstat (limited to 'SDL_Core/src/components/transport_manager/src/usb')
9 files changed, 1964 insertions, 0 deletions
diff --git a/SDL_Core/src/components/transport_manager/src/usb/libusb/platform_usb_device.cc b/SDL_Core/src/components/transport_manager/src/usb/libusb/platform_usb_device.cc new file mode 100644 index 000000000..2110ac3fc --- /dev/null +++ b/SDL_Core/src/components/transport_manager/src/usb/libusb/platform_usb_device.cc @@ -0,0 +1,79 @@ +/** + * \file platform_usb_device.cc + * \brief Libusb PlatformUsbDevice 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 "transport_manager/usb/libusb/platform_usb_device.h" +#include "transport_manager/transport_adapter/transport_adapter_impl.h" + +namespace transport_manager { +namespace transport_adapter { + +PlatformUsbDevice::PlatformUsbDevice( + uint8_t bus_number, uint8_t address, + const libusb_device_descriptor& device_descriptor, + libusb_device* device_libusb, libusb_device_handle* device_handle_libusb) + : bus_number_(bus_number), + address_(address), + vendor_id_(device_descriptor.idVendor), + product_id_(device_descriptor.idProduct), + device_descriptor_(device_descriptor), + libusb_device_handle_(device_handle_libusb), + libusb_device_(device_libusb) {} + +std::string PlatformUsbDevice::GetDescString(uint8_t index) const { + unsigned char buf[128]; + const int libusb_ret = libusb_get_string_descriptor_ascii( + libusb_device_handle_, index, buf, sizeof(buf)); + if (libusb_ret < 0) { + LOG4CXX_INFO(logger_, "Failed to get USB string descriptor: " + << libusb_error_name(libusb_ret)); + return ""; + } + return std::string(reinterpret_cast<char*>(buf)); +} + +std::string PlatformUsbDevice::GetManufacturer() const { + return GetDescString(device_descriptor_.iManufacturer); +} + +std::string PlatformUsbDevice::GetProductName() const { + return GetDescString(device_descriptor_.iProduct); +} + +std::string PlatformUsbDevice::GetSerialNumber() const { + return GetDescString(device_descriptor_.iSerialNumber); +} + +} // namespace +} // namespace 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 diff --git a/SDL_Core/src/components/transport_manager/src/usb/libusb/usb_handler.cc b/SDL_Core/src/components/transport_manager/src/usb/libusb/usb_handler.cc new file mode 100644 index 000000000..88c4ee1da --- /dev/null +++ b/SDL_Core/src/components/transport_manager/src/usb/libusb/usb_handler.cc @@ -0,0 +1,418 @@ +/** + * \file usb_handler.cc + * \brief UsbHandler 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 <cstring> +#include <cstdlib> + +#include "transport_manager/usb/common.h" +#include "transport_manager/transport_adapter/transport_adapter_impl.h" + +namespace transport_manager { +namespace transport_adapter { + +class UsbHandler::ControlTransferSequenceState { + public: + ControlTransferSequenceState(class UsbHandler* usb_handler, + UsbControlTransferSequence* sequence, + PlatformUsbDevice* device); + ~ControlTransferSequenceState(); + void Finish(); + bool Finished() const { return finished_; } + UsbControlTransfer* CurrentTransfer(); + UsbControlTransfer* Next(); + UsbHandler* usb_handler() const { return usb_handler_; } + PlatformUsbDevice* device() const { return device_; } + + private: + UsbHandler* usb_handler_; + PlatformUsbDevice* device_; + bool finished_; + UsbControlTransferSequence* sequence_; + UsbControlTransferSequence::Transfers::const_iterator current_transfer_; +}; + +UsbHandler::UsbHandler() + : shutdown_requested_(false), + thread_(), + usb_device_listeners_(), + devices_(), + transfer_sequences_(), + device_handles_to_close_(), + libusb_context_(NULL), + arrived_callback_handle_(), + left_callback_handle_() {} + +UsbHandler::~UsbHandler() { + shutdown_requested_ = true; + if (libusb_context_ != 0) { + libusb_hotplug_deregister_callback(libusb_context_, + arrived_callback_handle_); + libusb_hotplug_deregister_callback(libusb_context_, left_callback_handle_); + } + pthread_join(thread_, 0); + LOG4CXX_INFO(logger_, "UsbHandler thread finished"); + if (libusb_context_) { + libusb_exit(libusb_context_); + libusb_context_ = 0; + } +} + +void UsbHandler::DeviceArrived(libusb_device* device_libusb) { + const uint8_t bus_number = libusb_get_bus_number(device_libusb); + const uint8_t device_address = libusb_get_device_address(device_libusb); + libusb_device_descriptor descriptor; + int libusb_ret = libusb_get_device_descriptor(device_libusb, &descriptor); + if (LIBUSB_SUCCESS != libusb_ret) { + LOG4CXX_ERROR(logger_, + "libusb_get_device_descriptor failed: " << libusb_ret); + return; + } + + libusb_device_handle* device_handle_libusb; + libusb_ret = libusb_open(device_libusb, &device_handle_libusb); + if (libusb_ret != LIBUSB_SUCCESS) { + LOG4CXX_ERROR(logger_, + "libusb_open failed: " << libusb_error_name(libusb_ret)); + return; + } + + int configuration; + libusb_ret = libusb_get_configuration(device_handle_libusb, &configuration); + if (LIBUSB_SUCCESS != libusb_ret) { + LOG4CXX_INFO( + logger_, + "libusb_get_configuration failed: " << libusb_error_name(libusb_ret)); + return; + } + + if (configuration != kUsbConfiguration) { + libusb_ret = libusb_set_configuration(device_handle_libusb, kUsbConfiguration); + if (LIBUSB_SUCCESS != libusb_ret) { + LOG4CXX_INFO( + logger_, + "libusb_set_configuration failed: " << libusb_error_name(libusb_ret)); + return; + } + } + + libusb_ret = libusb_claim_interface(device_handle_libusb, 0); + if (LIBUSB_SUCCESS != libusb_ret) { + LOG4CXX_INFO(logger_, "libusb_claim_interface failed: " + << libusb_error_name(libusb_ret)); + CloseDeviceHandle(device_handle_libusb); + return; + } + + PlatformUsbDevice* device(new PlatformUsbDevice(bus_number, device_address, + descriptor, device_libusb, + device_handle_libusb)); + devices_.push_back(device); + + for (std::list<UsbDeviceListener*>::iterator it = + usb_device_listeners_.begin(); + it != usb_device_listeners_.end(); ++it) { + (*it)->OnDeviceArrived(device); + } +} + +void UsbHandler::DeviceLeft(libusb_device* device_libusb) { + PlatformUsbDevice* device = NULL; + for (Devices::iterator it = devices_.begin(); it != devices_.end(); ++it) { + if ((*it)->GetLibusbDevice() == device_libusb) { + device = *it; + break; + } + } + if (NULL == device) { + return; + } + + for (std::list<UsbDeviceListener*>::iterator it = + usb_device_listeners_.begin(); + it != usb_device_listeners_.end(); ++it) { + (*it)->OnDeviceLeft(device); + } + + for (Devices::iterator it = devices_.begin(); it != devices_.end(); ++it) { + if ((*it)->GetLibusbDevice() == device_libusb) { + devices_.erase(it); + break; + } + } + + if (device->GetLibusbHandle()) { + libusb_release_interface(device->GetLibusbHandle(), 0); + CloseDeviceHandle(device->GetLibusbHandle()); + } + delete device; +} + +void UsbHandler::StartControlTransferSequence( + UsbControlTransferSequence* sequence, PlatformUsbDevice* device) { + TransferSequences::iterator it = transfer_sequences_.insert( + transfer_sequences_.end(), + new ControlTransferSequenceState(this, sequence, device)); + SubmitControlTransfer(*it); +} + +void UsbHandler::CloseDeviceHandle(libusb_device_handle* device_handle) { + device_handles_to_close_.push_back(device_handle); +} + +void* UsbHandlerThread(void* data) { + static_cast<UsbHandler*>(data)->Thread(); + return 0; +} + +int ArrivedCallback(libusb_context* context, libusb_device* device, + libusb_hotplug_event event, void* data) { + LOG4CXX_INFO(logger_, "libusb device arrived (bus number " + << static_cast<int>(libusb_get_bus_number(device)) + << ", device address " + << static_cast<int>( + libusb_get_device_address(device)) << ")"); + UsbHandler* usb_handler = static_cast<UsbHandler*>(data); + usb_handler->DeviceArrived(device); + return 0; +} + +int LeftCallback(libusb_context* context, libusb_device* device, + libusb_hotplug_event event, void* data) { + LOG4CXX_INFO(logger_, "libusb device left (bus number " + << static_cast<int>(libusb_get_bus_number(device)) + << ", device address " + << static_cast<int>( + libusb_get_device_address(device)) << ")"); + UsbHandler* usb_handler = static_cast<UsbHandler*>(data); + usb_handler->DeviceLeft(device); + return 0; +} + +TransportAdapter::Error UsbHandler::Init() { + int libusb_ret = libusb_init(&libusb_context_); + + if (LIBUSB_SUCCESS != libusb_ret) { + LOG4CXX_ERROR(logger_, "libusb_init failed: " << libusb_ret); + return TransportAdapter::FAIL; + } + + if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) { + LOG4CXX_ERROR(logger_, "LIBUSB_CAP_HAS_HOTPLUG not supported"); + return TransportAdapter::FAIL; + } + + libusb_ret = libusb_hotplug_register_callback( + libusb_context_, LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED, + LIBUSB_HOTPLUG_ENUMERATE, LIBUSB_HOTPLUG_MATCH_ANY, + LIBUSB_HOTPLUG_MATCH_ANY, LIBUSB_HOTPLUG_MATCH_ANY, ArrivedCallback, this, + &arrived_callback_handle_); + + if (LIBUSB_SUCCESS != libusb_ret) { + LOG4CXX_ERROR(logger_, + "libusb_hotplug_register_callback failed: " << libusb_ret); + return TransportAdapter::FAIL; + } + + libusb_ret = libusb_hotplug_register_callback( + libusb_context_, LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT, + static_cast<libusb_hotplug_flag>(0), LIBUSB_HOTPLUG_MATCH_ANY, + LIBUSB_HOTPLUG_MATCH_ANY, LIBUSB_HOTPLUG_MATCH_ANY, LeftCallback, this, + &left_callback_handle_); + + if (LIBUSB_SUCCESS != libusb_ret) { + LOG4CXX_ERROR(logger_, + "libusb_hotplug_register_callback failed: " << libusb_ret); + return TransportAdapter::FAIL; + } + + const int thread_start_error = + pthread_create(&thread_, 0, &UsbHandlerThread, this); + if (0 == thread_start_error) { + LOG4CXX_INFO(logger_, "UsbHandler thread started"); + return TransportAdapter::OK; + } else { + LOG4CXX_ERROR(logger_, "USB device scanner thread start failed, error code " + << thread_start_error); + return TransportAdapter::FAIL; + } +} + +void UsbHandler::Thread() { + int completed = 0; + while (!shutdown_requested_) { + libusb_handle_events_completed(libusb_context_, &completed); + + for (TransferSequences::iterator it = transfer_sequences_.begin(); + it != transfer_sequences_.end();) { + ControlTransferSequenceState* sequence_state = *it; + if (sequence_state->Finished()) { + delete *it; + it = transfer_sequences_.erase(it); + } else { + ++it; + } + } + + for (std::list<libusb_device_handle*>::iterator it = + device_handles_to_close_.begin(); + it != device_handles_to_close_.end(); + it = device_handles_to_close_.erase(it)) { + libusb_close(*it); + } + } +} + +void UsbTransferSequenceCallback(libusb_transfer* transfer) { + UsbHandler::ControlTransferSequenceState* sequence_state = + static_cast<UsbHandler::ControlTransferSequenceState*>(transfer->user_data); + sequence_state->usb_handler()->ControlTransferCallback(transfer); +} + +void UsbHandler::SubmitControlTransfer( + ControlTransferSequenceState* sequence_state) { + UsbControlTransfer* transfer = sequence_state->CurrentTransfer(); + if (NULL == transfer) return; + + libusb_transfer* libusb_transfer = libusb_alloc_transfer(0); + if (0 == libusb_transfer) { + LOG4CXX_ERROR(logger_, "libusb_alloc_transfer failed"); + sequence_state->Finish(); + return; + } + + assert(transfer->Type() == UsbControlTransfer::VENDOR); + const libusb_request_type request_type = LIBUSB_REQUEST_TYPE_VENDOR; + + libusb_endpoint_direction endpoint_direction; + if (transfer->Direction() == UsbControlTransfer::IN) { + endpoint_direction = LIBUSB_ENDPOINT_IN; + } else if (transfer->Direction() == UsbControlTransfer::OUT) { + endpoint_direction = LIBUSB_ENDPOINT_OUT; + } else { + assert(0); + } + const uint8_t request = transfer->Request(); + const uint16_t value = transfer->Value(); + const uint16_t index = transfer->Index(); + const uint16_t length = transfer->Length(); + + unsigned char* buffer = + static_cast<unsigned char*>(malloc(length + LIBUSB_CONTROL_SETUP_SIZE)); + if (NULL == buffer) { + LOG4CXX_ERROR(logger_, "buffer allocation failed"); + libusb_free_transfer(libusb_transfer); + sequence_state->Finish(); + return; + } + + libusb_fill_control_setup(buffer, request_type | endpoint_direction, request, + value, index, length); + + if (0 != length && endpoint_direction == LIBUSB_ENDPOINT_OUT) { + const char* data = static_cast<UsbControlOutTransfer*>(transfer)->Data(); + memcpy(buffer + LIBUSB_CONTROL_SETUP_SIZE, data, length); + } + libusb_fill_control_transfer( + libusb_transfer, sequence_state->device()->GetLibusbHandle(), buffer, + UsbTransferSequenceCallback, sequence_state, 0); + libusb_transfer->flags = LIBUSB_TRANSFER_FREE_BUFFER; + + const int libusb_ret = libusb_submit_transfer(libusb_transfer); + if (LIBUSB_SUCCESS != libusb_ret) { + LOG4CXX_ERROR(logger_, "libusb_submit_transfer failed: " + << libusb_error_name(libusb_ret)); + libusb_free_transfer(libusb_transfer); + sequence_state->Finish(); + } +} + +void UsbHandler::ControlTransferCallback(libusb_transfer* transfer) { + ControlTransferSequenceState* sequence_state = + static_cast<ControlTransferSequenceState*>(transfer->user_data); + if (transfer->status == LIBUSB_TRANSFER_COMPLETED) { + LOG4CXX_INFO(logger_, "USB control transfer completed"); + UsbControlTransfer* current_transfer = sequence_state->CurrentTransfer(); + bool submit_next = true; + if (current_transfer && + current_transfer->Direction() == UsbControlTransfer::IN) { + submit_next = + static_cast<UsbControlInTransfer*>(current_transfer) + ->OnCompleted(libusb_control_transfer_get_data(transfer)); + } + + sequence_state->Next(); + if (submit_next && !sequence_state->Finished()) { + SubmitControlTransfer(sequence_state); + } else { + sequence_state->Finish(); + } + } else { + LOG4CXX_ERROR(logger_, "USB control transfer failed: " << transfer->status); + sequence_state->Finish(); + } + libusb_free_transfer(transfer); +} + +UsbHandler::ControlTransferSequenceState::ControlTransferSequenceState( + UsbHandler* usb_handler, UsbControlTransferSequence* sequence, + PlatformUsbDevice* device) + : usb_handler_(usb_handler), + device_(device), + finished_(false), + sequence_(sequence), + current_transfer_(sequence->transfers().begin()) {} + +UsbHandler::ControlTransferSequenceState::~ControlTransferSequenceState() { + delete sequence_; +} + +UsbControlTransfer* UsbHandler::ControlTransferSequenceState::Next() { + if (finished_) return NULL; + if (++current_transfer_ == sequence_->transfers().end()) { + Finish(); + return NULL; + } else { + return *current_transfer_; + } +} + +UsbControlTransfer* UsbHandler::ControlTransferSequenceState::CurrentTransfer() { + return finished_ ? NULL : *current_transfer_; +} + +void UsbHandler::ControlTransferSequenceState::Finish() { finished_ = true; } + +} // namespace +} // namespace diff --git a/SDL_Core/src/components/transport_manager/src/usb/qnx/platform_usb_device.cc b/SDL_Core/src/components/transport_manager/src/usb/qnx/platform_usb_device.cc new file mode 100644 index 000000000..c17c2aab6 --- /dev/null +++ b/SDL_Core/src/components/transport_manager/src/usb/qnx/platform_usb_device.cc @@ -0,0 +1,75 @@ +/** + * \file platform_usb_device.cc + * \brief QNX PlatformUsbDevice 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 "transport_manager/usb/qnx/platform_usb_device.h" +#include "transport_manager/transport_adapter/transport_adapter_impl.h" + +namespace transport_manager { +namespace transport_adapter { + +PlatformUsbDevice::PlatformUsbDevice( + usbd_device_instance_t* instance, usbd_device* device, + const usbd_device_descriptor_t& device_descriptor) + : bus_number_(instance->path), + address_(instance->devno), + vendor_id_(instance->ident.vendor), + product_id_(instance->ident.device), + device_descriptor_(device_descriptor), + usbd_device_instance_(*instance), + usbd_device_(device) {} + +std::string PlatformUsbDevice::GetDescString(uint8_t index) const { + char* str = usbd_string(usbd_device_, index, 0); + if (NULL == str) { + LOG4CXX_INFO(logger_, "Failed to get USB string descriptor"); + return ""; + } + return std::string(str); +} + +std::string PlatformUsbDevice::GetManufacturer() const { + return GetDescString(device_descriptor_.iManufacturer); +} + +std::string PlatformUsbDevice::GetProductName() const { + return GetDescString(device_descriptor_.iProduct); +} + +std::string PlatformUsbDevice::GetSerialNumber() const { + return GetDescString(device_descriptor_.iSerialNumber); +} + +} // namespace +} // namespace diff --git a/SDL_Core/src/components/transport_manager/src/usb/qnx/usb_connection.cc b/SDL_Core/src/components/transport_manager/src/usb/qnx/usb_connection.cc new file mode 100644 index 000000000..dec5267ba --- /dev/null +++ b/SDL_Core/src/components/transport_manager/src/usb/qnx/usb_connection.cc @@ -0,0 +1,399 @@ +/** + * \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 <sched.h> +#include <cstring> + +#include "transport_manager/usb/qnx/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& 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) { + const uint32_t urb_status = status & USBD_URB_STATUS_MASK; + 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 { + RawMessageSptr 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) { + const uint32_t urb_status = status & USBD_URB_STATUS_MASK; + 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(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_, "Finalising"); + pthread_mutex_lock(&out_messages_mutex_); + disconnecting_ = true; + usbd_abort_pipe(in_pipe_); + usbd_abort_pipe(out_pipe_); + 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 (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; + } + const uint8_t interface_number = iface_desc->interface.bInterfaceNumber; + 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 diff --git a/SDL_Core/src/components/transport_manager/src/usb/qnx/usb_handler.cc b/SDL_Core/src/components/transport_manager/src/usb/qnx/usb_handler.cc new file mode 100644 index 000000000..ccb9cd0d5 --- /dev/null +++ b/SDL_Core/src/components/transport_manager/src/usb/qnx/usb_handler.cc @@ -0,0 +1,296 @@ +/** + * \file usb_handler.cc + * \brief UsbHandler 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 <cstring> +#include <cstdlib> + +#include "transport_manager/usb/common.h" +#include "transport_manager/transport_adapter/transport_adapter_impl.h" + +namespace transport_manager { +namespace transport_adapter { + +namespace { +UsbHandler* usb_handler; +} + +UsbHandler::UsbHandler() + : usb_device_listeners_(), + devices_(), + usbd_general_connection_(NULL), + usbd_aoa_connection_(NULL) { + usb_handler = this; +} + +UsbHandler::~UsbHandler() { + usb_handler = NULL; + if (usbd_general_connection_) { + const int disconnect_rc = usbd_disconnect(usbd_general_connection_); + if (EOK != disconnect_rc) { + LOG4CXX_ERROR(logger_, "usbd_disconnect failed, error code " + << disconnect_rc); + } + } + if (usbd_aoa_connection_) { + const int disconnect_rc = usbd_disconnect(usbd_aoa_connection_); + if (EOK != disconnect_rc) { + LOG4CXX_ERROR(logger_, "usbd_disconnect failed, error code " + << disconnect_rc); + } + } +} + +bool operator==(const usbd_device_instance_t& a, + const usbd_device_instance_t& b) { + return a.path == b.path && a.devno == b.devno; +} + +void UsbHandler::DeviceArrived(usbd_connection* connection, + usbd_device_instance_t* instance) { + for (Devices::const_iterator it = devices_.begin(); it != devices_.end(); + ++it) { + if ((*it)->GetDeviceInstance() == *instance) return; + } + usbd_device* device_usbd = 0; + const int attach_rc = usbd_attach(connection, instance, 0, &device_usbd); + if (EOK != attach_rc) { + LOG4CXX_ERROR(logger_, "usbd_attach failed: " << attach_rc); + return; + } + + usbd_desc_node* node; + usbd_device_descriptor_t* descriptor = + usbd_device_descriptor(device_usbd, &node); + if (NULL == descriptor) { + LOG4CXX_ERROR(logger_, "usbd_device_descriptor failed"); + return; + } + + PlatformUsbDevice* device( + new PlatformUsbDevice(instance, device_usbd, *descriptor)); + devices_.push_back(device); + for (std::list<UsbDeviceListener*>::iterator it = + usb_device_listeners_.begin(); + it != usb_device_listeners_.end(); ++it) { + (*it)->OnDeviceArrived(device); + } +} + +void UsbHandler::DeviceLeft(usbd_device_instance_t* instance) { + PlatformUsbDevice* device = NULL; + for (Devices::iterator it = devices_.begin(); it != devices_.end(); ++it) { + if (((*it)->GetDeviceInstance()) == *instance) { + device = *it; + break; + } + } + if (NULL == device) { + return; + } + + for (std::list<UsbDeviceListener*>::iterator it = + usb_device_listeners_.begin(); + it != usb_device_listeners_.end(); ++it) { + (*it)->OnDeviceLeft(device); + } + + for (Devices::iterator it = devices_.begin(); it != devices_.end(); ++it) { + if ((*it)->GetDeviceInstance() == *instance) { + const int detach_rc = usbd_detach((*it)->GetUsbdDevice()); + if (EOK != detach_rc) + LOG4CXX_ERROR(logger_, "usbd_detach failed: " << detach_rc); + devices_.erase(it); + break; + } + } + + delete device; +} + +void UsbHandler::StartControlTransferSequence( + UsbControlTransferSequence* sequence, PlatformUsbDevice* device) { + usbd_descriptors_t* descriptor = usbd_parse_descriptors( + device->GetUsbdDevice(), NULL, USB_DESC_DEVICE, 0, NULL); + if (NULL == descriptor) { + LOG4CXX_ERROR(logger_, "usbd_parse_descriptors failed"); + } + + usbd_pipe* usb_pipe = 0; + const int open_pipe_rc = + usbd_open_pipe(device->GetUsbdDevice(), descriptor, &usb_pipe); + if (EOK != open_pipe_rc) { + LOG4CXX_ERROR(logger_, "usbd_open_pipe failed, error " << open_pipe_rc); + return; + } + + for (UsbControlTransferSequence::Transfers::const_iterator it = + sequence->transfers().begin(); + it != sequence->transfers().end(); ++it) { + UsbControlTransfer* transfer = *it; + + usbd_urb* urb = usbd_alloc_urb(NULL); + if (NULL == urb) { + LOG4CXX_ERROR(logger_, "usbd_alloc_urb failed"); + break; + } + + unsigned char* buf = + static_cast<unsigned char*>(usbd_alloc(transfer->Length())); + if (NULL == buf) { + LOG4CXX_ERROR(logger_, "usbd_alloc failed"); + break; + } + + uint32_t flags = 0; + if (transfer->Direction() == UsbControlTransfer::IN) { + flags = URB_DIR_IN; + } else if (transfer->Direction() == UsbControlTransfer::OUT) { + flags = URB_DIR_OUT; + UsbControlOutTransfer* out_transfer = + static_cast<UsbControlOutTransfer*>(transfer); + std::copy(out_transfer->Data(), + out_transfer->Data() + out_transfer->Length(), buf); + } else { + assert(0); + } + + assert(transfer->Type() == UsbControlTransfer::VENDOR); + + usbd_setup_vendor(urb, flags, transfer->Request(), USB_TYPE_VENDOR, + transfer->Value(), transfer->Index(), buf, + transfer->Length()); + const int io_rc = usbd_io(urb, usb_pipe, 0, 0, USBD_TIME_DEFAULT); + + bool submit_next = true; + if (io_rc == EOK) { + if (transfer->Direction() == UsbControlTransfer::IN) { + submit_next = + static_cast<UsbControlInTransfer*>(transfer)->OnCompleted(buf); + } + } else { + LOG4CXX_ERROR(logger_, "usbd_io failed, error " << io_rc); + submit_next = false; + } + usbd_free(buf); + usbd_free_urb(urb); + if (!submit_next) break; + } + usbd_close_pipe(usb_pipe); + + delete sequence; +} + +void ArrivedCallback(usbd_connection* connection, + usbd_device_instance_t* instance) { + if (kAoaVid == instance->ident.vendor) return; + LOG4CXX_INFO(logger_, "USB device arrived (path " + << static_cast<int>(instance->path) << ", devno " + << static_cast<int>(instance->devno) << ", config " + << static_cast<int>(instance->config) << ", iface " + << static_cast<int>(instance->iface) << ")"); + usb_handler->DeviceArrived(connection, instance); +} + +void ArrivedAoaCallback(usbd_connection* connection, + usbd_device_instance_t* instance) { + if (kAoaVid != instance->ident.vendor) return; + LOG4CXX_INFO(logger_, "USB AOA device arrived (path " + << static_cast<int>(instance->path) << ", devno " + << static_cast<int>(instance->devno) << ", config " + << static_cast<int>(instance->config) << ", iface " + << static_cast<int>(instance->iface) << ")"); + usb_handler->DeviceArrived(connection, instance); +} + +void LeftCallback(usbd_connection* connection, + usbd_device_instance_t* instance) { + if (kAoaVid == instance->ident.vendor) return; + LOG4CXX_INFO(logger_, "USB device left (path " + << static_cast<int>(instance->path) << ", devno " + << static_cast<int>(instance->devno) << ", config " + << static_cast<int>(instance->config) << ", iface " + << static_cast<int>(instance->iface) << ")"); + usb_handler->DeviceLeft(instance); +} + +void LeftAoaCallback(usbd_connection* connection, + usbd_device_instance_t* instance) { + if (kAoaVid != instance->ident.vendor) return; + LOG4CXX_INFO(logger_, "USB AOA device left (path " + << static_cast<int>(instance->path) << ", devno " + << static_cast<int>(instance->devno) << ", config " + << static_cast<int>(instance->config) << ", iface " + << static_cast<int>(instance->iface) << ")"); + usb_handler->DeviceLeft(instance); +} + +TransportAdapter::Error UsbHandler::Init() { + { + usbd_device_ident_t interest = { + USBD_CONNECT_WILDCARD, USBD_CONNECT_WILDCARD, kAoaInterfaceSubclass, + USBD_CONNECT_WILDCARD, USBD_CONNECT_WILDCARD}; + usbd_funcs_t funcs = {_USBDI_NFUNCS, ArrivedCallback, LeftCallback, NULL}; + usbd_connect_parm_t cparms = {NULL, USB_VERSION, USBD_VERSION, 0, 0, + NULL, 0, &interest, &funcs}; + + const int connect_rc = usbd_connect(&cparms, &usbd_general_connection_); + + if (EOK != connect_rc) { + LOG4CXX_ERROR(logger_, "usbd_connect failed, error code " << connect_rc); + return TransportAdapter::FAIL; + } + } + { + usbd_device_ident_t interest = { + kAoaVid, USBD_CONNECT_WILDCARD, USBD_CONNECT_WILDCARD, + kAoaInterfaceSubclass, USBD_CONNECT_WILDCARD}; + usbd_funcs_t funcs = {_USBDI_NFUNCS, ArrivedAoaCallback, + LeftAoaCallback, NULL}; + usbd_connect_parm_t cparms = {NULL, USB_VERSION, USBD_VERSION, 0, 0, + NULL, 0, &interest, &funcs}; + + const int connect_rc = usbd_connect(&cparms, &usbd_aoa_connection_); + + if (EOK != connect_rc) { + LOG4CXX_ERROR(logger_, "usbd_connect failed, error code " << connect_rc); + return TransportAdapter::FAIL; + } + } + + return TransportAdapter::OK; +} + +} // namespace +} // namespace diff --git a/SDL_Core/src/components/transport_manager/src/usb/usb_aoa_adapter.cc b/SDL_Core/src/components/transport_manager/src/usb/usb_aoa_adapter.cc new file mode 100644 index 000000000..88487bcc1 --- /dev/null +++ b/SDL_Core/src/components/transport_manager/src/usb/usb_aoa_adapter.cc @@ -0,0 +1,80 @@ +/** + * \file usb_aoa_adapter.cpp + * \brief UsbAoaAdapter 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 "transport_manager/usb/usb_aoa_adapter.h" +#include "transport_manager/usb/usb_device_scanner.h" +#include "transport_manager/usb/usb_connection_factory.h" +#include "transport_manager/usb/common.h" + +namespace transport_manager { +namespace transport_adapter { + +UsbAoaAdapter::UsbAoaAdapter() + : TransportAdapterImpl(new UsbDeviceScanner(this), + new UsbConnectionFactory(this), 0), + is_initialised_(false), + usb_handler_(new UsbHandler()) { + static_cast<UsbDeviceScanner*>(device_scanner_)->SetUsbHandler(usb_handler_); + static_cast<UsbConnectionFactory*>(server_connection_factory_) + ->SetUsbHandler(usb_handler_); +} + +UsbAoaAdapter::~UsbAoaAdapter() {} + +DeviceType UsbAoaAdapter::GetDeviceType() const { return "sdl-usb-aoa"; } + +bool UsbAoaAdapter::IsInitialised() const { + return is_initialised_ && TransportAdapterImpl::IsInitialised(); +} + +TransportAdapter::Error UsbAoaAdapter::Init() { + TransportAdapter::Error error = usb_handler_->Init(); + if (error != TransportAdapter::OK) { + return error; + } + error = TransportAdapterImpl::Init(); + if (error != TransportAdapter::OK) { + return error; + } + is_initialised_ = true; + return TransportAdapter::OK; +} + +bool UsbAoaAdapter::ToBeAutoConnected(DeviceSptr device) const { + return true; +} + +} // namespace transport_adapter +} // namespace transport_manager diff --git a/SDL_Core/src/components/transport_manager/src/usb/usb_connection_factory.cc b/SDL_Core/src/components/transport_manager/src/usb/usb_connection_factory.cc new file mode 100644 index 000000000..4081ac40f --- /dev/null +++ b/SDL_Core/src/components/transport_manager/src/usb/usb_connection_factory.cc @@ -0,0 +1,91 @@ +/** + * \file usb_connection_factory.cc + * \brief UsbConnectionFactory 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 "transport_manager/usb/usb_connection_factory.h" +#include "transport_manager/usb/usb_device.h" +#include "transport_manager/transport_adapter/transport_adapter_impl.h" + +#if defined(__QNX__) || defined(__QNXNTO__) +#include "transport_manager/usb/qnx/usb_connection.h" +#else +#include "transport_manager/usb/libusb/usb_connection.h" +#endif + +namespace transport_manager { +namespace transport_adapter { + +UsbConnectionFactory::UsbConnectionFactory( + TransportAdapterController* controller) + : controller_(controller), usb_handler_() {} + +TransportAdapter::Error UsbConnectionFactory::Init() { + return TransportAdapter::OK; +} + +void UsbConnectionFactory::SetUsbHandler(const UsbHandlerSptr& usb_handler) { + usb_handler_ = usb_handler; +} + +TransportAdapter::Error UsbConnectionFactory::CreateConnection( + const DeviceUID& device_uid, const ApplicationHandle& app_handle) { + DeviceSptr device = controller_->FindDevice(device_uid); + if (!device.valid()) { + LOG4CXX_ERROR(logger_, "device " << device_uid << " not found"); + return TransportAdapter::BAD_PARAM; + } + + UsbDevice* usb_device = static_cast<UsbDevice*>(device.get()); + UsbConnection* usb_connection = + new UsbConnection(device_uid, app_handle, controller_, usb_handler_, + usb_device->usb_device()); + ConnectionSptr connection(usb_connection); + + controller_->ConnectionCreated(connection, device_uid, app_handle); + if (!usb_connection->Init()) { + return TransportAdapter::FAIL; + } + LOG4CXX_INFO(logger_, "Usb connection initialised"); + + return TransportAdapter::OK; +} + +void UsbConnectionFactory::Terminate() {} + +bool UsbConnectionFactory::IsInitialised() const { return true; } + +UsbConnectionFactory::~UsbConnectionFactory() {} + +} // namespace transport_adapter +} // namespace transport_manager diff --git a/SDL_Core/src/components/transport_manager/src/usb/usb_device_scanner.cc b/SDL_Core/src/components/transport_manager/src/usb/usb_device_scanner.cc new file mode 100644 index 000000000..78358d419 --- /dev/null +++ b/SDL_Core/src/components/transport_manager/src/usb/usb_device_scanner.cc @@ -0,0 +1,213 @@ +/** + * \file usb_device_scanner.cc + * \brief UsbDeviceScanner 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 <sstream> + +#include "transport_manager/transport_adapter/transport_adapter_impl.h" +#include "transport_manager/usb/usb_device_scanner.h" +#include "transport_manager/usb/usb_device.h" +#include "transport_manager/usb/common.h" + +namespace transport_manager { +namespace transport_adapter { + +class AoaInitSequence : public UsbControlTransferSequence { + public: + AoaInitSequence(); + virtual ~AoaInitSequence() {} + + private: + class AoaGetProtocolRequest; + class AoaSendIdString; + class AoaTurnIntoAccessoryMode; +}; + +bool IsGoogleAccessory(uint16_t vid, uint16_t pid) { + return (vid == kAoaVid) && (pid == kAoaPid1 || pid == kAoaPid2); +} + +void UsbDeviceScanner::OnDeviceArrived(PlatformUsbDevice* device) { + if (IsGoogleAccessory(device->vendor_id(), device->product_id())) { + GoogleAccessoryFound(device); + } else { + TurnIntoAccessoryMode(device); + } +} + +void UsbDeviceScanner::OnDeviceLeft(PlatformUsbDevice* device) { + bool list_changed = false; + pthread_mutex_lock(&devices_mutex_); + for (Devices::iterator it = devices_.begin(); it != devices_.end(); ++it) { + if (device == *it) { + devices_.erase(it); + list_changed = true; + break; + } + } + pthread_mutex_unlock(&devices_mutex_); + if (list_changed) UpdateList(); +} + +UsbDeviceScanner::UsbDeviceScanner(TransportAdapterController* controller) + : controller_(controller) { + pthread_mutex_init(&devices_mutex_, 0); +} + +UsbDeviceScanner::~UsbDeviceScanner() { + pthread_mutex_destroy(&devices_mutex_); +} + +class AoaInitSequence::AoaGetProtocolRequest : public UsbControlInTransfer { + virtual ~AoaGetProtocolRequest() {} + virtual RequestType Type() const { return VENDOR; } + virtual uint8_t Request() const { return 51; } + virtual uint16_t Value() const { return 0; } + virtual uint16_t Index() const { return 0; } + virtual uint16_t Length() const { return 2; } + virtual bool OnCompleted(unsigned char* data) const { + const int protocol_version = data[1] << 8 | data[0]; + LOG4CXX_INFO(logger_, "AOA protocol version " << protocol_version); + if (protocol_version == 0) { + // AOA protocol not supported + return false; + } + return true; + } +}; + +class AoaInitSequence::AoaSendIdString : public UsbControlOutTransfer { + public: + AoaSendIdString(uint16_t index, const char* string, uint16_t length) + : index_(index), string_(string), length_(length) {} + + private: + virtual ~AoaSendIdString() {} + virtual RequestType Type() const { return VENDOR; } + virtual uint8_t Request() const { return 52; } + virtual uint16_t Value() const { return 0; } + virtual uint16_t Index() const { return index_; } + virtual uint16_t Length() const { return length_; } + virtual const char* Data() const { return string_; } + uint16_t index_; + const char* string_; + uint16_t length_; +}; + +class AoaInitSequence::AoaTurnIntoAccessoryMode : public UsbControlOutTransfer { + virtual ~AoaTurnIntoAccessoryMode() {} + virtual RequestType Type() const { return VENDOR; } + virtual uint8_t Request() const { return 53; } + virtual uint16_t Value() const { return 0; } + virtual uint16_t Index() const { return 0; } + virtual uint16_t Length() const { return 0; } + virtual const char* Data() const { return 0; } +}; + +static char manufacturer[] = "Ford"; +static char model_name[] = "HMI"; +static char description[] = "Human machine interface"; +static char version[] = "1.0"; +static char uri[] = "http://www.ford.com"; +static char serial_num[] = "N000000"; + +AoaInitSequence::AoaInitSequence() : UsbControlTransferSequence() { + AddTransfer(new AoaGetProtocolRequest); + AddTransfer(new AoaSendIdString(0, manufacturer, sizeof(manufacturer))); + AddTransfer(new AoaSendIdString(1, model_name, sizeof(model_name))); + AddTransfer(new AoaSendIdString(2, description, sizeof(description))); + AddTransfer(new AoaSendIdString(3, version, sizeof(version))); + AddTransfer(new AoaSendIdString(4, uri, sizeof(uri))); + AddTransfer(new AoaSendIdString(5, serial_num, sizeof(serial_num))); + AddTransfer(new AoaTurnIntoAccessoryMode); +} + +void UsbDeviceScanner::TurnIntoAccessoryMode(PlatformUsbDevice* device) { + LOG4CXX_INFO(logger_, "USB device VID:" << device->vendor_id() + << " PID:" << device->product_id() + << " turning into accessory mode"); + GetUsbHandler()->StartControlTransferSequence(new AoaInitSequence, device); +} + +void UsbDeviceScanner::GoogleAccessoryFound(PlatformUsbDevice* device) { + LOG4CXX_INFO(logger_, "Google accessory found"); + + pthread_mutex_lock(&devices_mutex_); + devices_.push_back(device); + pthread_mutex_unlock(&devices_mutex_); + LOG4CXX_INFO(logger_, "Google accessory (bus number " + << static_cast<int>(device->bus_number()) + << ", address " + << static_cast<int>(device->address()) + << ") identified as: " << device->GetManufacturer() + << ", " << device->GetProductName()); + UpdateList(); +} + +TransportAdapter::Error UsbDeviceScanner::Init() { + return TransportAdapter::OK; +} + +TransportAdapter::Error UsbDeviceScanner::Scan() { + return TransportAdapter::OK; +} + +void UsbDeviceScanner::UpdateList() { + DeviceVector device_vector; + pthread_mutex_lock(&devices_mutex_); + for (Devices::const_iterator it = devices_.begin(); it != devices_.end(); + ++it) { + const std::string device_name = + (*it)->GetManufacturer() + " " + (*it)->GetProductName(); + std::ostringstream oss; + oss << (*it)->GetManufacturer() << ":" << (*it)->GetProductName() << ":" + << (*it)->GetSerialNumber() << ":" + << static_cast<int>((*it)->bus_number()) << ":" + << static_cast<int>((*it)->address()); + const DeviceUID device_uid = oss.str(); + DeviceSptr device(new UsbDevice(*it, device_name, device_uid)); + device_vector.push_back(device); + } + pthread_mutex_unlock(&devices_mutex_); + + LOG4CXX_INFO(logger_, "USB search done " << device_vector.size()); + controller_->SearchDeviceDone(device_vector); +} + +void UsbDeviceScanner::Terminate() {} + +bool UsbDeviceScanner::IsInitialised() const { return true; } + +} // namespace +} // namespace |