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 "third_party/re2/re2/re2.h" | |
8 | 15 |
9 namespace device { | 16 namespace device { |
10 | 17 |
11 namespace { | 18 namespace { |
12 | 19 |
13 int BitrateToSpeedConstant(int bitrate) { | 20 int BitrateToSpeedConstant(int bitrate) { |
14 #define BITRATE_TO_SPEED_CASE(x) \ | 21 #define BITRATE_TO_SPEED_CASE(x) \ |
15 case x: \ | 22 case x: \ |
16 return CBR_##x; | 23 return CBR_##x; |
17 switch (bitrate) { | 24 switch (bitrate) { |
(...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
123 serial::StopBits StopBitsConstantToEnum(int stop_bits) { | 130 serial::StopBits StopBitsConstantToEnum(int stop_bits) { |
124 switch (stop_bits) { | 131 switch (stop_bits) { |
125 case TWOSTOPBITS: | 132 case TWOSTOPBITS: |
126 return serial::STOP_BITS_TWO; | 133 return serial::STOP_BITS_TWO; |
127 case ONESTOPBIT: | 134 case ONESTOPBIT: |
128 default: | 135 default: |
129 return serial::STOP_BITS_ONE; | 136 return serial::STOP_BITS_ONE; |
130 } | 137 } |
131 } | 138 } |
132 | 139 |
140 // Wrapper around a HDEVINFO that automatically destroys it. | |
141 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
| |
142 public: | |
143 explicit ScopedDeviceInfoList(HDEVINFO handle) : handle_(handle) {} | |
144 | |
145 ~ScopedDeviceInfoList() { | |
146 if (valid()) { | |
147 SetupDiDestroyDeviceInfoList(handle_); | |
148 } | |
149 } | |
150 | |
151 bool valid() { return handle_ != INVALID_HANDLE_VALUE; } | |
152 | |
153 HDEVINFO get() { return handle_; } | |
154 | |
155 private: | |
156 HDEVINFO handle_; | |
157 | |
158 DISALLOW_COPY_AND_ASSIGN(ScopedDeviceInfoList); | |
159 }; | |
160 | |
161 // Wrapper around an SP_DEVINFO_DATA that initializes it properly and | |
162 // automatically deletes it. | |
163 class ScopedDeviceInfo { | |
164 public: | |
165 ScopedDeviceInfo() { | |
166 memset(&dev_info_data_, 0, sizeof(dev_info_data_)); | |
167 dev_info_data_.cbSize = sizeof(dev_info_data_); | |
168 } | |
169 | |
170 ~ScopedDeviceInfo() { | |
171 if (dev_info_set_ != INVALID_HANDLE_VALUE) { | |
172 SetupDiDeleteDeviceInfo(dev_info_set_, &dev_info_data_); | |
173 } | |
174 } | |
175 | |
176 // Once the SP_DEVINFO_DATA has been populated it must be freed using the | |
177 // HDEVINFO it was created from. | |
178 void set_valid(HDEVINFO dev_info_set) { | |
179 DCHECK(dev_info_set_ == INVALID_HANDLE_VALUE); | |
180 DCHECK(dev_info_set != INVALID_HANDLE_VALUE); | |
181 dev_info_set_ = dev_info_set; | |
182 } | |
183 | |
184 PSP_DEVINFO_DATA get() { return &dev_info_data_; } | |
185 | |
186 private: | |
187 HDEVINFO dev_info_set_ = INVALID_HANDLE_VALUE; | |
188 SP_DEVINFO_DATA dev_info_data_; | |
189 }; | |
190 | |
191 // Searches for the COM port in the device's friendly name, assigns its value to | |
192 // com_port, and returns whether the operation was successful. | |
193 bool GetCOMPort(const std::string friendly_name, std::string* com_port) { | |
194 return RE2::PartialMatch(friendly_name, ".* \\((COM[0-9]+)\\)", com_port); | |
195 } | |
196 | |
133 } // namespace | 197 } // namespace |
134 | 198 |
135 // static | 199 // static |
136 scoped_refptr<SerialIoHandler> SerialIoHandler::Create( | 200 scoped_refptr<SerialIoHandler> SerialIoHandler::Create( |
137 scoped_refptr<base::SingleThreadTaskRunner> file_thread_task_runner, | 201 scoped_refptr<base::SingleThreadTaskRunner> file_thread_task_runner, |
138 scoped_refptr<base::SingleThreadTaskRunner> ui_thread_task_runner) { | 202 scoped_refptr<base::SingleThreadTaskRunner> ui_thread_task_runner) { |
139 return new SerialIoHandlerWin(file_thread_task_runner, ui_thread_task_runner); | 203 return new SerialIoHandlerWin(file_thread_task_runner, ui_thread_task_runner); |
140 } | 204 } |
141 | 205 |
206 class SerialIoHandlerWin::UiThreadHelper : public DeviceMonitorWin::Observer { | |
207 public: | |
208 UiThreadHelper( | |
209 base::WeakPtr<SerialIoHandlerWin> io_handler, | |
210 scoped_refptr<base::SingleThreadTaskRunner> io_thread_task_runner) | |
211 : device_observer_(this), | |
212 io_handler_(io_handler), | |
213 io_thread_task_runner_(io_thread_task_runner) {} | |
214 | |
215 ~UiThreadHelper() { DCHECK(thread_checker_.CalledOnValidThread()); } | |
216 | |
217 static void Start(scoped_ptr<UiThreadHelper> self) { | |
218 self->thread_checker_.DetachFromThread(); | |
219 DeviceMonitorWin* device_monitor = DeviceMonitorWin::GetForAllInterfaces(); | |
220 if (device_monitor) { | |
221 self->device_observer_.Add(device_monitor); | |
222 } | |
223 // |self| is now owned by the current message loop. | |
224 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.
| |
225 } | |
226 | |
227 private: | |
228 // DeviceMonitorWin::Observer | |
229 void OnDeviceRemoved(const GUID& class_guid, | |
230 const std::string& device_path) override { | |
231 DCHECK(thread_checker_.CalledOnValidThread()); | |
232 io_thread_task_runner_->PostTask( | |
233 FROM_HERE, base::Bind(&SerialIoHandlerWin::RemoveDevice, io_handler_, | |
234 device_path)); | |
235 } | |
236 | |
237 base::ThreadChecker thread_checker_; | |
238 ScopedObserver<DeviceMonitorWin, DeviceMonitorWin::Observer> device_observer_; | |
239 | |
240 // This weak pointer is only valid when checked on this task runner. | |
241 base::WeakPtr<SerialIoHandlerWin> io_handler_; | |
242 scoped_refptr<base::SingleThreadTaskRunner> io_thread_task_runner_; | |
243 | |
244 DISALLOW_COPY_AND_ASSIGN(UiThreadHelper); | |
245 }; | |
246 | |
247 void SerialIoHandlerWin::RemoveDevice(const std::string& device_path) { | |
248 DCHECK(CalledOnValidThread()); | |
249 ScopedDeviceInfoList dev_info_list(SetupDiCreateDeviceInfoList(NULL, NULL)); | |
250 if (!dev_info_list.valid()) { | |
251 VPLOG(1) << "Failed to create a device information set"; | |
252 return; | |
253 } | |
254 | |
255 // This will add the device to |dev_info_list| so we can query driver info. | |
256 if (!SetupDiOpenDeviceInterfaceA(dev_info_list.get(), device_path.c_str(), 0, | |
257 NULL)) { | |
258 VPLOG(1) << "Failed to get device interface data for " << device_path; | |
259 return; | |
260 } | |
261 | |
262 ScopedDeviceInfo dev_info; | |
263 if (!SetupDiEnumDeviceInfo(dev_info_list.get(), 0, dev_info.get())) { | |
264 VPLOG(1) << "Failed to get device info for " << device_path; | |
265 return; | |
266 } | |
267 | |
268 dev_info.set_valid(dev_info_list.get()); | |
269 | |
270 BYTE friendly_name[512]; | |
271 if (!SetupDiGetDeviceRegistryPropertyA( | |
272 dev_info_list.get(), dev_info.get(), SPDRP_FRIENDLYNAME, NULL, | |
273 friendly_name, sizeof(friendly_name), NULL)) { | |
274 VPLOG(1) << "Failed to get device service property"; | |
275 return; | |
276 } | |
277 | |
278 std::string port; | |
279 if (!GetCOMPort(reinterpret_cast<const char*>(friendly_name), &port)) | |
280 return; | |
281 | |
282 if (port_ == port) | |
283 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.
| |
284 } | |
285 | |
142 bool SerialIoHandlerWin::PostOpen() { | 286 bool SerialIoHandlerWin::PostOpen() { |
143 DCHECK(!comm_context_); | 287 DCHECK(!comm_context_); |
144 DCHECK(!read_context_); | 288 DCHECK(!read_context_); |
145 DCHECK(!write_context_); | 289 DCHECK(!write_context_); |
146 | 290 |
147 base::MessageLoopForIO::current()->RegisterIOHandler(file().GetPlatformFile(), | 291 base::MessageLoopForIO::current()->RegisterIOHandler(file().GetPlatformFile(), |
148 this); | 292 this); |
149 | 293 |
150 comm_context_.reset(new base::MessageLoopForIO::IOContext()); | 294 comm_context_.reset(new base::MessageLoopForIO::IOContext()); |
151 comm_context_->handler = this; | 295 comm_context_->handler = this; |
(...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
264 return false; | 408 return false; |
265 } | 409 } |
266 return true; | 410 return true; |
267 } | 411 } |
268 | 412 |
269 SerialIoHandlerWin::SerialIoHandlerWin( | 413 SerialIoHandlerWin::SerialIoHandlerWin( |
270 scoped_refptr<base::SingleThreadTaskRunner> file_thread_task_runner, | 414 scoped_refptr<base::SingleThreadTaskRunner> file_thread_task_runner, |
271 scoped_refptr<base::SingleThreadTaskRunner> ui_thread_task_runner) | 415 scoped_refptr<base::SingleThreadTaskRunner> ui_thread_task_runner) |
272 : SerialIoHandler(file_thread_task_runner, ui_thread_task_runner), | 416 : SerialIoHandler(file_thread_task_runner, ui_thread_task_runner), |
273 event_mask_(0), | 417 event_mask_(0), |
274 is_comm_pending_(false) { | 418 is_comm_pending_(false), |
419 weak_factory_(this) { | |
420 scoped_refptr<base::SingleThreadTaskRunner> io_thread_task_runner = | |
421 base::ThreadTaskRunnerHandle::Get(); | |
422 scoped_ptr<UiThreadHelper> helper( | |
423 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.
| |
424 helper_ = helper.get(); | |
425 ui_thread_task_runner->PostTask( | |
426 FROM_HERE, base::Bind(&UiThreadHelper::Start, base::Passed(&helper))); | |
275 } | 427 } |
276 | 428 |
277 SerialIoHandlerWin::~SerialIoHandlerWin() { | 429 SerialIoHandlerWin::~SerialIoHandlerWin() { |
430 ui_thread_task_runner_->DeleteSoon(FROM_HERE, helper_); | |
278 } | 431 } |
279 | 432 |
280 void SerialIoHandlerWin::OnIOCompleted( | 433 void SerialIoHandlerWin::OnIOCompleted( |
281 base::MessageLoopForIO::IOContext* context, | 434 base::MessageLoopForIO::IOContext* context, |
282 DWORD bytes_transferred, | 435 DWORD bytes_transferred, |
283 DWORD error) { | 436 DWORD error) { |
284 DCHECK(CalledOnValidThread()); | 437 DCHECK(CalledOnValidThread()); |
285 if (context == comm_context_) { | 438 if (context == comm_context_) { |
286 DWORD errors; | 439 DWORD errors; |
287 COMSTAT status; | 440 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) { | 587 std::string SerialIoHandler::MaybeFixUpPortName(const std::string& port_name) { |
435 // For COM numbers less than 9, CreateFile is called with a string such as | 588 // 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. | 589 // "COM1". For numbers greater than 9, a prefix of "\\\\.\\" must be added. |
437 if (port_name.length() > std::string("COM9").length()) | 590 if (port_name.length() > std::string("COM9").length()) |
438 return std::string("\\\\.\\").append(port_name); | 591 return std::string("\\\\.\\").append(port_name); |
439 | 592 |
440 return port_name; | 593 return port_name; |
441 } | 594 } |
442 | 595 |
443 } // namespace device | 596 } // namespace device |
OLD | NEW |