Chromium Code Reviews| 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..478aadd1f4ff88e172dba0a7fc3d28795f8cf6af 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,83 @@ 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()); |
| + |
| + device::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; |
| + } |
| + |
| + BYTE friendly_name[512]; |
| + if (!SetupDiGetDeviceRegistryPropertyA( |
| + device_info_query.device_info_list(), |
| + device_info_query.device_info_data(), SPDRP_FRIENDLYNAME, NULL, |
| + friendly_name, sizeof(friendly_name), NULL)) { |
| + DVPLOG(1) << "Failed to get device service property"; |
| + return; |
| + } |
| + |
| + std::string com_port; |
| + if (!GetCOMPort(reinterpret_cast<const char*>(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 +250,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 |
| @@ -166,7 +264,7 @@ bool SerialIoHandlerWin::PostOpen() { |
| COMMTIMEOUTS timeouts = {0}; |
| timeouts.ReadIntervalTimeout = MAXDWORD; |
| if (!::SetCommTimeouts(file().GetPlatformFile(), &timeouts)) { |
| - VPLOG(1) << "Failed to set serial timeouts"; |
|
Reilly Grant (use Gerrit)
2015/11/14 01:54:54
Don't change all of these to debug logs as well. T
juncai
2015/11/16 21:10:33
Done.
|
| + DVPLOG(1) << "Failed to set serial timeouts"; |
| return false; |
| } |
| @@ -179,14 +277,14 @@ void SerialIoHandlerWin::ReadImpl() { |
| DCHECK(file().IsValid()); |
| if (!SetCommMask(file().GetPlatformFile(), EV_RXCHAR)) { |
| - VPLOG(1) << "Failed to set serial event flags"; |
| + DVPLOG(1) << "Failed to set serial event flags"; |
| } |
| event_mask_ = 0; |
| BOOL ok = ::WaitCommEvent( |
| file().GetPlatformFile(), &event_mask_, &comm_context_->overlapped); |
| if (!ok && GetLastError() != ERROR_IO_PENDING) { |
| - VPLOG(1) << "Failed to receive serial event"; |
| + DVPLOG(1) << "Failed to receive serial event"; |
| QueueReadCompleted(0, serial::RECEIVE_ERROR_SYSTEM_ERROR); |
| } |
| is_comm_pending_ = true; |
| @@ -203,7 +301,7 @@ void SerialIoHandlerWin::WriteImpl() { |
| NULL, |
| &write_context_->overlapped); |
| if (!ok && GetLastError() != ERROR_IO_PENDING) { |
| - VPLOG(1) << "Write failed"; |
| + DVPLOG(1) << "Write failed"; |
| QueueWriteCompleted(0, serial::SEND_ERROR_SYSTEM_ERROR); |
| } |
| } |
| @@ -224,7 +322,7 @@ bool SerialIoHandlerWin::ConfigurePortImpl() { |
| DCB config = {0}; |
| config.DCBlength = sizeof(config); |
| if (!GetCommState(file().GetPlatformFile(), &config)) { |
| - VPLOG(1) << "Failed to get serial port info"; |
| + DVPLOG(1) << "Failed to get serial port info"; |
| return false; |
| } |
| @@ -260,7 +358,7 @@ bool SerialIoHandlerWin::ConfigurePortImpl() { |
| } |
| if (!SetCommState(file().GetPlatformFile(), &config)) { |
| - VPLOG(1) << "Failed to set serial port info"; |
| + DVPLOG(1) << "Failed to set serial port info"; |
| return false; |
| } |
| return true; |
| @@ -271,10 +369,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( |
| @@ -314,7 +413,7 @@ void SerialIoHandlerWin::OnIOCompleted( |
| NULL, |
| &read_context_->overlapped); |
| if (!ok && GetLastError() != ERROR_IO_PENDING) { |
| - VPLOG(1) << "Read failed"; |
| + DVPLOG(1) << "Read failed"; |
| ReadCompleted(0, serial::RECEIVE_ERROR_SYSTEM_ERROR); |
| } |
| } |
| @@ -359,7 +458,7 @@ void SerialIoHandlerWin::OnIOCompleted( |
| bool SerialIoHandlerWin::Flush() const { |
| if (!PurgeComm(file().GetPlatformFile(), PURGE_RXCLEAR | PURGE_TXCLEAR)) { |
| - VPLOG(1) << "Failed to flush serial port"; |
| + DVPLOG(1) << "Failed to flush serial port"; |
| return false; |
| } |
| return true; |
| @@ -368,7 +467,7 @@ bool SerialIoHandlerWin::Flush() const { |
| serial::DeviceControlSignalsPtr SerialIoHandlerWin::GetControlSignals() const { |
| DWORD status; |
| if (!GetCommModemStatus(file().GetPlatformFile(), &status)) { |
| - VPLOG(1) << "Failed to get port control signals"; |
| + DVPLOG(1) << "Failed to get port control signals"; |
| return serial::DeviceControlSignalsPtr(); |
| } |
| @@ -385,14 +484,14 @@ bool SerialIoHandlerWin::SetControlSignals( |
| if (signals.has_dtr) { |
| if (!EscapeCommFunction(file().GetPlatformFile(), |
| signals.dtr ? SETDTR : CLRDTR)) { |
| - VPLOG(1) << "Failed to configure DTR signal"; |
| + DVPLOG(1) << "Failed to configure DTR signal"; |
| return false; |
| } |
| } |
| if (signals.has_rts) { |
| if (!EscapeCommFunction(file().GetPlatformFile(), |
| signals.rts ? SETRTS : CLRRTS)) { |
| - VPLOG(1) << "Failed to configure RTS signal"; |
| + DVPLOG(1) << "Failed to configure RTS signal"; |
| return false; |
| } |
| } |
| @@ -403,7 +502,7 @@ serial::ConnectionInfoPtr SerialIoHandlerWin::GetPortInfo() const { |
| DCB config = {0}; |
| config.DCBlength = sizeof(config); |
| if (!GetCommState(file().GetPlatformFile(), &config)) { |
| - VPLOG(1) << "Failed to get serial port info"; |
| + DVPLOG(1) << "Failed to get serial port info"; |
| return serial::ConnectionInfoPtr(); |
| } |
| serial::ConnectionInfoPtr info(serial::ConnectionInfo::New()); |
| @@ -417,7 +516,7 @@ serial::ConnectionInfoPtr SerialIoHandlerWin::GetPortInfo() const { |
| bool SerialIoHandlerWin::SetBreak() { |
| if (!SetCommBreak(file().GetPlatformFile())) { |
| - VPLOG(1) << "Failed to set break"; |
| + DVPLOG(1) << "Failed to set break"; |
| return false; |
| } |
| return true; |
| @@ -425,7 +524,7 @@ bool SerialIoHandlerWin::SetBreak() { |
| bool SerialIoHandlerWin::ClearBreak() { |
| if (!ClearCommBreak(file().GetPlatformFile())) { |
| - VPLOG(1) << "Failed to clear break"; |
| + DVPLOG(1) << "Failed to clear break"; |
| return false; |
| } |
| return true; |