Home | History | Annotate | Download | only in hid
      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.
      4 
      5 #include "device/hid/hid_service_mac.h"
      6 
      7 #include <CoreFoundation/CoreFoundation.h>
      8 #include <IOKit/hid/IOHIDManager.h>
      9 
     10 #include <string>
     11 #include <vector>
     12 
     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"
     21 
     22 namespace device {
     23 
     24 class HidServiceMac;
     25 
     26 namespace {
     27 
     28 typedef std::vector<IOHIDDeviceRef> HidDeviceList;
     29 
     30 HidServiceMac* HidServiceFromContext(void* context) {
     31   return static_cast<HidServiceMac*>(context);
     32 }
     33 
     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);
     40 }
     41 
     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));
     47   if (devices)
     48     CFSetApplyFunction(devices, HidEnumerationBackInserter, device_list);
     49 }
     50 
     51 }  // namespace
     52 
     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));
     58   if (!hid_manager_) {
     59     LOG(ERROR) << "Failed to initialize HidManager";
     60     return;
     61   }
     62   DCHECK(CFGetTypeID(hid_manager_) == IOHIDManagerGetTypeID());
     63   IOHIDManagerOpen(hid_manager_, kIOHIDOptionsTypeNone);
     64   IOHIDManagerSetDeviceMatching(hid_manager_, NULL);
     65 
     66   // Enumerate all the currently known devices.
     67   Enumerate();
     68 
     69   // Register for plug/unplug notifications.
     70   StartWatchingDevices();
     71 }
     72 
     73 HidServiceMac::~HidServiceMac() {
     74   StopWatchingDevices();
     75 }
     76 
     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);
     85 }
     86 
     87 void HidServiceMac::StopWatchingDevices() {
     88   DCHECK(thread_checker_.CalledOnValidThread());
     89   if (!hid_manager_)
     90     return;
     91   IOHIDManagerUnscheduleFromRunLoop(
     92       hid_manager_, CFRunLoopGetMain(), kCFRunLoopDefaultMode);
     93   IOHIDManagerClose(hid_manager_, kIOHIDOptionsTypeNone);
     94 }
     95 
     96 void HidServiceMac::AddDeviceCallback(void* context,
     97                                       IOReturn result,
     98                                       void* sender,
     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)));
    108 }
    109 
    110 void HidServiceMac::RemoveDeviceCallback(void* context,
    111                                          IOReturn result,
    112                                          void* sender,
    113                                          IOHIDDeviceRef hid_device) {
    114   DCHECK(CFRunLoopGetMain() == CFRunLoopGetCurrent());
    115   HidServiceMac* service = HidServiceFromContext(context);
    116   service->message_loop_->PostTask(
    117       FROM_HERE,
    118       base::Bind(&HidServiceMac::PlatformRemoveDevice,
    119                  base::Unretained(service),
    120                  base::Unretained(hid_device)));
    121 }
    122 
    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();
    129        ++iter) {
    130     IOHIDDeviceRef hid_device = *iter;
    131     PlatformAddDevice(hid_device);
    132   }
    133 }
    134 
    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());
    139 
    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));
    146   device_info.input_report_size =
    147       GetHidIntProperty(hid_device, CFSTR(kIOHIDMaxInputReportSizeKey));
    148   device_info.output_report_size =
    149       GetHidIntProperty(hid_device, CFSTR(kIOHIDMaxOutputReportSizeKey));
    150   device_info.feature_report_size =
    151       GetHidIntProperty(hid_device, CFSTR(kIOHIDMaxFeatureReportSizeKey));
    152   CFTypeRef deviceUsagePairsRaw =
    153       IOHIDDeviceGetProperty(hid_device, CFSTR(kIOHIDDeviceUsagePairsKey));
    154   CFArrayRef deviceUsagePairs =
    155       base::mac::CFCast<CFArrayRef>(deviceUsagePairsRaw);
    156   CFIndex deviceUsagePairsCount = CFArrayGetCount(deviceUsagePairs);
    157   for (CFIndex i = 0; i < deviceUsagePairsCount; i++) {
    158     CFDictionaryRef deviceUsagePair = base::mac::CFCast<CFDictionaryRef>(
    159         CFArrayGetValueAtIndex(deviceUsagePairs, i));
    160     CFNumberRef usage_raw = base::mac::CFCast<CFNumberRef>(
    161         CFDictionaryGetValue(deviceUsagePair, CFSTR(kIOHIDDeviceUsageKey)));
    162     uint16_t usage;
    163     CFNumberGetValue(usage_raw, kCFNumberSInt32Type, &usage);
    164     CFNumberRef page_raw = base::mac::CFCast<CFNumberRef>(
    165         CFDictionaryGetValue(deviceUsagePair, CFSTR(kIOHIDDeviceUsagePageKey)));
    166     HidUsageAndPage::Page page;
    167     CFNumberGetValue(page_raw, kCFNumberSInt32Type, &page);
    168     device_info.usages.push_back(HidUsageAndPage(usage, page));
    169   }
    170   device_info.product_name =
    171       GetHidStringProperty(hid_device, CFSTR(kIOHIDProductKey));
    172   device_info.serial_number =
    173       GetHidStringProperty(hid_device, CFSTR(kIOHIDSerialNumberKey));
    174   AddDevice(device_info);
    175 }
    176 
    177 void HidServiceMac::PlatformRemoveDevice(IOHIDDeviceRef hid_device) {
    178   DCHECK(thread_checker_.CalledOnValidThread());
    179   RemoveDevice(hid_device);
    180   CFRelease(hid_device);
    181 }
    182 
    183 scoped_refptr<HidConnection> HidServiceMac::Connect(
    184     const HidDeviceId& device_id) {
    185   DCHECK(thread_checker_.CalledOnValidThread());
    186   HidDeviceInfo device_info;
    187   if (!GetDeviceInfo(device_id, &device_info))
    188     return NULL;
    189   return scoped_refptr<HidConnection>(new HidConnectionMac(device_info));
    190 }
    191 
    192 }  // namespace device
    193