summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjuncai <juncai@chromium.org>2015-11-18 08:50:01 -0800
committerCommit bot <commit-bot@chromium.org>2015-11-18 16:50:57 +0000
commit8d09d9564ea10012d55ed634c0c2835efbd8d01f (patch)
tree65ea0e042bd47a3a9b57f3019c8e8532d0577a35
parentcf1af6314b8732015b8474badce1d15bef4cfada (diff)
downloadchromium_src-8d09d9564ea10012d55ed634c0c2835efbd8d01f.zip
chromium_src-8d09d9564ea10012d55ed634c0c2835efbd8d01f.tar.gz
chromium_src-8d09d9564ea10012d55ed634c0c2835efbd8d01f.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} Review URL: https://codereview.chromium.org/1439443002 Cr-Commit-Position: refs/heads/master@{#360347}
-rw-r--r--device/core/BUILD.gn2
-rw-r--r--device/core/core.gyp2
-rw-r--r--device/core/device_info_query_win.cc66
-rw-r--r--device/core/device_info_query_win.h49
-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
-rw-r--r--device/usb/usb.gyp1
-rw-r--r--device/usb/usb_service_impl.cc80
12 files changed, 252 insertions, 74 deletions
diff --git a/device/core/BUILD.gn b/device/core/BUILD.gn
index e85acb6..6f55190 100644
--- a/device/core/BUILD.gn
+++ b/device/core/BUILD.gn
@@ -8,6 +8,8 @@ component("core") {
sources = [
"device_client.cc",
"device_client.h",
+ "device_info_query_win.cc",
+ "device_info_query_win.h",
"device_monitor_win.cc",
"device_monitor_win.h",
]
diff --git a/device/core/core.gyp b/device/core/core.gyp
index d27073d..6002a5f 100644
--- a/device/core/core.gyp
+++ b/device/core/core.gyp
@@ -19,6 +19,8 @@
'sources': [
'device_client.cc',
'device_client.h',
+ 'device_info_query_win.cc',
+ 'device_info_query_win.h',
'device_monitor_win.cc',
'device_monitor_win.h',
],
diff --git a/device/core/device_info_query_win.cc b/device/core/device_info_query_win.cc
new file mode 100644
index 0000000..4d62b32
--- /dev/null
+++ b/device/core/device_info_query_win.cc
@@ -0,0 +1,66 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/core/device_info_query_win.h"
+
+#include <string.h>
+
+#include "base/strings/string_util.h"
+
+namespace device {
+
+DeviceInfoQueryWin::DeviceInfoQueryWin()
+ : device_info_list_(SetupDiCreateDeviceInfoList(nullptr, nullptr)) {
+ memset(&device_info_data_, 0, sizeof(device_info_data_));
+}
+
+DeviceInfoQueryWin::~DeviceInfoQueryWin() {
+ if (device_info_list_valid()) {
+ // Release |device_info_data_| only when it is valid.
+ if (device_info_data_.cbSize != 0)
+ SetupDiDeleteDeviceInfo(device_info_list_, &device_info_data_);
+ SetupDiDestroyDeviceInfoList(device_info_list_);
+ }
+}
+
+bool DeviceInfoQueryWin::AddDevice(const char* device_path) {
+ return SetupDiOpenDeviceInterfaceA(device_info_list_, device_path, 0,
+ nullptr) != FALSE;
+}
+
+bool DeviceInfoQueryWin::GetDeviceInfo() {
+ DCHECK_EQ(0U, device_info_data_.cbSize);
+ device_info_data_.cbSize = sizeof(device_info_data_);
+ if (!SetupDiEnumDeviceInfo(device_info_list_, 0, &device_info_data_)) {
+ // Clear cbSize to maintain the invariant.
+ device_info_data_.cbSize = 0;
+ return false;
+ }
+ return true;
+}
+
+bool DeviceInfoQueryWin::GetDeviceStringProperty(DWORD property,
+ std::string* property_buffer) {
+ DWORD property_reg_data_type;
+ const size_t property_buffer_length = 512;
+ if (!SetupDiGetDeviceRegistryPropertyA(
+ device_info_list_, &device_info_data_, property,
+ &property_reg_data_type,
+ reinterpret_cast<PBYTE>(
+ base::WriteInto(property_buffer, property_buffer_length)),
+ static_cast<DWORD>(property_buffer_length), nullptr))
+ return false;
+
+ if (property_reg_data_type != REG_SZ)
+ return false;
+
+ // Shrink |property_buffer| down to its correct size.
+ size_t eos = property_buffer->find('\0');
+ if (eos != std::string::npos)
+ property_buffer->resize(eos);
+
+ return true;
+}
+
+} // namespace device
diff --git a/device/core/device_info_query_win.h b/device/core/device_info_query_win.h
new file mode 100644
index 0000000..2e399e4
--- /dev/null
+++ b/device/core/device_info_query_win.h
@@ -0,0 +1,49 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_CORE_DEVICE_INFO_QUERY_WIN_H_
+#define DEVICE_CORE_DEVICE_INFO_QUERY_WIN_H_
+
+#include <windows.h>
+#include <setupapi.h>
+
+#include <string>
+
+#include "base/macros.h"
+#include "device/core/device_core_export.h"
+
+namespace device {
+
+// Wraps HDEVINFO and SP_DEVINFO_DATA into a class that can automatically
+// release them. Provides interfaces that can add a device using its
+// device path, get device info and get device string property.
+class DEVICE_CORE_EXPORT DeviceInfoQueryWin {
+ public:
+ DeviceInfoQueryWin();
+ ~DeviceInfoQueryWin();
+
+ // Add a device to |device_info_list_| using its |device_path| so that
+ // its device info can be retrieved.
+ bool AddDevice(const char* device_path);
+ // Get the device info and store it into |device_info_data_|, this function
+ // should be called at most once.
+ bool GetDeviceInfo();
+ // Get device string property and store it into |property_buffer|.
+ bool GetDeviceStringProperty(DWORD property, std::string* property_buffer);
+
+ bool device_info_list_valid() {
+ return device_info_list_ != INVALID_HANDLE_VALUE;
+ }
+
+ private:
+ HDEVINFO device_info_list_ = INVALID_HANDLE_VALUE;
+ // When device_info_data_.cbSize != 0, |device_info_data_| is valid.
+ SP_DEVINFO_DATA device_info_data_;
+
+ DISALLOW_COPY_AND_ASSIGN(DeviceInfoQueryWin);
+};
+
+} // namespace device
+
+#endif // DEVICE_CORE_DEVICE_INFO_QUERY_WIN_H_
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 de18133..0e56df1 100644
--- a/device/serial/serial.gyp
+++ b/device/serial/serial.gyp
@@ -50,6 +50,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);
};
diff --git a/device/usb/usb.gyp b/device/usb/usb.gyp
index c7bf776..453ba1a 100644
--- a/device/usb/usb.gyp
+++ b/device/usb/usb.gyp
@@ -14,6 +14,7 @@
'../../components/components.gyp:device_event_log_component',
'../../net/net.gyp:net',
'../../third_party/libusb/libusb.gyp:libusb',
+ '../core/core.gyp:device_core',
],
'include_dirs': [
'../..',
diff --git a/device/usb/usb_service_impl.cc b/device/usb/usb_service_impl.cc
index dd5cc90..9770007 100644
--- a/device/usb/usb_service_impl.cc
+++ b/device/usb/usb_service_impl.cc
@@ -27,6 +27,7 @@
#include <usbiodef.h>
#include "base/strings/string_util.h"
+#include "device/core/device_info_query_win.h"
#endif // OS_WIN
#if defined(USE_UDEV)
@@ -53,96 +54,33 @@ const int kControlTransferTimeout = 60000; // 1 minute
#if defined(OS_WIN)
-// Wrapper around a HDEVINFO that automatically destroys it.
-class ScopedDeviceInfoList {
- public:
- explicit ScopedDeviceInfoList(HDEVINFO handle) : handle_(handle) {}
-
- ~ScopedDeviceInfoList() {
- if (valid()) {
- SetupDiDestroyDeviceInfoList(handle_);
- }
- }
-
- bool valid() { return handle_ != INVALID_HANDLE_VALUE; }
-
- HDEVINFO get() { return handle_; }
-
- private:
- HDEVINFO handle_;
-
- DISALLOW_COPY_AND_ASSIGN(ScopedDeviceInfoList);
-};
-
-// Wrapper around an SP_DEVINFO_DATA that initializes it properly and
-// automatically deletes it.
-class ScopedDeviceInfo {
- public:
- ScopedDeviceInfo() {
- memset(&dev_info_data_, 0, sizeof(dev_info_data_));
- dev_info_data_.cbSize = sizeof(dev_info_data_);
- }
-
- ~ScopedDeviceInfo() {
- if (dev_info_set_ != INVALID_HANDLE_VALUE) {
- SetupDiDeleteDeviceInfo(dev_info_set_, &dev_info_data_);
- }
- }
-
- // Once the SP_DEVINFO_DATA has been populated it must be freed using the
- // HDEVINFO it was created from.
- void set_valid(HDEVINFO dev_info_set) {
- DCHECK(dev_info_set_ == INVALID_HANDLE_VALUE);
- DCHECK(dev_info_set != INVALID_HANDLE_VALUE);
- dev_info_set_ = dev_info_set;
- }
-
- PSP_DEVINFO_DATA get() { return &dev_info_data_; }
-
- private:
- HDEVINFO dev_info_set_ = INVALID_HANDLE_VALUE;
- SP_DEVINFO_DATA dev_info_data_;
-};
-
bool IsWinUsbInterface(const std::string& device_path) {
- ScopedDeviceInfoList dev_info_list(SetupDiCreateDeviceInfoList(NULL, NULL));
- if (!dev_info_list.valid()) {
+ DeviceInfoQueryWin device_info_query;
+ if (!device_info_query.device_info_list_valid()) {
USB_PLOG(ERROR) << "Failed to create a device information set";
return false;
}
- // This will add the device to |dev_info_list| so we can query driver info.
- if (!SetupDiOpenDeviceInterfaceA(dev_info_list.get(), device_path.c_str(), 0,
- NULL)) {
+ // This will add the device so we can query driver info.
+ if (!device_info_query.AddDevice(device_path.c_str())) {
USB_PLOG(ERROR) << "Failed to get device interface data for "
<< device_path;
return false;
}
- ScopedDeviceInfo dev_info;
- if (!SetupDiEnumDeviceInfo(dev_info_list.get(), 0, dev_info.get())) {
+ if (!device_info_query.GetDeviceInfo()) {
USB_PLOG(ERROR) << "Failed to get device info for " << device_path;
return false;
}
- dev_info.set_valid(dev_info_list.get());
- DWORD reg_data_type;
- BYTE buffer[256];
- if (!SetupDiGetDeviceRegistryPropertyA(dev_info_list.get(), dev_info.get(),
- SPDRP_SERVICE, &reg_data_type,
- &buffer[0], sizeof buffer, NULL)) {
+ std::string buffer;
+ if (!device_info_query.GetDeviceStringProperty(SPDRP_SERVICE, &buffer)) {
USB_PLOG(ERROR) << "Failed to get device service property";
return false;
}
- if (reg_data_type != REG_SZ) {
- USB_LOG(ERROR) << "Unexpected data type for driver service: "
- << reg_data_type;
- return false;
- }
USB_LOG(DEBUG) << "Driver for " << device_path << " is " << buffer << ".";
- if (base::StartsWith(reinterpret_cast<const char*>(buffer), "WinUSB",
- base::CompareCase::INSENSITIVE_ASCII))
+ if (base::StartsWith(buffer, "WinUSB", base::CompareCase::INSENSITIVE_ASCII))
return true;
return false;
}