diff options
author | Justin Dickow <jjdickow@gmail.com> | 2014-10-20 17:44:41 -0400 |
---|---|---|
committer | Justin Dickow <jjdickow@gmail.com> | 2014-10-20 17:44:41 -0400 |
commit | 34e7256493ff0e6594029b9857d7e2aa31f5dbeb (patch) | |
tree | 367306b507c52d3af211533810adbc22004e0192 /src/components/transport_manager/src | |
parent | 2eef966e9b5fd4d94dd98820095eb765e200c64b (diff) | |
download | sdl_core-34e7256493ff0e6594029b9857d7e2aa31f5dbeb.tar.gz |
SDL 3.8!
Signed-off-by: Justin Dickow <jjdickow@gmail.com>
Diffstat (limited to 'src/components/transport_manager/src')
25 files changed, 6875 insertions, 0 deletions
diff --git a/src/components/transport_manager/src/bluetooth/bluetooth_connection_factory.cc b/src/components/transport_manager/src/bluetooth/bluetooth_connection_factory.cc new file mode 100644 index 0000000000..421104422b --- /dev/null +++ b/src/components/transport_manager/src/bluetooth/bluetooth_connection_factory.cc @@ -0,0 +1,82 @@ +/** + * \file bluetooth_connection_factory.cc + * \brief BluetoothConnectionFactory 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/bluetooth/bluetooth_connection_factory.h" +#include "transport_manager/transport_adapter/transport_adapter_controller.h" +#include "transport_manager/bluetooth/bluetooth_socket_connection.h" + +#include "utils/logger.h" + +namespace transport_manager { +namespace transport_adapter { + +CREATE_LOGGERPTR_GLOBAL(logger_, "TransportManager") + +BluetoothConnectionFactory::BluetoothConnectionFactory( + TransportAdapterController* controller) + : controller_(controller) { +} + +TransportAdapter::Error BluetoothConnectionFactory::Init() { + return TransportAdapter::OK; +} + +TransportAdapter::Error BluetoothConnectionFactory::CreateConnection( + const DeviceUID& device_uid, const ApplicationHandle& app_handle) { + LOG4CXX_TRACE(logger_, "enter. device_uid: " << &device_uid << ", app_handle: " << + &app_handle); + BluetoothSocketConnection* connection( + new BluetoothSocketConnection(device_uid, app_handle, controller_)); + TransportAdapter::Error error = connection->Start(); + if (TransportAdapter::OK != error) { + LOG4CXX_ERROR(logger_, "connection::Start() failed"); + delete connection; + } + LOG4CXX_TRACE(logger_, "exit with error: " << error); + return error; +} + +void BluetoothConnectionFactory::Terminate() { +} + +bool BluetoothConnectionFactory::IsInitialised() const { + return true; +} + +BluetoothConnectionFactory::~BluetoothConnectionFactory() { +} + +} // namespace transport_adapter +} // namespace transport_manager diff --git a/src/components/transport_manager/src/bluetooth/bluetooth_device.cc b/src/components/transport_manager/src/bluetooth/bluetooth_device.cc new file mode 100644 index 0000000000..1a47f6c7ff --- /dev/null +++ b/src/components/transport_manager/src/bluetooth/bluetooth_device.cc @@ -0,0 +1,112 @@ +/* + * + * 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/bluetooth/bluetooth_device.h" + +#include <errno.h> +#include <sys/types.h> +#include <sys/socket.h> + +#include <algorithm> +#include <limits> +#include "utils/logger.h" + +namespace transport_manager { +namespace transport_adapter { +CREATE_LOGGERPTR_GLOBAL(logger_, "TransportManager") + +bool BluetoothDevice::GetRfcommChannel(const ApplicationHandle app_handle, + uint8_t* channel_out) { + LOG4CXX_TRACE(logger_, "enter. app_handle: " << app_handle << ", channel_out: " << + channel_out); + if (app_handle < 0 || app_handle > std::numeric_limits<uint8_t>::max()) { + LOG4CXX_TRACE(logger_, + "exit with FALSE. Condition: app_handle < 0 || app_handle > numeric_limits::max()"); + return false; + } + const uint8_t channel = static_cast<uint8_t>(app_handle); + RfcommChannelVector::const_iterator it = std::find(rfcomm_channels_.begin(), + rfcomm_channels_.end(), + channel); + if (it == rfcomm_channels_.end()) { + LOG4CXX_TRACE(logger_, "exit with FALSE. Condition: channel not found in RfcommChannelVector"); + return false; + } + *channel_out = channel; + LOG4CXX_TRACE(logger_, "exit with TRUE"); + return true; +} + +std::string BluetoothDevice::GetUniqueDeviceId(const bdaddr_t& device_address) { + LOG4CXX_TRACE(logger_, "enter. device_adress: " << &device_address); + char device_address_string[32]; + ba2str(&device_address, device_address_string); + LOG4CXX_TRACE(logger_, "exit with BT-" << device_address_string); + return std::string("BT-") + device_address_string; +} + +BluetoothDevice::BluetoothDevice(const bdaddr_t& device_address, const char* device_name, + const RfcommChannelVector& rfcomm_channels) + : Device(device_name, GetUniqueDeviceId(device_address)), + address_(device_address), + rfcomm_channels_(rfcomm_channels) { +} + +bool BluetoothDevice::IsSameAs(const Device* other) const { + LOG4CXX_TRACE(logger_, "enter. device: " << other); + bool result = false; + + const BluetoothDevice* other_bluetooth_device = + dynamic_cast<const BluetoothDevice*>(other); + + if (0 != other_bluetooth_device) { + if (0 + == memcmp(&address_, &other_bluetooth_device->address_, + sizeof(bdaddr_t))) { + result = true; + } + } + if (result) { + LOG4CXX_TRACE(logger_, "exit with TRUE"); + } else { + LOG4CXX_TRACE(logger_, "exit with FALSE"); + } + return result; +} + +ApplicationList BluetoothDevice::GetApplicationList() const { + return ApplicationList(rfcomm_channels_.begin(), rfcomm_channels_.end()); +} + +} // namespace transport_adapter +} // namespace transport_manager diff --git a/src/components/transport_manager/src/bluetooth/bluetooth_device_scanner.cc b/src/components/transport_manager/src/bluetooth/bluetooth_device_scanner.cc new file mode 100644 index 0000000000..e440ccb830 --- /dev/null +++ b/src/components/transport_manager/src/bluetooth/bluetooth_device_scanner.cc @@ -0,0 +1,497 @@ +/** + * \file bluetooth_device_scanner.cc + * \brief BluetoothDeviceScanner class header 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/bluetooth/bluetooth_device_scanner.h" + +#include <bluetooth/bluetooth.h> +#include <bluetooth/hci.h> +#include <bluetooth/hci_lib.h> +#include <bluetooth/sdp.h> +#include <bluetooth/sdp_lib.h> +#include <bluetooth/rfcomm.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <unistd.h> +#include <vector> +#include <sstream> +#include "transport_manager/bluetooth/bluetooth_transport_adapter.h" +#include "transport_manager/bluetooth/bluetooth_device.h" + +#include "utils/logger.h" + +namespace transport_manager { +namespace transport_adapter { + +CREATE_LOGGERPTR_GLOBAL(logger_, "TransportManager") + +namespace { +char* SplitToAddr(char* dev_list_entry) { + char* bl_address = strtok(dev_list_entry, "()"); + if (bl_address != NULL) { + bl_address = strtok(NULL, "()"); + return bl_address; + } else { + return NULL; + } +} + +int FindPairedDevs(std::vector<bdaddr_t>* result) { + LOG4CXX_TRACE(logger_, "enter. result adress: " << result); + DCHECK(result != NULL); + + const char* cmd = "bt-device -l"; + + FILE* pipe = popen(cmd, "r"); + if (!pipe) { + LOG4CXX_TRACE(logger_, "exit -1. Condition: !pipe"); + return -1; + } + char* buffer = new char[1028]; + size_t counter = 0; + while (fgets(buffer, 1028, pipe) != NULL) { + if (0 < counter++) { // skip first line + char* bt_address = SplitToAddr(buffer); + if (bt_address) { + bdaddr_t address; + str2ba(bt_address, &address); + result->push_back(address); + } + } + delete [] buffer; + buffer = new char[1028]; + } + pclose(pipe); + LOG4CXX_TRACE(logger_, "exit with 0"); + return 0; +} +} // namespace + +BluetoothDeviceScanner::BluetoothDeviceScanner( + TransportAdapterController* controller, bool auto_repeat_search, + int auto_repeat_pause_sec) + : controller_(controller), + thread_(NULL), + shutdown_requested_(false), + ready_(true), + device_scan_requested_(false), + device_scan_requested_lock_(), + device_scan_requested_cv_(), + auto_repeat_search_(auto_repeat_search), + auto_repeat_pause_sec_(auto_repeat_pause_sec) { + uint8_t smart_device_link_service_uuid_data[] = { 0x93, 0x6D, 0xA0, 0x1F, + 0x9A, 0xBD, 0x4D, 0x9D, 0x80, 0xC7, 0x02, 0xAF, 0x85, 0xC8, 0x22, 0xA8 + }; + sdp_uuid128_create(&smart_device_link_service_uuid_, + smart_device_link_service_uuid_data); + thread_ = new threads::Thread("BT Device Scaner", new BluetoothDeviceScannerDelegate(this)); +} + +BluetoothDeviceScanner::~BluetoothDeviceScanner() { +} + + +bool BluetoothDeviceScanner::IsInitialised() const { + return thread_->is_running(); +} + +void BluetoothDeviceScanner::UpdateTotalDeviceList() { + LOG4CXX_TRACE(logger_, "enter"); + DeviceVector devices; + devices.insert(devices.end(), paired_devices_with_sdl_.begin(), + paired_devices_with_sdl_.end()); + devices.insert(devices.end(), found_devices_with_sdl_.begin(), + found_devices_with_sdl_.end()); + controller_->SearchDeviceDone(devices); + LOG4CXX_TRACE(logger_, "exit"); +} + +void BluetoothDeviceScanner::DoInquiry() { + LOG4CXX_TRACE(logger_, "enter"); + + const int device_id = hci_get_route(0); + if (device_id < 0) { + LOG4CXX_INFO(logger_, "HCI device is not available"); + shutdown_requested_ = true; + controller_->SearchDeviceFailed(SearchDeviceError()); + LOG4CXX_TRACE(logger_, "exit. Condition: device_id < 0"); + return; + } + + int device_handle = hci_open_dev(device_id); + if (device_handle < 0) { + controller_->SearchDeviceFailed(SearchDeviceError()); + LOG4CXX_TRACE(logger_, "exit. Condition: device_handle < 0"); + return; + } + + if (paired_devices_.empty()) { + LOG4CXX_INFO(logger_, "Searching for paired devices."); + if (-1 == FindPairedDevs(&paired_devices_)) { + LOG4CXX_ERROR(logger_, "Failed to retrieve list of paired devices."); + controller_->SearchDeviceFailed(SearchDeviceError()); + } + } + + LOG4CXX_INFO(logger_, "Check rfcomm channel on " + << paired_devices_.size() << " paired devices."); + + paired_devices_with_sdl_.clear(); + CheckSDLServiceOnDevices(paired_devices_, device_handle, + &paired_devices_with_sdl_); + UpdateTotalDeviceList(); + + LOG4CXX_INFO(logger_, "Starting hci_inquiry on device " << device_id); + const uint8_t inquiry_time = 8u; // Time unit is 1.28 seconds + const size_t max_devices = 256u; + inquiry_info* inquiry_info_list = new inquiry_info[max_devices]; + + const int number_of_devices = hci_inquiry(device_id, inquiry_time, + max_devices, 0, &inquiry_info_list, + IREQ_CACHE_FLUSH); + + if (number_of_devices >= 0) { + LOG4CXX_INFO(logger_, + "hci_inquiry: found " << number_of_devices << " devices"); + std::vector < bdaddr_t > found_devices(number_of_devices); + for (int i = 0; i < number_of_devices; ++i) { + found_devices[i] = inquiry_info_list[i].bdaddr; + } + found_devices_with_sdl_.clear(); + CheckSDLServiceOnDevices(found_devices, device_handle, + &found_devices_with_sdl_); + } + UpdateTotalDeviceList(); + + close(device_handle); + delete[] inquiry_info_list; + + if (number_of_devices < 0) { + LOG4CXX_DEBUG(logger_, "number_of_devices < 0"); + controller_->SearchDeviceFailed(SearchDeviceError()); + } + + LOG4CXX_TRACE(logger_, "exit"); +} + +void BluetoothDeviceScanner::CheckSDLServiceOnDevices( + const std::vector<bdaddr_t>& bd_addresses, int device_handle, + DeviceVector* discovered_devices) { + LOG4CXX_TRACE(logger_, "enter. bd_addresses: " << &bd_addresses << ", device_handle: " << + device_handle << ", discovered_devices: " << discovered_devices); + std::vector<RfcommChannelVector> sdl_rfcomm_channels = + DiscoverSmartDeviceLinkRFCOMMChannels(bd_addresses); + + for (size_t i = 0; i < bd_addresses.size(); ++i) { + if (sdl_rfcomm_channels[i].empty()) { + continue; + } + + const bdaddr_t& bd_address = bd_addresses[i]; + char deviceName[256]; + int hci_read_remote_name_ret = hci_read_remote_name( + device_handle, &bd_address, sizeof(deviceName) / sizeof(deviceName[0]), + deviceName, 0); + + if (hci_read_remote_name_ret != 0) { + LOG4CXX_ERROR_WITH_ERRNO(logger_, "hci_read_remote_name failed"); + strncpy(deviceName, + BluetoothDevice::GetUniqueDeviceId(bd_address).c_str(), + sizeof(deviceName) / sizeof(deviceName[0])); + } + + Device* bluetooth_device = new BluetoothDevice(bd_address, deviceName, + sdl_rfcomm_channels[i]); + if (bluetooth_device) { + LOG4CXX_INFO(logger_, "Bluetooth device created successfully"); + discovered_devices->push_back(bluetooth_device); + } else { + LOG4CXX_WARN(logger_, "Can't create bluetooth device " << deviceName); + } + } + LOG4CXX_TRACE(logger_, "exit"); +} + +std::vector<BluetoothDeviceScanner::RfcommChannelVector> +BluetoothDeviceScanner::DiscoverSmartDeviceLinkRFCOMMChannels( + const std::vector<bdaddr_t>& device_addresses) { + LOG4CXX_TRACE(logger_, "enter device_addresses: " << &device_addresses); + const size_t size = device_addresses.size(); + std::vector<RfcommChannelVector> result(size); + + static const int attempts = 4; + static const int attempt_timeout = 5; + std::vector<bool> processed(size, false); + unsigned processed_count = 0; + for (int nattempt = 0; nattempt < attempts; ++nattempt) { + for (size_t i = 0; i < size; ++i) { + if (processed[i]) { + continue; + } + const bool final = DiscoverSmartDeviceLinkRFCOMMChannels( + device_addresses[i], &result[i]); + if (final) { + processed[i] = true; + ++processed_count; + } + } + if (++processed_count >= size) { + break; + } + sleep(attempt_timeout); + } + LOG4CXX_TRACE(logger_, "exit with vector<RfcommChannelVector>: size = " << result.size()); + return result; +} + +bool BluetoothDeviceScanner::DiscoverSmartDeviceLinkRFCOMMChannels( + const bdaddr_t& device_address, RfcommChannelVector* channels) { + LOG4CXX_TRACE(logger_, "enter. device_address: " << &device_address << ", channels: " << + channels); + static bdaddr_t any_address = { { 0, 0, 0, 0, 0, 0 } }; + + sdp_session_t* sdp_session = sdp_connect( + &any_address, &device_address, SDP_RETRY_IF_BUSY | SDP_WAIT_ON_CLOSE); + if (sdp_session == 0) { + bool result = !(errno == 31 || errno == 16 || errno == 117 || errno == 114); + if (result) { + LOG4CXX_TRACE(logger_, "exit with TRUE. Condition: sdp_session == 0"); + } else { + LOG4CXX_TRACE(logger_, "exit with FALSE. Condition: sdp_session == 0"); + } + return result; + } + + sdp_list_t* search_list = sdp_list_append(0, + &smart_device_link_service_uuid_); + uint32_t range = 0x0000ffff; + sdp_list_t* attr_list = sdp_list_append(0, &range); + sdp_list_t* response_list = 0; + + if (0 == sdp_service_search_attr_req(sdp_session, search_list, + SDP_ATTR_REQ_RANGE, attr_list, + &response_list)) { + for (sdp_list_t* r = response_list; 0 != r; r = r->next) { + sdp_record_t* sdp_record = static_cast<sdp_record_t*>(r->data); + sdp_list_t* proto_list = 0; + + if (0 == sdp_get_access_protos(sdp_record, &proto_list)) { + for (sdp_list_t* p = proto_list; 0 != p; p = p->next) { + sdp_list_t* pdsList = static_cast<sdp_list_t*>(p->data); + + for (sdp_list_t* pds = pdsList; 0 != pds; pds = pds->next) { + sdp_data_t* sdpData = static_cast<sdp_data_t*>(pds->data); + int proto = 0; + + for (sdp_data_t* d = sdpData; 0 != d; d = d->next) { + switch (d->dtd) { + case SDP_UUID16: + case SDP_UUID32: + case SDP_UUID128: + proto = sdp_uuid_to_proto(&d->val.uuid); + break; + + case SDP_UINT8: + if (RFCOMM_UUID == proto) { + channels->push_back(d->val.uint8); + } + break; + } + } + } + + sdp_list_free(pdsList, 0); + } + + sdp_list_free(proto_list, 0); + } + } + } + sdp_list_free(search_list, 0); + sdp_list_free(attr_list, 0); + sdp_list_free(response_list, 0); + sdp_close(sdp_session); + + if (!channels->empty()) { + LOG4CXX_INFO(logger_, "channels not empty"); + std::stringstream rfcomm_channels_string; + + for (RfcommChannelVector::const_iterator it = channels->begin(); + it != channels->end(); ++it) { + if (it != channels->begin()) { + rfcomm_channels_string << ", "; + } + rfcomm_channels_string << static_cast<uint32_t>(*it); + } + + LOG4CXX_INFO(logger_, + "SmartDeviceLink service was discovered on device " + << BluetoothDevice::GetUniqueDeviceId(device_address) + << " at channel(s): " << rfcomm_channels_string.str().c_str()); + } else { + LOG4CXX_INFO(logger_, + "SmartDeviceLink service was not discovered on device " + << BluetoothDevice::GetUniqueDeviceId(device_address)); + } + LOG4CXX_TRACE(logger_, "exit with TRUE"); + return true; +} + +void BluetoothDeviceScanner::Thread() { + LOG4CXX_TRACE(logger_, "enter"); + ready_ = true; + if (auto_repeat_search_) { + while (!shutdown_requested_) { + DoInquiry(); + device_scan_requested_ = false; + TimedWaitForDeviceScanRequest(); + } + } else { // search only on demand + while (true) { + { + sync_primitives::AutoLock auto_lock(device_scan_requested_lock_); + while (!(device_scan_requested_ || shutdown_requested_)) { + device_scan_requested_cv_.Wait(auto_lock); + } + } + if (shutdown_requested_) { + break; + } + DoInquiry(); + device_scan_requested_ = false; + } + } + + LOG4CXX_TRACE(logger_, "exit"); +} + +void BluetoothDeviceScanner::TimedWaitForDeviceScanRequest() { + LOG4CXX_TRACE(logger_, "enter"); + + if (auto_repeat_pause_sec_ == 0) { + LOG4CXX_TRACE(logger_, "exit. Condition: auto_repeat_pause_sec_ == 0"); + return; + } + + { + sync_primitives::AutoLock auto_lock(device_scan_requested_lock_); + while (!(device_scan_requested_ || shutdown_requested_)) { + const sync_primitives::ConditionalVariable::WaitStatus wait_status = + device_scan_requested_cv_.WaitFor(auto_lock, auto_repeat_pause_sec_ * 1000); + if (wait_status == sync_primitives::ConditionalVariable::kTimeout) { + LOG4CXX_INFO(logger_, "Bluetooth scanner timeout, performing scan"); + device_scan_requested_ = true; + } + } + } + + LOG4CXX_TRACE(logger_, "exit"); +} + +TransportAdapter::Error BluetoothDeviceScanner::Init() { + LOG4CXX_TRACE(logger_, "enter"); + if(!thread_->start()) { + LOG4CXX_ERROR(logger_, "Bluetooth device scanner thread start failed"); + LOG4CXX_TRACE(logger_, "exit with TransportAdapter:Fail"); + return TransportAdapter::FAIL; + } + LOG4CXX_INFO(logger_, "Bluetooth device scanner thread started"); + LOG4CXX_TRACE(logger_, "exit with TransportAdapter:OK"); + return TransportAdapter::OK; +} + +void BluetoothDeviceScanner::Terminate() { + LOG4CXX_TRACE(logger_, "enter"); + shutdown_requested_ = true; + if (thread_->is_running()) { + { + sync_primitives::AutoLock auto_lock(device_scan_requested_lock_); + device_scan_requested_ = false; + device_scan_requested_cv_.NotifyOne(); + } + LOG4CXX_INFO(logger_, + "Waiting for bluetooth device scanner thread termination"); + thread_->join(); + LOG4CXX_INFO(logger_, "PASA Bluetooth device scanner thread joined"); + } + delete thread_; + LOG4CXX_TRACE(logger_, "exit"); +} + +TransportAdapter::Error BluetoothDeviceScanner::Scan() { + LOG4CXX_TRACE(logger_, "enter"); + if ((!IsInitialised()) || shutdown_requested_) { + LOG4CXX_TRACE(logger_, "exit with TransportAdapter::BAD_STATE"); + return TransportAdapter::BAD_STATE; + } + if (auto_repeat_pause_sec_ == 0) { + LOG4CXX_TRACE(logger_, "exit with TransportAdapter::OK"); + return TransportAdapter::OK; + } + TransportAdapter::Error ret = TransportAdapter::OK; + + { + sync_primitives::AutoLock auto_lock(device_scan_requested_lock_); + if (false == device_scan_requested_) { + LOG4CXX_INFO(logger_, "Requesting device Scan"); + device_scan_requested_ = true; + device_scan_requested_cv_.NotifyOne(); + } else { + ret = TransportAdapter::BAD_STATE; + LOG4CXX_INFO(logger_, "Device Scan is currently in progress"); + } + } + + LOG4CXX_TRACE(logger_, "exit with Error: " << ret); + return ret; +} + +BluetoothDeviceScanner::BluetoothDeviceScannerDelegate::BluetoothDeviceScannerDelegate( + BluetoothDeviceScanner* scanner) + : scanner_(scanner) { +} + +void BluetoothDeviceScanner::BluetoothDeviceScannerDelegate::threadMain() +{ + LOG4CXX_TRACE_ENTER(logger_); + DCHECK(scanner_); + scanner_->Thread(); + LOG4CXX_TRACE_EXIT(logger_); +} + +} // namespace transport_adapter +} // namespace transport_manager + diff --git a/src/components/transport_manager/src/bluetooth/bluetooth_socket_connection.cc b/src/components/transport_manager/src/bluetooth/bluetooth_socket_connection.cc new file mode 100644 index 0000000000..ac1ccecfb5 --- /dev/null +++ b/src/components/transport_manager/src/bluetooth/bluetooth_socket_connection.cc @@ -0,0 +1,132 @@ +/** + * + * 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/bluetooth/bluetooth_socket_connection.h" + +#include <unistd.h> +#include <bluetooth/bluetooth.h> +#include <bluetooth/hci.h> +#include <bluetooth/hci_lib.h> +#include <bluetooth/sdp.h> +#include <bluetooth/sdp_lib.h> +#include <bluetooth/rfcomm.h> + +#include "transport_manager/bluetooth/bluetooth_device.h" +#include "transport_manager/transport_adapter/transport_adapter_controller.h" + +#include "utils/logger.h" + +namespace transport_manager { +namespace transport_adapter { +CREATE_LOGGERPTR_GLOBAL(logger_, "TransportManager") + +BluetoothSocketConnection::BluetoothSocketConnection( + const DeviceUID& device_uid, const ApplicationHandle& app_handle, + TransportAdapterController* controller) + : ThreadedSocketConnection(device_uid, app_handle, controller) { +} + +BluetoothSocketConnection::~BluetoothSocketConnection() { +} + +bool BluetoothSocketConnection::Establish(ConnectError** error) { + LOG4CXX_TRACE(logger_, "enter. (#" << pthread_self() << "), error: " << error); + DeviceSptr device = controller()->FindDevice(device_handle()); + + BluetoothDevice* bluetooth_device = + static_cast<BluetoothDevice*>(device.get()); + + uint8_t rfcomm_channel; + if (!bluetooth_device->GetRfcommChannel(application_handle(), + &rfcomm_channel)) { + LOG4CXX_DEBUG(logger_, + "Application " << application_handle() << " not found"); + *error = new ConnectError(); + LOG4CXX_TRACE(logger_, "exit with FALSE"); + return false; + } + + struct sockaddr_rc remoteSocketAddress = { 0 }; + remoteSocketAddress.rc_family = AF_BLUETOOTH; + memcpy(&remoteSocketAddress.rc_bdaddr, &bluetooth_device->address(), + sizeof(bdaddr_t)); + remoteSocketAddress.rc_channel = rfcomm_channel; + + int rfcomm_socket; + + int attempts = 4; + int connect_status = 0; + LOG4CXX_DEBUG(logger_, "start rfcomm Connect attempts"); + do { + rfcomm_socket = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); + if (-1 == rfcomm_socket) { + LOG4CXX_ERROR_WITH_ERRNO(logger_, + "Failed to create RFCOMM socket for device " << device_handle()); + *error = new ConnectError(); + LOG4CXX_TRACE(logger_, "exit with FALSE"); + return false; + } + connect_status = ::connect(rfcomm_socket, + (struct sockaddr*) &remoteSocketAddress, + sizeof(remoteSocketAddress)); + if (0 == connect_status) { + LOG4CXX_DEBUG(logger_, "rfcomm Connect ok"); + break; + } + if (errno != 111 && errno != 104) { + LOG4CXX_DEBUG(logger_, "rfcomm Connect errno " << errno); + break; + } + if (errno) { + LOG4CXX_DEBUG(logger_, "rfcomm Connect errno " << errno); + close(rfcomm_socket); + } + sleep(2); + } while (--attempts > 0); + LOG4CXX_INFO(logger_, "rfcomm Connect attempts finished"); + if (0 != connect_status) { + LOG4CXX_DEBUG(logger_, + "Failed to Connect to remote device " << BluetoothDevice::GetUniqueDeviceId( + remoteSocketAddress.rc_bdaddr) << " for session " << this); + *error = new ConnectError(); + LOG4CXX_TRACE(logger_, "exit with FALSE"); + return false; + } + + set_socket(rfcomm_socket); + LOG4CXX_TRACE(logger_, "exit with TRUE"); + return true; +} + +} // namespace transport_adapter +} // namespace transport_manager diff --git a/src/components/transport_manager/src/bluetooth/bluetooth_transport_adapter.cc b/src/components/transport_manager/src/bluetooth/bluetooth_transport_adapter.cc new file mode 100644 index 0000000000..7d60213b01 --- /dev/null +++ b/src/components/transport_manager/src/bluetooth/bluetooth_transport_adapter.cc @@ -0,0 +1,161 @@ +/** + * \file bluetooth_transport_adapter.cc + * \brief BluetoothTransportAdapter 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 <sys/types.h> +#include <sys/socket.h> +#include <unistd.h> + +#include <iomanip> +#include <set> +#include <bluetooth/bluetooth.h> + +#include "resumption/last_state.h" + +#include "transport_manager/bluetooth/bluetooth_transport_adapter.h" +#include "transport_manager/bluetooth/bluetooth_device_scanner.h" +#include "transport_manager/bluetooth/bluetooth_connection_factory.h" +#include "transport_manager/bluetooth/bluetooth_device.h" + +#include "utils/logger.h" + +namespace transport_manager { +namespace transport_adapter { + +CREATE_LOGGERPTR_GLOBAL(logger_, "TransportManager") + +BluetoothTransportAdapter::~BluetoothTransportAdapter() { +} + +BluetoothTransportAdapter::BluetoothTransportAdapter() + : TransportAdapterImpl(new BluetoothDeviceScanner(this, true, 0), + new BluetoothConnectionFactory(this), 0) { +} + +DeviceType BluetoothTransportAdapter::GetDeviceType() const { + return "sdl-bluetooth"; +} + +void BluetoothTransportAdapter::Store() const { + LOG4CXX_TRACE(logger_, "enter"); + Json::Value bluetooth_adapter_dictionary; + Json::Value devices_dictionary; + DeviceList device_ids = GetDeviceList(); + for (DeviceList::const_iterator i = device_ids.begin(); i != device_ids.end(); ++i) { + DeviceUID device_id = *i; + DeviceSptr device = FindDevice(device_id); + if (!device) { // device could have been disconnected + continue; + } + utils::SharedPtr<BluetoothDevice> bluetooth_device = + DeviceSptr::static_pointer_cast<BluetoothDevice>(device); + Json::Value device_dictionary; + device_dictionary["name"] = bluetooth_device->name(); + char address[18]; + ba2str(&bluetooth_device->address(), address); + device_dictionary["address"] = std::string(address); + Json::Value applications_dictionary; + ApplicationList app_ids = bluetooth_device->GetApplicationList(); + for (ApplicationList::const_iterator j = app_ids.begin(); j != app_ids.end(); ++j) { + ApplicationHandle app_handle = *j; + if (FindEstablishedConnection(bluetooth_device->unique_device_id(), app_handle)) { + uint8_t rfcomm_channel; + bluetooth_device->GetRfcommChannel(app_handle, &rfcomm_channel); + Json::Value application_dictionary; + char rfcomm_channel_record[4]; + sprintf(rfcomm_channel_record, "%u", rfcomm_channel); + application_dictionary["rfcomm_channel"] = std::string(rfcomm_channel_record); + applications_dictionary.append(application_dictionary); + } + } + if (!applications_dictionary.empty()) { + device_dictionary["applications"] = applications_dictionary; + devices_dictionary.append(device_dictionary); + } + } + bluetooth_adapter_dictionary["devices"] = devices_dictionary; + resumption::LastState::instance()->dictionary["TransportManager"]["BluetoothAdapter"] = + bluetooth_adapter_dictionary; + LOG4CXX_TRACE(logger_, "exit"); +} + +bool BluetoothTransportAdapter::Restore() { + LOG4CXX_TRACE(logger_, "enter"); + bool errors_occured = false; + const Json::Value bluetooth_adapter_dictionary = + resumption::LastState::instance()->dictionary["TransportManager"]["BluetoothAdapter"]; + const Json::Value devices_dictionary = bluetooth_adapter_dictionary["devices"]; + for (Json::Value::const_iterator i = devices_dictionary.begin(); + i != devices_dictionary.end(); ++i) { + const Json::Value device_dictionary = *i; + std::string name = device_dictionary["name"].asString(); + std::string address_record = device_dictionary["address"].asString(); + bdaddr_t address; + str2ba(address_record.c_str(), &address); + RfcommChannelVector rfcomm_channels; + const Json::Value applications_dictionary = device_dictionary["applications"]; + for (Json::Value::const_iterator j = applications_dictionary.begin(); + j != applications_dictionary.end(); ++j) { + const Json::Value application_dictionary = *j; + std::string rfcomm_channel_record = + application_dictionary["rfcomm_channel"].asString(); + uint8_t rfcomm_channel = + static_cast<uint8_t>(atoi(rfcomm_channel_record.c_str())); + rfcomm_channels.push_back(rfcomm_channel); + } + BluetoothDevice* bluetooth_device = + new BluetoothDevice(address, name.c_str(), rfcomm_channels); + DeviceSptr device(bluetooth_device); + AddDevice(device); + for (RfcommChannelVector::const_iterator j = + rfcomm_channels.begin(); j != rfcomm_channels.end(); ++j) { + ApplicationHandle app_handle = + *j; // for Bluetooth device app_handle is just RFCOMM channel + if (Error::OK != Connect(device->unique_device_id(), app_handle)) { + errors_occured = true; + } + } + } + bool result = !errors_occured; + if (result) { + LOG4CXX_TRACE(logger_, "exit with TRUE"); + } else { + LOG4CXX_TRACE(logger_, "exit with FALSE"); + } + return result; +} + +} // namespace transport_adapter +} // namespace transport_manager diff --git a/src/components/transport_manager/src/tcp/dnssd_service_browser.cc b/src/components/transport_manager/src/tcp/dnssd_service_browser.cc new file mode 100644 index 0000000000..5580585766 --- /dev/null +++ b/src/components/transport_manager/src/tcp/dnssd_service_browser.cc @@ -0,0 +1,381 @@ +/** + * + * 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 <algorithm> +#include <map> + +#include "transport_manager/transport_adapter/transport_adapter_impl.h" +#include "transport_manager/tcp/tcp_device.h" +#include "transport_manager/tcp/dnssd_service_browser.h" +#include "utils/logger.h" + +namespace transport_manager { +namespace transport_adapter { + +CREATE_LOGGERPTR_GLOBAL(logger_, "TransportManager") + +bool operator==(const DnssdServiceRecord& a, const DnssdServiceRecord& b) { + return a.name == b.name && a.type == b.type && a.interface == b.interface + && a.protocol == b.protocol && a.domain_name == b.domain_name; +} + +void DnssdServiceBrowser::Terminate() { + LOG4CXX_TRACE(logger_, "enter"); + if (0 != avahi_threaded_poll_) { + avahi_threaded_poll_stop(avahi_threaded_poll_); + } + if (0 != avahi_service_browser_) { + avahi_service_browser_free(avahi_service_browser_); + } + if (0 != avahi_client_) { + avahi_client_free(avahi_client_); + } + if (0 != avahi_threaded_poll_) { + avahi_threaded_poll_free(avahi_threaded_poll_); + } + LOG4CXX_TRACE(logger_, "exit"); +} + +bool DnssdServiceBrowser::IsInitialised() const { + return initialised_; +} + +DnssdServiceBrowser::DnssdServiceBrowser(TransportAdapterController* controller) + : controller_(controller), + avahi_service_browser_(0), + avahi_threaded_poll_(0), + avahi_client_(0), + service_records_(), + mutex_(), + initialised_(false) { + pthread_mutex_init(&mutex_, 0); +} + +DnssdServiceBrowser::~DnssdServiceBrowser() { + pthread_mutex_destroy(&mutex_); +} + +void DnssdServiceBrowser::OnClientConnected() { + initialised_ = true; + LOG4CXX_INFO(logger_, "AvahiClient ready"); +} + +void DnssdServiceBrowser::OnClientFailure() { + LOG4CXX_TRACE(logger_, "enter"); + const int avahi_errno = avahi_client_errno(avahi_client_); + if (avahi_errno == AVAHI_ERR_DISCONNECTED) { + LOG4CXX_DEBUG(logger_, "AvahiClient disconnected"); + CreateAvahiClientAndBrowser(); + } else { + LOG4CXX_ERROR(logger_, + "AvahiClient failure: " << avahi_strerror(avahi_errno)); + } + LOG4CXX_TRACE(logger_, "exit"); +} + +void AvahiClientCallback(AvahiClient* avahi_client, + AvahiClientState avahi_client_state, void* data) { + LOG4CXX_TRACE(logger_, + "enter. avahi_client " << avahi_client << "avahi_client_state " << + avahi_client_state << "data " << data); + DnssdServiceBrowser* dnssd_service_browser = + static_cast<DnssdServiceBrowser*>(data); + + switch (avahi_client_state) { + case AVAHI_CLIENT_S_RUNNING: + dnssd_service_browser->OnClientConnected(); + LOG4CXX_DEBUG(logger_, "avahi_client_state: AVAHI_CLIENT_S_RUNNING"); + break; + case AVAHI_CLIENT_FAILURE: + dnssd_service_browser->OnClientFailure(); + LOG4CXX_DEBUG(logger_, "avahi_client_state: AVAHI_CLIENT_FAILURE"); + break; + default: { + LOG4CXX_ERROR(logger_, "Unknown avahi_client_state: " << avahi_client_state); + } + } + LOG4CXX_TRACE(logger_, "exit"); +} + +void AvahiServiceBrowserCallback(AvahiServiceBrowser* avahi_service_browser, + AvahiIfIndex interface, AvahiProtocol protocol, + AvahiBrowserEvent event, const char* name, + const char* type, const char* domain, + AvahiLookupResultFlags flags, void* data) { + LOG4CXX_TRACE(logger_, "enter. avahi_service_browser " << avahi_service_browser << + " interface " << interface << " protocol " << protocol << + " event " << event << " name " << name << " type " << type << " domain " << domain << + " flags " << flags << " data " << data); + DnssdServiceBrowser* dnssd_service_browser = + static_cast<DnssdServiceBrowser*>(data); + + switch (event) { + case AVAHI_BROWSER_FAILURE: + LOG4CXX_ERROR( + logger_, + "AvahiServiceBrowser failure: " << avahi_strerror( + avahi_client_errno( + avahi_service_browser_get_client( + avahi_service_browser)))); + break; + + case AVAHI_BROWSER_NEW: + dnssd_service_browser->AddService(interface, protocol, name, type, + domain); + LOG4CXX_DEBUG(logger_, "event: AVAHI_BROWSER_NEW"); + break; + + case AVAHI_BROWSER_REMOVE: + dnssd_service_browser->RemoveService(interface, protocol, name, type, + domain); + LOG4CXX_DEBUG(logger_, "event: AVAHI_BROWSER_REMOVE"); + break; + + case AVAHI_BROWSER_ALL_FOR_NOW: + LOG4CXX_DEBUG(logger_, "event: AVAHI_BROWSER_ALL_FOR_NOW"); + case AVAHI_BROWSER_CACHE_EXHAUSTED: + LOG4CXX_DEBUG(logger_, "event: AVAHI_BROWSER_CACHE_EXHAUSTED"); + break; + } + LOG4CXX_TRACE(logger_, "exit"); +} + +void DnssdServiceBrowser::ServiceResolved( + const DnssdServiceRecord& service_record) { + LOG4CXX_TRACE(logger_, "enter"); + pthread_mutex_lock(&mutex_); + ServiceRecords::iterator service_record_it = std::find( + service_records_.begin(), service_records_.end(), service_record); + if (service_record_it != service_records_.end()) { + *service_record_it = service_record; + } + DeviceVector device_vector = PrepareDeviceVector(); + controller_->SearchDeviceDone(device_vector); + pthread_mutex_unlock(&mutex_); + LOG4CXX_TRACE(logger_, "exit"); +} + +void DnssdServiceBrowser::ServiceResolveFailed( + const DnssdServiceRecord& service_record) { + LOG4CXX_TRACE(logger_, "enter"); + LOG4CXX_ERROR(logger_, + "AvahiServiceResolver failure for: " << service_record.name); + pthread_mutex_lock(&mutex_); + ServiceRecords::iterator service_record_it = std::find( + service_records_.begin(), service_records_.end(), service_record); + if (service_record_it != service_records_.end()) { + service_records_.erase(service_record_it); + } + pthread_mutex_unlock(&mutex_); + LOG4CXX_TRACE(logger_, "exit"); +} + +void AvahiServiceResolverCallback(AvahiServiceResolver* avahi_service_resolver, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiResolverEvent event, const char* name, + const char* type, const char* domain, + const char* host_name, + const AvahiAddress* avahi_address, + uint16_t port, AvahiStringList* txt, + AvahiLookupResultFlags flags, void* data) { + LOG4CXX_TRACE(logger_, "enter. avahi_service_resolver " << avahi_service_resolver << + " interface " << interface << + " protocol " << protocol << " event " << event << " name " << name << " type " << type << + " domain " << domain << " host_name " << host_name << + " avahi_address " << avahi_address << " port " << port << " txt " << txt << " flags " << + flags << " data " << data); + DnssdServiceBrowser* dnssd_service_browser = + static_cast<DnssdServiceBrowser*>(data); + + DnssdServiceRecord service_record; + service_record.interface = interface; + service_record.protocol = protocol; + service_record.domain_name = domain; + service_record.host_name = host_name; + service_record.name = name; + service_record.type = type; + switch (event) { + case AVAHI_RESOLVER_FOUND: + service_record.addr = avahi_address->data.ipv4.address; + service_record.port = port; + dnssd_service_browser->ServiceResolved(service_record); + LOG4CXX_DEBUG(logger_, "event: AVAHI_RESOLVER_FOUND"); + break; + case AVAHI_RESOLVER_FAILURE: + dnssd_service_browser->ServiceResolveFailed(service_record); + LOG4CXX_DEBUG(logger_, "event: AVAHI_RESOLVER_FAILURE"); + break; + } + + avahi_service_resolver_free(avahi_service_resolver); + LOG4CXX_TRACE(logger_, "exit"); +} + +TransportAdapter::Error DnssdServiceBrowser::CreateAvahiClientAndBrowser() { + LOG4CXX_TRACE(logger_, "enter"); + if (0 != avahi_service_browser_) { + avahi_service_browser_free(avahi_service_browser_); + } + if (0 != avahi_client_) { + avahi_client_free(avahi_client_); + } + + int avahi_error; + avahi_client_ = avahi_client_new( + avahi_threaded_poll_get(avahi_threaded_poll_), AVAHI_CLIENT_NO_FAIL, + AvahiClientCallback, this, &avahi_error); + if (0 == avahi_client_) { + LOG4CXX_ERROR(logger_, "Failed to create AvahiClient: " << avahi_strerror(avahi_error)); + LOG4CXX_TRACE(logger_, "exit with TransportAdapter::FAIL"); + return TransportAdapter::FAIL; + } + + pthread_mutex_lock(&mutex_); + service_records_.clear(); + pthread_mutex_unlock(&mutex_); + + avahi_service_browser_ = avahi_service_browser_new( + avahi_client_, AVAHI_IF_UNSPEC, /* TODO use only required iface */ + AVAHI_PROTO_INET, DNSSD_DEFAULT_SERVICE_TYPE, NULL, /* use default domain */ + static_cast<AvahiLookupFlags>(0), AvahiServiceBrowserCallback, this); + LOG4CXX_TRACE(logger_, "exit with TransportAdapter::OK"); + return TransportAdapter::OK; +} + +TransportAdapter::Error DnssdServiceBrowser::Init() { + LOG4CXX_TRACE(logger_, "enter"); + avahi_threaded_poll_ = avahi_threaded_poll_new(); + if (0 == avahi_threaded_poll_) { + LOG4CXX_ERROR(logger_, "Failed to create AvahiThreadedPoll"); + LOG4CXX_TRACE(logger_, "exit with TransportAdapter::FAIL"); + return TransportAdapter::FAIL; + } + + const TransportAdapter::Error err = CreateAvahiClientAndBrowser(); + if (err != TransportAdapter::OK) { + LOG4CXX_TRACE(logger_, "exit with error " << err); + return err; + } + + const int poll_start_status = avahi_threaded_poll_start(avahi_threaded_poll_); + if (0 != poll_start_status) { + LOG4CXX_ERROR(logger_, "Failed to start AvahiThreadedPoll"); + LOG4CXX_TRACE(logger_, "exit with TransportAdapter::FAIL. Condition: 0 != poll_start_status"); + return TransportAdapter::FAIL; + } + LOG4CXX_TRACE(logger_, "exit with TransportAdapter::OK"); + return TransportAdapter::OK; +} + +TransportAdapter::Error DnssdServiceBrowser::Scan() { + return TransportAdapter::NOT_SUPPORTED; +} + +void DnssdServiceBrowser::AddService(AvahiIfIndex interface, + AvahiProtocol protocol, const char* name, + const char* type, const char* domain) { + LOG4CXX_TRACE(logger_, "enter: interface " << interface << " protocol " << protocol << + " name " << name << " type " << type << " domain " << domain); + DnssdServiceRecord record; + record.interface = interface; + record.protocol = protocol; + record.domain_name = domain; + record.name = name; + record.type = type; + + pthread_mutex_lock(&mutex_); + if (service_records_.end() + == std::find(service_records_.begin(), service_records_.end(), record)) { + service_records_.push_back(record); + avahi_service_resolver_new( + avahi_client_, interface, protocol, name, type, domain, + AVAHI_PROTO_INET, static_cast<AvahiLookupFlags>(0), + AvahiServiceResolverCallback, this); + } + pthread_mutex_unlock(&mutex_); + LOG4CXX_TRACE(logger_, "exit"); +} + +void DnssdServiceBrowser::RemoveService(AvahiIfIndex interface, + AvahiProtocol protocol, + const char* name, const char* type, + const char* domain) { + LOG4CXX_TRACE(logger_, "enter: interface " << interface << " protocol " << protocol << + " name " << name << " type " << type << " domain " << domain); + DnssdServiceRecord record; + record.interface = interface; + record.protocol = protocol; + record.name = name; + record.type = type; + record.domain_name = domain; + + pthread_mutex_lock(&mutex_); + service_records_.erase( + std::remove(service_records_.begin(), service_records_.end(), record), + service_records_.end()); + pthread_mutex_unlock(&mutex_); + LOG4CXX_TRACE(logger_, "exit"); +} + +DeviceVector DnssdServiceBrowser::PrepareDeviceVector() const { + LOG4CXX_TRACE(logger_, "enter"); + std::map<uint32_t, TcpDevice*> devices; + for (ServiceRecords::const_iterator it = service_records_.begin(); + it != service_records_.end(); ++it) { + const DnssdServiceRecord& service_record = *it; + if (service_record.host_name.empty()) { + continue; + } + if (devices[service_record.addr] == 0) { + devices[service_record.addr] = new TcpDevice(service_record.addr, + service_record.host_name); + } + if (devices[service_record.addr] != 0) { + devices[service_record.addr]->AddDiscoveredApplication( + service_record.port); + } + } + DeviceVector device_vector; + device_vector.reserve(devices.size()); + for (std::map<uint32_t, TcpDevice*>::const_iterator it = devices.begin(); + it != devices.end(); ++it) { + device_vector.push_back(DeviceSptr(it->second)); + } + LOG4CXX_TRACE(logger_, "exit"); + return device_vector; +} + +} // namespace +} // namespace + diff --git a/src/components/transport_manager/src/tcp/tcp_client_listener.cc b/src/components/transport_manager/src/tcp/tcp_client_listener.cc new file mode 100644 index 0000000000..514fbe66eb --- /dev/null +++ b/src/components/transport_manager/src/tcp/tcp_client_listener.cc @@ -0,0 +1,286 @@ +/** + * + * 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/tcp/tcp_client_listener.h" + +#include <memory.h> +#include <signal.h> +#include <errno.h> +#include <arpa/inet.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/sysctl.h> +#include <sys/socket.h> +#ifdef __linux__ +# include <linux/tcp.h> +#else // __linux__ +# include <sys/time.h> +# include <netinet/in.h> +# include <netinet/tcp.h> +# include <netinet/tcp_var.h> +#endif // __linux__ + +#include "utils/logger.h" + +#include "transport_manager/transport_adapter/transport_adapter_controller.h" +#include "transport_manager/tcp/tcp_device.h" +#include "transport_manager/tcp/tcp_socket_connection.h" + +namespace transport_manager { +namespace transport_adapter { + +CREATE_LOGGERPTR_GLOBAL(logger_, "TransportManager") + +TcpClientListener::TcpClientListener(TransportAdapterController* controller, + const uint16_t port, + const bool enable_keepalive) + : port_(port), + enable_keepalive_(enable_keepalive), + controller_(controller), + thread_(), + socket_(-1), + thread_started_(false), + shutdown_requested_(false), + thread_stop_requested_(false) {} + +void* tcpClientListenerThread(void* data) { + LOG4CXX_TRACE(logger_, "enter. data " << data); + TcpClientListener* tcpClientListener = static_cast<TcpClientListener*>(data); + assert(tcpClientListener != 0); + tcpClientListener->Thread(); + LOG4CXX_TRACE(logger_, "exit"); + return 0; +} + +TransportAdapter::Error TcpClientListener::Init() { + return TransportAdapter::OK; +} + +void TcpClientListener::Terminate() { + LOG4CXX_TRACE(logger_, "enter"); + shutdown_requested_ = true; + if (TransportAdapter::OK != StopListening()) { + LOG4CXX_ERROR(logger_, "Cannot stop listening TCP"); + } + LOG4CXX_TRACE(logger_, "exit"); +} + +bool TcpClientListener::IsInitialised() const { + return true; +} + +TcpClientListener::~TcpClientListener() { + LOG4CXX_INFO(logger_, "destructor"); +} + +void SetKeepaliveOptions(const int fd) { + LOG4CXX_TRACE(logger_, "enter. fd: " << fd); + int yes = 1; + int keepidle = 3; // 3 seconds to disconnection detecting + int keepcnt = 5; + int keepintvl = 1; +#ifdef __linux__ + int user_timeout = 7000; // milliseconds + setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &yes, sizeof(yes)); + setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &keepidle, sizeof(keepidle)); + setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &keepcnt, sizeof(keepcnt)); + setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &keepintvl, sizeof(keepintvl)); + setsockopt(fd, IPPROTO_TCP, TCP_USER_TIMEOUT, &user_timeout, + sizeof(user_timeout)); +#elif __QNX__ // __linux__ + // TODO (KKolodiy): Out of order! + const int kMidLength = 4; + int mib[kMidLength]; + timeval tval; + + mib[0] = CTL_NET; + mib[1] = AF_INET; + mib[2] = IPPROTO_TCP; + mib[3] = TCPCTL_KEEPIDLE; + sysctl(mib, kMidLength, NULL, NULL, &keepidle, sizeof(keepidle)); + + mib[0] = CTL_NET; + mib[1] = AF_INET; + mib[2] = IPPROTO_TCP; + mib[3] = TCPCTL_KEEPCNT; + sysctl(mib, kMidLength, NULL, NULL, &keepcnt, sizeof(keepcnt)); + + mib[0] = CTL_NET; + mib[1] = AF_INET; + mib[2] = IPPROTO_TCP; + mib[3] = TCPCTL_KEEPINTVL; + sysctl(mib, kMidLength, NULL, NULL, &keepintvl, sizeof(keepintvl)); + + memset(&tval, sizeof(tval), 0); + tval.tv_sec = keepidle; + setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &yes, sizeof(yes)); + setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE, &tval, sizeof(tval)); +#endif // __QNX__ + LOG4CXX_TRACE(logger_, "exit"); +} + +void TcpClientListener::Thread() { + LOG4CXX_TRACE(logger_, "enter"); + while (false == thread_stop_requested_) { + sockaddr_in client_address; + socklen_t client_address_size = sizeof(client_address); + const int connection_fd = accept(socket_, (struct sockaddr*)&client_address, + &client_address_size); + if (thread_stop_requested_) { + LOG4CXX_DEBUG(logger_, "thread_stop_requested_"); + break; + } + + if (connection_fd < 0) { + LOG4CXX_ERROR_WITH_ERRNO(logger_, "accept() failed"); + continue; + } + + if (AF_INET != client_address.sin_family) { + LOG4CXX_DEBUG(logger_, "Address of connected client is invalid"); + continue; + } + + char device_name[32]; + strncpy(device_name, inet_ntoa(client_address.sin_addr), + sizeof(device_name) / sizeof(device_name[0])); + LOG4CXX_INFO(logger_, "Connected client " << device_name); + + if (enable_keepalive_) { + SetKeepaliveOptions(connection_fd); + } + + TcpDevice* tcp_device = new TcpDevice(client_address.sin_addr.s_addr, device_name); + DeviceSptr device = controller_->AddDevice(tcp_device); + tcp_device = static_cast<TcpDevice*>(device.get()); + const ApplicationHandle app_handle = tcp_device->AddIncomingApplication( + connection_fd); + + TcpSocketConnection* connection( + new TcpSocketConnection(device->unique_device_id(), app_handle, + controller_)); + connection->set_socket(connection_fd); + const TransportAdapter::Error error = connection->Start(); + if (error != TransportAdapter::OK) { + delete connection; + } + } + LOG4CXX_TRACE(logger_, "exit"); +} + +TransportAdapter::Error TcpClientListener::StartListening() { + LOG4CXX_TRACE(logger_, "enter"); + if (thread_started_) { + LOG4CXX_TRACE(logger_, + "exit with TransportAdapter::BAD_STATE. Condition: thread_started_"); + return TransportAdapter::BAD_STATE; + } + + socket_ = socket(AF_INET, SOCK_STREAM, 0); + + if (-1 == socket_) { + LOG4CXX_ERROR_WITH_ERRNO(logger_, "Failed to create socket"); + LOG4CXX_TRACE(logger_, "exit with TransportAdapter::FAIL. Condition: -1 == socket_"); + return TransportAdapter::FAIL; + } + + sockaddr_in server_address; + memset(&server_address, 0, sizeof(server_address)); + server_address.sin_family = AF_INET; + server_address.sin_port = htons(port_); + server_address.sin_addr.s_addr = INADDR_ANY; + + int optval = 1; + setsockopt(socket_, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); + + if (0 != bind(socket_, (sockaddr*) &server_address, sizeof(server_address))) { + LOG4CXX_ERROR_WITH_ERRNO(logger_, "bind() failed"); + LOG4CXX_TRACE(logger_, + "exit with TransportAdapter::FAIL. Condition: 0 != bind(socket_, (sockaddr*) &server_address, sizeof(server_address))"); + return TransportAdapter::FAIL; + } + + if (0 != listen(socket_, 128)) { + LOG4CXX_ERROR_WITH_ERRNO(logger_, "listen() failed"); + LOG4CXX_TRACE(logger_, + "exit with TransportAdapter::FAIL. Condition: 0 != listen(socket_, 128)"); + return TransportAdapter::FAIL; + } + + const int thread_start_error = pthread_create(&thread_, 0, + &tcpClientListenerThread, this); + if (0 == thread_start_error) { + thread_started_ = true; + LOG4CXX_DEBUG(logger_, "Tcp client listener thread started"); + pthread_setname_np(thread_, "TCP listener"); + } else { + LOG4CXX_ERROR(logger_, "Tcp client listener thread start failed, error code " + << thread_start_error); + LOG4CXX_TRACE(logger_, + "exit with TransportAdapter::FAIL. Condition: 0 !== thread_start_error"); + return TransportAdapter::FAIL; + } + LOG4CXX_TRACE(logger_, "exit with TransportAdapter::OK"); + return TransportAdapter::OK; +} + +TransportAdapter::Error TcpClientListener::StopListening() { + LOG4CXX_TRACE(logger_, "enter"); + if (!thread_started_) { + LOG4CXX_TRACE(logger_, + "exit with TransportAdapter::BAD_STATE. Condition !thread_started_"); + return TransportAdapter::BAD_STATE; + } + + thread_stop_requested_ = true; + int byebyesocket = socket(AF_INET, SOCK_STREAM, 0); + sockaddr_in server_address; + memset(&server_address, 0, sizeof(server_address)); + server_address.sin_family = AF_INET; + server_address.sin_port = htons(port_); + server_address.sin_addr.s_addr = INADDR_ANY; + connect(byebyesocket, (sockaddr*)&server_address, sizeof(server_address)); + shutdown(byebyesocket, SHUT_RDWR); + close(byebyesocket); + pthread_join(thread_, 0); + LOG4CXX_DEBUG(logger_, "Tcp client listener thread terminated"); + close(socket_); + socket_ = -1; + thread_started_ = false; + thread_stop_requested_ = false; + LOG4CXX_TRACE(logger_, "exit with TransportAdapter::OK"); + return TransportAdapter::OK; +} + +} // namespace transport_adapter +} // namespace transport_manager diff --git a/src/components/transport_manager/src/tcp/tcp_connection_factory.cc b/src/components/transport_manager/src/tcp/tcp_connection_factory.cc new file mode 100644 index 0000000000..69173a0e06 --- /dev/null +++ b/src/components/transport_manager/src/tcp/tcp_connection_factory.cc @@ -0,0 +1,79 @@ +/** + * + * 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/tcp/tcp_connection_factory.h" +#include "transport_manager/tcp/tcp_socket_connection.h" + +#include "utils/logger.h" + +namespace transport_manager { +namespace transport_adapter { + +CREATE_LOGGERPTR_GLOBAL(logger_, "TransportManager") + +TcpConnectionFactory::TcpConnectionFactory(TransportAdapterController* controller) + : controller_(controller) { +} + +TransportAdapter::Error TcpConnectionFactory::Init() { + return TransportAdapter::OK; +} + +TransportAdapter::Error TcpConnectionFactory::CreateConnection( + const DeviceUID& device_uid, const ApplicationHandle& app_handle) { + LOG4CXX_TRACE(logger_, "enter. DeviceUID: " << &device_uid << ", ApplicationHandle: " << + &app_handle); + TcpServerOiginatedSocketConnection* connection( + new TcpServerOiginatedSocketConnection(device_uid, app_handle, + controller_)); + TransportAdapter::Error error = connection->Start(); + if (TransportAdapter::OK != error) { + LOG4CXX_ERROR(logger_, "Transport adapter error " << error); + delete connection; + } + LOG4CXX_TRACE(logger_, "exit with error " << error); + return error; +} + +void TcpConnectionFactory::Terminate() { +} + +bool TcpConnectionFactory::IsInitialised() const { + return true; +} + +TcpConnectionFactory::~TcpConnectionFactory() { +} + +} // namespace transport_adapter +} // namespace transport_manager diff --git a/src/components/transport_manager/src/tcp/tcp_device.cc b/src/components/transport_manager/src/tcp/tcp_device.cc new file mode 100644 index 0000000000..2540c26ed0 --- /dev/null +++ b/src/components/transport_manager/src/tcp/tcp_device.cc @@ -0,0 +1,153 @@ +/** + * + * 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/tcp/tcp_device.h" + +#include "utils/logger.h" + +namespace transport_manager { +namespace transport_adapter { + +CREATE_LOGGERPTR_GLOBAL(logger_, "TransportManager") + +TcpDevice::TcpDevice(const in_addr_t& in_addr, const std::string& name) + : + Device(name, name), + in_addr_(in_addr), + last_handle_(0) { + pthread_mutex_init(&applications_mutex_, 0); +} + +bool TcpDevice::IsSameAs(const Device* other) const { + LOG4CXX_TRACE(logger_, "enter. Device: " << other); + const TcpDevice* other_tcp_device = static_cast<const TcpDevice*>(other); + + if (other_tcp_device->in_addr_ == in_addr_) { + LOG4CXX_TRACE(logger_, + "exit with TRUE. Condition: other_tcp_device->in_addr_ == in_addr_"); + return true; + } else { + LOG4CXX_TRACE(logger_, "exit with FALSE"); + return false; + } +} + +ApplicationList TcpDevice::GetApplicationList() const { + LOG4CXX_TRACE(logger_, "enter"); + pthread_mutex_lock(&applications_mutex_); + ApplicationList app_list; + for (std::map<ApplicationHandle, Application>::const_iterator it = + applications_.begin(); it != applications_.end(); ++it) { + app_list.push_back(it->first); + } + pthread_mutex_unlock(&applications_mutex_); + LOG4CXX_TRACE(logger_, "exit with app_list. It's size = " << app_list.size()); + return app_list; +} + +ApplicationHandle TcpDevice::AddIncomingApplication(int socket_fd) { + LOG4CXX_TRACE(logger_, "enter. Socket_fd: " << socket_fd); + Application app; + app.incoming = true; + app.socket = socket_fd; + app.port = 0; // this line removes compiler warning + pthread_mutex_lock(&applications_mutex_); + const ApplicationHandle app_handle = ++last_handle_; + applications_[app_handle] = app; + pthread_mutex_unlock(&applications_mutex_); + LOG4CXX_TRACE(logger_, "exit with app_handle " << app_handle); + return app_handle; +} + +ApplicationHandle TcpDevice::AddDiscoveredApplication(int port) { + LOG4CXX_TRACE(logger_, "enter. port " << port); + Application app; + app.incoming = false; + app.socket = 0; // this line removes compiler warning + app.port = port; + pthread_mutex_lock(&applications_mutex_); + const ApplicationHandle app_handle = ++last_handle_; + applications_[app_handle] = app; + pthread_mutex_unlock(&applications_mutex_); + LOG4CXX_TRACE(logger_, "exit with app_handle " << app_handle); + return app_handle; +} + + +void TcpDevice::RemoveApplication(const ApplicationHandle app_handle) { + LOG4CXX_TRACE(logger_, "enter. ApplicationHandle: " << app_handle); + pthread_mutex_lock(&applications_mutex_); + applications_.erase(app_handle); + pthread_mutex_unlock(&applications_mutex_); + LOG4CXX_TRACE(logger_, "exit"); +} + +TcpDevice::~TcpDevice() { + pthread_mutex_destroy(&applications_mutex_); +} + +int TcpDevice::GetApplicationSocket(const ApplicationHandle app_handle) const { + LOG4CXX_TRACE(logger_, "enter. ApplicationHandle: " << app_handle); + std::map<ApplicationHandle, Application>::const_iterator it = applications_.find( + app_handle); + if (applications_.end() == it) { + LOG4CXX_TRACE(logger_, "exit with -1. Condition: applications_.end() == it"); + return -1; + } + if (!it->second.incoming) { + LOG4CXX_TRACE(logger_, "exit with -1. Condition: !it->second.incoming"); + return -1; + } + LOG4CXX_TRACE(logger_, "exit with socket " << it->second.socket); + return it->second.socket; +} + +int TcpDevice::GetApplicationPort(const ApplicationHandle app_handle) const { + LOG4CXX_TRACE(logger_, "enter. ApplicationHandle: " << app_handle); + std::map<ApplicationHandle, Application>::const_iterator it = applications_.find( + app_handle); + if (applications_.end() == it) { + LOG4CXX_TRACE(logger_, "exit with -1. Condition: applications_.end() == it"); + return -1; + } + if (it->second.incoming) { + LOG4CXX_TRACE(logger_, "exit with -1. Condition: it->second.incoming"); + return -1; + } + LOG4CXX_TRACE(logger_, "exit with port " << it->second.port); + return it->second.port; +} + + +} // namespace transport_adapter +} // namespace transport_manager diff --git a/src/components/transport_manager/src/tcp/tcp_socket_connection.cc b/src/components/transport_manager/src/tcp/tcp_socket_connection.cc new file mode 100644 index 0000000000..cea5805348 --- /dev/null +++ b/src/components/transport_manager/src/tcp/tcp_socket_connection.cc @@ -0,0 +1,121 @@ +/** + * + * 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/transport_adapter/transport_adapter_controller.h" +#include "transport_manager/tcp/tcp_socket_connection.h" +#include "transport_manager/tcp/tcp_device.h" +#include "utils/logger.h" + +#include <memory.h> +#include <signal.h> +#include <errno.h> + +namespace transport_manager { +namespace transport_adapter { + +CREATE_LOGGERPTR_GLOBAL(logger_, "TransportManager") + + +TcpSocketConnection::TcpSocketConnection(const DeviceUID& device_uid, + const ApplicationHandle& app_handle, + TransportAdapterController* controller) + : ThreadedSocketConnection(device_uid, app_handle, controller) { +} + +TcpSocketConnection::~TcpSocketConnection() { +} + +bool TcpSocketConnection::Establish(ConnectError** error) { + return true; +} + +TcpServerOiginatedSocketConnection::TcpServerOiginatedSocketConnection( + const DeviceUID& device_uid, const ApplicationHandle& app_handle, + TransportAdapterController* controller) + : ThreadedSocketConnection(device_uid, app_handle, controller) { +} + +TcpServerOiginatedSocketConnection::~TcpServerOiginatedSocketConnection() { +} + +bool TcpServerOiginatedSocketConnection::Establish(ConnectError** error) { + LOG4CXX_TRACE(logger_, "enter. error " << error); + DeviceSptr device = controller()->FindDevice(device_handle()); + if (!device.valid()) { + LOG4CXX_ERROR(logger_, "Device " << device_handle() << " not found"); + *error = new ConnectError(); + LOG4CXX_TRACE(logger_, "exit with FALSE. Condition: !device.valid()"); + return false; + } + TcpDevice* tcp_device = static_cast<TcpDevice*>(device.get()); + + int port; + if (-1 == (port = tcp_device->GetApplicationPort(application_handle()))) { + LOG4CXX_ERROR(logger_, "Application port for " << application_handle() << " not found"); + *error = new ConnectError(); + LOG4CXX_TRACE(logger_, "exit with FALSE. Condition: port not found"); + return false; + } + + const int socket = ::socket(AF_INET, SOCK_STREAM, 0); + if (socket < 0) { + LOG4CXX_ERROR(logger_, "Failed to create socket"); + *error = new ConnectError(); + LOG4CXX_TRACE(logger_, "exit with FALSE. Condition: failed to create socket"); + return false; + } + + struct sockaddr_in addr; + memset((char*) &addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = tcp_device->in_addr(); + addr.sin_port = htons(port); + + LOG4CXX_INFO(logger_, "Connecting " << inet_ntoa(addr.sin_addr) << ":" + << port); + if (::connect(socket, (struct sockaddr*) &addr, sizeof(addr)) < 0) { + LOG4CXX_ERROR(logger_, "Failed to connect for application " + << application_handle() << ", error " << errno); + *error = new ConnectError(); + LOG4CXX_TRACE(logger_, "exit with FALSE. Condition: failed to connect to application"); + return false; + } + + set_socket(socket); + LOG4CXX_TRACE(logger_, "exit with TRUE"); + return true; +} + +} // namespace transport_adapter +} // namespace transport_manager + diff --git a/src/components/transport_manager/src/tcp/tcp_transport_adapter.cc b/src/components/transport_manager/src/tcp/tcp_transport_adapter.cc new file mode 100644 index 0000000000..6067def111 --- /dev/null +++ b/src/components/transport_manager/src/tcp/tcp_transport_adapter.cc @@ -0,0 +1,158 @@ +/** + * + * 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 <memory.h> +#include <signal.h> +#include <errno.h> +#include <sstream> +#include <cstdlib> + +#include "resumption/last_state.h" + +#include "utils/logger.h" + +#include "transport_manager/tcp/tcp_transport_adapter.h" +#include "transport_manager/tcp/tcp_client_listener.h" +#include "transport_manager/tcp/tcp_connection_factory.h" +#include "transport_manager/tcp/tcp_device.h" + +#ifdef AVAHI_SUPPORT +#include "transport_manager/tcp/dnssd_service_browser.h" +#endif + +namespace transport_manager { +namespace transport_adapter { + +CREATE_LOGGERPTR_GLOBAL(logger_, "TransportAdapterImpl") + +TcpTransportAdapter::TcpTransportAdapter(const uint16_t port) + : TransportAdapterImpl( +#ifdef AVAHI_SUPPORT + new DnssdServiceBrowser(this), +#else + NULL, +#endif + new TcpConnectionFactory(this), + new TcpClientListener(this, port, false)) { +} + +TcpTransportAdapter::~TcpTransportAdapter() { +} + +DeviceType TcpTransportAdapter::GetDeviceType() const { + return "sdl-tcp"; +} + +void TcpTransportAdapter::Store() const { + LOG4CXX_TRACE(logger_, "enter"); + Json::Value tcp_adapter_dictionary; + Json::Value devices_dictionary; + DeviceList device_ids = GetDeviceList(); + for (DeviceList::const_iterator i = device_ids.begin(); i != device_ids.end(); ++i) { + DeviceUID device_id = *i; + DeviceSptr device = FindDevice(device_id); + if (!device) { // device could have been disconnected + continue; + } + utils::SharedPtr<TcpDevice> tcp_device = + DeviceSptr::static_pointer_cast<TcpDevice>(device); + Json::Value device_dictionary; + device_dictionary["name"] = tcp_device->name(); + struct in_addr address; + address.s_addr = tcp_device->in_addr(); + device_dictionary["address"] = std::string(inet_ntoa(address)); + Json::Value applications_dictionary; + ApplicationList app_ids = tcp_device->GetApplicationList(); + for (ApplicationList::const_iterator j = app_ids.begin(); j != app_ids.end(); ++j) { + ApplicationHandle app_handle = *j; + if (FindEstablishedConnection(tcp_device->unique_device_id(), app_handle)) { + int port = tcp_device->GetApplicationPort(app_handle); + if (port != -1) { // don't want to store incoming applications + Json::Value application_dictionary; + char port_record[12]; + sprintf(port_record, "%d", port); + application_dictionary["port"] = std::string(port_record); + applications_dictionary.append(application_dictionary); + } + } + } + if (!applications_dictionary.empty()) { + device_dictionary["applications"] = applications_dictionary; + devices_dictionary.append(device_dictionary); + } + } + tcp_adapter_dictionary["devices"] = devices_dictionary; + resumption::LastState::instance()->dictionary["TransportManager"]["TcpAdapter"] = + tcp_adapter_dictionary; + LOG4CXX_TRACE(logger_, "exit"); +} + +bool TcpTransportAdapter::Restore() { + LOG4CXX_TRACE(logger_, "enter"); + bool errors_occurred = false; + const Json::Value tcp_adapter_dictionary = + resumption::LastState::instance()->dictionary["TransportManager"]["TcpAdapter"]; + const Json::Value devices_dictionary = tcp_adapter_dictionary["devices"]; + for (Json::Value::const_iterator i = devices_dictionary.begin(); + i != devices_dictionary.end(); ++i) { + const Json::Value device_dictionary = *i; + std::string name = device_dictionary["name"].asString(); + std::string address_record = device_dictionary["address"].asString(); + in_addr_t address = inet_addr(address_record.c_str()); + TcpDevice* tcp_device = new TcpDevice(address, name); + DeviceSptr device(tcp_device); + AddDevice(device); + const Json::Value applications_dictionary = device_dictionary["applications"]; + for (Json::Value::const_iterator j = applications_dictionary.begin(); + j != applications_dictionary.end(); ++j) { + const Json::Value application_dictionary = *j; + std::string port_record = application_dictionary["port"].asString(); + int port = atoi(port_record.c_str()); + ApplicationHandle app_handle = tcp_device->AddDiscoveredApplication(port); + if (Error::OK != Connect(device->unique_device_id(), app_handle)) { + errors_occurred = true; + } + } + } + bool result = !errors_occurred; + if (result) { + LOG4CXX_TRACE(logger_, "exit with TRUE"); + } else { + LOG4CXX_TRACE(logger_, "exit with FALSE"); + } + return result; +} + +} // namespace transport_adapter +} // namespace transport_manager + diff --git a/src/components/transport_manager/src/transport_adapter/threaded_socket_connection.cc b/src/components/transport_manager/src/transport_adapter/threaded_socket_connection.cc new file mode 100644 index 0000000000..2c365ca405 --- /dev/null +++ b/src/components/transport_manager/src/transport_adapter/threaded_socket_connection.cc @@ -0,0 +1,357 @@ +/** + * 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 <fcntl.h> +#include <memory.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/socket.h> +#include "utils/logger.h" + +#include "transport_manager/transport_adapter/threaded_socket_connection.h" +#include "transport_manager/transport_adapter/transport_adapter_controller.h" + +namespace transport_manager { +namespace transport_adapter { +CREATE_LOGGERPTR_GLOBAL(logger_, "TransportManager") + +ThreadedSocketConnection::ThreadedSocketConnection( + const DeviceUID& device_id, const ApplicationHandle& app_handle, + TransportAdapterController* controller) + : read_fd_(-1), write_fd_(-1), controller_(controller), + frames_to_send_(), + frames_to_send_mutex_(), + thread_(), + socket_(-1), + terminate_flag_(false), + unexpected_disconnect_(false), + device_uid_(device_id), + app_handle_(app_handle) { + pthread_mutex_init(&frames_to_send_mutex_, 0); +} + +ThreadedSocketConnection::~ThreadedSocketConnection() { + terminate_flag_ = true; + Notify(); + pthread_join(thread_, 0); + pthread_mutex_destroy(&frames_to_send_mutex_); + + if (-1 != read_fd_) { + close(read_fd_); + } + if (-1 != write_fd_) { + close(write_fd_); + } +} + +void ThreadedSocketConnection::Abort() { + LOG4CXX_TRACE(logger_, "enter"); + unexpected_disconnect_ = true; + terminate_flag_ = true; + LOG4CXX_TRACE(logger_, "exit"); +} + +void* StartThreadedSocketConnection(void* v) { + LOG4CXX_TRACE(logger_, "enter"); + ThreadedSocketConnection* connection = + static_cast<ThreadedSocketConnection*>(v); + connection->Thread(); + LOG4CXX_TRACE(logger_, "exit with 0"); + return 0; +} + +TransportAdapter::Error ThreadedSocketConnection::Start() { + LOG4CXX_TRACE(logger_, "enter"); + int fds[2]; + const int pipe_ret = pipe(fds); + if (0 == pipe_ret) { + LOG4CXX_DEBUG(logger_, "pipe created(#" << pthread_self() << ")"); + read_fd_ = fds[0]; + write_fd_ = fds[1]; + } else { + LOG4CXX_WARN(logger_, "pipe creation failed (#" << pthread_self() << ")"); + LOG4CXX_TRACE(logger_, "exit with TransportAdapter::FAIL"); + return TransportAdapter::FAIL; + } + const int fcntl_ret = fcntl(read_fd_, F_SETFL, + fcntl(read_fd_, F_GETFL) | O_NONBLOCK); + if (0 != fcntl_ret) { + LOG4CXX_WARN(logger_, "fcntl failed (#" << pthread_self() << ")"); + LOG4CXX_TRACE(logger_, "exit with TransportAdapter::FAIL"); + return TransportAdapter::FAIL; + } + + if (0 != pthread_create(&thread_, 0, &StartThreadedSocketConnection, this)) { + LOG4CXX_WARN(logger_, "thread creation failed (#" << pthread_self() << ")"); + LOG4CXX_TRACE(logger_, "exit with TransportAdapter::FAIL"); + return TransportAdapter::FAIL; + } + LOG4CXX_DEBUG(logger_, "thread created (#" << pthread_self() << ")"); + LOG4CXX_TRACE(logger_, "exit with TransportAdapter::OK"); + const std::string thread_name = std::string("Socket ") + device_handle(); + pthread_setname_np(thread_, thread_name.c_str()); + return TransportAdapter::OK; +} + +void ThreadedSocketConnection::Finalize() { + LOG4CXX_TRACE(logger_, "enter"); + if (unexpected_disconnect_) { + LOG4CXX_DEBUG(logger_, "unexpected_disconnect (#" << pthread_self() << ")"); + controller_->ConnectionAborted(device_handle(), application_handle(), + CommunicationError()); + } else { + LOG4CXX_DEBUG(logger_, "not unexpected_disconnect (#" << pthread_self() << ")"); + controller_->ConnectionFinished(device_handle(), application_handle()); + } + close(socket_); + LOG4CXX_TRACE(logger_, "exit"); +} + +TransportAdapter::Error ThreadedSocketConnection::Notify() const { + LOG4CXX_TRACE(logger_, "enter"); + if (-1 == write_fd_) { + LOG4CXX_ERROR_WITH_ERRNO(logger_, + "Failed to wake up connection thread for connection " << this); + LOG4CXX_TRACE(logger_, "exit with TransportAdapter::BAD_STATE"); + return TransportAdapter::BAD_STATE; + } + uint8_t c = 0; + if (1 == write(write_fd_, &c, 1)) { + LOG4CXX_TRACE(logger_, "exit with TransportAdapter::OK"); + return TransportAdapter::OK; + } else { + LOG4CXX_ERROR_WITH_ERRNO( + logger_, "Failed to wake up connection thread for connection " << this); + LOG4CXX_TRACE(logger_, "exit with TransportAdapter::FAIL"); + return TransportAdapter::FAIL; + } +} + +TransportAdapter::Error ThreadedSocketConnection::SendData( + RawMessagePtr message) { + LOG4CXX_TRACE(logger_, "enter"); + pthread_mutex_lock(&frames_to_send_mutex_); + frames_to_send_.push(message); + pthread_mutex_unlock(&frames_to_send_mutex_); + TransportAdapter::Error error = Notify(); + LOG4CXX_TRACE(logger_, "exit with error" << error); + return error; +} + +TransportAdapter::Error ThreadedSocketConnection::Disconnect() { + LOG4CXX_TRACE(logger_, "enter"); + terminate_flag_ = true; + TransportAdapter::Error error = Notify(); + LOG4CXX_TRACE(logger_, "exit with error" << error); + return error; +} + +void ThreadedSocketConnection::Thread() { + LOG4CXX_TRACE(logger_, "enter"); + controller_->ConnectionCreated(this, device_uid_, app_handle_); + ConnectError* connect_error = NULL; + if (Establish(&connect_error)) { + LOG4CXX_DEBUG(logger_, "Connection established (#" << pthread_self() << ")"); + controller_->ConnectDone(device_handle(), application_handle()); + while (!terminate_flag_) { + Transmit(); + } + LOG4CXX_DEBUG(logger_, "Connection is to finalize (#" << pthread_self() << ")"); + Finalize(); + while (!frames_to_send_.empty()) { + LOG4CXX_INFO(logger_, "removing message (#" << pthread_self() << ")"); + RawMessagePtr message = frames_to_send_.front(); + frames_to_send_.pop(); + controller_->DataSendFailed(device_handle(), application_handle(), + message, DataSendError()); + } + controller_->DisconnectDone(device_handle(), application_handle()); + } else { + LOG4CXX_ERROR(logger_, "Connection Establish failed (#" << pthread_self() << ")"); + controller_->ConnectFailed(device_handle(), application_handle(), + *connect_error); + delete connect_error; + } + LOG4CXX_TRACE(logger_, "exit"); +} + +void ThreadedSocketConnection::Transmit() { + LOG4CXX_TRACE(logger_, "enter"); + + const nfds_t poll_fds_size = 2; + pollfd poll_fds[poll_fds_size]; + poll_fds[0].fd = socket_; + poll_fds[0].events = POLLIN | POLLPRI | (frames_to_send_.empty() ? 0 : POLLOUT); + poll_fds[1].fd = read_fd_; + poll_fds[1].events = POLLIN | POLLPRI; + + LOG4CXX_DEBUG(logger_, "poll (#" << pthread_self() << ") " << this); + if (-1 == poll(poll_fds, poll_fds_size, -1)) { + LOG4CXX_ERROR_WITH_ERRNO(logger_, "poll failed for connection " << this); + Abort(); + LOG4CXX_TRACE(logger_, "exit. Condition: -1 == poll(poll_fds, poll_fds_size, -1)"); + return; + } + LOG4CXX_DEBUG(logger_, "poll is ok (#" << pthread_self() << ") " << this << " revents0:" + << + std::hex << poll_fds[0].revents << " revents1:" << std::hex << poll_fds[1].revents); + // error check + if (0 != (poll_fds[1].revents & (POLLERR | POLLHUP | POLLNVAL))) { + LOG4CXX_ERROR(logger_, + "Notification pipe for connection " << this << " terminated"); + Abort(); + LOG4CXX_TRACE(logger_, + "exit. Condition: 0 != (poll_fds[1].revents & (POLLERR | POLLHUP | POLLNVAL))"); + return; + } + + if (poll_fds[0].revents & (POLLERR | POLLHUP | POLLNVAL)) { + LOG4CXX_WARN(logger_, "Connection " << this << " terminated"); + Abort(); + LOG4CXX_TRACE(logger_, + "exit. Condition: poll_fds[0].revents & (POLLERR | POLLHUP | POLLNVAL"); + return; + } + + // clear notifications + char buffer[256]; + ssize_t bytes_read = -1; + do { + bytes_read = read(read_fd_, buffer, sizeof(buffer)); + } while (bytes_read > 0); + if ((bytes_read < 0) && (EAGAIN != errno)) { + LOG4CXX_ERROR_WITH_ERRNO(logger_, "Failed to clear notification pipe"); + LOG4CXX_ERROR_WITH_ERRNO(logger_, "poll failed for connection " << this); + Abort(); + LOG4CXX_TRACE(logger_, "exit. Condition: (bytes_read < 0) && (EAGAIN != errno)"); + return; + } + + // send data if possible + if (!frames_to_send_.empty() && (poll_fds[0].revents | POLLOUT)) { + LOG4CXX_DEBUG(logger_, "frames_to_send_ not empty() (#" << pthread_self() << ")"); + + // send data + const bool send_ok = Send(); + if (!send_ok) { + LOG4CXX_ERROR(logger_, "Send() failed (#" << pthread_self() << ")"); + Abort(); + LOG4CXX_TRACE(logger_, "exit. Condition: !send_ok"); + return; + } + } + + // receive data + if (poll_fds[0].revents & (POLLIN | POLLPRI)) { + const bool receive_ok = Receive(); + if (!receive_ok) { + LOG4CXX_ERROR(logger_, "Receive() failed (#" << pthread_self() << ")"); + Abort(); + LOG4CXX_TRACE(logger_, "exit. Condition: !receive_ok"); + return; + } + } + LOG4CXX_TRACE(logger_, "exit"); +} + +bool ThreadedSocketConnection::Receive() { + LOG4CXX_TRACE(logger_, "enter"); + uint8_t buffer[4096]; + ssize_t bytes_read = -1; + + do { + bytes_read = recv(socket_, buffer, sizeof(buffer), MSG_DONTWAIT); + + if (bytes_read > 0) { + LOG4CXX_DEBUG( + logger_, + "Received " << bytes_read << " bytes for connection " << this); + RawMessagePtr frame( + new protocol_handler::RawMessage(0, 0, buffer, bytes_read)); + controller_->DataReceiveDone(device_handle(), application_handle(), + frame); + } else if (bytes_read < 0) { + if (EAGAIN != errno && EWOULDBLOCK != errno) { + LOG4CXX_ERROR_WITH_ERRNO(logger_, + "recv() failed for connection " << this); + LOG4CXX_TRACE(logger_, + "exit with FALSE. Condition: EAGAIN != errno && EWOULDBLOCK != errno"); + return false; + } + } else { + LOG4CXX_WARN(logger_, "Connection " << this << " closed by remote peer"); + LOG4CXX_TRACE(logger_, "exit with FALSE. Condition: bytes_read >= 0"); + return false; + } + } while (bytes_read > 0); + LOG4CXX_TRACE(logger_, "exit with TRUE"); + return true; +} + +bool ThreadedSocketConnection::Send() { + LOG4CXX_TRACE(logger_, "enter"); + FrameQueue frames_to_send; + pthread_mutex_lock(&frames_to_send_mutex_); + std::swap(frames_to_send, frames_to_send_); + pthread_mutex_unlock(&frames_to_send_mutex_); + + size_t offset = 0; + while (!frames_to_send.empty()) { + LOG4CXX_INFO(logger_, "frames_to_send is not empty" << pthread_self() << ")"); + RawMessagePtr frame = frames_to_send.front(); + const ssize_t bytes_sent = ::send(socket_, frame->data() + offset, + frame->data_size() - offset, 0); + + if (bytes_sent >= 0) { + LOG4CXX_DEBUG(logger_, "bytes_sent >= 0" << pthread_self() << ")"); + offset += bytes_sent; + if (offset == frame->data_size()) { + frames_to_send.pop(); + offset = 0; + controller_->DataSendDone(device_handle(), application_handle(), frame); + } + } else { + LOG4CXX_DEBUG(logger_, "bytes_sent < 0" << pthread_self() << ")"); + LOG4CXX_ERROR_WITH_ERRNO(logger_, "Send failed for connection " << this); + frames_to_send.pop(); + offset = 0; + controller_->DataSendFailed(device_handle(), application_handle(), frame, + DataSendError()); + } + } + LOG4CXX_TRACE(logger_, "exit with TRUE"); + return true; +} + +} // namespace +} // namespace diff --git a/src/components/transport_manager/src/transport_adapter/transport_adapter_impl.cc b/src/components/transport_manager/src/transport_adapter/transport_adapter_impl.cc new file mode 100644 index 0000000000..e1d6197072 --- /dev/null +++ b/src/components/transport_manager/src/transport_adapter/transport_adapter_impl.cc @@ -0,0 +1,845 @@ +/* + * Copyright (c) 2014, 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 "config_profile/profile.h" +#include "utils/logger.h" + +#include "transport_manager/transport_adapter/transport_adapter_impl.h" +#include "transport_manager/transport_adapter/transport_adapter_listener.h" +#include "transport_manager/transport_adapter/device_scanner.h" +#include "transport_manager/transport_adapter/server_connection_factory.h" +#include "transport_manager/transport_adapter/client_connection_listener.h" + +namespace transport_manager { +namespace transport_adapter { + +CREATE_LOGGERPTR_GLOBAL(logger_, "TransportManager") + +TransportAdapterImpl::TransportAdapterImpl( + DeviceScanner* device_scanner, + ServerConnectionFactory* server_connection_factory, + ClientConnectionListener* client_connection_listener) + : listeners_(), + initialised_(0), + devices_(), + devices_mutex_(), + connections_(), + connections_mutex_(), + device_scanner_(device_scanner), + server_connection_factory_(server_connection_factory), + client_connection_listener_(client_connection_listener) +#ifdef TIME_TESTER + , metric_observer_(NULL) +#endif // TIME_TESTER +{ + pthread_mutex_init(&devices_mutex_, 0); + pthread_mutex_init(&connections_mutex_, 0); +} + +TransportAdapterImpl::~TransportAdapterImpl() { + if (device_scanner_) { + device_scanner_->Terminate(); + LOG4CXX_DEBUG(logger_, "device_scanner_ " << device_scanner_ << " terminated."); + delete device_scanner_; + LOG4CXX_DEBUG(logger_, "device_scanner_ " << device_scanner_ << " deleted."); + } + if (server_connection_factory_) { + server_connection_factory_->Terminate(); + LOG4CXX_DEBUG(logger_, "server_connection_factory " << server_connection_factory_ << " terminated."); + delete server_connection_factory_; + LOG4CXX_DEBUG(logger_, "server_connection_factory " << server_connection_factory_ << " deleted."); + } + if (client_connection_listener_) { + client_connection_listener_->Terminate(); + LOG4CXX_DEBUG(logger_, "client_connection_listener_ " << client_connection_listener_ << " terminated."); + delete client_connection_listener_; + LOG4CXX_DEBUG(logger_, "client_connection_listener_ " << client_connection_listener_ << " deleted."); + } + + pthread_mutex_lock(&connections_mutex_); + ConnectionMap connections; + std::swap(connections, connections_); + pthread_mutex_unlock(&connections_mutex_); + connections.clear(); + + LOG4CXX_DEBUG(logger_, "Connections deleted"); + + pthread_mutex_lock(&devices_mutex_); + DeviceMap devices; + std::swap(devices, devices_); + pthread_mutex_unlock(&devices_mutex_); + devices.clear(); + + LOG4CXX_DEBUG(logger_, "Devices deleted"); + + pthread_mutex_destroy(&connections_mutex_); + pthread_mutex_destroy(&devices_mutex_); +} + +TransportAdapter::Error TransportAdapterImpl::Init() { + LOG4CXX_TRACE(logger_, "enter"); + + Error error = OK; + + if ((error == OK) && device_scanner_) { + error = device_scanner_->Init(); + } + if ((error == OK) && server_connection_factory_) { + error = server_connection_factory_->Init(); + } + if ((error == OK) && client_connection_listener_) { + error = client_connection_listener_->Init(); + } + + initialised_ = (error == OK); + + if (profile::Profile::instance()->use_last_state()) { + if (!Restore()) { + LOG4CXX_WARN(logger_, "could not restore transport adapter state"); + error = FAIL; + } + } + LOG4CXX_TRACE(logger_, "exit with error: " << error); + return error; +} + +TransportAdapter::Error TransportAdapterImpl::SearchDevices() { + LOG4CXX_TRACE(logger_, "enter"); + if (device_scanner_ == NULL) { + LOG4CXX_TRACE(logger_, "exit with NOT_SUPPORTED"); + return NOT_SUPPORTED; + } else if (!device_scanner_->IsInitialised()) { + LOG4CXX_TRACE(logger_, "exit with BAD_STATE"); + return BAD_STATE; + } + TransportAdapter::Error er = device_scanner_->Scan(); + LOG4CXX_TRACE(logger_, "exit with error: " << er); + return er; +} + +TransportAdapter::Error TransportAdapterImpl::Connect( + const DeviceUID& device_id, const ApplicationHandle& app_handle) { + LOG4CXX_TRACE(logger_, "enter. DeviceUID " << device_id << " ApplicationHandle " << + app_handle); + if (server_connection_factory_ == 0) { + LOG4CXX_TRACE(logger_, "exit with NOT_SUPPORTED"); + return NOT_SUPPORTED; + } + if (!server_connection_factory_->IsInitialised()) { + LOG4CXX_TRACE(logger_, "exit with BAD_STATE"); + return BAD_STATE; + } + + pthread_mutex_lock(&connections_mutex_); + const bool already_exists = + connections_.end() != + connections_.find(std::make_pair(device_id, app_handle)); + if (!already_exists) { + ConnectionInfo& info = connections_[std::make_pair(device_id, app_handle)]; + info.app_handle = app_handle; + info.device_id = device_id; + info.state = ConnectionInfo::NEW; + } + pthread_mutex_unlock(&connections_mutex_); + if (already_exists) { + LOG4CXX_TRACE(logger_, "exit with ALREADY_EXISTS"); + return ALREADY_EXISTS; + } + + const TransportAdapter::Error err = + server_connection_factory_->CreateConnection(device_id, app_handle); + if (TransportAdapter::OK != err) { + pthread_mutex_lock(&connections_mutex_); + connections_.erase(std::make_pair(device_id, app_handle)); + pthread_mutex_unlock(&connections_mutex_); + } + LOG4CXX_TRACE(logger_, "exit with error: " << err); + return err; +} + +TransportAdapter::Error TransportAdapterImpl::ConnectDevice( + const DeviceUID& device_handle) { + LOG4CXX_TRACE(logger_, "enter with device_handle: " << &device_handle); + DeviceSptr device = FindDevice(device_handle); + if (device) { + TransportAdapter::Error err = ConnectDevice(device); + LOG4CXX_TRACE(logger_, "exit with error: " << err); + return err; + } else { + LOG4CXX_TRACE(logger_, "exit with BAD_PARAM"); + return BAD_PARAM; + } +} + +TransportAdapter::Error TransportAdapterImpl::Disconnect( + const DeviceUID& device_id, const ApplicationHandle& app_handle) { + LOG4CXX_TRACE(logger_, "enter. device_id: " << &device_id << ", device_id: " << + &device_id); + if (!initialised_) { + LOG4CXX_TRACE(logger_, "exit with BAD_STATE"); + return BAD_STATE; + } + ConnectionSptr connection = FindEstablishedConnection(device_id, app_handle); + if (connection.valid()) { + TransportAdapter::Error err = connection->Disconnect(); + LOG4CXX_TRACE(logger_, "exit with error: " << err); + return err; + } else { + LOG4CXX_TRACE(logger_, "exit with BAD_PARAM"); + return BAD_PARAM; + } +} + +TransportAdapter::Error TransportAdapterImpl::DisconnectDevice( + const DeviceUID& device_id) { + LOG4CXX_TRACE(logger_, "enter. device_id: " << &device_id); + if (!initialised_) { + LOG4CXX_TRACE(logger_, "exit with BAD_STATE"); + return BAD_STATE; + } + + Error error = OK; + pthread_mutex_lock(&connections_mutex_); + for (ConnectionMap::iterator it = connections_.begin(); + it != connections_.end(); ++it) { + + ConnectionInfo& info = it->second; + if (info.device_id == device_id && + info.state != ConnectionInfo::FINALISING) { + if (OK != info.connection->Disconnect()) { + error = FAIL; + LOG4CXX_ERROR(logger_, "Error on disconnect" << error); + } + } + } + pthread_mutex_unlock(&connections_mutex_); + LOG4CXX_TRACE(logger_, "exit with error " << error); + return error; +} + +TransportAdapter::Error TransportAdapterImpl::SendData( + const DeviceUID& device_id, const ApplicationHandle& app_handle, + const RawMessagePtr data) { + LOG4CXX_TRACE(logger_, "enter. device_id: " << &device_id << ", app_handle: " << + &app_handle << ", data: " << data); + if (!initialised_) { + LOG4CXX_TRACE(logger_, "exit with BAD_STATE"); + return BAD_STATE; + } + + ConnectionSptr connection = FindEstablishedConnection(device_id, app_handle); + if (connection.get() != 0) { + TransportAdapter::Error err = connection->SendData(data); + LOG4CXX_TRACE(logger_, "exit with error: " << err); + return err; + } else { + LOG4CXX_TRACE(logger_, "exit with BAD_PARAM"); + return BAD_PARAM; + } +} + +TransportAdapter::Error TransportAdapterImpl::StartClientListening() { + LOG4CXX_TRACE(logger_, "enter"); + if (client_connection_listener_ == 0) { + LOG4CXX_TRACE(logger_, "exit with NOT_SUPPORTED"); + return NOT_SUPPORTED; + } + if (!client_connection_listener_->IsInitialised()) { + LOG4CXX_TRACE(logger_, "exit with BAD_STATE"); + return BAD_STATE; + } + TransportAdapter::Error err = client_connection_listener_->StartListening(); + LOG4CXX_TRACE(logger_, "exit with error: " << err); + return err; +} + +TransportAdapter::Error TransportAdapterImpl::StopClientListening() { + LOG4CXX_TRACE(logger_, "enter"); + if (client_connection_listener_ == 0) { + LOG4CXX_TRACE(logger_, "exit with NOT_SUPPORTED"); + return NOT_SUPPORTED; + } + if (!client_connection_listener_->IsInitialised()) { + LOG4CXX_TRACE(logger_, "exit with BAD_STATE"); + return BAD_STATE; + } + TransportAdapter::Error err = client_connection_listener_->StopListening(); + LOG4CXX_TRACE(logger_, "exit with error: " << err); + return err; +} + +DeviceList TransportAdapterImpl::GetDeviceList() const { + LOG4CXX_TRACE(logger_, "enter"); + DeviceList devices; + pthread_mutex_lock(&devices_mutex_); + for (DeviceMap::const_iterator it = devices_.begin(); it != devices_.end(); + ++it) { + devices.push_back(it->first); + } + pthread_mutex_unlock(&devices_mutex_); + LOG4CXX_TRACE(logger_, "exit with DeviceList. It's' size = " << devices.size()); + return devices; +} + +DeviceSptr TransportAdapterImpl::AddDevice(DeviceSptr device) { + LOG4CXX_TRACE(logger_, "enter. device: " << device); + DeviceSptr existing_device; + bool same_device_found = false; + pthread_mutex_lock(&devices_mutex_); + for (DeviceMap::const_iterator i = devices_.begin(); i != devices_.end(); + ++i) { + existing_device = i->second; + if (device->IsSameAs(existing_device.get())) { + same_device_found = true; + LOG4CXX_DEBUG(logger_, "device " << device << "already exists"); + break; + } + } + if (!same_device_found) { + devices_[device->unique_device_id()] = device; + } + pthread_mutex_unlock(&devices_mutex_); + if (same_device_found) { + LOG4CXX_TRACE(logger_, "exit with TRUE. Condition: same_device_found"); + return existing_device; + } else { + for (TransportAdapterListenerList::iterator it = listeners_.begin(); + it != listeners_.end(); ++it) { + (*it)->OnDeviceListUpdated(this); + } + if (ToBeAutoConnected(device)) { + ConnectDevice(device); + } + LOG4CXX_TRACE(logger_, "exit with DeviceSptr " << device); + return device; + } +} + +void TransportAdapterImpl::SearchDeviceDone(const DeviceVector& devices) { + LOG4CXX_TRACE(logger_, "enter. devices: " << &devices); + DeviceMap new_devices; + for (DeviceVector::const_iterator it = devices.begin(); it != devices.end(); + ++it) { + DeviceSptr device = *it; + bool device_found = false; + + pthread_mutex_lock(&devices_mutex_); + for (DeviceMap::iterator it = devices_.begin(); it != devices_.end(); + ++it) { + DeviceSptr existing_device = it->second; + if (device->IsSameAs(existing_device.get())) { + existing_device->set_keep_on_disconnect(true); + device_found = true; + LOG4CXX_DEBUG(logger_, "device found. DeviceSptr" << it->second); + break; + } + } + pthread_mutex_unlock(&devices_mutex_); + + if (!device_found) { + LOG4CXX_INFO(logger_, "Adding new device " << device->unique_device_id() + << " (\"" << device->name() + << "\")"); + } + + device->set_keep_on_disconnect(true); + new_devices[device->unique_device_id()] = device; + } + + pthread_mutex_lock(&connections_mutex_); + std::set<DeviceUID> connected_devices; + for (ConnectionMap::const_iterator it = connections_.begin(); + it != connections_.end(); ++it) { + const ConnectionInfo& info = it->second; + if (info.state != ConnectionInfo::FINALISING) { + connected_devices.insert(info.device_id); + } + } + pthread_mutex_unlock(&connections_mutex_); + + DeviceMap all_devices = new_devices; + pthread_mutex_lock(&devices_mutex_); + for (DeviceMap::iterator it = devices_.begin(); it != devices_.end(); ++it) { + DeviceSptr existing_device = it->second; + + if (all_devices.end() == all_devices.find(it->first)) { + if (connected_devices.end() != connected_devices.find(it->first)) { + existing_device->set_keep_on_disconnect(false); + all_devices[it->first] = existing_device; + } + } + } + devices_ = all_devices; + pthread_mutex_unlock(&devices_mutex_); + + for (TransportAdapterListenerList::iterator it = listeners_.begin(); + it != listeners_.end(); ++it) { + (*it)->OnDeviceListUpdated(this); + (*it)->OnSearchDeviceDone(this); + } + + for (DeviceMap::iterator it = new_devices.begin(); it != new_devices.end(); ++it) { + DeviceSptr device = it->second; + if (ToBeAutoConnected(device)) { + ConnectDevice(device); + } + } + LOG4CXX_TRACE(logger_, "exit"); +} + +void TransportAdapterImpl::ApplicationListUpdated(const DeviceUID& device_handle) { + // default implementation does nothing + // and is reimplemented in MME transport adapter only +} + +void TransportAdapterImpl::FindNewApplicationsRequest() { + LOG4CXX_TRACE(logger_, "enter"); + for (TransportAdapterListenerList::iterator i = listeners_.begin(); i != listeners_.end(); + ++i) { + TransportAdapterListener* listener = *i; + listener->OnFindNewApplicationsRequest(this); + } + LOG4CXX_TRACE(logger_, "exit"); +} + +void TransportAdapterImpl::SearchDeviceFailed(const SearchDeviceError& error) { + LOG4CXX_TRACE(logger_, "enter"); + for (TransportAdapterListenerList::iterator it = listeners_.begin(); + it != listeners_.end(); ++it) { + (*it)->OnSearchDeviceFailed(this, error); + } + LOG4CXX_TRACE(logger_, "exit"); +} + +bool TransportAdapterImpl::IsSearchDevicesSupported() const { + LOG4CXX_TRACE(logger_, "enter"); + return device_scanner_ != 0; + LOG4CXX_TRACE(logger_, "exit"); +} + +bool TransportAdapterImpl::IsServerOriginatedConnectSupported() const { + LOG4CXX_TRACE(logger_, "enter"); + return server_connection_factory_ != 0; + LOG4CXX_TRACE(logger_, "exit"); +} + +bool TransportAdapterImpl::IsClientOriginatedConnectSupported() const { + LOG4CXX_TRACE(logger_, "enter"); + return client_connection_listener_ != 0; + LOG4CXX_TRACE(logger_, "exit"); +} + +void TransportAdapterImpl::ConnectionCreated( + ConnectionSptr connection, const DeviceUID& device_id, + const ApplicationHandle& app_handle) { + LOG4CXX_TRACE(logger_, "enter connection:" << connection << ", device_id: " << &device_id + << ", app_handle: " << &app_handle); + pthread_mutex_lock(&connections_mutex_); + ConnectionInfo& info = connections_[std::make_pair(device_id, app_handle)]; + info.app_handle = app_handle; + info.device_id = device_id; + info.connection = connection; + info.state = ConnectionInfo::NEW; + pthread_mutex_unlock(&connections_mutex_); + LOG4CXX_TRACE(logger_, "exit"); +} + +void TransportAdapterImpl::DeviceDisconnected( + const DeviceUID& device_handle, const DisconnectDeviceError& error) { + const DeviceUID device_uid = device_handle; + LOG4CXX_TRACE(logger_, "enter. device_handle: " << &device_uid << ", error: " << + &error); + ApplicationList app_list = GetApplicationList(device_uid); + for (ApplicationList::const_iterator i = app_list.begin(); i != app_list.end(); ++i) { + ApplicationHandle app_handle = *i; + for (TransportAdapterListenerList::iterator it = listeners_.begin(); + it != listeners_.end(); ++it) { + TransportAdapterListener* listener = *it; + listener->OnUnexpectedDisconnect(this, device_uid, app_handle, CommunicationError()); + } + } + + pthread_mutex_lock(&connections_mutex_); + for (TransportAdapterListenerList::iterator it = listeners_.begin(); + it != listeners_.end(); ++it) { + TransportAdapterListener* listener = *it; + listener->OnDisconnectDeviceDone(this, device_uid); + } + for (ApplicationList::const_iterator i = app_list.begin(); i != app_list.end(); ++i) { + ApplicationHandle app_handle = *i; + connections_.erase(std::make_pair(device_uid, app_handle)); + } + pthread_mutex_unlock(&connections_mutex_); + + RemoveDevice(device_uid); + LOG4CXX_TRACE(logger_, "exit"); +} + +void TransportAdapterImpl::DisconnectDone( + const DeviceUID& device_handle, const ApplicationHandle& app_handle) { + const DeviceUID device_uid = device_handle; + const ApplicationHandle app_uid = app_handle; + LOG4CXX_TRACE(logger_, "enter. device_id: " << &device_uid << ", app_handle: " << + &app_uid); + bool device_disconnected = true; + pthread_mutex_lock(&connections_mutex_); + for (ConnectionMap::const_iterator it = connections_.begin(); + it != connections_.end(); ++it) { + const DeviceUID& current_device_id = it->first.first; + const ApplicationHandle& current_app_handle = it->first.second; + if (current_device_id == device_uid && current_app_handle != app_uid) { + device_disconnected = false; + LOG4CXX_DEBUG(logger_, + "break. Condition: current_device_id == device_id && current_app_handle != app_handle"); + break; + } + } + pthread_mutex_unlock(&connections_mutex_); + for (TransportAdapterListenerList::iterator it = listeners_.begin(); + it != listeners_.end(); ++it) { + TransportAdapterListener* listener = *it; + listener->OnDisconnectDone(this, device_uid, app_uid); + if (device_disconnected) { + listener->OnDisconnectDeviceDone(this, device_uid); + } + } + pthread_mutex_lock(&connections_mutex_); + connections_.erase(std::make_pair(device_uid, app_uid)); + pthread_mutex_unlock(&connections_mutex_); + + if (device_disconnected) { + RemoveDevice(device_uid); + } + + Store(); + LOG4CXX_TRACE(logger_, "exit"); +} + +void TransportAdapterImpl::DataReceiveDone(const DeviceUID& device_id, + const ApplicationHandle& app_handle, + RawMessagePtr message) { + LOG4CXX_TRACE(logger_, "enter. device_id: " << &device_id << ", app_handle: " << + &app_handle << ", message: " << message); + +#ifdef TIME_TESTER + if (metric_observer_) { + metric_observer_->StartRawMsg(message.get()); + } +#endif // TIME_TESTER + for (TransportAdapterListenerList::iterator it = listeners_.begin(); + it != listeners_.end(); ++it) { + (*it)->OnDataReceiveDone(this, device_id, app_handle, message); + } + LOG4CXX_TRACE(logger_, "exit"); +} + +void TransportAdapterImpl::DataReceiveFailed( + const DeviceUID& device_id, const ApplicationHandle& app_handle, + const DataReceiveError& error) { + LOG4CXX_TRACE(logger_, "enter"); + for (TransportAdapterListenerList::iterator it = listeners_.begin(); + it != listeners_.end(); ++it) { + (*it)->OnDataReceiveFailed(this, device_id, app_handle, error); + } + LOG4CXX_TRACE(logger_, "exit"); +} + +void TransportAdapterImpl::DataSendDone(const DeviceUID& device_id, + const ApplicationHandle& app_handle, + RawMessagePtr message) { + LOG4CXX_TRACE(logger_, "enter"); + for (TransportAdapterListenerList::iterator it = listeners_.begin(); + it != listeners_.end(); ++it) { + (*it)->OnDataSendDone(this, device_id, app_handle, message); + } + LOG4CXX_TRACE(logger_, "exit"); +} + +void TransportAdapterImpl::DataSendFailed(const DeviceUID& device_id, + const ApplicationHandle& app_handle, + RawMessagePtr message, + const DataSendError& error) { + LOG4CXX_TRACE(logger_, "enter"); + for (TransportAdapterListenerList::iterator it = listeners_.begin(); + it != listeners_.end(); ++it) { + (*it)->OnDataSendFailed(this, device_id, app_handle, message, error); + } + LOG4CXX_TRACE(logger_, "exit"); +} + +DeviceSptr TransportAdapterImpl::FindDevice(const DeviceUID& device_id) const { + LOG4CXX_TRACE(logger_, "enter. device_id: " << &device_id); + DeviceSptr ret; + LOG4CXX_DEBUG(logger_, "devices_.size() = " << devices_.size()); + pthread_mutex_lock(&devices_mutex_); + DeviceMap::const_iterator it = devices_.find(device_id); + if (it != devices_.end()) { + ret = it->second; + } else { + LOG4CXX_WARN(logger_, "Device " << device_id << " not found."); + } + pthread_mutex_unlock(&devices_mutex_); + LOG4CXX_TRACE(logger_, "exit with DeviceSptr: " << ret); + return ret; +} + +void TransportAdapterImpl::ConnectDone(const DeviceUID& device_id, + const ApplicationHandle& app_handle) { + LOG4CXX_TRACE(logger_, "enter. device_id: " << &device_id << ", app_handle: " << + &app_handle); + pthread_mutex_lock(&connections_mutex_); + ConnectionMap::iterator it_conn = + connections_.find(std::make_pair(device_id, app_handle)); + if (it_conn != connections_.end()) { + ConnectionInfo& info = it_conn->second; + info.state = ConnectionInfo::ESTABLISHED; + } + pthread_mutex_unlock(&connections_mutex_); + + for (TransportAdapterListenerList::iterator it = listeners_.begin(); + it != listeners_.end(); ++it) { + (*it)->OnConnectDone(this, device_id, app_handle); + } + + Store(); + LOG4CXX_TRACE(logger_, "exit"); +} + +void TransportAdapterImpl::ConnectFailed(const DeviceUID& device_handle, + const ApplicationHandle& app_handle, const ConnectError& error) { + const DeviceUID device_uid = device_handle; + const ApplicationHandle app_uid = app_handle; + LOG4CXX_TRACE(logger_, "enter. device_id: " << &device_uid << ", app_handle: " << + &app_uid << ", error: " << &error); + pthread_mutex_lock(&connections_mutex_); + connections_.erase(std::make_pair(device_uid, app_uid)); + pthread_mutex_unlock(&connections_mutex_); + for (TransportAdapterListenerList::iterator it = listeners_.begin(); + it != listeners_.end(); ++it) { + (*it)->OnConnectFailed(this, device_uid, app_uid, error); + } + LOG4CXX_TRACE(logger_, "exit"); +} + +void TransportAdapterImpl::AddListener(TransportAdapterListener* listener) { + LOG4CXX_TRACE(logger_, "enter"); + listeners_.push_back(listener); + LOG4CXX_TRACE(logger_, "exit"); +} + +ApplicationList TransportAdapterImpl::GetApplicationList( + const DeviceUID& device_id) const { + LOG4CXX_TRACE(logger_, "enter. device_id: " << &device_id); + DeviceSptr device = FindDevice(device_id); + if (device.valid()) { + ApplicationList lst = device->GetApplicationList(); + LOG4CXX_TRACE(logger_, "exit with ApplicationList. It's size = " << lst.size() << + " Condition: device.valid()"); + return lst; + } + LOG4CXX_TRACE(logger_, "exit with empty ApplicationList. Condition: NOT device.valid()"); + return ApplicationList(); +} + +void TransportAdapterImpl::ConnectionFinished( + const DeviceUID& device_id, const ApplicationHandle& app_handle) { + LOG4CXX_TRACE(logger_, "enter. device_id: " << &device_id << ", app_handle: " << + &app_handle); + pthread_mutex_lock(&connections_mutex_); + ConnectionMap::iterator it = + connections_.find(std::make_pair(device_id, app_handle)); + if (it != connections_.end()) { + ConnectionInfo& info = it->second; + info.state = ConnectionInfo::FINALISING; + } + pthread_mutex_unlock(&connections_mutex_); + LOG4CXX_TRACE(logger_, "exit"); +} + +void TransportAdapterImpl::ConnectionAborted( + const DeviceUID& device_id, const ApplicationHandle& app_handle, + const CommunicationError& error) { + ConnectionFinished(device_id, app_handle); + for (TransportAdapterListenerList::iterator it = listeners_.begin(); + it != listeners_.end(); ++it) { + (*it)->OnUnexpectedDisconnect(this, device_id, app_handle, error); + } +} + +bool TransportAdapterImpl::IsInitialised() const { + LOG4CXX_TRACE(logger_, "enter"); + if (!initialised_) { + LOG4CXX_TRACE(logger_, "exit with FALSE. Condition: !initialised_"); + return false; + } + if (device_scanner_ && !device_scanner_->IsInitialised()) { + LOG4CXX_TRACE(logger_, + "exit with FALSE. Condition: device_scanner_ && !device_scanner_->IsInitialised()"); + return false; + } + if (server_connection_factory_ && + !server_connection_factory_->IsInitialised()) { + LOG4CXX_TRACE(logger_, + "exit with FALSE. Condition: server_connection_factory_ && !server_connection_factory_->IsInitialised()"); + return false; + } + if (client_connection_listener_ && + !client_connection_listener_->IsInitialised()) { + LOG4CXX_TRACE(logger_, + "exit with FALSE. Condition: client_connection_listener_ && !client_connection_listener_->IsInitialised()"); + return false; + } + LOG4CXX_TRACE(logger_, "exit with TRUE"); + return true; +} + +std::string TransportAdapterImpl::DeviceName(const DeviceUID& device_id) const { + DeviceSptr device = FindDevice(device_id); + if (device.valid()) { + return device->name(); + } else { + return ""; + } +} + +std::string TransportAdapterImpl::GetConnectionType() const { + const std::string deviceType = GetDeviceType(); + std::string result("USB_serial_number"); + if ("sdl-tcp" == deviceType || "sdl-bluetooth" == deviceType) { + result.assign("BTMAC"); + } + return result; +} + +#ifdef TIME_TESTER +void TransportAdapterImpl::SetTimeMetricObserver(TMMetricObserver* observer) { + metric_observer_ = observer; +} +#endif // TIME_TESTER + +#ifdef TIME_TESTER +TMMetricObserver* TransportAdapterImpl::GetTimeMetricObserver() { + return metric_observer_; +} +#endif // TIME_TESTER + +void TransportAdapterImpl::Store() const { +} + +bool TransportAdapterImpl::Restore() { + return true; +} + +bool TransportAdapterImpl::ToBeAutoConnected(DeviceSptr device) const { + return false; +} + +ConnectionSptr TransportAdapterImpl::FindEstablishedConnection( + const DeviceUID& device_id, const ApplicationHandle& app_handle) const { + LOG4CXX_TRACE(logger_, "enter. device_id: " << &device_id << ", app_handle: " << + &app_handle); + ConnectionSptr connection; + pthread_mutex_lock(&connections_mutex_); + ConnectionMap::const_iterator it = + connections_.find(std::make_pair(device_id, app_handle)); + if (it != connections_.end()) { + const ConnectionInfo& info = it->second; + if (info.state == ConnectionInfo::ESTABLISHED) { + connection = info.connection; + } + } + pthread_mutex_unlock(&connections_mutex_); + LOG4CXX_TRACE(logger_, "exit with ConnectionSptr: " << connection); + return connection; +} + +TransportAdapter::Error TransportAdapterImpl::ConnectDevice(DeviceSptr device) { + LOG4CXX_TRACE(logger_, "enter. device: " << device); + DeviceUID device_id = device->unique_device_id(); + ApplicationList app_list = device->GetApplicationList(); + LOG4CXX_INFO(logger_, "Device " << device->name() << " has " + << app_list.size() << " applications."); + bool errors_occurred = false; + for (ApplicationList::iterator it = app_list.begin(); it != app_list.end(); ++it) { + const ApplicationHandle app_handle = *it; + LOG4CXX_DEBUG(logger_, "Attempt to connect device " << device_id << + ", channel " << app_handle); + const Error error = Connect(device_id, app_handle); + switch (error) { + case OK: + LOG4CXX_DEBUG(logger_, "error = OK"); + break; + case ALREADY_EXISTS: + LOG4CXX_DEBUG(logger_, "error = ALREADY_EXISTS"); + break; + default: + LOG4CXX_ERROR(logger_, "Connect to device " << device_id << + ", channel " << app_handle << + " failed with error " << error); + errors_occurred = true; + LOG4CXX_DEBUG(logger_, "switch (error), default case"); + break; + } + } + if (errors_occurred) { + LOG4CXX_TRACE(logger_, "exit with error:FAIL"); + return FAIL; + } else { + LOG4CXX_TRACE(logger_, "exit with error:OK"); + return OK; + } +} + +void TransportAdapterImpl::RemoveDevice(const DeviceUID& device_handle) { + LOG4CXX_TRACE(logger_, "enter. device_handle: " << &device_handle); + pthread_mutex_lock(&devices_mutex_); + DeviceMap::iterator i = devices_.find( + device_handle); //ykazakov: there is no erase for const iterator on QNX + if (i != devices_.end()) { + DeviceSptr device = i->second; + if (!device->keep_on_disconnect()) { + devices_.erase(i); + for (TransportAdapterListenerList::iterator it = listeners_.begin(); + it != listeners_.end(); ++it) { + TransportAdapterListener* listener = *it; + listener->OnDeviceListUpdated(this); + } + } + } + pthread_mutex_unlock(&devices_mutex_); + LOG4CXX_TRACE(logger_, "exit"); +} + + +} // namespace transport_adapter + +} // namespace transport_manager diff --git a/src/components/transport_manager/src/transport_adapter/transport_adapter_listener_impl.cc b/src/components/transport_manager/src/transport_adapter/transport_adapter_listener_impl.cc new file mode 100644 index 0000000000..18128d681e --- /dev/null +++ b/src/components/transport_manager/src/transport_adapter/transport_adapter_listener_impl.cc @@ -0,0 +1,277 @@ +/* + * Copyright (c) 2014, 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 <algorithm> + +#include "utils/logger.h" + +#include "transport_manager/transport_adapter/transport_adapter_listener_impl.h" +#include "transport_manager/transport_manager_impl.h" +#include "transport_manager/transport_adapter/transport_adapter_event.h" + +namespace transport_manager { +CREATE_LOGGERPTR_GLOBAL(logger_, "TransportManager") + +TransportAdapterListenerImpl::TransportAdapterListenerImpl( + TransportManagerImpl* manager, TransportAdapter* adapter) : + transport_manager_impl_(manager), transport_adapter_(adapter) { +} + +void TransportAdapterListenerImpl::OnSearchDeviceDone( + const TransportAdapter* adapter) { + LOG4CXX_TRACE(logger_, "enter. adapter* " << adapter); + const TransportAdapterEvent event( + TransportAdapterListenerImpl::EventTypeEnum::ON_SEARCH_DONE, + transport_adapter_, "", 0, RawMessagePtr(), BaseErrorPtr()); + if (transport_manager::E_SUCCESS + != transport_manager_impl_->ReceiveEventFromDevice(event)) { + LOG4CXX_WARN(logger_, "Failed to receive event from device"); + } + LOG4CXX_TRACE(logger_, "exit"); +} + +void TransportAdapterListenerImpl::OnSearchDeviceFailed( + const TransportAdapter* adapter, const SearchDeviceError& error) { + LOG4CXX_TRACE(logger_, "enter. adapter: " << adapter << ", error: " << &error); + SearchDeviceError* err = new SearchDeviceError(error); + const TransportAdapterEvent event( + TransportAdapterListenerImpl::EventTypeEnum::ON_SEARCH_FAIL, + transport_adapter_, "", 0, RawMessagePtr(), BaseErrorPtr(err)); + if (transport_manager::E_SUCCESS + != transport_manager_impl_->ReceiveEventFromDevice(event)) { + LOG4CXX_WARN(logger_, "Failed to receive event from device"); + } + LOG4CXX_TRACE(logger_, "exit"); +} + +void TransportAdapterListenerImpl::OnDeviceListUpdated( + const TransportAdapter* adapter) { + LOG4CXX_TRACE(logger_, "enter. adapter* " << adapter); + const TransportAdapterEvent event( + TransportAdapterListenerImpl::EventTypeEnum::ON_DEVICE_LIST_UPDATED, + transport_adapter_, "", 0, RawMessagePtr(), BaseErrorPtr()); + if (transport_manager::E_SUCCESS + != transport_manager_impl_->ReceiveEventFromDevice(event)) { + LOG4CXX_WARN(logger_, "Failed to receive event from device"); + } + LOG4CXX_TRACE(logger_, "exit"); +} + +void TransportAdapterListenerImpl::OnFindNewApplicationsRequest( + const TransportAdapter* adapter) { + LOG4CXX_TRACE(logger_, "enter. adapter* " << adapter); + const TransportAdapterEvent event( + TransportAdapterListenerImpl::ON_FIND_NEW_APPLICATIONS_REQUEST, + transport_adapter_, "", 0, RawMessagePtr(), BaseErrorPtr()); + if (transport_manager::E_SUCCESS + != transport_manager_impl_->ReceiveEventFromDevice(event)) { + LOG4CXX_WARN(logger_, "Failed to receive event from device"); + } + LOG4CXX_TRACE(logger_, "exit"); +} + +void TransportAdapterListenerImpl::OnConnectDone( + const TransportAdapter* adapter, const DeviceUID& device, + const ApplicationHandle& application_id) { + LOG4CXX_TRACE(logger_, "enter adapter*: " << adapter << ", device: " << &device << + ", application_id: " << &application_id); + const TransportAdapterEvent event( + TransportAdapterListenerImpl::EventTypeEnum::ON_CONNECT_DONE, + transport_adapter_, device, application_id, RawMessagePtr(), + BaseErrorPtr(new BaseError())); + if (transport_manager::E_SUCCESS + != transport_manager_impl_->ReceiveEventFromDevice(event)) { + LOG4CXX_WARN(logger_, "Failed to receive event from device"); + } + LOG4CXX_TRACE(logger_, "exit"); +} + +void TransportAdapterListenerImpl::OnConnectFailed( + const TransportAdapter* adapter, const DeviceUID& device, + const ApplicationHandle& app_id, const ConnectError& error) { + LOG4CXX_TRACE(logger_, "enter. adapter: " << adapter << ", device: " << &device << + ", application_id: " << &app_id << ", error: " << &error); + ConnectError* err = new ConnectError(error); + const TransportAdapterEvent event( + TransportAdapterListenerImpl::EventTypeEnum::ON_CONNECT_FAIL, + transport_adapter_, device, app_id, RawMessagePtr(), BaseErrorPtr(err)); + if (transport_manager::E_SUCCESS + != transport_manager_impl_->ReceiveEventFromDevice(event)) { + LOG4CXX_WARN(logger_, "Failed to receive event from device"); + } + LOG4CXX_TRACE(logger_, "exit"); +} + +void TransportAdapterListenerImpl::OnDisconnectDone( + const TransportAdapter* adapter, const DeviceUID& device, + const ApplicationHandle& app_id) { + LOG4CXX_TRACE(logger_, "enter. adapter: " << adapter << ", device: " << &device << + ", application_id: " << &app_id); + const TransportAdapterEvent event( + TransportAdapterListenerImpl::EventTypeEnum::ON_DISCONNECT_DONE, + transport_adapter_, device, app_id, RawMessagePtr(), + BaseErrorPtr(new BaseError())); + if (transport_manager::E_SUCCESS + != transport_manager_impl_->ReceiveEventFromDevice(event)) { + LOG4CXX_WARN(logger_, "Failed to receive event from device"); + } + LOG4CXX_TRACE(logger_, "exit"); +} + +void TransportAdapterListenerImpl::OnDisconnectFailed( + const TransportAdapter* adapter, const DeviceUID& device, + const ApplicationHandle& app_id, const DisconnectError& error) { + LOG4CXX_TRACE(logger_, "enter. adapter: " << adapter << ", device: " << &device << + ", application_id: " << &app_id << ", error: " << &error); + DisconnectError* err = new DisconnectError(error); + const TransportAdapterEvent event( + TransportAdapterListenerImpl::EventTypeEnum::ON_DISCONNECT_FAIL, + transport_adapter_, device, app_id, RawMessagePtr(), BaseErrorPtr(err)); + if (transport_manager::E_SUCCESS + != transport_manager_impl_->ReceiveEventFromDevice(event)) { + LOG4CXX_WARN(logger_, "Failed to receive event from device"); + } + LOG4CXX_TRACE(logger_, "exit"); +} + +void TransportAdapterListenerImpl::OnDisconnectDeviceDone( + const TransportAdapter* adapter, const DeviceUID& device) { +} + +void TransportAdapterListenerImpl::OnDisconnectDeviceFailed( + const TransportAdapter* adapter, const DeviceUID& device, + const DisconnectDeviceError& error) { +} + +void TransportAdapterListenerImpl::OnDataReceiveDone( + const TransportAdapter* adapter, const DeviceUID& device, + const ApplicationHandle& app_id, const RawMessagePtr data_container) { + LOG4CXX_TRACE(logger_, "enter. adapter: " << adapter << ", device: " << &device << + ", application_id: " << &app_id << ", data_container: " << data_container); + const TransportAdapterEvent event( + TransportAdapterListenerImpl::EventTypeEnum::ON_RECEIVED_DONE, + transport_adapter_, device, app_id, data_container, + BaseErrorPtr(new BaseError())); + if (transport_manager::E_SUCCESS + != transport_manager_impl_->ReceiveEventFromDevice(event)) { + LOG4CXX_WARN(logger_, "Failed to receive event from device"); + } + LOG4CXX_TRACE(logger_, "exit"); +} + +void TransportAdapterListenerImpl::OnDataReceiveFailed( + const TransportAdapter* adapter, const DeviceUID& device, + const ApplicationHandle& app_id, const DataReceiveError& error) { + LOG4CXX_TRACE(logger_, "enter. adapter: " << adapter << ", device: " << &device << + ", application_id: " << &app_id << ", error: " << &error); + DataReceiveError* err = new DataReceiveError(error); + const TransportAdapterEvent event( + TransportAdapterListenerImpl::EventTypeEnum::ON_RECEIVED_FAIL, + transport_adapter_, device, app_id, RawMessagePtr(), BaseErrorPtr(err)); + if (transport_manager::E_SUCCESS + != transport_manager_impl_->ReceiveEventFromDevice(event)) { + LOG4CXX_WARN(logger_, "Failed to receive event from device"); + } + LOG4CXX_TRACE(logger_, "exit"); +} + +void TransportAdapterListenerImpl::OnDataSendDone( + const TransportAdapter* adapter, const DeviceUID& device, + const ApplicationHandle& app_id, const RawMessagePtr data_container) { + LOG4CXX_TRACE(logger_, "enter. adapter: " << adapter << ", device: " << &device << + ", application_id: " << &app_id << ", data_container: " << data_container); + const TransportAdapterEvent event( + TransportAdapterListenerImpl::EventTypeEnum::ON_SEND_DONE, + transport_adapter_, device, app_id, data_container, + new BaseError()); + if (transport_manager::E_SUCCESS + != transport_manager_impl_->ReceiveEventFromDevice(event)) { + LOG4CXX_WARN(logger_, "Failed to receive event from device"); + } + LOG4CXX_TRACE(logger_, "exit"); +} + +void TransportAdapterListenerImpl::OnDataSendFailed( + const TransportAdapter* adapter, const DeviceUID& device, + const ApplicationHandle& app_id, const RawMessagePtr data_container, + const DataSendError& error) { + LOG4CXX_TRACE(logger_, "enter. adapter: " << adapter << ", device: " << &device << + ", application_id: " << &app_id << ", data_container: " << data_container << ", error: " + << &error); + DataSendError* err = new DataSendError(error); + const TransportAdapterEvent event( + TransportAdapterListenerImpl::EventTypeEnum::ON_SEND_FAIL, + transport_adapter_, device, app_id, data_container, BaseErrorPtr(err)); + if (transport_manager::E_SUCCESS + != transport_manager_impl_->ReceiveEventFromDevice(event)) { + LOG4CXX_WARN(logger_, "Failed to receive event from device"); + } + LOG4CXX_TRACE(logger_, "exit"); +} + +void TransportAdapterListenerImpl::OnConnectRequested( + const TransportAdapter* adapter, const DeviceUID& device_handle, + const ApplicationHandle& app_handle) { +} + +void TransportAdapterListenerImpl::OnUnexpectedDisconnect( + const TransportAdapter* adapter, const DeviceUID& device, + const ApplicationHandle& application, const CommunicationError& error) { + LOG4CXX_TRACE(logger_, "enter. adapter: " << adapter << ", device: " << &device << + ", application: " << &application << ", error: " << &error); + CommunicationError* err = new CommunicationError(error); + const TransportAdapterEvent event( + TransportAdapterListenerImpl::EventTypeEnum::ON_UNEXPECTED_DISCONNECT, + transport_adapter_, device, application, RawMessagePtr(), BaseErrorPtr(err)); + if (transport_manager::E_SUCCESS + != transport_manager_impl_->ReceiveEventFromDevice(event)) { + LOG4CXX_WARN(logger_, "Failed to receive event from device"); + } + LOG4CXX_TRACE(logger_, "exit"); +} + +void TransportAdapterListenerImpl::OnCommunicationError( + const TransportAdapter* adapter, const DeviceUID& device, + const ApplicationHandle& app_id) { + LOG4CXX_TRACE(logger_, "enter. adapter: " << adapter << ", device: " << &device << + ", application_id: " << &app_id); + const TransportAdapterEvent event( + TransportAdapterListenerImpl::EventTypeEnum::ON_COMMUNICATION_ERROR, + transport_adapter_, device, app_id, RawMessagePtr(), + BaseErrorPtr(new BaseError())); + if (transport_manager::E_SUCCESS + != transport_manager_impl_->ReceiveEventFromDevice(event)) { + LOG4CXX_WARN(logger_, "Failed to receive event from device"); + } + LOG4CXX_TRACE(logger_, "exit"); +} +} // namespace transport_manager diff --git a/src/components/transport_manager/src/transport_manager_default.cc b/src/components/transport_manager/src/transport_manager_default.cc new file mode 100644 index 0000000000..ffd1b3d860 --- /dev/null +++ b/src/components/transport_manager/src/transport_manager_default.cc @@ -0,0 +1,107 @@ +/* + * \file transport_manager_default.cc + * \brief TransportManagerDefault 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 "config_profile/profile.h" + +#include "transport_manager/transport_manager_default.h" +#include "transport_manager/tcp/tcp_transport_adapter.h" +#include "utils/logger.h" + +#ifdef BLUETOOTH_SUPPORT +#include "transport_manager/bluetooth/bluetooth_transport_adapter.h" +#endif + +// CUSTOMER_PASA + +#if defined(USB_SUPPORT) +#include "transport_manager/usb/usb_aoa_adapter.h" +#endif // USB_SUPPORT + + + +namespace transport_manager { +CREATE_LOGGERPTR_GLOBAL(logger_, "TransportManager") + +int TransportManagerDefault::Init() { + LOG4CXX_TRACE(logger_, "enter"); + if (E_SUCCESS != TransportManagerImpl::Init()) { + LOG4CXX_TRACE(logger_, "exit with E_TM_IS_NOT_INITIALIZED. Condition: E_SUCCESS != TransportManagerImpl::Init()"); + return E_TM_IS_NOT_INITIALIZED; + } + transport_adapter::TransportAdapterImpl* ta; +#ifdef BLUETOOTH_SUPPORT + + ta = new transport_adapter::BluetoothTransportAdapter; + +#ifdef TIME_TESTER + if (metric_observer_) { + ta->SetTimeMetricObserver(metric_observer_); + } +#endif // TIME_TESTER + AddTransportAdapter(ta); +#endif + uint16_t port = profile::Profile::instance()->transport_manager_tcp_adapter_port(); + ta = new transport_adapter::TcpTransportAdapter(port); +#ifdef TIME_TESTER + if (metric_observer_) { + ta->SetTimeMetricObserver(metric_observer_); + } +#endif // TIME_TESTER + AddTransportAdapter(ta); + +// CUSTOMER_PASA + +#if defined(USB_SUPPORT) + ta = new transport_adapter::UsbAoaAdapter(); +#ifdef TIME_TESTER + if (metric_observer_) { + ta->SetTimeMetricObserver(metric_observer_); + } +#endif // TIME_TESTER + AddTransportAdapter(ta); +#endif // USB_SUPPORT + + + + LOG4CXX_TRACE(logger_, "exit with E_SUCCESS"); + return E_SUCCESS; +} + +TransportManagerDefault::~TransportManagerDefault() {} + +TransportManagerDefault::TransportManagerDefault() + : TransportManagerImpl() {} + +} // namespace transport_manager diff --git a/src/components/transport_manager/src/transport_manager_impl.cc b/src/components/transport_manager/src/transport_manager_impl.cc new file mode 100644 index 0000000000..16810ef47d --- /dev/null +++ b/src/components/transport_manager/src/transport_manager_impl.cc @@ -0,0 +1,987 @@ +/* + * Copyright (c) 2014, 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/transport_manager_impl.h" + +#include <pthread.h> +#include <stdint.h> +#include <cstring> +#include <queue> +#include <set> +#include <algorithm> +#include <limits> +#include <functional> +#include <sstream> + +#include "utils/macro.h" +#include "utils/logger.h" +#include "transport_manager/common.h" +#include "transport_manager/transport_manager_listener.h" +#include "transport_manager/transport_manager_listener_empty.h" +#include "transport_manager/transport_adapter/transport_adapter.h" +#include "transport_manager/transport_adapter/transport_adapter_event.h" +#include "config_profile/profile.h" + +using ::transport_manager::transport_adapter::TransportAdapter; + +namespace transport_manager { + +CREATE_LOGGERPTR_GLOBAL(logger_, "TransportManager") + +TransportManagerImpl::Connection TransportManagerImpl::convert( + const TransportManagerImpl::ConnectionInternal& p) { + LOG4CXX_TRACE(logger_, "enter. ConnectionInternal: " << &p); + TransportManagerImpl::Connection c; + c.application = p.application; + c.device = p.device; + c.id = p.id; + LOG4CXX_TRACE(logger_, "exit with TransportManagerImpl::Connection. It's ConnectionUID = " + << c.id); + return c; +} + +TransportManagerImpl::TransportManagerImpl() + : message_queue_mutex_(), + all_thread_active_(false), + message_queue_thread_(), + event_queue_thread_(), + device_listener_thread_wakeup_(), + is_initialized_(false), +#ifdef TIME_TESTER + metric_observer_(NULL), +#endif // TIME_TESTER + connection_id_counter_(0) { + LOG4CXX_INFO(logger_, "=============================================="); + pthread_mutex_init(&message_queue_mutex_, NULL); + pthread_cond_init(&message_queue_cond_, NULL); + pthread_mutex_init(&event_queue_mutex_, 0); + pthread_cond_init(&device_listener_thread_wakeup_, NULL); + LOG4CXX_DEBUG(logger_, "TransportManager object created"); +} + +TransportManagerImpl::~TransportManagerImpl() { + LOG4CXX_DEBUG(logger_, "TransportManager object destroying"); + + for (std::vector<TransportAdapter*>::iterator it = + transport_adapters_.begin(); + it != transport_adapters_.end(); ++it) { + delete *it; + } + + for (std::map<TransportAdapter*, TransportAdapterListenerImpl*>::iterator it = + transport_adapter_listeners_.begin(); + it != transport_adapter_listeners_.end(); ++it) { + delete it->second; + } + + pthread_mutex_destroy(&message_queue_mutex_); + pthread_cond_destroy(&message_queue_cond_); + pthread_mutex_destroy(&event_queue_mutex_); + pthread_cond_destroy(&device_listener_thread_wakeup_); + LOG4CXX_INFO(logger_, "TransportManager object destroyed"); +} + +int TransportManagerImpl::ConnectDevice(const DeviceHandle& device_handle) { + LOG4CXX_TRACE(logger_, "enter. DeviceHandle: " << &device_handle); + if (!this->is_initialized_) { + LOG4CXX_ERROR(logger_, "TransportManager is not initialized."); + LOG4CXX_TRACE(logger_, + "exit with E_TM_IS_NOT_INITIALIZED. Condition: !this->is_initialized_"); + return E_TM_IS_NOT_INITIALIZED; + } + + DeviceUID device_id = converter_.HandleToUid(device_handle); + LOG4CXX_DEBUG(logger_, "Convert handle to id " << device_id); + + transport_adapter::TransportAdapter* ta = device_to_adapter_map_[device_id]; + if (NULL == ta) { + LOG4CXX_ERROR(logger_, "No device adapter found by id " << device_id); + LOG4CXX_TRACE(logger_, "exit with E_INVALID_HANDLE. Condition: NULL == ta"); + return E_INVALID_HANDLE; + } + + TransportAdapter::Error ta_error = ta->ConnectDevice(device_id); + int err = (TransportAdapter::OK == ta_error) ? E_SUCCESS : E_INTERNAL_ERROR; + LOG4CXX_TRACE(logger_, "exit with error: " << err); + return err; +} + +int TransportManagerImpl::DisconnectDevice(const DeviceHandle& device_handle) { + LOG4CXX_TRACE(logger_, "enter. DeviceHandle: " << &device_handle); + if (!this->is_initialized_) { + LOG4CXX_ERROR(logger_, "TransportManager is not initialized."); + LOG4CXX_TRACE(logger_, + "exit with E_TM_IS_NOT_INITIALIZED. Condition: !this->is_initialized_"); + return E_TM_IS_NOT_INITIALIZED; + } + DeviceUID device_id = converter_.HandleToUid(device_handle); + LOG4CXX_DEBUG(logger_, "Convert handle to id" << device_id); + + transport_adapter::TransportAdapter* ta = device_to_adapter_map_[device_id]; + if (NULL == ta) { + LOG4CXX_WARN(logger_, "No device adapter found by id " << device_id); + LOG4CXX_TRACE(logger_, "exit with E_INVALID_HANDLE. Condition: NULL == ta"); + return E_INVALID_HANDLE; + } + ta->DisconnectDevice(device_id); + LOG4CXX_TRACE(logger_, "exit with E_SUCCESS"); + return E_SUCCESS; +} + +int TransportManagerImpl::Disconnect(const ConnectionUID& cid) { + LOG4CXX_TRACE(logger_, "enter. ConnectionUID: " << &cid); + if (!this->is_initialized_) { + LOG4CXX_ERROR(logger_, "TransportManager is not initialized."); + LOG4CXX_TRACE(logger_, + "exit with E_TM_IS_NOT_INITIALIZED. Condition: !this->is_initialized_"); + return E_TM_IS_NOT_INITIALIZED; + } + + ConnectionInternal* connection = GetConnection(cid); + if (NULL == connection) { + LOG4CXX_ERROR(logger_, "TransportManagerImpl::Disconnect: Connection does not exist."); + LOG4CXX_TRACE(logger_, "exit with E_INVALID_HANDLE. Condition: NULL == connection"); + return E_INVALID_HANDLE; + } + + pthread_mutex_lock(&event_queue_mutex_); + int messages_count = 0; + for (EventQueue::const_iterator it = event_queue_.begin(); + it != event_queue_.end(); + ++it) { + if (it->application_id == static_cast<ApplicationHandle>(cid)) { + ++messages_count; + } + } + pthread_mutex_unlock(&event_queue_mutex_); + + if (messages_count > 0) { + connection->messages_count = messages_count; + connection->shutDown = true; + + const uint32_t disconnect_timeout = + profile::Profile::instance()->transport_manager_disconnect_timeout(); + if (disconnect_timeout > 0) { + connection->timer->start(disconnect_timeout); + } + } else { + connection->transport_adapter->Disconnect(connection->device, + connection->application); + } + LOG4CXX_TRACE(logger_, "exit with E_SUCCESS"); + return E_SUCCESS; +} + +int TransportManagerImpl::DisconnectForce(const ConnectionUID& cid) { + LOG4CXX_TRACE(logger_, "enter ConnectionUID: " << &cid); + if (false == this->is_initialized_) { + LOG4CXX_ERROR(logger_, "TransportManager is not initialized."); + LOG4CXX_TRACE(logger_, + "exit with E_TM_IS_NOT_INITIALIZED. Condition: false == this->is_initialized_"); + return E_TM_IS_NOT_INITIALIZED; + } + pthread_mutex_lock(&event_queue_mutex_); + // Clear messages for this connection + // Note that MessageQueue typedef is assumed to be std::list, + // or there is a problem here. One more point versus typedefs-everywhere + MessageQueue::iterator e = message_queue_.begin(); + while (e != message_queue_.end()) { + if (static_cast<ConnectionUID>((*e)->connection_key()) == cid) { + RaiseEvent(&TransportManagerListener::OnTMMessageSendFailed, + DataSendTimeoutError(), *e); + e = message_queue_.erase(e); + } else { + ++e; + } + } + pthread_mutex_unlock(&event_queue_mutex_); + const ConnectionInternal* connection = GetConnection(cid); + if (NULL == connection) { + LOG4CXX_ERROR( + logger_, + "TransportManagerImpl::DisconnectForce: Connection does not exist."); + LOG4CXX_TRACE(logger_, "exit with E_INVALID_HANDLE. Condition: NULL == connection"); + return E_INVALID_HANDLE; + } + connection->transport_adapter->Disconnect(connection->device, + connection->application); + LOG4CXX_TRACE(logger_, "exit with E_SUCCESS"); + return E_SUCCESS; +} + +int TransportManagerImpl::AddEventListener(TransportManagerListener* listener) { + LOG4CXX_TRACE(logger_, "enter. TransportManagerListener: " << listener); + transport_manager_listener_.push_back(listener); + LOG4CXX_TRACE(logger_, "exit with E_SUCCESS"); + return E_SUCCESS; +} + +int TransportManagerImpl::Stop() { + LOG4CXX_TRACE(logger_, "enter"); + if (!all_thread_active_) { + LOG4CXX_TRACE(logger_, + "exit with E_TM_IS_NOT_INITIALIZED. Condition: !all_thread_active_"); + return E_TM_IS_NOT_INITIALIZED; + } + all_thread_active_ = false; + + pthread_mutex_lock(&event_queue_mutex_); + pthread_cond_signal(&device_listener_thread_wakeup_); + pthread_mutex_unlock(&event_queue_mutex_); + + pthread_mutex_lock(&message_queue_mutex_); + pthread_cond_signal(&message_queue_cond_); + pthread_mutex_unlock(&message_queue_mutex_); + + pthread_join(message_queue_thread_, 0); + pthread_join(event_queue_thread_, 0); + + LOG4CXX_TRACE(logger_, "exit with E_SUCCESS"); + return E_SUCCESS; +} + +int TransportManagerImpl::SendMessageToDevice(const RawMessagePtr message) { + LOG4CXX_TRACE(logger_, "enter. RawMessageSptr: " << message); + LOG4CXX_INFO(logger_, "Send message to device called with arguments " + << message.get()); + if (false == this->is_initialized_) { + LOG4CXX_ERROR(logger_, "TM is not initialized."); + LOG4CXX_TRACE(logger_, + "exit with E_TM_IS_NOT_INITIALIZED. Condition: false == this->is_initialized_"); + return E_TM_IS_NOT_INITIALIZED; + } + + const ConnectionInternal* connection = + GetConnection(message->connection_key()); + if (NULL == connection) { + LOG4CXX_ERROR(logger_, "Connection with id " << message->connection_key() + << " does not exist."); + LOG4CXX_TRACE(logger_, "exit with E_INVALID_HANDLE. Condition: NULL == connection"); + return E_INVALID_HANDLE; + } + + if (connection->shutDown) { + LOG4CXX_ERROR(logger_, "TransportManagerImpl::Disconnect: Connection is to shut down."); + LOG4CXX_TRACE(logger_, + "exit with E_CONNECTION_IS_TO_SHUTDOWN. Condition: connection->shutDown"); + return E_CONNECTION_IS_TO_SHUTDOWN; + } +#ifdef TIME_TESTER + if (metric_observer_) { + metric_observer_->StartRawMsg(message.get()); + } +#endif // TIME_TESTER + this->PostMessage(message); + LOG4CXX_TRACE(logger_, "exit with E_SUCCESS"); + return E_SUCCESS; +} + +int TransportManagerImpl::ReceiveEventFromDevice(const TransportAdapterEvent& event) { + LOG4CXX_TRACE(logger_, "enter. TransportAdapterEvent: " << &event); + if (!is_initialized_) { + LOG4CXX_ERROR(logger_, "TM is not initialized."); + LOG4CXX_TRACE(logger_, + "exit with E_TM_IS_NOT_INITIALIZED. Condition: false == this->is_initialized_"); + return E_TM_IS_NOT_INITIALIZED; + } + this->PostEvent(event); + LOG4CXX_TRACE(logger_, "exit with E_SUCCESS"); + return E_SUCCESS; +} + +int TransportManagerImpl::RemoveDevice(const DeviceHandle& device_handle) { + LOG4CXX_TRACE(logger_, "enter. DeviceHandle: " << &device_handle); + DeviceUID device_id = converter_.HandleToUid(device_handle); + if (false == this->is_initialized_) { + LOG4CXX_ERROR(logger_, "TM is not initialized."); + LOG4CXX_TRACE(logger_, + "exit with E_TM_IS_NOT_INITIALIZED. Condition: false == this->is_initialized_"); + return E_TM_IS_NOT_INITIALIZED; + } + device_to_adapter_map_.erase(device_id); + LOG4CXX_TRACE(logger_, "exit with E_SUCCESS"); + return E_SUCCESS; +} + +int TransportManagerImpl::AddTransportAdapter( + transport_adapter::TransportAdapter* transport_adapter) { + LOG4CXX_TRACE(logger_, "enter. TransportAdapter: " << transport_adapter); + + if (transport_adapter_listeners_.find(transport_adapter) != + transport_adapter_listeners_.end()) { + LOG4CXX_ERROR(logger_, "Adapter already exists."); + LOG4CXX_TRACE(logger_, + "exit with E_ADAPTER_EXISTS. Condition: transport_adapter_listeners_.find(transport_adapter) != transport_adapter_listeners_.end()"); + return E_ADAPTER_EXISTS; + } + transport_adapter_listeners_[transport_adapter] = + new TransportAdapterListenerImpl(this, transport_adapter); + transport_adapter->AddListener( + transport_adapter_listeners_[transport_adapter]); + + if (transport_adapter->IsInitialised() || + transport_adapter->Init() == TransportAdapter::OK) { + transport_adapters_.push_back(transport_adapter); + } + LOG4CXX_TRACE(logger_, "exit with E_SUCCESS"); + return E_SUCCESS; +} + +int TransportManagerImpl::SearchDevices() { + LOG4CXX_TRACE(logger_, "enter"); + if (!this->is_initialized_) { + LOG4CXX_ERROR(logger_, "TM is not initialized"); + LOG4CXX_TRACE(logger_, + "exit with E_TM_IS_NOT_INITIALIZED. Condition: !this->is_initialized_"); + return E_TM_IS_NOT_INITIALIZED; + } + + LOG4CXX_INFO(logger_, "Search device called"); + + bool success_occurred = false; + + for (std::vector<TransportAdapter*>::iterator it = + transport_adapters_.begin(); + it != transport_adapters_.end(); ++it) { + LOG4CXX_DEBUG(logger_, "Iterating over transport adapters"); + TransportAdapter::Error scanResult = (*it)->SearchDevices(); + if (transport_adapter::TransportAdapter::OK == scanResult) { + success_occurred = true; + } else { + LOG4CXX_ERROR(logger_, "Transport Adapter search failed " + << *it << "[" << (*it)->GetDeviceType() + << "]"); + switch (scanResult) { + case transport_adapter::TransportAdapter::NOT_SUPPORTED: { + LOG4CXX_ERROR(logger_, "Search feature is not supported " + << *it << "[" << (*it)->GetDeviceType() + << "]"); + LOG4CXX_DEBUG(logger_, "scanResult = TransportAdapter::NOT_SUPPORTED"); + break; + } + case transport_adapter::TransportAdapter::BAD_STATE: { + LOG4CXX_ERROR(logger_, "Transport Adapter has bad state " << *it << "[" << + (*it)->GetDeviceType() + << "]"); + LOG4CXX_DEBUG(logger_, "scanResult = TransportAdapter::BAD_STATE"); + break; + } + default: { + LOG4CXX_ERROR(logger_, "Invalid scan result"); + LOG4CXX_DEBUG(logger_, "scanResult = default switch case"); + return E_ADAPTERS_FAIL; + } + } + } + } + int transport_adapter_search = (success_occurred || transport_adapters_.empty()) + ? E_SUCCESS + : E_ADAPTERS_FAIL; + if (transport_adapter_search == E_SUCCESS) { + LOG4CXX_TRACE(logger_, + "exit with E_SUCCESS. Condition: success_occured || transport_adapters_.empty()"); + } else { + LOG4CXX_TRACE(logger_, + "exit with E_ADAPTERS_FAIL. Condition: success_occured || transport_adapters_.empty()"); + } + return transport_adapter_search; +} + +int TransportManagerImpl::Init() { + LOG4CXX_TRACE(logger_, "enter"); + all_thread_active_ = true; + + int error_code = pthread_create(&message_queue_thread_, 0, + &MessageQueueStartThread, this); + if (0 != error_code) { + LOG4CXX_ERROR(logger_, + "Message queue thread is not created exit with error code " + << error_code); + LOG4CXX_TRACE(logger_, "exit with E_TM_IS_NOT_INITIALIZED. Condition: 0 != error_code"); + return E_TM_IS_NOT_INITIALIZED; + } + pthread_setname_np(message_queue_thread_, "TM MessageQueue"); + + error_code = pthread_create(&event_queue_thread_, 0, + &EventListenerStartThread, this); + if (0 != error_code) { + LOG4CXX_ERROR(logger_, + "Event queue thread is not created exit with error code " + << error_code); + LOG4CXX_TRACE(logger_, "exit with E_TM_IS_NOT_INITIALIZED. Condition: 0 != error_code"); + return E_TM_IS_NOT_INITIALIZED; + } + pthread_setname_np(event_queue_thread_, "TM EventListener"); + + is_initialized_ = true; + LOG4CXX_TRACE(logger_, "exit with E_SUCCESS"); + return E_SUCCESS; +} + +int TransportManagerImpl::Visibility(const bool& on_off) const { + LOG4CXX_TRACE(logger_, "enter. On_off: " << &on_off); + TransportAdapter::Error ret; + + LOG4CXX_DEBUG(logger_, "Visibility change requested to " << on_off); + if (false == this->is_initialized_) { + LOG4CXX_ERROR(logger_, "TM is not initialized"); + LOG4CXX_TRACE(logger_, + "exit with E_TM_IS_NOT_INITIALIZED. Condition: false == this->is_initialized_"); + return E_TM_IS_NOT_INITIALIZED; + } + + for (std::vector<TransportAdapter*>::const_iterator it = + transport_adapters_.begin(); + it != transport_adapters_.end(); ++it) { + if (on_off) { + ret = (*it)->StartClientListening(); + } else { + ret = (*it)->StopClientListening(); + } + if (TransportAdapter::Error::NOT_SUPPORTED == ret) { + LOG4CXX_DEBUG(logger_, "Visibility change is not supported for adapter " + << *it << "[" << (*it)->GetDeviceType() << "]"); + } + } + LOG4CXX_TRACE(logger_, "exit with E_SUCCESS"); + return E_SUCCESS; +} + +void TransportManagerImpl::UpdateDeviceList(TransportAdapter* ta) { + LOG4CXX_TRACE(logger_, "enter. TransportAdapter: " << ta); + std::set<DeviceInfo> old_devices; + for (DeviceInfoList::iterator it = device_list_.begin(); + it != device_list_.end();) { + if (it->first == ta) { + old_devices.insert(it->second); + it = device_list_.erase(it); + } else { + ++it; + } + } + + std::set<DeviceInfo> new_devices; + const DeviceList dev_list = ta->GetDeviceList(); + for (DeviceList::const_iterator it = dev_list.begin(); + it != dev_list.end(); ++it) { + DeviceHandle device_handle = converter_.UidToHandle(*it); + DeviceInfo info(device_handle, *it, ta->DeviceName(*it), ta->GetConnectionType()); + device_list_.push_back(std::make_pair(ta, info)); + new_devices.insert(info); + } + + std::set<DeviceInfo> added_devices; + std::set_difference(new_devices.begin(), new_devices.end(), + old_devices.begin(), old_devices.end(), + std::inserter(added_devices, added_devices.begin())); + for (std::set<DeviceInfo>::const_iterator it = added_devices.begin(); + it != added_devices.end(); + ++it) { + RaiseEvent(&TransportManagerListener::OnDeviceAdded, *it); + } + + std::set<DeviceInfo> removed_devices; + std::set_difference(old_devices.begin(), old_devices.end(), + new_devices.begin(), new_devices.end(), + std::inserter(removed_devices, removed_devices.begin())); + + for (std::set<DeviceInfo>::const_iterator it = removed_devices.begin(); + it != removed_devices.end(); + ++it) { + RaiseEvent(&TransportManagerListener::OnDeviceRemoved, *it); + } + LOG4CXX_TRACE(logger_, "exit"); +} + +void TransportManagerImpl::PostMessage(const RawMessagePtr message) { + LOG4CXX_TRACE(logger_, "enter. RawMessageSptr: " << message); +#ifdef USE_RWLOCK + message_queue_rwlock_.AcquireForWriting(); +#else + pthread_mutex_lock(&message_queue_mutex_); +#endif + message_queue_.push_back(message); + pthread_cond_signal(&message_queue_cond_); +#ifdef USE_RWLOCK + message_queue_rwlock_.Release(); +#else + pthread_mutex_unlock(&message_queue_mutex_); +#endif + LOG4CXX_TRACE(logger_, "exit"); +} + +void TransportManagerImpl::RemoveMessage(const RawMessagePtr message) { + // TODO: Reconsider necessity of this method, remove if it's useless, + // make to work otherwise. + // 2013-08-21 dchmerev@luxoft.com + LOG4CXX_TRACE(logger_, "enter RawMessageSptr: " << message); +#ifdef USE_RWLOCK + message_queue_rwlock_.AcquireForWriting(); +#else + pthread_mutex_lock(&message_queue_mutex_); +#endif + std::remove(message_queue_.begin(), message_queue_.end(), message); +#ifdef USE_RWLOCK + message_queue_rwlock_.Release(); +#else + pthread_mutex_unlock(&message_queue_mutex_); +#endif + LOG4CXX_TRACE(logger_, "exit"); +} + +void TransportManagerImpl::PostEvent(const TransportAdapterEvent& event) { + LOG4CXX_TRACE(logger_, "enter. TransportAdapterEvent: " << &event); +#ifdef USE_RWLOCK + event_queue_rwlock_.AcquireForWriting(); +#else + pthread_mutex_lock(&event_queue_mutex_); +#endif + event_queue_.push_back(event); + pthread_cond_signal(&device_listener_thread_wakeup_); +#ifdef USE_RWLOCK + event_queue_rwlock_.Release(); +#else + pthread_mutex_unlock(&event_queue_mutex_); +#endif + LOG4CXX_TRACE(logger_, "exit"); +} + +void* TransportManagerImpl::EventListenerStartThread(void* data) { + LOG4CXX_TRACE(logger_, "enter Data: " << data); + if (NULL != data) { + static_cast<TransportManagerImpl*>(data)->EventListenerThread(); + } + LOG4CXX_TRACE(logger_, "exit with 0"); + return 0; +} + +void TransportManagerImpl::AddConnection(const ConnectionInternal& c) { + LOG4CXX_TRACE(logger_, "enter ConnectionInternal: " << &c); + connections_.push_back(c); + LOG4CXX_TRACE(logger_, "exit"); +} + +void TransportManagerImpl::RemoveConnection(uint32_t id) { + LOG4CXX_TRACE(logger_, "enter Id: " << id); + for (std::vector<ConnectionInternal>::iterator it = connections_.begin(); + it != connections_.end(); ++it) { + if (it->id == id) { + connections_.erase(it); + break; + } + } + LOG4CXX_TRACE(logger_, "exit"); +} + +TransportManagerImpl::ConnectionInternal* TransportManagerImpl::GetConnection( + const ConnectionUID& id) { + LOG4CXX_TRACE(logger_, "enter. ConnectionUID: " << &id); + for (std::vector<ConnectionInternal>::iterator it = connections_.begin(); + it != connections_.end(); ++it) { + if (it->id == id) { + LOG4CXX_TRACE(logger_, "exit with ConnectionInternal. It's adress: " << &*it); + return &*it; + } + } + LOG4CXX_TRACE(logger_, "exit with NULL"); + return NULL; + +} + +TransportManagerImpl::ConnectionInternal* TransportManagerImpl::GetConnection( + const DeviceUID& device, const ApplicationHandle& application) { + LOG4CXX_TRACE(logger_, "enter DeviceUID: " << &device << "ApplicationHandle: " << + &application); + for (std::vector<ConnectionInternal>::iterator it = connections_.begin(); + it != connections_.end(); ++it) { + if (it->device == device && it->application == application) { + LOG4CXX_TRACE(logger_, "exit with ConnectionInternal. It's adress: " << &*it); + return &*it; + } + } + LOG4CXX_TRACE(logger_, "exit with NULL"); + return NULL; +} + +void TransportManagerImpl::OnDeviceListUpdated(TransportAdapter* ta) { + LOG4CXX_TRACE(logger_, "enter. TransportAdapter: " << ta); + const DeviceList device_list = ta->GetDeviceList(); + LOG4CXX_INFO(logger_, "DEVICE_LIST_UPDATED " << device_list.size()); + for (DeviceList::const_iterator it = device_list.begin(); + it != device_list.end(); ++it) { + device_to_adapter_map_.insert(std::make_pair(*it, ta)); + DeviceHandle device_handle = converter_.UidToHandle(*it); + DeviceInfo info(device_handle, *it, ta->DeviceName(*it), ta->GetConnectionType()); + RaiseEvent(&TransportManagerListener::OnDeviceFound, info); + } + UpdateDeviceList(ta); + std::vector<DeviceInfo> device_infos; + for (DeviceInfoList::const_iterator it = device_list_.begin(); + it != device_list_.end(); ++it) { + device_infos.push_back(it->second); + } + RaiseEvent(&TransportManagerListener::OnDeviceListUpdated, device_infos); + LOG4CXX_TRACE(logger_, "exit"); +} + +void TransportManagerImpl::EventListenerThread() { + LOG4CXX_TRACE(logger_, "enter"); +#ifndef USE_RWLOCK + pthread_mutex_lock(&event_queue_mutex_); +#endif + LOG4CXX_INFO(logger_, "Event listener thread started"); + while (true) { + while (event_queue_.size() > 0) { +#ifdef USE_RWLOCK + event_queue_rwlock_.AcquireForReading(); +#endif + LOG4CXX_INFO(logger_, "Event listener queue pushed to process events"); + EventQueue::iterator current_it = event_queue_.begin(); + const TransportAdapterEvent event = *(current_it); + event_queue_.erase(current_it); +#ifdef USE_RWLOCK + event_queue_rwlock_.Release(); +#else + pthread_mutex_unlock(&event_queue_mutex_); +#endif + ConnectionInternal* connection = GetConnection(event.device_uid, event.application_id); + + switch (event.event_type) { + case TransportAdapterListenerImpl::EventTypeEnum::ON_SEARCH_DONE: { + RaiseEvent(&TransportManagerListener::OnScanDevicesFinished); + LOG4CXX_DEBUG(logger_, "event_type = ON_SEARCH_DONE"); + break; + } + case TransportAdapterListenerImpl::EventTypeEnum::ON_SEARCH_FAIL: { + // error happened in real search process (external error) + RaiseEvent(&TransportManagerListener::OnScanDevicesFailed, + *static_cast<SearchDeviceError*>(event.event_error.get())); + LOG4CXX_DEBUG(logger_, "event_type = ON_SEARCH_FAIL"); + break; + } + case TransportAdapterListenerImpl::EventTypeEnum::ON_DEVICE_LIST_UPDATED: { + OnDeviceListUpdated(event.transport_adapter); + LOG4CXX_DEBUG(logger_, "event_type = ON_DEVICE_LIST_UPDATED"); + break; + } + case TransportAdapterListenerImpl::ON_FIND_NEW_APPLICATIONS_REQUEST: { + RaiseEvent(&TransportManagerListener::OnFindNewApplicationsRequest); + LOG4CXX_DEBUG(logger_, "event_type = ON_FIND_NEW_APPLICATIONS_REQUEST"); + break; + } + case TransportAdapterListenerImpl::EventTypeEnum::ON_CONNECT_DONE: { + const DeviceHandle device_handle = converter_.UidToHandle(event.device_uid); + AddConnection(ConnectionInternal(this, event.transport_adapter, ++connection_id_counter_, + event.device_uid, event.application_id, + device_handle)); + RaiseEvent(&TransportManagerListener::OnConnectionEstablished, + DeviceInfo(device_handle, event.device_uid, + event.transport_adapter->DeviceName(event.device_uid), + event.transport_adapter->GetConnectionType()), + connection_id_counter_); + LOG4CXX_DEBUG(logger_, "event_type = ON_CONNECT_DONE"); + break; + } + case TransportAdapterListenerImpl::EventTypeEnum::ON_CONNECT_FAIL: { + RaiseEvent(&TransportManagerListener::OnConnectionFailed, + DeviceInfo(converter_.UidToHandle(event.device_uid), event.device_uid, + event.transport_adapter->DeviceName(event.device_uid), + event.transport_adapter->GetConnectionType()), + ConnectError()); + LOG4CXX_DEBUG(logger_, "event_type = ON_CONNECT_FAIL"); + break; + } + case TransportAdapterListenerImpl::EventTypeEnum::ON_DISCONNECT_DONE: { + if (NULL == connection) { + LOG4CXX_ERROR(logger_, "Connection not found"); + LOG4CXX_DEBUG(logger_, + "event_type = ON_DISCONNECT_DONE && NULL == connection"); + break; + } + RaiseEvent(&TransportManagerListener::OnConnectionClosed, + connection->id); + RemoveConnection(connection->id); + LOG4CXX_DEBUG(logger_, "event_type = ON_DISCONNECT_DONE"); + break; + } + case TransportAdapterListenerImpl::EventTypeEnum::ON_DISCONNECT_FAIL: { + const DeviceHandle device_handle = converter_.UidToHandle(event.device_uid); + RaiseEvent(&TransportManagerListener::OnDisconnectFailed, + device_handle, DisconnectDeviceError()); + LOG4CXX_DEBUG(logger_, "event_type = ON_DISCONNECT_FAIL"); + break; + } + case TransportAdapterListenerImpl::EventTypeEnum::ON_SEND_DONE: { +#ifdef TIME_TESTER + if (metric_observer_) { + metric_observer_->StopRawMsg(event.event_data.get()); + } +#endif // TIME_TESTER + if (connection == NULL) { + LOG4CXX_ERROR(logger_, "Connection ('" << event.device_uid << ", " + << event.application_id + << ") not found"); + LOG4CXX_DEBUG(logger_, "event_type = ON_SEND_DONE. Condition: NULL == connection"); + break; + } + RaiseEvent(&TransportManagerListener::OnTMMessageSend, event.event_data); + this->RemoveMessage(event.event_data); + if (connection->shutDown && --connection->messages_count == 0) { + connection->timer->stop(); + connection->transport_adapter->Disconnect(connection->device, + connection->application); + } + LOG4CXX_DEBUG(logger_, "event_type = ON_SEND_DONE"); + break; + } + case TransportAdapterListenerImpl::EventTypeEnum::ON_SEND_FAIL: { +#ifdef TIME_TESTER + if (metric_observer_) { + metric_observer_->StopRawMsg(event.event_data.get()); + } +#endif // TIME_TESTER + if (connection == NULL) { + LOG4CXX_ERROR(logger_, "Connection ('" << event.device_uid << ", " + << event.application_id + << ") not found"); + LOG4CXX_DEBUG(logger_, "event_type = ON_SEND_FAIL. Condition: NULL == connection"); + break; + } + + // TODO(YK): start timer here to wait before notify caller + // and remove unsent messages + LOG4CXX_ERROR(logger_, "Transport adapter failed to send data"); + // TODO(YK): potential error case -> thread unsafe + // update of message content + if (event.event_data.valid()) { + event.event_data->set_waiting(true); + } else { + LOG4CXX_DEBUG(logger_, "Data is invalid"); + } + LOG4CXX_DEBUG(logger_, "eevent_type = ON_SEND_FAIL"); + break; + } + case TransportAdapterListenerImpl::EventTypeEnum::ON_RECEIVED_DONE: { + if (connection == NULL) { + LOG4CXX_ERROR(logger_, "Connection ('" << event.device_uid << ", " + << event.application_id + << ") not found"); + LOG4CXX_DEBUG(logger_, "event_type = ON_RECEIVED_DONE. Condition: NULL == connection"); + break; + } + event.event_data->set_connection_key(connection->id); +#ifdef TIME_TESTER + if (metric_observer_) { + metric_observer_->StopRawMsg(event.event_data.get()); + } +#endif // TIME_TESTER + RaiseEvent(&TransportManagerListener::OnTMMessageReceived, event.event_data); + LOG4CXX_DEBUG(logger_, "event_type = ON_RECEIVED_DONE"); + break; + } + case TransportAdapterListenerImpl::EventTypeEnum::ON_RECEIVED_FAIL: { + LOG4CXX_DEBUG(logger_, "Event ON_RECEIVED_FAIL"); + if (connection == NULL) { + LOG4CXX_ERROR(logger_, "Connection ('" << event.device_uid << ", " + << event.application_id + << ") not found"); + break; + } + + RaiseEvent(&TransportManagerListener::OnTMMessageReceiveFailed, + connection->id, *static_cast<DataReceiveError*>(event.event_error.get())); + LOG4CXX_DEBUG(logger_, "event_type = ON_RECEIVED_FAIL"); + break; + } + case TransportAdapterListenerImpl::EventTypeEnum::ON_COMMUNICATION_ERROR: { + LOG4CXX_DEBUG(logger_, "event_type = ON_COMMUNICATION_ERROR"); + break; + } + case TransportAdapterListenerImpl::EventTypeEnum::ON_UNEXPECTED_DISCONNECT: { + if (connection) { + RaiseEvent(&TransportManagerListener::OnUnexpectedDisconnect, + connection->id, + *static_cast<CommunicationError*>(event.event_error.get())); + RemoveConnection(connection->id); + } else { + LOG4CXX_ERROR(logger_, "Connection ('" << event.device_uid << ", " + << event.application_id + << ") not found"); + } + LOG4CXX_DEBUG(logger_, "eevent_type = ON_UNEXPECTED_DISCONNECT"); + break; + } + } // switch +#ifndef USE_RWLOCK + pthread_mutex_lock(&event_queue_mutex_); +#endif + } // while (event_queue_.size() > 0) + + if (all_thread_active_) { + pthread_cond_wait(&device_listener_thread_wakeup_, &event_queue_mutex_); + } else { + LOG4CXX_DEBUG(logger_, "break. Condition: NOT all_thread_active_"); + break; + } + } // while (true) + + pthread_mutex_unlock(&event_queue_mutex_); + + LOG4CXX_TRACE(logger_, "exit"); +} + +#ifdef TIME_TESTER +void TransportManagerImpl::SetTimeMetricObserver(TMMetricObserver* observer) { + metric_observer_ = observer; +} +#endif // TIME_TESTER + +void* TransportManagerImpl::MessageQueueStartThread(void* data) { + LOG4CXX_TRACE(logger_, "enter. Data:" << data); + if (NULL != data) { + static_cast<TransportManagerImpl*>(data)->MessageQueueThread(); + } + LOG4CXX_TRACE(logger_, "exit with 0"); + return 0; +} + +void TransportManagerImpl::MessageQueueThread() { + LOG4CXX_TRACE(logger_, "enter"); +#ifndef USE_RWLOCK + pthread_mutex_lock(&message_queue_mutex_); +#endif + + while (all_thread_active_) { + // TODO(YK): add priority processing + + while (message_queue_.size() > 0) { +#ifdef USE_RWLOCK + message_queue_rwlock_.AcquireForReading(); +#endif + MessageQueue::iterator it = message_queue_.begin(); + while (it != message_queue_.end() && it->valid() && (*it)->IsWaiting()) { + ++it; + } + if (it == message_queue_.end()) { +#ifdef USE_RWLOCK + message_queue_rwlock_.Release(); +#endif + LOG4CXX_TRACE(logger_, "break. Condition: it == message_queue_.end()"); + break; + } + RawMessagePtr active_msg = *it; +#ifdef USE_RWLOCK + message_queue_rwlock_.Release(); +#else + pthread_mutex_unlock(&message_queue_mutex_); +#endif + if (active_msg.valid() && !active_msg->IsWaiting()) { + ConnectionInternal* connection = + GetConnection(active_msg->connection_key()); + if (connection == NULL) { + std::stringstream ss; + ss << "Connection " << active_msg->connection_key() << " not found"; + LOG4CXX_ERROR(logger_, ss.str()); + RaiseEvent(&TransportManagerListener::OnTMMessageSendFailed, + DataSendError(ss.str()), active_msg); + message_queue_.remove(active_msg); + continue; + } + TransportAdapter* transport_adapter = connection->transport_adapter; + LOG4CXX_DEBUG(logger_, "Got adapter " + << transport_adapter << "[" + << transport_adapter->GetDeviceType() << "]" + << " by session id " + << active_msg->connection_key()); + + if (NULL == transport_adapter) { + std::string error_text = + "Transport adapter is not found - message removed"; + LOG4CXX_ERROR(logger_, error_text); + RaiseEvent(&TransportManagerListener::OnTMMessageSendFailed, + DataSendError(error_text), active_msg); + message_queue_.remove(active_msg); + } else { + if (TransportAdapter::OK == + transport_adapter->SendData( + connection->device, connection->application, active_msg)) { + LOG4CXX_INFO(logger_, "Data sent to adapter"); + active_msg->set_waiting(true); + } else { + LOG4CXX_ERROR(logger_, "Data sent error"); + RaiseEvent(&TransportManagerListener::OnTMMessageSendFailed, + DataSendError("Send failed - message removed"), + active_msg); + message_queue_.remove(active_msg); + } + } + } +#ifndef USE_RWLOCK + pthread_mutex_lock(&message_queue_mutex_); +#endif + } + pthread_cond_wait(&message_queue_cond_, &message_queue_mutex_); + } // while(true) + + message_queue_.clear(); + + pthread_mutex_unlock(&message_queue_mutex_); + LOG4CXX_TRACE(logger_, "exit"); +} + +TransportManagerImpl::ConnectionInternal::ConnectionInternal( + TransportManagerImpl* transport_manager, TransportAdapter* transport_adapter, + const ConnectionUID& id, const DeviceUID& dev_id, const ApplicationHandle& app_id, + const DeviceHandle& device_handle) + : transport_manager(transport_manager), + transport_adapter(transport_adapter), + timer(new TimerInternal("TM DiscRoutine", this, &ConnectionInternal::DisconnectFailedRoutine)), + shutDown(false), + device_handle_(device_handle), + messages_count(0) { + Connection::id = id; + Connection::device = dev_id; + Connection::application = app_id; +} + +void TransportManagerImpl::ConnectionInternal::DisconnectFailedRoutine() { + LOG4CXX_TRACE(logger_, "enter"); + transport_manager->RaiseEvent(&TransportManagerListener::OnDisconnectFailed, + device_handle_, DisconnectDeviceError()); + shutDown = false; + timer->stop(); + LOG4CXX_TRACE(logger_, "exit"); +} + +} // namespace transport_manager + + diff --git a/src/components/transport_manager/src/usb/libusb/platform_usb_device.cc b/src/components/transport_manager/src/usb/libusb/platform_usb_device.cc new file mode 100644 index 0000000000..103e03a4f6 --- /dev/null +++ b/src/components/transport_manager/src/usb/libusb/platform_usb_device.cc @@ -0,0 +1,85 @@ +/** + * + * 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" + +#include "utils/logger.h" + +namespace transport_manager { +namespace transport_adapter { + +CREATE_LOGGERPTR_GLOBAL(logger_, "TransportManager") + + +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 { + LOG4CXX_TRACE(logger_, "enter. index: " << int(index)); + 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_ERROR(logger_, "Failed to get USB string descriptor: " + << libusb_error_name(libusb_ret)); + LOG4CXX_TRACE(logger_, "exit with empty string"); + return ""; + } + LOG4CXX_TRACE(logger_, "exit"); + 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/src/components/transport_manager/src/usb/libusb/usb_connection.cc b/src/components/transport_manager/src/usb/libusb/usb_connection.cc new file mode 100644 index 0000000000..76f2f99e4c --- /dev/null +++ b/src/components/transport_manager/src/usb/libusb/usb_connection.cc @@ -0,0 +1,348 @@ +/* + * Copyright (c) 2013-2014, 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 <unistd.h> +#include <iomanip> + +#include <libusb/libusb.h> + +#include <sstream> + +#include "transport_manager/usb/libusb/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& 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_(NULL), + in_transfer_(NULL), + out_transfer_(0), + out_messages_(), + current_out_message_(), + bytes_sent_(0), + disconnecting_(false), + waiting_in_transfer_cancel_(false), + waiting_out_transfer_cancel_(false) { +} + +UsbConnection::~UsbConnection() { + LOG4CXX_TRACE(logger_, "enter with this" << this); + Finalise(); + libusb_free_transfer(in_transfer_); + delete[] in_buffer_; + LOG4CXX_TRACE(logger_, "exit"); +} + +// Callback for handling income and outcome data from lib_usb +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() { + LOG4CXX_TRACE(logger_, "enter"); + 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)); + LOG4CXX_TRACE(logger_, + "exit with FALSE. Condition: LIBUSB_SUCCESS != libusb_submit_transfer"); + return false; + } + LOG4CXX_TRACE(logger_, "exit with TRUE"); + return true; +} + +std::string hex_data(const unsigned char* const buffer, const int actual_length) { + std::ostringstream hexdata; + for (int i = 0; i < actual_length; ++i) { + hexdata << " " << std::hex << std::setw(2) << std::setfill('0') + << int(buffer[i]); + } + return hexdata.str(); +} + +void UsbConnection::OnInTransfer(libusb_transfer* transfer) { + LOG4CXX_TRACE(logger_, "enter with Libusb_transfer*: " << transfer); + if (transfer->status == LIBUSB_TRANSFER_COMPLETED) { + LOG4CXX_DEBUG(logger_, + "USB incoming transfer, size:" << transfer->actual_length + << ", data:" << hex_data(transfer->buffer, transfer->actual_length)); + RawMessagePtr data(new protocol_handler::RawMessage( + 0, 0, in_buffer_, transfer->actual_length)); + controller_->DataReceiveDone(device_uid_, app_handle_, data); + } else { + LOG4CXX_ERROR(logger_, "USB incoming transfer failed: " + << libusb_error_name(transfer->status)); + controller_->DataReceiveFailed(device_uid_, app_handle_, + DataReceiveError()); + } + if (disconnecting_) { + waiting_in_transfer_cancel_ = false; + } else { + if (!PostInTransfer()) { + LOG4CXX_ERROR(logger_, "USB incoming transfer failed with " + << "LIBUSB_TRANSFER_NO_DEVICE. Abort connection."); + AbortConnection(); + } + } + LOG4CXX_TRACE(logger_, "exit"); +} + +void UsbConnection::PopOutMessage() { + LOG4CXX_TRACE(logger_, "enter"); + bytes_sent_ = 0; + if (out_messages_.empty()) { + current_out_message_.reset(); + } else { + current_out_message_ = out_messages_.front(); + out_messages_.pop_front(); + PostOutTransfer(); + } + LOG4CXX_TRACE(logger_, "exit"); +} + +bool UsbConnection::PostOutTransfer() { + LOG4CXX_TRACE(logger_, "enter"); + out_transfer_ = libusb_alloc_transfer(0); + if (0 == out_transfer_) { + LOG4CXX_ERROR(logger_, "libusb_alloc_transfer failed"); + LOG4CXX_TRACE(logger_, "exit with FALSE. Condition: 0 == out_transfer_"); + 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) << ". Abort connection."); + AbortConnection(); + LOG4CXX_TRACE(logger_, "exit with FALSE. Condition: " + << "LIBUSB_SUCCESS != libusb_fill_bulk_transfer"); + return false; + } + LOG4CXX_TRACE(logger_, "exit with TRUE"); + return true; +} + +void UsbConnection::OnOutTransfer(libusb_transfer* transfer) { + LOG4CXX_TRACE(logger_, "enter with Libusb_transfer*: " << transfer); + sync_primitives::AutoLock locker(out_messages_mutex_); + if (transfer->status == LIBUSB_TRANSFER_COMPLETED) { + bytes_sent_ += transfer->actual_length; + if (bytes_sent_ == current_out_message_->data_size()) { + LOG4CXX_DEBUG(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 out transfer failed: " + << libusb_error_name(transfer->status)); + controller_->DataSendFailed(device_uid_, app_handle_, current_out_message_, + DataSendError()); + PopOutMessage(); + } + if (!current_out_message_.valid()) { + libusb_free_transfer(transfer); + out_transfer_ = NULL; + waiting_out_transfer_cancel_ = false; + } + LOG4CXX_TRACE(logger_, "exit"); +} + +TransportAdapter::Error UsbConnection::SendData(RawMessagePtr message) { + LOG4CXX_TRACE(logger_, "enter with RawMessagePtr: " << message.get()); + if (disconnecting_) { + LOG4CXX_TRACE(logger_, "exit with TransportAdapter::BAD_STATE. Condition: " + << "disconnecting_"); + return TransportAdapter::BAD_STATE; + } + sync_primitives::AutoLock locker(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()); + LOG4CXX_TRACE(logger_, "exit with TransportAdapter::FAIL. Condition: !PostOutTransfer()"); + return TransportAdapter::FAIL; + } + } + LOG4CXX_TRACE(logger_, "exit with TransportAdapter::OK."); + return TransportAdapter::OK; +} + +void UsbConnection::Finalise() { + LOG4CXX_TRACE(logger_, "enter"); + LOG4CXX_DEBUG(logger_, "Finalise USB connection " << device_uid_); + { + sync_primitives::AutoLock locker(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<RawMessagePtr>::iterator it = out_messages_.begin(); + it != out_messages_.end(); it = out_messages_.erase(it)) { + controller_->DataSendFailed(device_uid_, app_handle_, *it, DataSendError()); + } + } + while (waiting_in_transfer_cancel_ || waiting_out_transfer_cancel_) { + pthread_yield(); + } + LOG4CXX_TRACE(logger_, "exit"); +} + +void UsbConnection::AbortConnection() { + LOG4CXX_TRACE(logger_, "enter"); + controller_->ConnectionAborted(device_uid_, app_handle_, CommunicationError()); + Disconnect(); + LOG4CXX_TRACE(logger_, "exit"); +} + +TransportAdapter::Error UsbConnection::Disconnect() { + Finalise(); + controller_->DisconnectDone(device_uid_, app_handle_); + return TransportAdapter::OK; +} + +bool UsbConnection::Init() { + LOG4CXX_TRACE(logger_, "enter"); + if (!FindEndpoints()) { + LOG4CXX_ERROR(logger_, "EndPoints was not found"); + LOG4CXX_TRACE(logger_, "exit with FALSE. Condition: !FindEndpoints()"); + return false; + } + in_buffer_ = new unsigned char[in_endpoint_max_packet_size_]; + in_transfer_ = libusb_alloc_transfer(0); + if (NULL == in_transfer_) { + LOG4CXX_ERROR(logger_, "libusb_alloc_transfer failed"); + LOG4CXX_TRACE(logger_, "exit with FALSE. Condition: NULL == in_transfer_"); + return false; + } + + controller_->ConnectDone(device_uid_, app_handle_); + if (!PostInTransfer()) { + LOG4CXX_ERROR(logger_, "PostInTransfer failed. Call ConnectionAborted"); + controller_->ConnectionAborted(device_uid_, app_handle_, + CommunicationError()); + LOG4CXX_TRACE(logger_, "exit with FALSE. Condition: !PostInTransfer()"); + return false; + } + + LOG4CXX_TRACE(logger_, "exit with TRUE"); + return true; +} + +bool UsbConnection::FindEndpoints() { + LOG4CXX_TRACE(logger_, "enter"); + 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)); + LOG4CXX_TRACE(logger_, "exit with FALSE. Condition: LIBUSB_SUCCESS != 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); + + const bool result = !(find_in_endpoint || find_out_endpoint); + LOG4CXX_TRACE(logger_, "exit with " << (result ? "TRUE" : "FALSE")); + return result; +} +} // namespace transport_adapter +} // namespace transport_manager diff --git a/src/components/transport_manager/src/usb/libusb/usb_handler.cc b/src/components/transport_manager/src/usb/libusb/usb_handler.cc new file mode 100644 index 0000000000..eb0ffdb4dc --- /dev/null +++ b/src/components/transport_manager/src/usb/libusb/usb_handler.cc @@ -0,0 +1,474 @@ +/** + * \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" + +#include "utils/logger.h" + +namespace transport_manager { +namespace transport_adapter { + +CREATE_LOGGERPTR_GLOBAL(logger_, "TransportManager") + +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) { + LOG4CXX_TRACE(logger_, "enter. 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); + LOG4CXX_TRACE(logger_, "exit. Condition: LIBUSB_SUCCESS != 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)); + LOG4CXX_TRACE(logger_, "exit. Condition: libusb_ret != LIBUSB_SUCCESS"); + 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)); + LOG4CXX_TRACE(logger_, "exit. Condition: LIBUSB_SUCCESS != 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)); + LOG4CXX_TRACE(logger_, "exit. Condition: LIBUSB_SUCCESS != 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); + LOG4CXX_TRACE(logger_, "exit. Condition: LIBUSB_SUCCESS != libusb_ret"); + 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); + } + LOG4CXX_TRACE(logger_, "exit"); +} + +void UsbHandler::DeviceLeft(libusb_device* device_libusb) { + LOG4CXX_TRACE(logger_, "enter. 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) { + LOG4CXX_TRACE(logger_, "enter. Condition: 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; + LOG4CXX_TRACE(logger_, "exit"); +} + +void UsbHandler::StartControlTransferSequence( + UsbControlTransferSequence* sequence, PlatformUsbDevice* device) { + LOG4CXX_TRACE(logger_, "enter. UsbControlTransferSequence* " << sequence << + "PlatformUsbDevice* " << device); + TransferSequences::iterator it = transfer_sequences_.insert( + transfer_sequences_.end(), + new ControlTransferSequenceState(this, sequence, device)); + SubmitControlTransfer(*it); + LOG4CXX_TRACE(logger_, "exit"); +} + +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_TRACE(logger_, "enter. 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); + LOG4CXX_TRACE(logger_, "exit with 0"); + return 0; +} + +int LeftCallback(libusb_context* context, libusb_device* device, + libusb_hotplug_event event, void* data) { + LOG4CXX_TRACE(logger_, "enter 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); + LOG4CXX_TRACE(logger_, "exit with 0"); + return 0; +} + +TransportAdapter::Error UsbHandler::Init() { + LOG4CXX_TRACE(logger_, "enter"); + int libusb_ret = libusb_init(&libusb_context_); + + if (LIBUSB_SUCCESS != libusb_ret) { + LOG4CXX_ERROR(logger_, "libusb_init failed: " << libusb_ret); + LOG4CXX_TRACE(logger_, + "exit with TransportAdapter::FAIL. Condition: LIBUSB_SUCCESS != libusb_ret"); + return TransportAdapter::FAIL; + } + + if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) { + LOG4CXX_ERROR(logger_, "LIBUSB_CAP_HAS_HOTPLUG not supported"); + LOG4CXX_TRACE(logger_, + "exit with TransportAdapter::FAIL. Condition: !libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)"); + 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); + LOG4CXX_TRACE(logger_, + "exit with TransportAdapter::FAIL. Condition: LIBUSB_SUCCESS != 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); + LOG4CXX_TRACE(logger_, + "exit with TransportAdapter::FAIL. Condition: LIBUSB_SUCCESS != libusb_ret"); + return TransportAdapter::FAIL; + } + + const int thread_start_error = + pthread_create(&thread_, 0, &UsbHandlerThread, this); + if (0 != thread_start_error) { + LOG4CXX_ERROR(logger_, "USB device scanner thread start failed, error code " + << thread_start_error); + LOG4CXX_TRACE(logger_, + "exit with TransportAdapter::FAIL. Condition: 0 !== thread_start_error"); + return TransportAdapter::FAIL; + } + LOG4CXX_INFO(logger_, "UsbHandler thread started"); + pthread_setname_np(thread_, "UsbHandler" ); + LOG4CXX_TRACE(logger_, + "exit with TransportAdapter::OK. Condition: 0 == thread_start_error"); + return TransportAdapter::OK; +} + +void UsbHandler::Thread() { + LOG4CXX_TRACE(logger_, "enter"); + 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); + } + } + LOG4CXX_TRACE(logger_, "exit"); +} + +void UsbTransferSequenceCallback(libusb_transfer* transfer) { + LOG4CXX_TRACE(logger_, "enter. libusb_transfer* " << transfer); + UsbHandler::ControlTransferSequenceState* sequence_state = + static_cast<UsbHandler::ControlTransferSequenceState*>(transfer->user_data); + sequence_state->usb_handler()->ControlTransferCallback(transfer); + LOG4CXX_TRACE(logger_, "exit"); +} + +void UsbHandler::SubmitControlTransfer( + ControlTransferSequenceState* sequence_state) { + LOG4CXX_TRACE(logger_, "enter. ControlTransferSequenceState* " << sequence_state); + UsbControlTransfer* transfer = sequence_state->CurrentTransfer(); + if (NULL == transfer) { + LOG4CXX_TRACE(logger_, "exit. Condition: 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(); + LOG4CXX_TRACE(logger_, "exit. Condition: 0 == libusb_transfer"); + 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(); + LOG4CXX_TRACE(logger_, "exit. Condition: NULL == buffer"); + 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(); + } + LOG4CXX_TRACE(logger_, "exit"); +} + +void UsbHandler::ControlTransferCallback(libusb_transfer* transfer) { + LOG4CXX_TRACE(logger_, "enter. 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); + LOG4CXX_TRACE(logger_, "exit"); +} + +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() { + LOG4CXX_TRACE(logger_, "enter"); + if (finished_) { + LOG4CXX_TRACE(logger_, "exit with NULL. Condition: finished_"); + return NULL; + } + if (++current_transfer_ == sequence_->transfers().end()) { + Finish(); + LOG4CXX_TRACE(logger_, + "exit with NULL. Condition: ++current_transfer_ == sequence_->transfers().end()"); + return NULL; + } else { + LOG4CXX_TRACE(logger_, "exit with UsbControlTransfer* " << *current_transfer_ << + ".Condition: ++current_transfer_ !== sequence_->transfers().end()"); + return *current_transfer_; + } +} + +UsbControlTransfer* UsbHandler::ControlTransferSequenceState::CurrentTransfer() { + return finished_ ? NULL : *current_transfer_; +} + +void UsbHandler::ControlTransferSequenceState::Finish() { + finished_ = true; +} + +} // namespace +} // namespace diff --git a/src/components/transport_manager/src/usb/qnx/platform_usb_device.cc b/src/components/transport_manager/src/usb/qnx/platform_usb_device.cc new file mode 100644 index 0000000000..0f35a16ae7 --- /dev/null +++ b/src/components/transport_manager/src/usb/qnx/platform_usb_device.cc @@ -0,0 +1,79 @@ +/** + * \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" + +#include "utils/logger.h" + +namespace transport_manager { +namespace transport_adapter { + +CREATE_LOGGERPTR_GLOBAL(logger_, "TransportManager") + +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/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 diff --git a/src/components/transport_manager/src/usb/qnx/usb_handler.cc b/src/components/transport_manager/src/usb/qnx/usb_handler.cc new file mode 100644 index 0000000000..7ab36169b1 --- /dev/null +++ b/src/components/transport_manager/src/usb/qnx/usb_handler.cc @@ -0,0 +1,302 @@ +/** + * \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 <errno.h> + +#include <cstring> +#include <cstdlib> + +#include "transport_manager/usb/common.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") + +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/src/components/transport_manager/src/usb/usb_aoa_adapter.cc b/src/components/transport_manager/src/usb/usb_aoa_adapter.cc new file mode 100644 index 0000000000..464b9ea35e --- /dev/null +++ b/src/components/transport_manager/src/usb/usb_aoa_adapter.cc @@ -0,0 +1,90 @@ +/** + * \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" +#include "utils/logger.h" + +namespace transport_manager { +namespace transport_adapter { + +CREATE_LOGGERPTR_GLOBAL(logger_, "TransportManager") +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() { + LOG4CXX_TRACE(logger_, "enter"); + TransportAdapter::Error error = usb_handler_->Init(); + if (error != TransportAdapter::OK) { + LOG4CXX_TRACE(logger_, "exit with error " << error << + ". Condition: error != TransportAdapter::OK"); + return error; + } + error = TransportAdapterImpl::Init(); + if (error != TransportAdapter::OK) { + LOG4CXX_TRACE(logger_, "exit with error " << error << + ". Condition: error != TransportAdapter::OK"); + return error; + } + is_initialised_ = true; + LOG4CXX_TRACE(logger_, "exit with TransportAdapter::OK"); + return TransportAdapter::OK; +} + +bool UsbAoaAdapter::ToBeAutoConnected(DeviceSptr device) const { + return true; +} + +} // namespace transport_adapter +} // namespace transport_manager diff --git a/src/components/transport_manager/src/usb/usb_connection_factory.cc b/src/components/transport_manager/src/usb/usb_connection_factory.cc new file mode 100644 index 0000000000..f68edebd06 --- /dev/null +++ b/src/components/transport_manager/src/usb/usb_connection_factory.cc @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2014, 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" +#include "utils/logger.h" + +#if 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 { + +CREATE_LOGGERPTR_GLOBAL(logger_, "TransportManager") + +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) { + LOG4CXX_TRACE(logger_, "enter DeviceUID: " << &device_uid << ", ApplicationHandle: " << + &app_handle); + DeviceSptr device = controller_->FindDevice(device_uid); + if (!device.valid()) { + LOG4CXX_ERROR(logger_, "device " << device_uid << " not found"); + LOG4CXX_TRACE(logger_, + "exit with TransportAdapter::BAD_PARAM. Condition: !device.valid()"); + 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()) { + LOG4CXX_INFO(logger_, "USB connection initialised"); + LOG4CXX_TRACE(logger_, "exit with TransportAdapter::OK. Condition: USB connection initialised"); + return TransportAdapter::OK; + } else { + LOG4CXX_TRACE(logger_, "exit with TransportAdapter::FAIL. Condition: USB connection NOT initialised"); + return TransportAdapter::FAIL; + } +} + +void UsbConnectionFactory::Terminate() {} + +bool UsbConnectionFactory::IsInitialised() const { + return true; +} + +UsbConnectionFactory::~UsbConnectionFactory() {} + +} // namespace transport_adapter +} // namespace transport_manager diff --git a/src/components/transport_manager/src/usb/usb_device_scanner.cc b/src/components/transport_manager/src/usb/usb_device_scanner.cc new file mode 100644 index 0000000000..bf9535941c --- /dev/null +++ b/src/components/transport_manager/src/usb/usb_device_scanner.cc @@ -0,0 +1,258 @@ +/* + * Copyright (c) 2014, 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" + +#include "utils/logger.h" + +namespace transport_manager { +namespace transport_adapter { + +CREATE_LOGGERPTR_GLOBAL(logger_, "TransportManager") + +class AoaInitSequence : public UsbControlTransferSequence { + public: + AoaInitSequence(); + virtual ~AoaInitSequence() {} + + private: + class AoaGetProtocolRequest; + class AoaSendIdString; + class AoaTurnIntoAccessoryMode; +}; + +void UsbDeviceScanner::OnDeviceArrived(PlatformUsbDevice* device) { + LOG4CXX_TRACE(logger_, "enter. PlatformUsbDevice* " << device); + if (IsAppleDevice(device)) { + SupportedDeviceFound(device); + } else { + if (IsGoogleAccessory(device)) { + SupportedDeviceFound(device); + } else { + TurnIntoAccessoryMode(device); + } + } + LOG4CXX_TRACE(logger_, "exit"); +} + +void UsbDeviceScanner::OnDeviceLeft(PlatformUsbDevice* device) { + LOG4CXX_TRACE(logger_, "enter. 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(); + } + LOG4CXX_TRACE(logger_, "exit"); +} + +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_TRACE(logger_, "enter. PlatformUsbDevice: " << device); + GetUsbHandler()->StartControlTransferSequence(new AoaInitSequence, device); + LOG4CXX_TRACE(logger_, "exit"); +} + +void UsbDeviceScanner::SupportedDeviceFound(PlatformUsbDevice* device) { + LOG4CXX_TRACE(logger_, "enter PlatformUsbDevice: " << device); + + pthread_mutex_lock(&devices_mutex_); + devices_.push_back(device); + pthread_mutex_unlock(&devices_mutex_); + LOG4CXX_INFO(logger_, "USB device (bus number " + << static_cast<int>(device->bus_number()) + << ", address " + << static_cast<int>(device->address()) + << ") identified as: " << device->GetManufacturer() + << ", " << device->GetProductName()); + UpdateList(); + LOG4CXX_TRACE(logger_, "exit"); +} + +TransportAdapter::Error UsbDeviceScanner::Init() { + return TransportAdapter::OK; +} + +TransportAdapter::Error UsbDeviceScanner::Scan() { + return TransportAdapter::OK; +} + +void UsbDeviceScanner::UpdateList() { + LOG4CXX_TRACE(logger_, "enter"); + 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); + LOG4CXX_TRACE(logger_, "exit"); +} + +void UsbDeviceScanner::Terminate() {} + +bool UsbDeviceScanner::IsInitialised() const { + return true; +} + +} // namespace +} // namespace |