1 /*
2  * Copyright (C) 2012 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #define LOG_NDEBUG 0
18 
19 #include <fcntl.h>
20 #include <errno.h>
21 #include <math.h>
22 #include <unistd.h>
23 #include <dirent.h>
24 #include <sys/select.h>
25 #include <cutils/log.h>
26 #include <linux/input.h>
27 #include <string.h>
28 
29 #include "CompassSensor.IIO.9150.h"
30 #include "sensors.h"
31 #include "MPLSupport.h"
32 #include "sensor_params.h"
33 #include "ml_sysfs_helper.h"
34 #include "local_log_def.h"
35 
36 #define COMPASS_MAX_SYSFS_ATTRB sizeof(compassSysFs) / sizeof(char*)
37 
38 #if defined COMPASS_YAS53x
39 #   warning "Invensense compass cal with YAS53x IIO on secondary bus"
40 #   define USE_MPL_COMPASS_HAL          (1)
41 #   define COMPASS_NAME                 "INV_YAS530"
42 #elif defined COMPASS_AK8975
43 #   warning "Invensense compass cal with AK8975 on primary bus"
44 #   define USE_MPL_COMPASS_HAL          (1)
45 #   define COMPASS_NAME                 "INV_AK8975"
46 #elif defined INVENSENSE_COMPASS_CAL
47 #   warning "Invensense compass cal with compass IIO on secondary bus"
48 #   define USE_MPL_COMPASS_HAL          (1)
49 #   define COMPASS_NAME                 "INV_COMPASS"
50 #else
51 #   warning "third party compass cal HAL"
52 #   define USE_MPL_COMPASS_HAL          (0)
53 // TODO: change to vendor's name
54 #   define COMPASS_NAME                 "AKM8975"
55 #endif
56 
57 
58 /*****************************************************************************/
59 
CompassSensor()60 CompassSensor::CompassSensor()
61                   : SensorBase(NULL, NULL),
62                     compass_fd(-1),
63                     mCompassTimestamp(0),
64                     mCompassInputReader(8)
65 {
66     VFUNC_LOG;
67 
68     if(inv_init_sysfs_attributes()) {
69         LOGE("Error Instantiating Compass\n");
70         return;
71     }
72 
73     if (!strcmp(COMPASS_NAME, "INV_COMPASS")) {
74         mI2CBus = COMPASS_BUS_SECONDARY;
75     } else {
76         mI2CBus = COMPASS_BUS_PRIMARY;
77     }
78 
79     memset(mCachedCompassData, 0, sizeof(mCachedCompassData));
80 
81     LOGV_IF(SYSFS_VERBOSE, "HAL:sysfs:cat %s (%lld)",
82             compassSysFs.compass_orient, getTimestamp());
83     FILE *fptr;
84     fptr = fopen(compassSysFs.compass_orient, "r");
85     if (fptr != NULL) {
86         int om[9];
87         fscanf(fptr, "%d,%d,%d,%d,%d,%d,%d,%d,%d",
88                &om[0], &om[1], &om[2], &om[3], &om[4], &om[5],
89                &om[6], &om[7], &om[8]);
90         fclose(fptr);
91 
92         LOGV_IF(EXTRA_VERBOSE,
93                 "HAL:compass mounting matrix: "
94                 "%+d %+d %+d %+d %+d %+d %+d %+d %+d",
95                 om[0], om[1], om[2], om[3], om[4], om[5], om[6], om[7], om[8]);
96 
97         mCompassOrientation[0] = om[0];
98         mCompassOrientation[1] = om[1];
99         mCompassOrientation[2] = om[2];
100         mCompassOrientation[3] = om[3];
101         mCompassOrientation[4] = om[4];
102         mCompassOrientation[5] = om[5];
103         mCompassOrientation[6] = om[6];
104         mCompassOrientation[7] = om[7];
105         mCompassOrientation[8] = om[8];
106     } else {
107         LOGE("HAL:Couldn't read compass mounting matrix");
108     }
109 
110     if (!isIntegrated()) {
111         enable(ID_M, 0);
112     }
113 }
114 
~CompassSensor()115 CompassSensor::~CompassSensor()
116 {
117     VFUNC_LOG;
118 
119     free(pathP);
120     if( compass_fd > 0)
121         close(compass_fd);
122 }
123 
getFd() const124 int CompassSensor::getFd() const
125 {
126     VHANDLER_LOG;
127     return compass_fd;
128 }
129 
130 /**
131  *  @brief        This function will enable/disable sensor.
132  *  @param[in]    handle
133  *                  which sensor to enable/disable.
134  *  @param[in]    en
135  *                  en=1, enable;
136  *                  en=0, disable
137  *  @return       if the operation is successful.
138  */
enable(int32_t,int en)139 int CompassSensor::enable(int32_t /*handle*/, int en)
140 {
141     VFUNC_LOG;
142 
143     mEnable = en;
144     int res;
145 
146     res = write_sysfs_int(compassSysFs.compass_enable, en);
147     LOGE_IF(res < 0, "HAL:enable compass failed");
148 
149     if (en) {
150         res = write_sysfs_int(compassSysFs.compass_x_fifo_enable, en);
151         res = write_sysfs_int(compassSysFs.compass_y_fifo_enable, en);
152         res = write_sysfs_int(compassSysFs.compass_z_fifo_enable, en);
153     }
154 
155     return res;
156 }
157 
setDelay(int32_t,int64_t ns)158 int CompassSensor::setDelay(int32_t /*handle*/, int64_t ns)
159 {
160     VFUNC_LOG;
161     int tempFd;
162     int res;
163 
164     LOGV_IF(SYSFS_VERBOSE, "HAL:sysfs:echo %.0f > %s (%lld)",
165             1000000000.f / ns, compassSysFs.compass_rate, getTimestamp());
166     mDelay = ns;
167     if (ns == 0)
168         return -1;
169     tempFd = open(compassSysFs.compass_rate, O_RDWR);
170     res = write_attribute_sensor(tempFd, 1000000000.f / ns);
171     if(res < 0) {
172         LOGE("HAL:Compass update delay error");
173     }
174     return res;
175 }
176 
177 
178 /**
179     @brief      This function will return the state of the sensor.
180     @return     1=enabled; 0=disabled
181 **/
getEnable(int32_t)182 int CompassSensor::getEnable(int32_t /*handle*/)
183 {
184     VFUNC_LOG;
185     return mEnable;
186 }
187 
188 /* use for Invensense compass calibration */
189 #define COMPASS_EVENT_DEBUG (0)
processCompassEvent(const input_event * event)190 void CompassSensor::processCompassEvent(const input_event *event)
191 {
192     VHANDLER_LOG;
193 
194     switch (event->code) {
195     case EVENT_TYPE_ICOMPASS_X:
196         LOGV_IF(COMPASS_EVENT_DEBUG, "EVENT_TYPE_ICOMPASS_X\n");
197         mCachedCompassData[0] = event->value;
198         break;
199     case EVENT_TYPE_ICOMPASS_Y:
200         LOGV_IF(COMPASS_EVENT_DEBUG, "EVENT_TYPE_ICOMPASS_Y\n");
201         mCachedCompassData[1] = event->value;
202         break;
203     case EVENT_TYPE_ICOMPASS_Z:
204         LOGV_IF(COMPASS_EVENT_DEBUG, "EVENT_TYPE_ICOMPASS_Z\n");
205         mCachedCompassData[2] = event->value;
206         break;
207     }
208 
209     mCompassTimestamp =
210         (int64_t)event->time.tv_sec * 1000000000L + event->time.tv_usec * 1000L;
211 }
212 
getOrientationMatrix(signed char * orient)213 void CompassSensor::getOrientationMatrix(signed char *orient)
214 {
215     VFUNC_LOG;
216     memcpy(orient, mCompassOrientation, sizeof(mCompassOrientation));
217 }
218 
getSensitivity()219 long CompassSensor::getSensitivity()
220 {
221     VFUNC_LOG;
222 
223     long sensitivity;
224     LOGV_IF(SYSFS_VERBOSE, "HAL:sysfs:cat %s (%lld)",
225             compassSysFs.compass_scale, getTimestamp());
226     inv_read_data(compassSysFs.compass_scale, &sensitivity);
227     return sensitivity;
228 }
229 
230 /**
231     @brief         This function is called by sensors_mpl.cpp
232                    to read sensor data from the driver.
233     @param[out]    data      sensor data is stored in this variable. Scaled such that
234                              1 uT = 2^16
235     @para[in]      timestamp data's timestamp
236     @return        1, if 1   sample read, 0, if not, negative if error
237  */
readSample(long * data,int64_t * timestamp)238 int CompassSensor::readSample(long *data, int64_t *timestamp)
239 {
240     VHANDLER_LOG;
241 
242     int numEventReceived = 0, done = 0;
243 
244     ssize_t n = mCompassInputReader.fill(compass_fd);
245     if (n < 0) {
246         LOGE("HAL:no compass events read");
247         return n;
248     }
249 
250     input_event const* event;
251 
252     while (done == 0 && mCompassInputReader.readEvent(&event)) {
253         int type = event->type;
254         if (type == EV_REL) {
255             processCompassEvent(event);
256         } else if (type == EV_SYN) {
257             *timestamp = mCompassTimestamp;
258             memcpy(data, mCachedCompassData, sizeof(mCachedCompassData));
259             done = 1;
260         } else {
261             LOGE("HAL:Compass Sensor: unknown event (type=%d, code=%d)",
262                  type, event->code);
263         }
264         mCompassInputReader.next();
265     }
266 
267     return done;
268 }
269 
270 /**
271  *  @brief  This function will return the current delay for this sensor.
272  *  @return delay in nanoseconds.
273  */
getDelay(int32_t)274 int64_t CompassSensor::getDelay(int32_t /*handle*/)
275 {
276     VFUNC_LOG;
277     return mDelay;
278 }
279 
fillList(struct sensor_t * list)280 void CompassSensor::fillList(struct sensor_t *list)
281 {
282     VFUNC_LOG;
283 
284     const char *compass = COMPASS_NAME;
285 
286     if (compass) {
287         if(!strcmp(compass, "INV_COMPASS")) {
288             list->maxRange = COMPASS_MPU9150_RANGE;
289             /* since target platform would use AK8963 instead of AK8975,
290                need to adopt AK8963's resolution here */
291             // list->resolution = COMPASS_MPU9150_RESOLUTION;
292             list->resolution = COMPASS_AKM8963_RESOLUTION;
293             list->power = COMPASS_MPU9150_POWER;
294             list->minDelay = COMPASS_MPU9150_MINDELAY;
295             return;
296         }
297         if(!strcmp(compass, "compass")
298                 || !strcmp(compass, "INV_AK8975") ) {
299             list->maxRange = COMPASS_AKM8975_RANGE;
300             list->resolution = COMPASS_AKM8975_RESOLUTION;
301             list->power = COMPASS_AKM8975_POWER;
302             list->minDelay = COMPASS_AKM8975_MINDELAY;
303             return;
304         }
305         if(!strcmp(compass, "INV_YAS530")) {
306             list->maxRange = COMPASS_YAS53x_RANGE;
307             list->resolution = COMPASS_YAS53x_RESOLUTION;
308             list->power = COMPASS_YAS53x_POWER;
309             list->minDelay = COMPASS_YAS53x_MINDELAY;
310             return;
311         }
312         if(!strcmp(compass, "INV_AMI306")) {
313             list->maxRange = COMPASS_AMI306_RANGE;
314             list->resolution = COMPASS_AMI306_RESOLUTION;
315             list->power = COMPASS_AMI306_POWER;
316             list->minDelay = COMPASS_AMI306_MINDELAY;
317             return;
318         }
319     }
320 
321     LOGE("HAL:unknown compass id %s -- "
322          "params default to ak8975 and might be wrong.",
323          compass);
324     list->maxRange = COMPASS_AKM8975_RANGE;
325     list->resolution = COMPASS_AKM8975_RESOLUTION;
326     list->power = COMPASS_AKM8975_POWER;
327     list->minDelay = COMPASS_AKM8975_MINDELAY;
328 }
329 
inv_init_sysfs_attributes(void)330 int CompassSensor::inv_init_sysfs_attributes(void)
331 {
332     VFUNC_LOG;
333 
334     unsigned char i = 0;
335     char sysfs_path[MAX_SYSFS_NAME_LEN], iio_trigger_path[MAX_SYSFS_NAME_LEN], tbuf[2];
336     char *sptr;
337     char **dptr;
338     int num;
339 
340     pathP = (char*)malloc(
341                     sizeof(char[COMPASS_MAX_SYSFS_ATTRB][MAX_SYSFS_NAME_LEN]));
342     sptr = pathP;
343     dptr = (char**)&compassSysFs;
344     if (sptr == NULL)
345         return -1;
346 
347     do {
348         *dptr++ = sptr;
349         sptr += sizeof(char[MAX_SYSFS_NAME_LEN]);
350     } while (++i < COMPASS_MAX_SYSFS_ATTRB);
351 
352     // get proper (in absolute/relative) IIO path & build MPU's sysfs paths
353     // inv_get_sysfs_abs_path(sysfs_path);
354     if(INV_SUCCESS != inv_get_sysfs_path(sysfs_path)) {
355         ALOGE("CompassSensor failed get sysfs path");
356         return -1;
357     }
358 
359     inv_get_iio_trigger_path(iio_trigger_path);
360 
361 #if defined COMPASS_AK8975
362     inv_get_input_number(COMPASS_NAME, &num);
363     tbuf[0] = num + 0x30;
364     tbuf[1] = 0;
365     sprintf(sysfs_path, "%s%s", "sys/class/input/input", tbuf);
366     strcat(sysfs_path, "/ak8975");
367 
368     sprintf(compassSysFs.compass_enable, "%s%s", sysfs_path, "/enable");
369     sprintf(compassSysFs.compass_rate, "%s%s", sysfs_path, "/rate");
370     sprintf(compassSysFs.compass_scale, "%s%s", sysfs_path, "/scale");
371     sprintf(compassSysFs.compass_orient, "%s%s", sysfs_path, "/compass_matrix");
372 #else
373     sprintf(compassSysFs.compass_enable, "%s%s", sysfs_path, "/compass_enable");
374     sprintf(compassSysFs.compass_x_fifo_enable, "%s%s", sysfs_path, "/scan_elements/in_magn_x_en");
375     sprintf(compassSysFs.compass_y_fifo_enable, "%s%s", sysfs_path, "/scan_elements/in_magn_y_en");
376     sprintf(compassSysFs.compass_z_fifo_enable, "%s%s", sysfs_path, "/scan_elements/in_magn_z_en");
377     sprintf(compassSysFs.compass_rate, "%s%s", sysfs_path, "/sampling_frequency");
378     sprintf(compassSysFs.compass_scale, "%s%s", sysfs_path, "/in_magn_scale");
379     sprintf(compassSysFs.compass_orient, "%s%s", sysfs_path, "/compass_matrix");
380 #endif
381 
382 #if SYSFS_VERBOSE
383     // test print sysfs paths
384     dptr = (char**)&compassSysFs;
385     for (i = 0; i < COMPASS_MAX_SYSFS_ATTRB; i++) {
386         LOGE("HAL:sysfs path: %s", *dptr++);
387     }
388 #endif
389     return 0;
390 }
391