1/*
2 * Copyright (C) 2014 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
35#define COMPASS_MAX_SYSFS_ATTRB sizeof(compassSysFs) / sizeof(char*)
36#define COMPASS_NAME "USE_SYSFS"
37
38#if defined COMPASS_YAS53x
39#pragma message("HAL:build Invensense compass cal with YAS53x IIO on secondary bus")
40#define USE_MPL_COMPASS_HAL (1)
41#define COMPASS_NAME        "INV_YAS530"
42
43#elif defined COMPASS_AK8975
44#pragma message("HAL:build Invensense compass cal with AK8975 on primary bus")
45#define USE_MPL_COMPASS_HAL (1)
46#define COMPASS_NAME        "INV_AK8975"
47
48#elif defined INVENSENSE_COMPASS_CAL
49#   define COMPASS_NAME                 "USE_SYSFS"
50#pragma message("HAL:build Invensense compass cal with compass IIO on secondary bus")
51#define USE_MPL_COMPASS_HAL (1)
52#else
53#pragma message("HAL:build third party compass cal HAL")
54#define USE_MPL_COMPASS_HAL (0)
55// TODO: change to vendor's name
56#define COMPASS_NAME        "AKM8975"
57
58#endif
59
60/*****************************************************************************/
61
62CompassSensor::CompassSensor()
63                  : SensorBase(NULL, NULL),
64                    compass_fd(-1),
65                    mCompassTimestamp(0),
66                    mCompassInputReader(8)
67{
68    VFUNC_LOG;
69
70    if(!strcmp(COMPASS_NAME, "USE_SYSFS")) {
71        int result = find_name_by_sensor_type("in_magn_scale", "iio:device",
72                                              sensor_name);
73        if(result) {
74            LOGE("HAL:Cannot read secondary device name - (%d)", result);
75        }
76        dev_name = sensor_name;
77    }
78    LOGI_IF(PROCESS_VERBOSE, "HAL:Secondary Chip Id: %s", dev_name);
79
80    if(inv_init_sysfs_attributes()) {
81        LOGE("Error Instantiating Compass\n");
82        return;
83    }
84
85    memset(mCachedCompassData, 0, sizeof(mCachedCompassData));
86
87    LOGV_IF(SYSFS_VERBOSE, "HAL:sysfs:cat %s (%lld)",
88            compassSysFs.compass_orient, getTimestamp());
89    FILE *fptr;
90    fptr = fopen(compassSysFs.compass_orient, "r");
91    if (fptr != NULL) {
92        int om[9];
93        if (fscanf(fptr, "%d,%d,%d,%d,%d,%d,%d,%d,%d",
94               &om[0], &om[1], &om[2], &om[3], &om[4], &om[5],
95               &om[6], &om[7], &om[8]) < 0 || fclose(fptr) < 0) {
96            LOGE("HAL:Could not read compass mounting matrix");
97        } else {
98            LOGV_IF(EXTRA_VERBOSE, "HAL:compass mounting matrix: "
99                    "%+d %+d %+d %+d %+d %+d %+d %+d %+d", om[0], om[1], om[2],
100                    om[3], om[4], om[5], om[6], om[7], om[8]);
101            mCompassOrientation[0] = om[0];
102            mCompassOrientation[1] = om[1];
103            mCompassOrientation[2] = om[2];
104            mCompassOrientation[3] = om[3];
105            mCompassOrientation[4] = om[4];
106            mCompassOrientation[5] = om[5];
107            mCompassOrientation[6] = om[6];
108            mCompassOrientation[7] = om[7];
109            mCompassOrientation[8] = om[8];
110        }
111    }
112
113    if (!isIntegrated()) {
114        enable(ID_M, 0);
115    }
116}
117
118CompassSensor::~CompassSensor()
119{
120    VFUNC_LOG;
121    free(pathP);
122    if( compass_fd > 0)
123        close(compass_fd);
124}
125
126int CompassSensor::getFd() const
127{
128    VFUNC_LOG;
129    return compass_fd;
130}
131
132/**
133 *  @brief        This function will enable/disable sensor.
134 *  @param[in]    handle
135 *                  which sensor to enable/disable.
136 *  @param[in]    en
137 *                  en=1, enable;
138 *                  en=0, disable
139 *  @return       if the operation is successful.
140 */
141int CompassSensor::enable(int32_t handle, int en)
142{
143    VFUNC_LOG;
144    int res = 0;
145    LOGV_IF(SYSFS_VERBOSE, "HAL:sysfs:echo %d > %s (%lld)",
146            en, compassSysFs.compass_enable, getTimestamp());
147    res = write_sysfs_int(compassSysFs.compass_enable, en);
148    return res;
149}
150
151int CompassSensor::setDelay(int32_t handle, int64_t ns)
152{
153    VFUNC_LOG;
154    int tempFd;
155    int res;
156
157    LOGV_IF(SYSFS_VERBOSE, "HAL:sysfs:echo %.0f > %s (%lld)",
158            1000000000.f / ns, compassSysFs.compass_rate, getTimestamp());
159    mDelay = ns;
160    if (ns == 0)
161        return -1;
162    tempFd = open(compassSysFs.compass_rate, O_RDWR);
163    res = write_attribute_sensor(tempFd, 1000000000.f / ns);
164    if(res < 0) {
165        LOGE("HAL:Compass update delay error");
166    }
167    return res;
168}
169
170int CompassSensor::turnOffCompassFifo(void)
171{
172    int i, res = 0, tempFd;
173    LOGV_IF(SYSFS_VERBOSE, "HAL:sysfs:echo %d > %s (%lld)",
174                        0, compassSysFs.compass_fifo_enable, getTimestamp());
175    res += write_sysfs_int(compassSysFs.compass_fifo_enable, 0);
176    return res;
177}
178
179int CompassSensor::turnOnCompassFifo(void)
180{
181    int i, res = 0, tempFd;
182    LOGV_IF(SYSFS_VERBOSE, "HAL:sysfs:echo %d > %s (%lld)",
183                        1, compassSysFs.compass_fifo_enable, getTimestamp());
184    res += write_sysfs_int(compassSysFs.compass_fifo_enable, 1);
185    return res;
186}
187
188/**
189    @brief      This function will return the state of the sensor.
190    @return     1=enabled; 0=disabled
191**/
192int CompassSensor::getEnable(int32_t handle)
193{
194    VFUNC_LOG;
195    return mEnable;
196}
197
198/* use for Invensense compass calibration */
199#define COMPASS_EVENT_DEBUG (0)
200void CompassSensor::processCompassEvent(const input_event *event)
201{
202    VHANDLER_LOG;
203
204    switch (event->code) {
205    case EVENT_TYPE_ICOMPASS_X:
206        LOGV_IF(COMPASS_EVENT_DEBUG, "EVENT_TYPE_ICOMPASS_X\n");
207        mCachedCompassData[0] = event->value;
208        break;
209    case EVENT_TYPE_ICOMPASS_Y:
210        LOGV_IF(COMPASS_EVENT_DEBUG, "EVENT_TYPE_ICOMPASS_Y\n");
211        mCachedCompassData[1] = event->value;
212        break;
213    case EVENT_TYPE_ICOMPASS_Z:
214        LOGV_IF(COMPASS_EVENT_DEBUG, "EVENT_TYPE_ICOMPASS_Z\n");
215        mCachedCompassData[2] = event->value;
216        break;
217    }
218
219    mCompassTimestamp =
220        (int64_t)event->time.tv_sec * 1000000000L + event->time.tv_usec * 1000L;
221}
222
223void CompassSensor::getOrientationMatrix(signed char *orient)
224{
225    VFUNC_LOG;
226    memcpy(orient, mCompassOrientation, sizeof(mCompassOrientation));
227}
228
229long CompassSensor::getSensitivity()
230{
231    VFUNC_LOG;
232
233    long sensitivity;
234    LOGV_IF(SYSFS_VERBOSE, "HAL:sysfs:cat %s (%lld)",
235            compassSysFs.compass_scale, getTimestamp());
236    inv_read_data(compassSysFs.compass_scale, &sensitivity);
237    return sensitivity;
238}
239
240/**
241    @brief         This function is called by sensors_mpl.cpp
242                   to read sensor data from the driver.
243    @param[out]    data      sensor data is stored in this variable. Scaled such that
244                             1 uT = 2^16
245    @para[in]      timestamp data's timestamp
246    @return        1, if 1   sample read, 0, if not, negative if error
247 */
248int CompassSensor::readSample(long *data, int64_t *timestamp)
249{
250    VHANDLER_LOG;
251
252    int numEventReceived = 0, done = 0;
253
254    ssize_t n = mCompassInputReader.fill(compass_fd);
255    if (n < 0) {
256        LOGE("HAL:no compass events read");
257        return n;
258    }
259
260    input_event const* event;
261
262    while (done == 0 && mCompassInputReader.readEvent(&event)) {
263        int type = event->type;
264        if (type == EV_REL) {
265            processCompassEvent(event);
266        } else if (type == EV_SYN) {
267            *timestamp = mCompassTimestamp;
268            memcpy(data, mCachedCompassData, sizeof(mCachedCompassData));
269            done = 1;
270        } else {
271            LOGE("HAL:Compass Sensor: unknown event (type=%d, code=%d)",
272                 type, event->code);
273        }
274        mCompassInputReader.next();
275    }
276
277    return done;
278}
279
280/**
281 *  @brief  This function will return the current delay for this sensor.
282 *  @return delay in nanoseconds.
283 */
284int64_t CompassSensor::getDelay(int32_t handle)
285{
286    VFUNC_LOG;
287    return mDelay;
288}
289
290void CompassSensor::fillList(struct sensor_t *list)
291{
292    VFUNC_LOG;
293
294    const char *compass = sensor_name;
295
296    if (compass) {
297        if(!strcmp(compass, "INV_COMPASS")) {
298            list->maxRange = COMPASS_MPU9150_RANGE;
299            list->resolution = COMPASS_MPU9150_RESOLUTION;
300            list->power = COMPASS_MPU9150_POWER;
301            list->minDelay = COMPASS_MPU9150_MINDELAY;
302            return;
303        }
304        if(!strcmp(compass, "compass")
305                || !strcmp(compass, "INV_AK8975")
306                || !strcmp(compass, "AK8975")
307                || !strcmp(compass, "ak8975")) {
308            list->maxRange = COMPASS_AKM8975_RANGE;
309            list->resolution = COMPASS_AKM8975_RESOLUTION;
310            list->power = COMPASS_AKM8975_POWER;
311            list->minDelay = COMPASS_AKM8975_MINDELAY;
312            return;
313        }
314        if(!strcmp(compass, "compass")
315                || !strcmp(compass, "INV_AK8963")
316                || !strcmp(compass, "AK8963")
317                || !strcmp(compass, "ak8963")) {
318            list->maxRange = COMPASS_AKM8963_RANGE;
319            list->resolution = COMPASS_AKM8963_RESOLUTION;
320            list->power = COMPASS_AKM8963_POWER;
321            list->minDelay = COMPASS_AKM8963_MINDELAY;
322            return;
323        }
324        if(!strcmp(compass, "compass")
325                || !strcmp(compass, "INV_AK09911")
326                || !strcmp(compass, "AK09911")
327                || !strcmp(compass, "ak09911")) {
328            list->maxRange = COMPASS_AKM9911_RANGE;
329            list->resolution = COMPASS_AKM9911_RESOLUTION;
330            list->power = COMPASS_AKM9911_POWER;
331            list->minDelay = COMPASS_AKM9911_MINDELAY;
332            return;
333        }
334        if(!strcmp(compass, "compass")
335                || !strncmp(compass, "mlx90399",3)
336                || !strncmp(compass, "MLX90399",3)) {
337            list->maxRange = COMPASS_MPU9350_RANGE;
338            list->resolution = COMPASS_MPU9350_RESOLUTION;
339            list->power = COMPASS_MPU9350_POWER;
340            list->minDelay = COMPASS_MPU9350_MINDELAY;
341            return;
342        }
343        if(!strcmp(compass, "INV_YAS530")) {
344            list->maxRange = COMPASS_YAS53x_RANGE;
345            list->resolution = COMPASS_YAS53x_RESOLUTION;
346            list->power = COMPASS_YAS53x_POWER;
347            list->minDelay = COMPASS_YAS53x_MINDELAY;
348            return;
349        }
350        if(!strcmp(compass, "INV_AMI306")) {
351            list->maxRange = COMPASS_AMI306_RANGE;
352            list->resolution = COMPASS_AMI306_RESOLUTION;
353            list->power = COMPASS_AMI306_POWER;
354            list->minDelay = COMPASS_AMI306_MINDELAY;
355            return;
356        }
357    }
358    LOGE("HAL:unknown compass id %s -- "
359         "params default to ak8975 and might be wrong.",
360         compass);
361    list->maxRange = COMPASS_AKM8975_RANGE;
362    list->resolution = COMPASS_AKM8975_RESOLUTION;
363    list->power = COMPASS_AKM8975_POWER;
364    list->minDelay = COMPASS_AKM8975_MINDELAY;
365}
366
367int CompassSensor::inv_init_sysfs_attributes(void)
368{
369    VFUNC_LOG;
370
371    unsigned char i = 0;
372    char sysfs_path[MAX_SYSFS_NAME_LEN];
373    char iio_trigger_path[MAX_SYSFS_NAME_LEN], tbuf[2];
374    char *sptr;
375    char **dptr;
376    int num;
377
378    pathP = (char*)malloc(
379                    sizeof(char[COMPASS_MAX_SYSFS_ATTRB][MAX_SYSFS_NAME_LEN]));
380    sptr = pathP;
381    dptr = (char**)&compassSysFs;
382    if (sptr == NULL)
383        return -1;
384
385    memset(sysfs_path, 0, sizeof(sysfs_path));
386    memset(iio_trigger_path, 0, sizeof(iio_trigger_path));
387
388    do {
389        *dptr++ = sptr;
390        memset(sptr, 0, sizeof(sptr));
391        sptr += sizeof(char[MAX_SYSFS_NAME_LEN]);
392    } while (++i < COMPASS_MAX_SYSFS_ATTRB);
393
394    // get proper (in absolute/relative) IIO path & build MPU's sysfs paths
395    // inv_get_sysfs_abs_path(sysfs_path);
396    inv_get_sysfs_path(sysfs_path);
397    inv_get_iio_trigger_path(iio_trigger_path);
398
399#if defined COMPASS_AK8975
400    inv_get_input_number(dev_name, &num);
401    tbuf[0] = num + 0x30;
402    tbuf[1] = 0;
403    sprintf(sysfs_path, "%s%s", "sys/class/input/input", tbuf);
404    strcat(sysfs_path, "/ak8975");
405
406    sprintf(compassSysFs.compass_enable, "%s%s", sysfs_path, "/enable");
407    sprintf(compassSysFs.compass_rate, "%s%s", sysfs_path, "/rate");
408    sprintf(compassSysFs.compass_scale, "%s%s", sysfs_path, "/scale");
409    sprintf(compassSysFs.compass_orient, "%s%s", sysfs_path, "/compass_matrix");
410#else
411    sprintf(compassSysFs.compass_enable, "%s%s", sysfs_path, "/compass_enable");
412    sprintf(compassSysFs.compass_fifo_enable, "%s%s", sysfs_path, "/compass_fifo_enable");
413    sprintf(compassSysFs.compass_rate, "%s%s", sysfs_path, "/compass_rate");
414    sprintf(compassSysFs.compass_scale, "%s%s", sysfs_path, "/in_magn_scale");
415    sprintf(compassSysFs.compass_orient, "%s%s", sysfs_path, "/compass_matrix");
416#endif
417
418#if 0
419    // test print sysfs paths
420    dptr = (char**)&compassSysFs;
421    for (i = 0; i < COMPASS_MAX_SYSFS_ATTRB; i++) {
422        LOGE("HAL:sysfs path: %s", *dptr++);
423    }
424#endif
425    return 0;
426}
427