Linux: Depend on liberation-fonts package for RPMs.
[chromium-blink-merge.git] / device / serial / serial_device_enumerator_mac.cc
blob9159ad1c17c63014344a2d5ad6d8df14d7a92450
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/serial/serial_device_enumerator_mac.h"
7 #include <IOKit/serial/IOSerialKeys.h>
8 #include <IOKit/usb/IOUSBLib.h>
10 #include "base/files/file_enumerator.h"
11 #include "base/files/file_path.h"
12 #include "base/files/file_util.h"
13 #include "base/mac/scoped_cftyperef.h"
14 #include "base/mac/scoped_ioobject.h"
15 #include "base/memory/scoped_ptr.h"
16 #include "base/strings/pattern.h"
17 #include "base/strings/string_util.h"
18 #include "base/strings/sys_string_conversions.h"
20 namespace device {
22 namespace {
24 // Searches a service and all ancestor services for a property with the
25 // specified key, returning NULL if no such key was found.
26 CFTypeRef GetCFProperty(io_service_t service, const CFStringRef key) {
27 // We search for the specified property not only on the specified service, but
28 // all ancestors of that service. This is important because if a device is
29 // both serial and USB, in the registry tree it appears as a serial service
30 // with a USB service as its ancestor. Without searching ancestors services
31 // for the specified property, we'd miss all USB properties.
32 return IORegistryEntrySearchCFProperty(
33 service, kIOServicePlane, key, NULL,
34 kIORegistryIterateRecursively | kIORegistryIterateParents);
37 // Searches a service and all ancestor services for a string property with the
38 // specified key, returning NULL if no such key was found.
39 CFStringRef GetCFStringProperty(io_service_t service, const CFStringRef key) {
40 CFTypeRef value = GetCFProperty(service, key);
41 if (value && (CFGetTypeID(value) == CFStringGetTypeID()))
42 return static_cast<CFStringRef>(value);
44 return NULL;
47 // Searches a service and all ancestor services for a number property with the
48 // specified key, returning NULL if no such key was found.
49 CFNumberRef GetCFNumberProperty(io_service_t service, const CFStringRef key) {
50 CFTypeRef value = GetCFProperty(service, key);
51 if (value && (CFGetTypeID(value) == CFNumberGetTypeID()))
52 return static_cast<CFNumberRef>(value);
54 return NULL;
57 // Searches the specified service for a string property with the specified key,
58 // sets value to that property's value, and returns whether the operation was
59 // successful.
60 bool GetStringProperty(io_service_t service,
61 const CFStringRef key,
62 mojo::String* value) {
63 CFStringRef propValue = GetCFStringProperty(service, key);
64 if (propValue) {
65 *value = base::SysCFStringRefToUTF8(propValue);
66 return value;
69 return false;
72 // Searches the specified service for a uint16 property with the specified key,
73 // sets value to that property's value, and returns whether the operation was
74 // successful.
75 bool GetUInt16Property(io_service_t service,
76 const CFStringRef key,
77 uint16_t* value) {
78 CFNumberRef propValue = GetCFNumberProperty(service, key);
79 if (propValue) {
80 int intValue;
81 if (CFNumberGetValue(propValue, kCFNumberIntType, &intValue)) {
82 *value = static_cast<uint16_t>(intValue);
83 return true;
87 return false;
90 } // namespace
92 // static
93 scoped_ptr<SerialDeviceEnumerator> SerialDeviceEnumerator::Create() {
94 return scoped_ptr<SerialDeviceEnumerator>(new SerialDeviceEnumeratorMac());
97 SerialDeviceEnumeratorMac::SerialDeviceEnumeratorMac() {}
99 SerialDeviceEnumeratorMac::~SerialDeviceEnumeratorMac() {}
101 mojo::Array<serial::DeviceInfoPtr> SerialDeviceEnumeratorMac::GetDevices() {
102 mojo::Array<serial::DeviceInfoPtr> devices(0);
104 // Make a service query to find all serial devices.
105 CFMutableDictionaryRef matchingDict =
106 IOServiceMatching(kIOSerialBSDServiceValue);
107 if (!matchingDict)
108 return devices.Pass();
110 io_iterator_t it;
111 kern_return_t kr =
112 IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict, &it);
113 if (kr != KERN_SUCCESS)
114 return devices.Pass();
116 base::mac::ScopedIOObject<io_iterator_t> scoped_it(it);
117 base::mac::ScopedIOObject<io_service_t> scoped_device;
118 while (scoped_device.reset(IOIteratorNext(scoped_it.get())), scoped_device) {
119 serial::DeviceInfoPtr callout_info(serial::DeviceInfo::New());
121 uint16_t vendorId;
122 if (GetUInt16Property(scoped_device.get(), CFSTR(kUSBVendorID),
123 &vendorId)) {
124 callout_info->has_vendor_id = true;
125 callout_info->vendor_id = vendorId;
128 uint16_t productId;
129 if (GetUInt16Property(scoped_device.get(), CFSTR(kUSBProductID),
130 &productId)) {
131 callout_info->has_product_id = true;
132 callout_info->product_id = productId;
135 mojo::String displayName;
136 if (GetStringProperty(scoped_device.get(), CFSTR(kUSBProductString),
137 &displayName)) {
138 callout_info->display_name = displayName;
141 // Each serial device has two "paths" in /dev/ associated with it: a
142 // "dialin" path starting with "tty" and a "callout" path starting with
143 // "cu". Each of these is considered a different device from Chrome's
144 // standpoint, but both should share the device's USB properties.
145 mojo::String dialinDevice;
146 if (GetStringProperty(scoped_device.get(), CFSTR(kIODialinDeviceKey),
147 &dialinDevice)) {
148 serial::DeviceInfoPtr dialin_info = callout_info.Clone();
149 dialin_info->path = dialinDevice;
150 devices.push_back(dialin_info.Pass());
153 mojo::String calloutDevice;
154 if (GetStringProperty(scoped_device.get(), CFSTR(kIOCalloutDeviceKey),
155 &calloutDevice)) {
156 callout_info->path = calloutDevice;
157 devices.push_back(callout_info.Pass());
161 return devices.Pass();
164 } // namespace device