Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1581)

Unified Diff: device/serial/serial_io_handler_win.cc

Issue 1439443002: Reland: Add code to deal with serial device disconnection detection on Windows (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: updated code to call thread helper from UI thread Created 5 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: device/serial/serial_io_handler_win.cc
diff --git a/device/serial/serial_io_handler_win.cc b/device/serial/serial_io_handler_win.cc
index cd1045107fe746e285d1bf422297236228339638..e591eb79e42000ace753db7c41f76e282fc6872d 100644
--- a/device/serial/serial_io_handler_win.cc
+++ b/device/serial/serial_io_handler_win.cc
@@ -2,9 +2,16 @@
// 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_monitor_win.h"
+#include "third_party/re2/re2/re2.h"
namespace device {
@@ -130,6 +137,63 @@ serial::StopBits StopBitsConstantToEnum(int stop_bits) {
}
}
+// Wrapper around a HDEVINFO that automatically destroys it.
+class ScopedDeviceInfoList {
Reilly Grant (use Gerrit) 2015/11/11 20:43:59 Let's see if we can get these into //base/win.
juncai 2015/11/12 05:27:08 Done.
Lei Zhang 2015/11/13 01:40:50 You can certainly try, but you'll need to back it
+ 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_;
+};
+
+// 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 +203,86 @@ 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(scoped_ptr<UiThreadHelper> self) {
+ self->thread_checker_.DetachFromThread();
+ DeviceMonitorWin* device_monitor = DeviceMonitorWin::GetForAllInterfaces();
+ if (device_monitor) {
+ self->device_observer_.Add(device_monitor);
+ }
+ // |self| is now owned by the current message loop.
+ ignore_result(self.release());
Reilly Grant (use Gerrit) 2015/11/11 20:43:59 Since this object is not also a MessageLoopDestruc
juncai 2015/11/12 05:27:08 Done.
+ }
+
+ 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::RemoveDevice, 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::RemoveDevice(const std::string& device_path) {
+ DCHECK(CalledOnValidThread());
+ ScopedDeviceInfoList dev_info_list(SetupDiCreateDeviceInfoList(NULL, NULL));
+ if (!dev_info_list.valid()) {
+ VPLOG(1) << "Failed to create a device information set";
+ return;
+ }
+
+ // 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)) {
+ VPLOG(1) << "Failed to get device interface data for " << device_path;
+ return;
+ }
+
+ ScopedDeviceInfo dev_info;
+ if (!SetupDiEnumDeviceInfo(dev_info_list.get(), 0, dev_info.get())) {
+ VPLOG(1) << "Failed to get device info for " << device_path;
+ return;
+ }
+
+ dev_info.set_valid(dev_info_list.get());
+
+ BYTE friendly_name[512];
+ if (!SetupDiGetDeviceRegistryPropertyA(
+ dev_info_list.get(), dev_info.get(), SPDRP_FRIENDLYNAME, NULL,
+ friendly_name, sizeof(friendly_name), NULL)) {
+ VPLOG(1) << "Failed to get device service property";
+ return;
+ }
+
+ std::string port;
+ if (!GetCOMPort(reinterpret_cast<const char*>(friendly_name), &port))
+ return;
+
+ if (port_ == port)
+ CancelRead(serial::RECEIVE_ERROR_SYSTEM_ERROR);
Reilly Grant (use Gerrit) 2015/11/11 20:43:59 s/SYSTEM_ERROR/DISCONNECTED/
juncai 2015/11/12 05:27:08 Done.
+}
+
bool SerialIoHandlerWin::PostOpen() {
DCHECK(!comm_context_);
DCHECK(!read_context_);
@@ -271,10 +415,19 @@ 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) {
+ scoped_refptr<base::SingleThreadTaskRunner> io_thread_task_runner =
+ base::ThreadTaskRunnerHandle::Get();
+ scoped_ptr<UiThreadHelper> helper(
+ new UiThreadHelper(weak_factory_.GetWeakPtr(), io_thread_task_runner));
Reilly Grant (use Gerrit) 2015/11/11 20:43:59 The helper should be created in PostOpen, which sh
juncai 2015/11/12 05:27:08 Done.
+ helper_ = helper.get();
+ ui_thread_task_runner->PostTask(
+ FROM_HERE, base::Bind(&UiThreadHelper::Start, base::Passed(&helper)));
}
SerialIoHandlerWin::~SerialIoHandlerWin() {
+ ui_thread_task_runner_->DeleteSoon(FROM_HERE, helper_);
}
void SerialIoHandlerWin::OnIOCompleted(

Powered by Google App Engine
This is Rietveld 408576698