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
3 // found in the LICENSE file.
5 #include "device/hid/hid_service_mac.h"
7 #include <CoreFoundation/CoreFoundation.h>
8 #include <IOKit/hid/IOHIDManager.h>
13 #include "base/bind.h"
14 #include "base/logging.h"
15 #include "base/message_loop/message_loop_proxy.h"
16 #include "base/stl_util.h"
17 #include "base/strings/string_number_conversions.h"
18 #include "base/threading/thread_restrictions.h"
19 #include "device/hid/hid_connection_mac.h"
20 #include "device/hid/hid_utils_mac.h"
28 typedef std::vector
<IOHIDDeviceRef
> HidDeviceList
;
30 HidServiceMac
* HidServiceFromContext(void* context
) {
31 return static_cast<HidServiceMac
*>(context
);
34 // Callback for CFSetApplyFunction as used by EnumerateHidDevices.
35 void HidEnumerationBackInserter(const void* value
, void* context
) {
36 HidDeviceList
* devices
= static_cast<HidDeviceList
*>(context
);
37 const IOHIDDeviceRef device
=
38 static_cast<IOHIDDeviceRef
>(const_cast<void*>(value
));
39 devices
->push_back(device
);
42 void EnumerateHidDevices(IOHIDManagerRef hid_manager
,
43 HidDeviceList
* device_list
) {
44 DCHECK(device_list
->size() == 0);
45 // Note that our ownership of each copied device is implied.
46 base::ScopedCFTypeRef
<CFSetRef
> devices(IOHIDManagerCopyDevices(hid_manager
));
48 CFSetApplyFunction(devices
, HidEnumerationBackInserter
, device_list
);
53 HidServiceMac::HidServiceMac() {
54 DCHECK(thread_checker_
.CalledOnValidThread());
55 message_loop_
= base::MessageLoopProxy::current();
56 DCHECK(message_loop_
);
57 hid_manager_
.reset(IOHIDManagerCreate(NULL
, 0));
59 LOG(ERROR
) << "Failed to initialize HidManager";
62 DCHECK(CFGetTypeID(hid_manager_
) == IOHIDManagerGetTypeID());
63 IOHIDManagerOpen(hid_manager_
, kIOHIDOptionsTypeNone
);
64 IOHIDManagerSetDeviceMatching(hid_manager_
, NULL
);
66 // Enumerate all the currently known devices.
69 // Register for plug/unplug notifications.
70 StartWatchingDevices();
73 HidServiceMac::~HidServiceMac() {
74 StopWatchingDevices();
77 void HidServiceMac::StartWatchingDevices() {
78 DCHECK(thread_checker_
.CalledOnValidThread());
79 IOHIDManagerRegisterDeviceMatchingCallback(
80 hid_manager_
, &AddDeviceCallback
, this);
81 IOHIDManagerRegisterDeviceRemovalCallback(
82 hid_manager_
, &RemoveDeviceCallback
, this);
83 IOHIDManagerScheduleWithRunLoop(
84 hid_manager_
, CFRunLoopGetMain(), kCFRunLoopDefaultMode
);
87 void HidServiceMac::StopWatchingDevices() {
88 DCHECK(thread_checker_
.CalledOnValidThread());
91 IOHIDManagerUnscheduleFromRunLoop(
92 hid_manager_
, CFRunLoopGetMain(), kCFRunLoopDefaultMode
);
93 IOHIDManagerClose(hid_manager_
, kIOHIDOptionsTypeNone
);
96 void HidServiceMac::AddDeviceCallback(void* context
,
99 IOHIDDeviceRef hid_device
) {
100 DCHECK(CFRunLoopGetMain() == CFRunLoopGetCurrent());
101 // Claim ownership of the device.
102 CFRetain(hid_device
);
103 HidServiceMac
* service
= HidServiceFromContext(context
);
104 service
->message_loop_
->PostTask(FROM_HERE
,
105 base::Bind(&HidServiceMac::PlatformAddDevice
,
106 base::Unretained(service
),
107 base::Unretained(hid_device
)));
110 void HidServiceMac::RemoveDeviceCallback(void* context
,
113 IOHIDDeviceRef hid_device
) {
114 DCHECK(CFRunLoopGetMain() == CFRunLoopGetCurrent());
115 HidServiceMac
* service
= HidServiceFromContext(context
);
116 service
->message_loop_
->PostTask(
118 base::Bind(&HidServiceMac::PlatformRemoveDevice
,
119 base::Unretained(service
),
120 base::Unretained(hid_device
)));
123 void HidServiceMac::Enumerate() {
124 DCHECK(thread_checker_
.CalledOnValidThread());
125 HidDeviceList devices
;
126 EnumerateHidDevices(hid_manager_
, &devices
);
127 for (HidDeviceList::const_iterator iter
= devices
.begin();
128 iter
!= devices
.end();
130 IOHIDDeviceRef hid_device
= *iter
;
131 PlatformAddDevice(hid_device
);
135 void HidServiceMac::PlatformAddDevice(IOHIDDeviceRef hid_device
) {
136 // Note that our ownership of hid_device is implied if calling this method.
137 // It is balanced in PlatformRemoveDevice.
138 DCHECK(thread_checker_
.CalledOnValidThread());
140 HidDeviceInfo device_info
;
141 device_info
.device_id
= hid_device
;
142 device_info
.vendor_id
=
143 GetHidIntProperty(hid_device
, CFSTR(kIOHIDVendorIDKey
));
144 device_info
.product_id
=
145 GetHidIntProperty(hid_device
, CFSTR(kIOHIDProductIDKey
));
147 GetHidIntProperty(hid_device
, CFSTR(kIOHIDPrimaryUsageKey
));
148 device_info
.usage_page
=
149 GetHidIntProperty(hid_device
, CFSTR(kIOHIDPrimaryUsagePageKey
));
150 device_info
.input_report_size
=
151 GetHidIntProperty(hid_device
, CFSTR(kIOHIDMaxInputReportSizeKey
));
152 device_info
.output_report_size
=
153 GetHidIntProperty(hid_device
, CFSTR(kIOHIDMaxOutputReportSizeKey
));
154 device_info
.feature_report_size
=
155 GetHidIntProperty(hid_device
, CFSTR(kIOHIDMaxFeatureReportSizeKey
));
156 device_info
.product_name
=
157 GetHidStringProperty(hid_device
, CFSTR(kIOHIDProductKey
));
158 device_info
.serial_number
=
159 GetHidStringProperty(hid_device
, CFSTR(kIOHIDSerialNumberKey
));
160 AddDevice(device_info
);
163 void HidServiceMac::PlatformRemoveDevice(IOHIDDeviceRef hid_device
) {
164 DCHECK(thread_checker_
.CalledOnValidThread());
165 RemoveDevice(hid_device
);
166 CFRelease(hid_device
);
169 scoped_refptr
<HidConnection
> HidServiceMac::Connect(
170 const HidDeviceId
& device_id
) {
171 DCHECK(thread_checker_
.CalledOnValidThread());
172 HidDeviceInfo device_info
;
173 if (!GetDeviceInfo(device_id
, &device_info
))
175 return scoped_refptr
<HidConnection
>(new HidConnectionMac(device_info
));
178 } // namespace device