OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
| 5 #include "device/serial/serial_io_handler_win.h" |
| 6 |
5 #include <windows.h> | 7 #include <windows.h> |
| 8 #include <setupapi.h> |
6 | 9 |
7 #include "device/serial/serial_io_handler_win.h" | 10 #include "base/bind.h" |
| 11 #include "base/scoped_observer.h" |
| 12 #include "base/threading/thread_checker.h" |
| 13 #include "device/core/device_monitor_win.h" |
| 14 #include "device/core/scoped_device_info_list.h" |
| 15 #include "device/core/scoped_device_info_object.h" |
| 16 #include "third_party/re2/re2/re2.h" |
8 | 17 |
9 namespace device { | 18 namespace device { |
10 | 19 |
11 namespace { | 20 namespace { |
12 | 21 |
13 int BitrateToSpeedConstant(int bitrate) { | 22 int BitrateToSpeedConstant(int bitrate) { |
14 #define BITRATE_TO_SPEED_CASE(x) \ | 23 #define BITRATE_TO_SPEED_CASE(x) \ |
15 case x: \ | 24 case x: \ |
16 return CBR_##x; | 25 return CBR_##x; |
17 switch (bitrate) { | 26 switch (bitrate) { |
(...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
123 serial::StopBits StopBitsConstantToEnum(int stop_bits) { | 132 serial::StopBits StopBitsConstantToEnum(int stop_bits) { |
124 switch (stop_bits) { | 133 switch (stop_bits) { |
125 case TWOSTOPBITS: | 134 case TWOSTOPBITS: |
126 return serial::STOP_BITS_TWO; | 135 return serial::STOP_BITS_TWO; |
127 case ONESTOPBIT: | 136 case ONESTOPBIT: |
128 default: | 137 default: |
129 return serial::STOP_BITS_ONE; | 138 return serial::STOP_BITS_ONE; |
130 } | 139 } |
131 } | 140 } |
132 | 141 |
| 142 // Searches for the COM port in the device's friendly name, assigns its value to |
| 143 // com_port, and returns whether the operation was successful. |
| 144 bool GetCOMPort(const std::string friendly_name, std::string* com_port) { |
| 145 return RE2::PartialMatch(friendly_name, ".* \\((COM[0-9]+)\\)", com_port); |
| 146 } |
| 147 |
133 } // namespace | 148 } // namespace |
134 | 149 |
135 // static | 150 // static |
136 scoped_refptr<SerialIoHandler> SerialIoHandler::Create( | 151 scoped_refptr<SerialIoHandler> SerialIoHandler::Create( |
137 scoped_refptr<base::SingleThreadTaskRunner> file_thread_task_runner, | 152 scoped_refptr<base::SingleThreadTaskRunner> file_thread_task_runner, |
138 scoped_refptr<base::SingleThreadTaskRunner> ui_thread_task_runner) { | 153 scoped_refptr<base::SingleThreadTaskRunner> ui_thread_task_runner) { |
139 return new SerialIoHandlerWin(file_thread_task_runner, ui_thread_task_runner); | 154 return new SerialIoHandlerWin(file_thread_task_runner, ui_thread_task_runner); |
140 } | 155 } |
141 | 156 |
| 157 class SerialIoHandlerWin::UiThreadHelper : public DeviceMonitorWin::Observer { |
| 158 public: |
| 159 UiThreadHelper( |
| 160 base::WeakPtr<SerialIoHandlerWin> io_handler, |
| 161 scoped_refptr<base::SingleThreadTaskRunner> io_thread_task_runner) |
| 162 : device_observer_(this), |
| 163 io_handler_(io_handler), |
| 164 io_thread_task_runner_(io_thread_task_runner) {} |
| 165 |
| 166 ~UiThreadHelper() { DCHECK(thread_checker_.CalledOnValidThread()); } |
| 167 |
| 168 static void Start(UiThreadHelper* self) { |
| 169 self->thread_checker_.DetachFromThread(); |
| 170 DeviceMonitorWin* device_monitor = DeviceMonitorWin::GetForAllInterfaces(); |
| 171 if (device_monitor) |
| 172 self->device_observer_.Add(device_monitor); |
| 173 } |
| 174 |
| 175 private: |
| 176 // DeviceMonitorWin::Observer |
| 177 void OnDeviceRemoved(const GUID& class_guid, |
| 178 const std::string& device_path) override { |
| 179 DCHECK(thread_checker_.CalledOnValidThread()); |
| 180 io_thread_task_runner_->PostTask( |
| 181 FROM_HERE, base::Bind(&SerialIoHandlerWin::OnDeviceRemoved, io_handler_, |
| 182 device_path)); |
| 183 } |
| 184 |
| 185 base::ThreadChecker thread_checker_; |
| 186 ScopedObserver<DeviceMonitorWin, DeviceMonitorWin::Observer> device_observer_; |
| 187 |
| 188 // This weak pointer is only valid when checked on this task runner. |
| 189 base::WeakPtr<SerialIoHandlerWin> io_handler_; |
| 190 scoped_refptr<base::SingleThreadTaskRunner> io_thread_task_runner_; |
| 191 |
| 192 DISALLOW_COPY_AND_ASSIGN(UiThreadHelper); |
| 193 }; |
| 194 |
| 195 void SerialIoHandlerWin::OnDeviceRemoved(const std::string& device_path) { |
| 196 DCHECK(CalledOnValidThread()); |
| 197 base::win::ScopedDeviceInfoList dev_info_list( |
| 198 SetupDiCreateDeviceInfoList(NULL, NULL)); |
| 199 if (!dev_info_list.IsValid()) { |
| 200 VPLOG(1) << "Failed to create a device information set"; |
| 201 return; |
| 202 } |
| 203 |
| 204 // This will add the device to |dev_info_list| so we can query driver info. |
| 205 if (!SetupDiOpenDeviceInterfaceA(dev_info_list.Get(), device_path.c_str(), 0, |
| 206 NULL)) { |
| 207 VPLOG(1) << "Failed to get device interface data for " << device_path; |
| 208 return; |
| 209 } |
| 210 |
| 211 base::win::ScopedDeviceInfo dev_info; |
| 212 if (!SetupDiEnumDeviceInfo(dev_info_list.Get(), 0, dev_info.get())) { |
| 213 VPLOG(1) << "Failed to get device info for " << device_path; |
| 214 return; |
| 215 } |
| 216 |
| 217 dev_info.set_valid(dev_info_list.Get()); |
| 218 |
| 219 BYTE friendly_name[512]; |
| 220 if (!SetupDiGetDeviceRegistryPropertyA( |
| 221 dev_info_list.Get(), dev_info.get(), SPDRP_FRIENDLYNAME, NULL, |
| 222 friendly_name, sizeof(friendly_name), NULL)) { |
| 223 VPLOG(1) << "Failed to get device service property"; |
| 224 return; |
| 225 } |
| 226 |
| 227 std::string port; |
| 228 if (!GetCOMPort(reinterpret_cast<const char*>(friendly_name), &port)) { |
| 229 VPLOG(1) << "Failed to get port name from \"" << friendly_name << "\"."; |
| 230 return; |
| 231 } |
| 232 |
| 233 if (port_ == port) |
| 234 CancelRead(serial::RECEIVE_ERROR_DISCONNECTED); |
| 235 } |
| 236 |
142 bool SerialIoHandlerWin::PostOpen() { | 237 bool SerialIoHandlerWin::PostOpen() { |
143 DCHECK(!comm_context_); | 238 DCHECK(!comm_context_); |
144 DCHECK(!read_context_); | 239 DCHECK(!read_context_); |
145 DCHECK(!write_context_); | 240 DCHECK(!write_context_); |
146 | 241 |
147 base::MessageLoopForIO::current()->RegisterIOHandler(file().GetPlatformFile(), | 242 base::MessageLoopForIO::current()->RegisterIOHandler(file().GetPlatformFile(), |
148 this); | 243 this); |
149 | 244 |
150 comm_context_.reset(new base::MessageLoopForIO::IOContext()); | 245 comm_context_.reset(new base::MessageLoopForIO::IOContext()); |
151 comm_context_->handler = this; | 246 comm_context_->handler = this; |
152 memset(&comm_context_->overlapped, 0, sizeof(comm_context_->overlapped)); | 247 memset(&comm_context_->overlapped, 0, sizeof(comm_context_->overlapped)); |
153 | 248 |
154 read_context_.reset(new base::MessageLoopForIO::IOContext()); | 249 read_context_.reset(new base::MessageLoopForIO::IOContext()); |
155 read_context_->handler = this; | 250 read_context_->handler = this; |
156 memset(&read_context_->overlapped, 0, sizeof(read_context_->overlapped)); | 251 memset(&read_context_->overlapped, 0, sizeof(read_context_->overlapped)); |
157 | 252 |
158 write_context_.reset(new base::MessageLoopForIO::IOContext()); | 253 write_context_.reset(new base::MessageLoopForIO::IOContext()); |
159 write_context_->handler = this; | 254 write_context_->handler = this; |
160 memset(&write_context_->overlapped, 0, sizeof(write_context_->overlapped)); | 255 memset(&write_context_->overlapped, 0, sizeof(write_context_->overlapped)); |
161 | 256 |
| 257 scoped_refptr<base::SingleThreadTaskRunner> io_thread_task_runner = |
| 258 base::ThreadTaskRunnerHandle::Get(); |
| 259 helper_ = |
| 260 new UiThreadHelper(weak_factory_.GetWeakPtr(), io_thread_task_runner); |
| 261 ui_thread_task_runner_->PostTask(FROM_HERE, |
| 262 base::Bind(&UiThreadHelper::Start, helper_)); |
| 263 |
162 // A ReadIntervalTimeout of MAXDWORD will cause async reads to complete | 264 // A ReadIntervalTimeout of MAXDWORD will cause async reads to complete |
163 // immediately with any data that's available, even if there is none. | 265 // immediately with any data that's available, even if there is none. |
164 // This is OK because we never issue a read request until WaitCommEvent | 266 // This is OK because we never issue a read request until WaitCommEvent |
165 // signals that data is available. | 267 // signals that data is available. |
166 COMMTIMEOUTS timeouts = {0}; | 268 COMMTIMEOUTS timeouts = {0}; |
167 timeouts.ReadIntervalTimeout = MAXDWORD; | 269 timeouts.ReadIntervalTimeout = MAXDWORD; |
168 if (!::SetCommTimeouts(file().GetPlatformFile(), &timeouts)) { | 270 if (!::SetCommTimeouts(file().GetPlatformFile(), &timeouts)) { |
169 VPLOG(1) << "Failed to set serial timeouts"; | 271 VPLOG(1) << "Failed to set serial timeouts"; |
170 return false; | 272 return false; |
171 } | 273 } |
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
264 return false; | 366 return false; |
265 } | 367 } |
266 return true; | 368 return true; |
267 } | 369 } |
268 | 370 |
269 SerialIoHandlerWin::SerialIoHandlerWin( | 371 SerialIoHandlerWin::SerialIoHandlerWin( |
270 scoped_refptr<base::SingleThreadTaskRunner> file_thread_task_runner, | 372 scoped_refptr<base::SingleThreadTaskRunner> file_thread_task_runner, |
271 scoped_refptr<base::SingleThreadTaskRunner> ui_thread_task_runner) | 373 scoped_refptr<base::SingleThreadTaskRunner> ui_thread_task_runner) |
272 : SerialIoHandler(file_thread_task_runner, ui_thread_task_runner), | 374 : SerialIoHandler(file_thread_task_runner, ui_thread_task_runner), |
273 event_mask_(0), | 375 event_mask_(0), |
274 is_comm_pending_(false) { | 376 is_comm_pending_(false), |
275 } | 377 weak_factory_(this) {} |
276 | 378 |
277 SerialIoHandlerWin::~SerialIoHandlerWin() { | 379 SerialIoHandlerWin::~SerialIoHandlerWin() { |
| 380 ui_thread_task_runner_->DeleteSoon(FROM_HERE, helper_); |
278 } | 381 } |
279 | 382 |
280 void SerialIoHandlerWin::OnIOCompleted( | 383 void SerialIoHandlerWin::OnIOCompleted( |
281 base::MessageLoopForIO::IOContext* context, | 384 base::MessageLoopForIO::IOContext* context, |
282 DWORD bytes_transferred, | 385 DWORD bytes_transferred, |
283 DWORD error) { | 386 DWORD error) { |
284 DCHECK(CalledOnValidThread()); | 387 DCHECK(CalledOnValidThread()); |
285 if (context == comm_context_) { | 388 if (context == comm_context_) { |
286 DWORD errors; | 389 DWORD errors; |
287 COMSTAT status; | 390 COMSTAT status; |
(...skipping 146 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
434 std::string SerialIoHandler::MaybeFixUpPortName(const std::string& port_name) { | 537 std::string SerialIoHandler::MaybeFixUpPortName(const std::string& port_name) { |
435 // For COM numbers less than 9, CreateFile is called with a string such as | 538 // For COM numbers less than 9, CreateFile is called with a string such as |
436 // "COM1". For numbers greater than 9, a prefix of "\\\\.\\" must be added. | 539 // "COM1". For numbers greater than 9, a prefix of "\\\\.\\" must be added. |
437 if (port_name.length() > std::string("COM9").length()) | 540 if (port_name.length() > std::string("COM9").length()) |
438 return std::string("\\\\.\\").append(port_name); | 541 return std::string("\\\\.\\").append(port_name); |
439 | 542 |
440 return port_name; | 543 return port_name; |
441 } | 544 } |
442 | 545 |
443 } // namespace device | 546 } // namespace device |
OLD | NEW |