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
60CompassSensor::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
115CompassSensor::~CompassSensor()
116{
117    VFUNC_LOG;
118
119    free(pathP);
120    if( compass_fd > 0)
121        close(compass_fd);
122}
123
124int 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 */
139int 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
158int 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**/
182int 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)
190void 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
213void CompassSensor::getOrientationMatrix(signed char *orient)
214{
215    VFUNC_LOG;
216    memcpy(orient, mCompassOrientation, sizeof(mCompassOrientation));
217}
218
219long 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 */
238int 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 */
274int64_t CompassSensor::getDelay(int32_t handle)
275{
276    VFUNC_LOG;
277    return mDelay;
278}
279
280void 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
330int 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