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

Side by Side 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 unified diff | Download patch
OLDNEW
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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698