OLD | NEW |
| (Empty) |
1 // Copyright 2016 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/generic_sensor/platform_sensor_ambient_light_mac.h" | |
6 | |
7 #include <stdint.h> | |
8 | |
9 #include <IOKit/IOMessage.h> | |
10 | |
11 #include "base/bind.h" | |
12 #include "device/base/synchronization/shared_memory_seqlock_buffer.h" | |
13 #include "device/generic_sensor/generic_sensor_consts.h" | |
14 #include "device/generic_sensor/platform_sensor_provider_mac.h" | |
15 | |
16 namespace { | |
17 | |
18 // Convert the value returned by the ambient light LMU sensor on Mac | |
19 // hardware to a lux value. | |
20 double LMUvalueToLux(uint64_t raw_value) { | |
21 // Conversion formula from regression. | |
22 // https://bugzilla.mozilla.org/show_bug.cgi?id=793728 | |
23 // Let x = raw_value, then | |
24 // lux = -2.978303814*(10^-27)*x^4 + 2.635687683*(10^-19)*x^3 - | |
25 // 3.459747434*(10^-12)*x^2 + 3.905829689*(10^-5)*x - 0.1932594532 | |
26 | |
27 static const long double k4 = pow(10.L, -7); | |
28 static const long double k3 = pow(10.L, -4); | |
29 static const long double k2 = pow(10.L, -2); | |
30 static const long double k1 = pow(10.L, 5); | |
31 long double scaled_value = raw_value / k1; | |
32 | |
33 long double lux_value = | |
34 (-3 * k4 * pow(scaled_value, 4)) + (2.6 * k3 * pow(scaled_value, 3)) + | |
35 (-3.4 * k2 * pow(scaled_value, 2)) + (3.9 * scaled_value) - 0.19; | |
36 | |
37 double lux = ceil(static_cast<double>(lux_value)); | |
38 return lux > 0 ? lux : 0; | |
39 } | |
40 | |
41 } // namespace | |
42 | |
43 namespace device { | |
44 | |
45 enum LmuFunctionIndex { | |
46 kGetSensorReadingID = 0, // getSensorReading(int *, int *) | |
47 }; | |
48 | |
49 PlatformSensorAmbientLightMac::PlatformSensorAmbientLightMac( | |
50 mojo::ScopedSharedBufferMapping mapping, | |
51 PlatformSensorProvider* provider) | |
52 : PlatformSensor(mojom::SensorType::AMBIENT_LIGHT, | |
53 std::move(mapping), | |
54 provider), | |
55 light_sensor_port_(nullptr), | |
56 current_lux_(0.0) {} | |
57 | |
58 PlatformSensorAmbientLightMac::~PlatformSensorAmbientLightMac() = default; | |
59 | |
60 mojom::ReportingMode PlatformSensorAmbientLightMac::GetReportingMode() { | |
61 return mojom::ReportingMode::ON_CHANGE; | |
62 } | |
63 | |
64 bool PlatformSensorAmbientLightMac::CheckSensorConfiguration( | |
65 const PlatformSensorConfiguration& configuration) { | |
66 return configuration.frequency() > 0 && | |
67 configuration.frequency() <= | |
68 mojom::SensorConfiguration::kMaxAllowedFrequency; | |
69 } | |
70 | |
71 PlatformSensorConfiguration | |
72 PlatformSensorAmbientLightMac::GetDefaultConfiguration() { | |
73 PlatformSensorConfiguration default_configuration; | |
74 default_configuration.set_frequency(kDefaultAmbientLightFrequencyHz); | |
75 return default_configuration; | |
76 } | |
77 | |
78 void PlatformSensorAmbientLightMac::IOServiceCallback(void* context, | |
79 io_service_t service, | |
80 natural_t message_type, | |
81 void* message_argument) { | |
82 PlatformSensorAmbientLightMac* sensor = | |
83 static_cast<PlatformSensorAmbientLightMac*>(context); | |
84 if (!sensor->ReadAndUpdate()) { | |
85 sensor->NotifySensorError(); | |
86 sensor->StopSensor(); | |
87 } | |
88 } | |
89 | |
90 bool PlatformSensorAmbientLightMac::StartSensor( | |
91 const PlatformSensorConfiguration& configuration) { | |
92 // Tested and verified by riju that the following call works on | |
93 // MacBookPro9,1 : Macbook Pro 15" (Mid 2012 model) | |
94 // MacBookPro10,1 : Macbook Pro 15" (Retina Display, Early 2013 model). | |
95 // MacBookPro10,2 : Macbook Pro 13" (Retina Display, Early 2013 model). | |
96 // MacBookAir5,2 : Macbook Air 13" (Mid 2012 model) (by François Beaufort). | |
97 // MacBookAir6,2 : Macbook Air 13" (Mid 2013 model). | |
98 // Testing plans : please download the code and follow the comments :- | |
99 // https://gist.github.com/riju/74af8c81a665e412d122/ | |
100 // and add an entry here about the model and the status returned by the code. | |
101 | |
102 // Look up a registered IOService object whose class is AppleLMUController. | |
103 light_sensor_service_.reset(IOServiceGetMatchingService( | |
104 kIOMasterPortDefault, IOServiceMatching("AppleLMUController"))); | |
105 | |
106 // Return early if the ambient light sensor is not present. | |
107 if (!light_sensor_service_) | |
108 return false; | |
109 | |
110 light_sensor_port_.reset(IONotificationPortCreate(kIOMasterPortDefault)); | |
111 if (!light_sensor_port_.is_valid()) | |
112 return false; | |
113 | |
114 IONotificationPortSetDispatchQueue( | |
115 light_sensor_port_.get(), | |
116 dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)); | |
117 | |
118 kern_return_t kr = IOServiceAddInterestNotification( | |
119 light_sensor_port_.get(), light_sensor_service_, kIOGeneralInterest, | |
120 IOServiceCallback, this, light_sensor_notification_.InitializeInto()); | |
121 if (kr != KERN_SUCCESS) | |
122 return false; | |
123 | |
124 kr = IOServiceAddInterestNotification( | |
125 light_sensor_port_.get(), light_sensor_service_, kIOBusyInterest, | |
126 IOServiceCallback, this, | |
127 light_sensor_busy_notification_.InitializeInto()); | |
128 if (kr != KERN_SUCCESS) | |
129 return false; | |
130 | |
131 kr = IOServiceOpen(light_sensor_service_, mach_task_self(), 0, | |
132 light_sensor_object_.InitializeInto()); | |
133 if (kr != KERN_SUCCESS) | |
134 return false; | |
135 | |
136 bool success = ReadAndUpdate(); | |
137 if (!success) | |
138 StopSensor(); | |
139 | |
140 return success; | |
141 } | |
142 | |
143 void PlatformSensorAmbientLightMac::StopSensor() { | |
144 light_sensor_port_.reset(); | |
145 light_sensor_notification_.reset(); | |
146 light_sensor_busy_notification_.reset(); | |
147 light_sensor_object_.reset(); | |
148 light_sensor_service_.reset(); | |
149 current_lux_ = 0.0; | |
150 } | |
151 | |
152 bool PlatformSensorAmbientLightMac::ReadAndUpdate() { | |
153 uint32_t scalar_output_count = 2; | |
154 uint64_t lux_values[2]; | |
155 kern_return_t kr = IOConnectCallMethod( | |
156 light_sensor_object_, LmuFunctionIndex::kGetSensorReadingID, nullptr, 0, | |
157 nullptr, 0, lux_values, &scalar_output_count, nullptr, 0); | |
158 | |
159 if (kr != KERN_SUCCESS) | |
160 return false; | |
161 | |
162 uint64_t mean = (lux_values[0] + lux_values[1]) / 2; | |
163 double lux = LMUvalueToLux(mean); | |
164 if (lux == current_lux_) | |
165 return true; | |
166 current_lux_ = lux; | |
167 | |
168 SensorReading reading; | |
169 reading.timestamp = (base::TimeTicks::Now() - base::TimeTicks()).InSecondsF(); | |
170 reading.values[0] = current_lux_; | |
171 UpdateSensorReading(reading, true); | |
172 return true; | |
173 } | |
174 | |
175 } // namespace device | |
OLD | NEW |