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