Chromium Code Reviews| Index: device/hid/hid_service_win.cc |
| diff --git a/device/hid/hid_service_win.cc b/device/hid/hid_service_win.cc |
| index a0fce8f3c6af5b2ec848181ac2abf98fffb69532..ccc18a71f99e2ebad7e6c44dd20eb1c3b93e378d 100644 |
| --- a/device/hid/hid_service_win.cc |
| +++ b/device/hid/hid_service_win.cc |
| @@ -4,30 +4,25 @@ |
| #include "device/hid/hid_service_win.h" |
| -#include <cstdlib> |
| +#define INITGUID |
| + |
| +#include <dbt.h> |
| +#include <setupapi.h> |
| +#include <winioctl.h> |
| #include "base/bind.h" |
| #include "base/files/file.h" |
| #include "base/location.h" |
| #include "base/single_thread_task_runner.h" |
| -#include "base/stl_util.h" |
| +#include "base/strings/string_util.h" |
| #include "base/strings/sys_string_conversions.h" |
| #include "base/thread_task_runner_handle.h" |
| #include "base/threading/thread_restrictions.h" |
| +#include "base/win/message_window.h" |
| #include "device/hid/hid_connection_win.h" |
| #include "device/hid/hid_device_info.h" |
| #include "net/base/io_buffer.h" |
| -#if defined(OS_WIN) |
| - |
| -#define INITGUID |
| - |
| -#include <setupapi.h> |
| -#include <winioctl.h> |
| -#include "base/win/scoped_handle.h" |
| - |
| -#endif // defined(OS_WIN) |
| - |
| // Setup API is required to enumerate HID devices. |
| #pragma comment(lib, "setupapi.lib") |
| #pragma comment(lib, "hid.lib") |
| @@ -36,36 +31,94 @@ namespace device { |
| namespace { |
| const char kHIDClass[] = "HIDClass"; |
| +const wchar_t kWindowClassName[] = L"HidServiceMessageWindow"; |
| } // namespace |
| HidServiceWin::HidServiceWin() { |
| task_runner_ = base::ThreadTaskRunnerHandle::Get(); |
| DCHECK(task_runner_.get()); |
| - Enumerate(); |
| + RegisterForDeviceNotifications(); |
| + DoInitialEnumeration(); |
| } |
| -HidServiceWin::~HidServiceWin() {} |
| +void HidServiceWin::Connect(const HidDeviceId& device_id, |
| + const ConnectCallback& callback) { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + const auto& map_entry = devices().find(device_id); |
| + if (map_entry == devices().end()) { |
| + task_runner_->PostTask(FROM_HERE, base::Bind(callback, nullptr)); |
| + return; |
| + } |
| + const HidDeviceInfo& device_info = map_entry->second; |
| -void HidServiceWin::Enumerate() { |
| - BOOL res; |
| - HDEVINFO device_info_set; |
| - SP_DEVINFO_DATA devinfo_data; |
| - SP_DEVICE_INTERFACE_DATA device_interface_data; |
| + base::win::ScopedHandle file(OpenDevice(device_info.device_id)); |
| + if (!file.IsValid()) { |
| + PLOG(ERROR) << "Failed to open device"; |
| + task_runner_->PostTask(FROM_HERE, base::Bind(callback, nullptr)); |
| + return; |
| + } |
| - memset(&devinfo_data, 0, sizeof(SP_DEVINFO_DATA)); |
| - devinfo_data.cbSize = sizeof(SP_DEVINFO_DATA); |
| - device_interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); |
| + task_runner_->PostTask( |
| + FROM_HERE, |
| + base::Bind(callback, new HidConnectionWin(device_info, file.Pass()))); |
| +} |
| - device_info_set = SetupDiGetClassDevs( |
| - &GUID_DEVINTERFACE_HID, |
| - NULL, |
| - NULL, |
| - DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); |
| +HidServiceWin::~HidServiceWin() { |
| + if (notify_handle_) { |
| + UnregisterDeviceNotification(notify_handle_); |
| + } |
| +} |
| - std::set<std::string> connected_devices; |
| +void HidServiceWin::RegisterForDeviceNotifications() { |
| + window_.reset(new base::win::MessageWindow()); |
|
Ken Rockot(use gerrit already)
2014/12/05 23:27:53
nit: Maybe file a bug + add a TODO which expresses
|
| + if (!window_->CreateNamed( |
| + base::Bind(&HidServiceWin::HandleMessage, base::Unretained(this)), |
| + base::string16(kWindowClassName))) { |
| + LOG(ERROR) << "Failed to create message window: " << kWindowClassName; |
| + window_.reset(); |
| + } |
| + DEV_BROADCAST_DEVICEINTERFACE db = { sizeof(DEV_BROADCAST_DEVICEINTERFACE), |
| + DBT_DEVTYP_DEVICEINTERFACE, |
| + 0, |
| + GUID_DEVINTERFACE_HID }; |
| + notify_handle_ = RegisterDeviceNotification(window_->hwnd(), &db, |
| + DEVICE_NOTIFY_WINDOW_HANDLE); |
| + if (!notify_handle_) { |
| + LOG(ERROR) << "Failed to register for device notifications."; |
| + window_.reset(); |
| + } |
| +} |
| + |
| +bool HidServiceWin::HandleMessage(UINT message, |
| + WPARAM wparam, |
| + LPARAM lparam, |
| + LRESULT* result) { |
| + if (message == WM_DEVICECHANGE) { |
| + DEV_BROADCAST_DEVICEINTERFACE* db = |
| + reinterpret_cast<DEV_BROADCAST_DEVICEINTERFACE*>(lparam); |
| + std::string device_path(base::SysWideToUTF8(db->dbcc_name)); |
| + DCHECK(base::IsStringASCII(device_path)); |
| + if (wparam == DBT_DEVICEARRIVAL) { |
| + PlatformAddDevice(base::StringToLowerASCII(device_path)); |
| + } else if (wparam == DBT_DEVICEREMOVECOMPLETE) { |
| + PlatformRemoveDevice(base::StringToLowerASCII(device_path)); |
| + } |
| + *result = NULL; |
| + return true; |
| + } |
| + return false; |
| +} |
| + |
| +void HidServiceWin::DoInitialEnumeration() { |
| + HDEVINFO device_info_set = |
| + SetupDiGetClassDevs(&GUID_DEVINTERFACE_HID, NULL, NULL, |
| + DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); |
| if (device_info_set != INVALID_HANDLE_VALUE) { |
| + SP_DEVICE_INTERFACE_DATA device_interface_data; |
| + device_interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); |
| + |
| for (int device_index = 0; |
| SetupDiEnumDeviceInterfaces(device_info_set, |
| NULL, |
| @@ -76,84 +129,32 @@ void HidServiceWin::Enumerate() { |
| DWORD required_size = 0; |
| // Determime the required size of detail struct. |
| - SetupDiGetDeviceInterfaceDetailA(device_info_set, |
| - &device_interface_data, |
| - NULL, |
| - 0, |
| - &required_size, |
| - NULL); |
| + SetupDiGetDeviceInterfaceDetail(device_info_set, &device_interface_data, |
| + NULL, 0, &required_size, NULL); |
| - scoped_ptr<SP_DEVICE_INTERFACE_DETAIL_DATA_A, base::FreeDeleter> |
| - device_interface_detail_data( |
| - static_cast<SP_DEVICE_INTERFACE_DETAIL_DATA_A*>( |
| - malloc(required_size))); |
| + scoped_ptr<SP_DEVICE_INTERFACE_DETAIL_DATA, base::FreeDeleter> |
| + device_interface_detail_data( |
| + static_cast<SP_DEVICE_INTERFACE_DETAIL_DATA*>(malloc(required_size))); |
| device_interface_detail_data->cbSize = |
| - sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A); |
| + sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); |
| // Get the detailed data for this device. |
| - res = SetupDiGetDeviceInterfaceDetailA(device_info_set, |
| - &device_interface_data, |
| - device_interface_detail_data.get(), |
| - required_size, |
| - NULL, |
| - NULL); |
| - if (!res) |
| + BOOL res = SetupDiGetDeviceInterfaceDetail( |
| + device_info_set, &device_interface_data, |
| + device_interface_detail_data.get(), required_size, NULL, NULL); |
| + if (!res) { |
| continue; |
| - |
| - // Enumerate device info. Looking for Setup Class "HIDClass". |
| - for (DWORD i = 0; |
| - SetupDiEnumDeviceInfo(device_info_set, i, &devinfo_data); |
| - i++) { |
| - char class_name[256] = {0}; |
| - res = SetupDiGetDeviceRegistryPropertyA(device_info_set, |
| - &devinfo_data, |
| - SPDRP_CLASS, |
| - NULL, |
| - (PBYTE) class_name, |
| - sizeof(class_name) - 1, |
| - NULL); |
| - if (!res) |
| - break; |
| - if (memcmp(class_name, kHIDClass, sizeof(kHIDClass)) == 0) { |
| - char driver_name[256] = {0}; |
| - // Get bounded driver. |
| - res = SetupDiGetDeviceRegistryPropertyA(device_info_set, |
| - &devinfo_data, |
| - SPDRP_DRIVER, |
| - NULL, |
| - (PBYTE) driver_name, |
| - sizeof(driver_name) - 1, |
| - NULL); |
| - if (res) { |
| - // Found the driver. |
| - break; |
| - } |
| - } |
| } |
| - if (!res) |
| - continue; |
| - |
| - PlatformAddDevice(device_interface_detail_data->DevicePath); |
| - connected_devices.insert(device_interface_detail_data->DevicePath); |
| - } |
| - } |
| - |
| - // Find disconnected devices. |
| - std::vector<std::string> disconnected_devices; |
| - for (DeviceMap::const_iterator it = devices().begin(); it != devices().end(); |
| - ++it) { |
| - if (!ContainsKey(connected_devices, it->first)) { |
| - disconnected_devices.push_back(it->first); |
| + std::string device_path( |
| + base::SysWideToUTF8(device_interface_detail_data->DevicePath)); |
| + DCHECK(base::IsStringASCII(device_path)); |
| + PlatformAddDevice(device_path); |
| } |
| } |
| - |
| - // Remove disconnected devices. |
| - for (size_t i = 0; i < disconnected_devices.size(); ++i) { |
| - PlatformRemoveDevice(disconnected_devices[i]); |
| - } |
| } |
| +// static |
| void HidServiceWin::CollectInfoFromButtonCaps( |
| PHIDP_PREPARSED_DATA preparsed_data, |
| HIDP_REPORT_TYPE report_type, |
| @@ -176,6 +177,7 @@ void HidServiceWin::CollectInfoFromButtonCaps( |
| } |
| } |
| +// static |
| void HidServiceWin::CollectInfoFromValueCaps( |
| PHIDP_PREPARSED_DATA preparsed_data, |
| HIDP_REPORT_TYPE report_type, |
| @@ -202,43 +204,21 @@ void HidServiceWin::PlatformAddDevice(const std::string& device_path) { |
| device_info.device_id = device_path; |
| // Try to open the device. |
| - base::win::ScopedHandle device_handle( |
| - CreateFileA(device_path.c_str(), |
| - GENERIC_WRITE | GENERIC_READ, |
| - FILE_SHARE_READ | FILE_SHARE_WRITE, |
| - NULL, |
| - OPEN_EXISTING, |
| - FILE_FLAG_OVERLAPPED, |
| - 0)); |
| - |
| - if (!device_handle.IsValid() && |
| - GetLastError() == base::File::FILE_ERROR_ACCESS_DENIED) { |
| - base::win::ScopedHandle device_handle( |
| - CreateFileA(device_path.c_str(), |
| - GENERIC_READ, |
| - FILE_SHARE_READ, |
| - NULL, |
| - OPEN_EXISTING, |
| - FILE_FLAG_OVERLAPPED, |
| - 0)); |
| - |
| - if (!device_handle.IsValid()) |
| - return; |
| + base::win::ScopedHandle device_handle(OpenDevice(device_path)); |
| + if (!device_handle.IsValid()) { |
| + return; |
| } |
| // Get VID/PID pair. |
| HIDD_ATTRIBUTES attrib = {0}; |
| attrib.Size = sizeof(HIDD_ATTRIBUTES); |
| - if (!HidD_GetAttributes(device_handle.Get(), &attrib)) |
| + if (!HidD_GetAttributes(device_handle.Get(), &attrib)) { |
| return; |
| + } |
| device_info.vendor_id = attrib.VendorID; |
| device_info.product_id = attrib.ProductID; |
| - for (ULONG i = 32; |
| - HidD_SetNumInputBuffers(device_handle.Get(), i); |
| - i <<= 1); |
| - |
| // Get usage and usage page (optional). |
| PHIDP_PREPARSED_DATA preparsed_data; |
| if (HidD_GetPreparsedData(device_handle.Get(), &preparsed_data) && |
| @@ -304,27 +284,18 @@ void HidServiceWin::PlatformRemoveDevice(const std::string& device_path) { |
| RemoveDevice(device_path); |
| } |
| -void HidServiceWin::GetDevices(std::vector<HidDeviceInfo>* devices) { |
| - Enumerate(); |
| - HidService::GetDevices(devices); |
| -} |
| - |
| -void HidServiceWin::Connect(const HidDeviceId& device_id, |
| - const ConnectCallback& callback) { |
| - DCHECK(thread_checker_.CalledOnValidThread()); |
| - const auto& map_entry = devices().find(device_id); |
| - if (map_entry == devices().end()) { |
| - task_runner_->PostTask(FROM_HERE, base::Bind(callback, nullptr)); |
| - return; |
| - } |
| - const HidDeviceInfo& device_info = map_entry->second; |
| - |
| - scoped_refptr<HidConnectionWin> connection(new HidConnectionWin(device_info)); |
| - if (!connection->available()) { |
| - PLOG(ERROR) << "Failed to open device"; |
| - connection = nullptr; |
| +base::win::ScopedHandle HidServiceWin::OpenDevice( |
| + const std::string& device_path) { |
| + base::win::ScopedHandle file( |
| + CreateFileA(device_path.c_str(), GENERIC_WRITE | GENERIC_READ, |
| + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, |
| + FILE_FLAG_OVERLAPPED, NULL)); |
| + if (!file.IsValid() && |
| + GetLastError() == base::File::FILE_ERROR_ACCESS_DENIED) { |
|
Nico
2015/12/09 17:05:45
Are you sure this is correct? The CreateFile() MSD
Reilly Grant (use Gerrit)
2015/12/09 18:49:29
Interesting. I wonder if this logic has ever been
Nico
2015/12/09 19:10:53
Deleting this block if it's broken and untested so
|
| + file.Set(CreateFileA(device_path.c_str(), GENERIC_READ, FILE_SHARE_READ, |
| + NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL)); |
| } |
| - task_runner_->PostTask(FROM_HERE, base::Bind(callback, connection)); |
| + return file.Pass(); |
| } |
| } // namespace device |