diff options
author | juncai <juncai@chromium.org> | 2015-11-20 21:54:27 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-11-21 05:55:25 +0000 |
commit | 0c9eb038cae670b299c303a5e18547ec85e6e3b0 (patch) | |
tree | f6fcebffd4e64adbb1ad659eeacc0e4897c6a241 /device/serial | |
parent | 1084bdca601104f8eb99fa4ff11cc1397d8dd1af (diff) | |
download | chromium_src-0c9eb038cae670b299c303a5e18547ec85e6e3b0.zip chromium_src-0c9eb038cae670b299c303a5e18547ec85e6e3b0.tar.gz chromium_src-0c9eb038cae670b299c303a5e18547ec85e6e3b0.tar.bz2 |
Add code to deal with serial device disconnection
detection on Windows.
This patch added code to deal with serial device
disconnection detection problem on Windows. It gets the
COM port information from the device path and compare
the COM port information with the port information that
serial io handler holds. If they match, cancel read for
that port.
BUG=361606
Committed: https://crrev.com/195a0f202c1b89540d4a385d881cd483abe757fa
Cr-Commit-Position: refs/heads/master@{#360193}
Committed: https://crrev.com/8d09d9564ea10012d55ed634c0c2835efbd8d01f
Cr-Commit-Position: refs/heads/master@{#360347}
Review URL: https://codereview.chromium.org/1439443002
Cr-Commit-Position: refs/heads/master@{#361009}
Diffstat (limited to 'device/serial')
-rw-r--r-- | device/serial/BUILD.gn | 1 | ||||
-rw-r--r-- | device/serial/serial.gyp | 1 | ||||
-rw-r--r-- | device/serial/serial_io_handler.cc | 1 | ||||
-rw-r--r-- | device/serial/serial_io_handler.h | 12 | ||||
-rw-r--r-- | device/serial/serial_io_handler_win.cc | 103 | ||||
-rw-r--r-- | device/serial/serial_io_handler_win.h | 8 |
6 files changed, 123 insertions, 3 deletions
diff --git a/device/serial/BUILD.gn b/device/serial/BUILD.gn index 1ee6c5f..f66cedf 100644 --- a/device/serial/BUILD.gn +++ b/device/serial/BUILD.gn @@ -45,6 +45,7 @@ static_library("serial") { public_deps = [ ":serial_mojo", "//base", + "//device/core", ] deps = [ "//mojo/public/cpp/system", diff --git a/device/serial/serial.gyp b/device/serial/serial.gyp index 623985f..98635cb 100644 --- a/device/serial/serial.gyp +++ b/device/serial/serial.gyp @@ -51,6 +51,7 @@ '../../net/net.gyp:net', '../../third_party/mojo/mojo_public.gyp:mojo_cpp_bindings', '../../third_party/re2/re2.gyp:re2', + '../core/core.gyp:device_core', ], 'export_dependent_settings': [ 'device_serial_mojo', diff --git a/device/serial/serial_io_handler.cc b/device/serial/serial_io_handler.cc index 452f91c..1cfb1cd 100644 --- a/device/serial/serial_io_handler.cc +++ b/device/serial/serial_io_handler.cc @@ -44,6 +44,7 @@ void SerialIoHandler::Open(const std::string& port, DCHECK(file_thread_task_runner_.get()); DCHECK(ui_thread_task_runner_.get()); MergeConnectionOptions(options); + port_ = port; #if defined(OS_CHROMEOS) chromeos::PermissionBrokerClient* client = diff --git a/device/serial/serial_io_handler.h b/device/serial/serial_io_handler.h index 81d7028..63e6c90 100644 --- a/device/serial/serial_io_handler.h +++ b/device/serial/serial_io_handler.h @@ -190,6 +190,16 @@ class SerialIoHandler : public base::NonThreadSafe, // Possibly fixes up a serial port path name in a platform-specific manner. static std::string MaybeFixUpPortName(const std::string& port_name); + base::SingleThreadTaskRunner* file_thread_task_runner() const { + return file_thread_task_runner_.get(); + } + + base::SingleThreadTaskRunner* ui_thread_task_runner() const { + return ui_thread_task_runner_.get(); + } + + const std::string& port() const { return port_; } + private: friend class base::RefCounted<SerialIoHandler>; @@ -229,6 +239,8 @@ class SerialIoHandler : public base::NonThreadSafe, // On Chrome OS, PermissionBrokerClient should be called on the UI thread. scoped_refptr<base::SingleThreadTaskRunner> ui_thread_task_runner_; + std::string port_; + DISALLOW_COPY_AND_ASSIGN(SerialIoHandler); }; diff --git a/device/serial/serial_io_handler_win.cc b/device/serial/serial_io_handler_win.cc index 56fe93b..2a1e752 100644 --- a/device/serial/serial_io_handler_win.cc +++ b/device/serial/serial_io_handler_win.cc @@ -2,9 +2,17 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "device/serial/serial_io_handler_win.h" + #include <windows.h> +#include <setupapi.h> -#include "device/serial/serial_io_handler_win.h" +#include "base/bind.h" +#include "base/scoped_observer.h" +#include "base/threading/thread_checker.h" +#include "device/core/device_info_query_win.h" +#include "device/core/device_monitor_win.h" +#include "third_party/re2/re2/re2.h" namespace device { @@ -130,6 +138,12 @@ serial::StopBits StopBitsConstantToEnum(int stop_bits) { } } +// Searches for the COM port in the device's friendly name, assigns its value to +// com_port, and returns whether the operation was successful. +bool GetCOMPort(const std::string friendly_name, std::string* com_port) { + return RE2::PartialMatch(friendly_name, ".* \\((COM[0-9]+)\\)", com_port); +} + } // namespace // static @@ -139,6 +153,81 @@ scoped_refptr<SerialIoHandler> SerialIoHandler::Create( return new SerialIoHandlerWin(file_thread_task_runner, ui_thread_task_runner); } +class SerialIoHandlerWin::UiThreadHelper : public DeviceMonitorWin::Observer { + public: + UiThreadHelper( + base::WeakPtr<SerialIoHandlerWin> io_handler, + scoped_refptr<base::SingleThreadTaskRunner> io_thread_task_runner) + : device_observer_(this), + io_handler_(io_handler), + io_thread_task_runner_(io_thread_task_runner) {} + + ~UiThreadHelper() { DCHECK(thread_checker_.CalledOnValidThread()); } + + static void Start(UiThreadHelper* self) { + self->thread_checker_.DetachFromThread(); + DeviceMonitorWin* device_monitor = DeviceMonitorWin::GetForAllInterfaces(); + if (device_monitor) + self->device_observer_.Add(device_monitor); + } + + private: + // DeviceMonitorWin::Observer + void OnDeviceRemoved(const GUID& class_guid, + const std::string& device_path) override { + DCHECK(thread_checker_.CalledOnValidThread()); + io_thread_task_runner_->PostTask( + FROM_HERE, base::Bind(&SerialIoHandlerWin::OnDeviceRemoved, io_handler_, + device_path)); + } + + base::ThreadChecker thread_checker_; + ScopedObserver<DeviceMonitorWin, DeviceMonitorWin::Observer> device_observer_; + + // This weak pointer is only valid when checked on this task runner. + base::WeakPtr<SerialIoHandlerWin> io_handler_; + scoped_refptr<base::SingleThreadTaskRunner> io_thread_task_runner_; + + DISALLOW_COPY_AND_ASSIGN(UiThreadHelper); +}; + +void SerialIoHandlerWin::OnDeviceRemoved(const std::string& device_path) { + DCHECK(CalledOnValidThread()); + + DeviceInfoQueryWin device_info_query; + if (!device_info_query.device_info_list_valid()) { + DVPLOG(1) << "Failed to create a device information set"; + return; + } + + // This will add the device so we can query driver info. + if (!device_info_query.AddDevice(device_path.c_str())) { + DVPLOG(1) << "Failed to get device interface data for " << device_path; + return; + } + + if (!device_info_query.GetDeviceInfo()) { + DVPLOG(1) << "Failed to get device info for " << device_path; + return; + } + + std::string friendly_name; + if (!device_info_query.GetDeviceStringProperty(SPDRP_FRIENDLYNAME, + &friendly_name)) { + DVPLOG(1) << "Failed to get device service property"; + return; + } + + std::string com_port; + if (!GetCOMPort(friendly_name, &com_port)) { + DVPLOG(1) << "Failed to get port name from \"" << friendly_name << "\"."; + return; + } + + if (port() == com_port) + CancelRead(serial::RECEIVE_ERROR_DISCONNECTED); +} + bool SerialIoHandlerWin::PostOpen() { DCHECK(!comm_context_); DCHECK(!read_context_); @@ -159,6 +248,13 @@ bool SerialIoHandlerWin::PostOpen() { write_context_->handler = this; memset(&write_context_->overlapped, 0, sizeof(write_context_->overlapped)); + scoped_refptr<base::SingleThreadTaskRunner> io_thread_task_runner = + base::ThreadTaskRunnerHandle::Get(); + helper_ = + new UiThreadHelper(weak_factory_.GetWeakPtr(), io_thread_task_runner); + ui_thread_task_runner()->PostTask( + FROM_HERE, base::Bind(&UiThreadHelper::Start, helper_)); + // A ReadIntervalTimeout of MAXDWORD will cause async reads to complete // immediately with any data that's available, even if there is none. // This is OK because we never issue a read request until WaitCommEvent @@ -271,10 +367,11 @@ SerialIoHandlerWin::SerialIoHandlerWin( scoped_refptr<base::SingleThreadTaskRunner> ui_thread_task_runner) : SerialIoHandler(file_thread_task_runner, ui_thread_task_runner), event_mask_(0), - is_comm_pending_(false) { -} + is_comm_pending_(false), + weak_factory_(this) {} SerialIoHandlerWin::~SerialIoHandlerWin() { + ui_thread_task_runner()->DeleteSoon(FROM_HERE, helper_); } void SerialIoHandlerWin::OnIOCompleted( diff --git a/device/serial/serial_io_handler_win.h b/device/serial/serial_io_handler_win.h index 7f438b5..831eab6 100644 --- a/device/serial/serial_io_handler_win.h +++ b/device/serial/serial_io_handler_win.h @@ -32,6 +32,7 @@ class SerialIoHandlerWin : public SerialIoHandler, bool PostOpen() override; private: + class UiThreadHelper; friend class SerialIoHandler; explicit SerialIoHandlerWin( @@ -44,6 +45,8 @@ class SerialIoHandlerWin : public SerialIoHandler, DWORD bytes_transfered, DWORD error) override; + void OnDeviceRemoved(const std::string& device_path); + // Context used for asynchronous WaitCommEvent calls. scoped_ptr<base::MessageLoopForIO::IOContext> comm_context_; @@ -61,6 +64,11 @@ class SerialIoHandlerWin : public SerialIoHandler, // after a corresponding WaitCommEvent has completed. bool is_comm_pending_; + // The helper lives on the UI thread and holds a weak reference back to the + // handler that owns it. + UiThreadHelper* helper_ = nullptr; + base::WeakPtrFactory<SerialIoHandlerWin> weak_factory_; + DISALLOW_COPY_AND_ASSIGN(SerialIoHandlerWin); }; |