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( |