summaryrefslogtreecommitdiffstats
path: root/device/serial
diff options
context:
space:
mode:
authorjuncai <juncai@chromium.org>2015-11-20 21:54:27 -0800
committerCommit bot <commit-bot@chromium.org>2015-11-21 05:55:25 +0000
commit0c9eb038cae670b299c303a5e18547ec85e6e3b0 (patch)
treef6fcebffd4e64adbb1ad659eeacc0e4897c6a241 /device/serial
parent1084bdca601104f8eb99fa4ff11cc1397d8dd1af (diff)
downloadchromium_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.gn1
-rw-r--r--device/serial/serial.gyp1
-rw-r--r--device/serial/serial_io_handler.cc1
-rw-r--r--device/serial/serial_io_handler.h12
-rw-r--r--device/serial/serial_io_handler_win.cc103
-rw-r--r--device/serial/serial_io_handler_win.h8
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);
};