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.primary.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_AK8975
39#pragma message("HAL:build Invensense compass cal with AK8975 on primary bus")
40#define USE_MPL_COMPASS_HAL (1)
41#define COMPASS_NAME        "INV_AK8975"
42#endif
43
44/******************************************************************************/
45
46CompassSensor::CompassSensor()
47                  : SensorBase(COMPASS_NAME, NULL),
48                    mCompassTimestamp(0),
49                    mCompassInputReader(8),
50                    mCoilsResetFd(0)
51{
52    FILE *fptr;
53
54    VFUNC_LOG;
55
56    mYasCompass = false;
57    if(!strcmp(dev_name, "USE_SYSFS")) {
58        char sensor_name[20];
59        find_name_by_sensor_type("in_magn_x_raw", "iio:device", sensor_name);
60        strncpy(dev_full_name, sensor_name,
61                sizeof(dev_full_name) / sizeof(dev_full_name[0]));
62        if(!strncmp(dev_full_name, "yas", 3)) {
63            mYasCompass = true;
64        }
65    } else {
66
67#ifdef COMPASS_YAS53x
68        /* for YAS53x compasses, dev_name is just a prefix,
69           we need to find the actual name */
70        if (fill_dev_full_name_by_prefix(dev_name,
71                dev_full_name, sizeof(dev_full_name) / sizeof(dev_full_name[0]))) {
72            LOGE("Cannot find Yamaha device with prefix name '%s' - "
73                 "magnetometer will likely not work.", dev_name);
74        } else {
75            mYasCompass = true;
76        }
77#else
78        strncpy(dev_full_name, dev_name,
79                sizeof(dev_full_name) / sizeof(dev_full_name[0]));
80#endif
81
82}
83
84    if (inv_init_sysfs_attributes()) {
85        LOGE("Error Instantiating Compass\n");
86        return;
87    }
88
89    if (!strcmp(dev_full_name, "INV_COMPASS")) {
90        mI2CBus = COMPASS_BUS_SECONDARY;
91    } else {
92        mI2CBus = COMPASS_BUS_PRIMARY;
93    }
94
95    memset(mCachedCompassData, 0, sizeof(mCachedCompassData));
96
97    if (!isIntegrated()) {
98        enable(ID_M, 0);
99    }
100
101    LOGV_IF(SYSFS_VERBOSE, "HAL:compass name: %s", dev_full_name);
102    enable_iio_sysfs();
103
104    LOGV_IF(SYSFS_VERBOSE, "HAL:sysfs:cat %s (%lld)",
105            compassSysFs.compass_orient, getTimestamp());
106    fptr = fopen(compassSysFs.compass_orient, "r");
107    if (fptr != NULL) {
108        int om[9];
109        if (fscanf(fptr, "%d,%d,%d,%d,%d,%d,%d,%d,%d",
110               &om[0], &om[1], &om[2], &om[3], &om[4], &om[5],
111               &om[6], &om[7], &om[8]) < 0 || fclose(fptr)) {
112            LOGE("HAL:could not read compass mounting matrix");
113        } else {
114
115            LOGV_IF(EXTRA_VERBOSE,
116                    "HAL:compass mounting matrix: "
117                    "%+d %+d %+d %+d %+d %+d %+d %+d %+d",
118                    om[0], om[1], om[2], om[3], om[4], om[5], om[6], om[7], om[8]);
119
120            mCompassOrientation[0] = om[0];
121            mCompassOrientation[1] = om[1];
122            mCompassOrientation[2] = om[2];
123            mCompassOrientation[3] = om[3];
124            mCompassOrientation[4] = om[4];
125            mCompassOrientation[5] = om[5];
126            mCompassOrientation[6] = om[6];
127            mCompassOrientation[7] = om[7];
128            mCompassOrientation[8] = om[8];
129        }
130    }
131
132    if(mYasCompass) {
133        mCoilsResetFd = fopen(compassSysFs.compass_attr_1, "r+");
134        if (fptr == NULL) {
135            LOGE("HAL:Could not open compass overunderflow");
136        }
137    }
138}
139
140void CompassSensor::enable_iio_sysfs()
141{
142    VFUNC_LOG;
143
144    int tempFd = 0;
145    char iio_device_node[MAX_CHIP_ID_LEN];
146    FILE *tempFp = NULL;
147    const char* compass = dev_full_name;
148
149    LOGV_IF(SYSFS_VERBOSE, "HAL:sysfs:echo %d > %s (%lld)",
150            1, compassSysFs.in_timestamp_en, getTimestamp());
151    write_sysfs_int(compassSysFs.in_timestamp_en, 1);
152
153    LOGV_IF(SYSFS_VERBOSE, "HAL:sysfs:echo %d > %s (%lld)",
154            IIO_BUFFER_LENGTH, compassSysFs.buffer_length, getTimestamp());
155    tempFp = fopen(compassSysFs.buffer_length, "w");
156    if (tempFp == NULL) {
157        LOGE("HAL:could not open buffer length");
158    } else {
159        if (fprintf(tempFp, "%d", IIO_BUFFER_LENGTH) < 0 || fclose(tempFp) < 0) {
160            LOGE("HAL:could not write buffer length");
161        }
162    }
163
164    sprintf(iio_device_node, "%s%d", "/dev/iio:device",
165            find_type_by_name(compass, "iio:device"));
166    compass_fd = open(iio_device_node, O_RDONLY);
167    int res = errno;
168    if (compass_fd < 0) {
169        LOGE("HAL:could not open '%s' iio device node in path '%s' - "
170             "error '%s' (%d)",
171             compass, iio_device_node, strerror(res), res);
172    } else {
173        LOGV_IF(EXTRA_VERBOSE,
174                "HAL:iio %s, compass_fd opened : %d", compass, compass_fd);
175    }
176
177    /* TODO: need further tests for optimization to reduce context-switch
178    LOGV_IF(SYSFS_VERBOSE, "HAL:sysfs:echo 1 > %s (%lld)",
179            compassSysFs.compass_x_fifo_enable, getTimestamp());
180    tempFd = open(compassSysFs.compass_x_fifo_enable, O_RDWR);
181    res = errno;
182    if (tempFd > 0) {
183        res = enable_sysfs_sensor(tempFd, 1);
184    } else {
185        LOGE("HAL:open of %s failed with '%s' (%d)",
186             compassSysFs.compass_x_fifo_enable, strerror(res), res);
187    }
188
189    LOGV_IF(SYSFS_VERBOSE, "HAL:sysfs:echo 1 > %s (%lld)",
190            compassSysFs.compass_y_fifo_enable, getTimestamp());
191    tempFd = open(compassSysFs.compass_y_fifo_enable, O_RDWR);
192    res = errno;
193    if (tempFd > 0) {
194        res = enable_sysfs_sensor(tempFd, 1);
195    } else {
196        LOGE("HAL:open of %s failed with '%s' (%d)",
197             compassSysFs.compass_y_fifo_enable, strerror(res), res);
198    }
199
200    LOGV_IF(SYSFS_VERBOSE, "HAL:sysfs:echo 1 > %s (%lld)",
201            compassSysFs.compass_z_fifo_enable, getTimestamp());
202    tempFd = open(compassSysFs.compass_z_fifo_enable, O_RDWR);
203    res = errno;
204    if (tempFd > 0) {
205        res = enable_sysfs_sensor(tempFd, 1);
206    } else {
207        LOGE("HAL:open of %s failed with '%s' (%d)",
208             compassSysFs.compass_z_fifo_enable, strerror(res), res);
209    }
210    */
211}
212
213CompassSensor::~CompassSensor()
214{
215    VFUNC_LOG;
216
217    free(pathP);
218    if( compass_fd > 0)
219        close(compass_fd);
220    if(mYasCompass) {
221        if( mCoilsResetFd != NULL )
222            fclose(mCoilsResetFd);
223    }
224}
225
226int CompassSensor::getFd(void) const
227{
228    VHANDLER_LOG;
229    LOGI_IF(0, "HAL:compass_fd=%d", compass_fd);
230    return compass_fd;
231}
232
233/**
234 *  @brief        This function will enable/disable sensor.
235 *  @param[in]    handle
236 *                  which sensor to enable/disable.
237 *  @param[in]    en
238 *                  en=1, enable;
239 *                  en=0, disable
240 *  @return       if the operation is successful.
241 */
242int CompassSensor::enable(int32_t handle, int en)
243{
244    VFUNC_LOG;
245
246    mEnable = en;
247    int tempFd;
248    int res = 0;
249
250    /* reset master enable */
251    res = masterEnable(0);
252    if (res < 0) {
253        return res;
254    }
255
256    if (en) {
257        LOGV_IF(SYSFS_VERBOSE, "HAL:sysfs:echo %d > %s (%lld)",
258                en, compassSysFs.compass_x_fifo_enable, getTimestamp());
259        res = write_sysfs_int(compassSysFs.compass_x_fifo_enable, en);
260        LOGV_IF(SYSFS_VERBOSE, "HAL:sysfs:echo %d > %s (%lld)",
261                en, compassSysFs.compass_y_fifo_enable, getTimestamp());
262        res += write_sysfs_int(compassSysFs.compass_y_fifo_enable, en);
263        LOGV_IF(SYSFS_VERBOSE, "HAL:sysfs:echo %d > %s (%lld)",
264                en, compassSysFs.compass_z_fifo_enable, getTimestamp());
265        res += write_sysfs_int(compassSysFs.compass_z_fifo_enable, en);
266
267        res = masterEnable(en);
268        if (res < en) {
269            return res;
270        }
271    }
272
273    return res;
274}
275
276int CompassSensor::masterEnable(int en)
277{
278    VFUNC_LOG;
279    LOGV_IF(SYSFS_VERBOSE, "HAL:sysfs:echo %d > %s (%lld)",
280            en, compassSysFs.chip_enable, getTimestamp());
281    return write_sysfs_int(compassSysFs.chip_enable, en);
282}
283
284int CompassSensor::setDelay(int32_t handle, int64_t ns)
285{
286    VFUNC_LOG;
287    int tempFd;
288    int res;
289
290    mDelay = ns;
291    if (ns == 0)
292        return -1;
293    tempFd = open(compassSysFs.compass_rate, O_RDWR);
294    LOGV_IF(SYSFS_VERBOSE, "HAL:sysfs:echo %.0f > %s (%lld)",
295            1000000000.f / ns, compassSysFs.compass_rate, getTimestamp());
296    res = write_attribute_sensor(tempFd, 1000000000.f / ns);
297    if(res < 0) {
298        LOGE("HAL:Compass update delay error");
299    }
300    return res;
301}
302
303/**
304    @brief      This function will return the state of the sensor.
305    @return     1=enabled; 0=disabled
306**/
307int CompassSensor::getEnable(int32_t handle)
308{
309    VFUNC_LOG;
310    return mEnable;
311}
312
313/* use for Invensense compass calibration */
314#define COMPASS_EVENT_DEBUG (0)
315void CompassSensor::processCompassEvent(const input_event *event)
316{
317    VHANDLER_LOG;
318
319    switch (event->code) {
320    case EVENT_TYPE_ICOMPASS_X:
321        LOGV_IF(COMPASS_EVENT_DEBUG, "EVENT_TYPE_ICOMPASS_X\n");
322        mCachedCompassData[0] = event->value;
323        break;
324    case EVENT_TYPE_ICOMPASS_Y:
325        LOGV_IF(COMPASS_EVENT_DEBUG, "EVENT_TYPE_ICOMPASS_Y\n");
326        mCachedCompassData[1] = event->value;
327        break;
328    case EVENT_TYPE_ICOMPASS_Z:
329        LOGV_IF(COMPASS_EVENT_DEBUG, "EVENT_TYPE_ICOMPASS_Z\n");
330        mCachedCompassData[2] = event->value;
331        break;
332    }
333
334    mCompassTimestamp =
335        (int64_t)event->time.tv_sec * 1000000000L + event->time.tv_usec * 1000L;
336}
337
338void CompassSensor::getOrientationMatrix(signed char *orient)
339{
340    VFUNC_LOG;
341    memcpy(orient, mCompassOrientation, sizeof(mCompassOrientation));
342}
343
344long CompassSensor::getSensitivity()
345{
346    VFUNC_LOG;
347
348    long sensitivity;
349    LOGV_IF(SYSFS_VERBOSE, "HAL:sysfs:cat %s (%lld)",
350            compassSysFs.compass_scale, getTimestamp());
351    inv_read_data(compassSysFs.compass_scale, &sensitivity);
352    return sensitivity;
353}
354
355/**
356    @brief         This function is called by sensors_mpl.cpp
357                   to read sensor data from the driver.
358    @param[out]    data      sensor data is stored in this variable. Scaled such that
359                             1 uT = 2^16
360    @para[in]      timestamp data's timestamp
361    @return        1, if 1   sample read, 0, if not, negative if error
362 */
363int CompassSensor::readSample(long *data, int64_t *timestamp) {
364    VFUNC_LOG;
365
366    int i;
367    char *rdata = mIIOBuffer;
368
369    size_t rsize = read(compass_fd, rdata, (8 * mEnable + 8) * 1);
370
371    if (!mEnable) {
372        rsize = read(compass_fd, rdata, (8 + 8) * IIO_BUFFER_LENGTH);
373        // LOGI("clear buffer with size: %d", rsize);
374    }
375/*
376    LOGI("get one sample of AMI IIO data with size: %d", rsize);
377    LOGI_IF(mEnable, "compass x/y/z: %d/%d/%d", *((short *) (rdata + 0)),
378        *((short *) (rdata + 2)), *((short *) (rdata + 4)));
379*/
380    if (mEnable) {
381        for (i = 0; i < 3; i++) {
382            data[i] = *((short *) (rdata + i * 2));
383        }
384        *timestamp = *((long long *) (rdata + 8 * mEnable));
385    }
386
387    return mEnable;
388}
389
390/**
391 *  @brief  This function will return the current delay for this sensor.
392 *  @return delay in nanoseconds.
393 */
394int64_t CompassSensor::getDelay(int32_t handle)
395{
396    VFUNC_LOG;
397    return mDelay;
398}
399
400void CompassSensor::fillList(struct sensor_t *list)
401{
402    VFUNC_LOG;
403
404    const char *compass = dev_full_name;
405
406    if (compass) {
407        if(!strcmp(compass, "INV_COMPASS")) {
408            list->maxRange = COMPASS_MPU9150_RANGE;
409            list->resolution = COMPASS_MPU9150_RESOLUTION;
410            list->power = COMPASS_MPU9150_POWER;
411            list->minDelay = COMPASS_MPU9150_MINDELAY;
412            mMinDelay = list->minDelay;
413            return;
414        }
415        if(!strcmp(compass, "compass")
416                || !strcmp(compass, "INV_AK8975")
417                || !strncmp(compass, "ak89xx", 2)) {
418            list->maxRange = COMPASS_AKM8975_RANGE;
419            list->resolution = COMPASS_AKM8975_RESOLUTION;
420            list->power = COMPASS_AKM8975_POWER;
421            list->minDelay = COMPASS_AKM8975_MINDELAY;
422            mMinDelay = list->minDelay;
423            return;
424        }
425        if(!strcmp(compass, "ami306")) {
426            list->maxRange = COMPASS_AMI306_RANGE;
427            list->resolution = COMPASS_AMI306_RESOLUTION;
428            list->power = COMPASS_AMI306_POWER;
429            list->minDelay = COMPASS_AMI306_MINDELAY;
430            mMinDelay = list->minDelay;
431            return;
432        }
433        if(!strcmp(compass, "yas530")
434                || !strcmp(compass, "yas532")
435                || !strcmp(compass, "yas533")) {
436            list->maxRange = COMPASS_YAS53x_RANGE;
437            list->resolution = COMPASS_YAS53x_RESOLUTION;
438            list->power = COMPASS_YAS53x_POWER;
439            list->minDelay = COMPASS_YAS53x_MINDELAY;
440            mMinDelay = list->minDelay;
441            return;
442        }
443    }
444
445    LOGE("HAL:unknown compass id %s -- "
446         "params default to ak8975 and might be wrong.",
447         compass);
448    list->maxRange = COMPASS_AKM8975_RANGE;
449    list->resolution = COMPASS_AKM8975_RESOLUTION;
450    list->power = COMPASS_AKM8975_POWER;
451    list->minDelay = COMPASS_AKM8975_MINDELAY;
452    mMinDelay = list->minDelay;
453}
454
455/* Read sysfs entry to determine whether overflow had happend
456   then write to sysfs to reset to zero */
457int CompassSensor::checkCoilsReset()
458{
459    int result=-1;
460    VFUNC_LOG;
461
462    if(mCoilsResetFd != NULL) {
463        int attr;
464        rewind(mCoilsResetFd);
465        fscanf(mCoilsResetFd, "%d", &attr);
466        if(attr == 0)
467            return 0;
468        else {
469            LOGV_IF(SYSFS_VERBOSE, "HAL:overflow detected");
470            rewind(mCoilsResetFd);
471            if(fprintf(mCoilsResetFd, "%d", 0) < 0)
472                LOGE("HAL:could not write overunderflow");
473            else
474                return 1;
475        }
476    } else {
477        LOGE("HAL:could not read overunderflow");
478    }
479    return result;
480}
481
482int CompassSensor::inv_init_sysfs_attributes(void)
483{
484    VFUNC_LOG;
485
486    unsigned char i = 0;
487    char sysfs_path[MAX_SYSFS_NAME_LEN], tbuf[2];
488    char *sptr;
489    char **dptr;
490    int num;
491    const char* compass = dev_full_name;
492
493    pathP = (char*)malloc(
494                    sizeof(char[COMPASS_MAX_SYSFS_ATTRB][MAX_SYSFS_NAME_LEN]));
495    sptr = pathP;
496    dptr = (char**)&compassSysFs;
497    if (sptr == NULL)
498        return -1;
499
500    do {
501        *dptr++ = sptr;
502        sptr += sizeof(char[MAX_SYSFS_NAME_LEN]);
503    } while (++i < COMPASS_MAX_SYSFS_ATTRB);
504
505    // get proper (in absolute/relative) IIO path & build sysfs paths
506    sprintf(sysfs_path, "%s%d", "/sys/bus/iio/devices/iio:device",
507    find_type_by_name(compass, "iio:device"));
508
509#if defined COMPASS_AK8975
510    inv_get_input_number(compass, &num);
511    tbuf[0] = num + 0x30;
512    tbuf[1] = 0;
513    sprintf(sysfs_path, "%s%s", "sys/class/input/input", tbuf);
514    strcat(sysfs_path, "/ak8975");
515
516    sprintf(compassSysFs.compass_enable, "%s%s", sysfs_path, "/enable");
517    sprintf(compassSysFs.compass_rate, "%s%s", sysfs_path, "/rate");
518    sprintf(compassSysFs.compass_scale, "%s%s", sysfs_path, "/scale");
519    sprintf(compassSysFs.compass_orient, "%s%s", sysfs_path, "/compass_matrix");
520#else /* IIO */
521    sprintf(compassSysFs.chip_enable, "%s%s", sysfs_path, "/buffer/enable");
522    sprintf(compassSysFs.in_timestamp_en, "%s%s", sysfs_path, "/scan_elements/in_timestamp_en");
523    sprintf(compassSysFs.buffer_length, "%s%s", sysfs_path, "/buffer/length");
524
525    sprintf(compassSysFs.compass_x_fifo_enable, "%s%s", sysfs_path, "/scan_elements/in_magn_x_en");
526    sprintf(compassSysFs.compass_y_fifo_enable, "%s%s", sysfs_path, "/scan_elements/in_magn_y_en");
527    sprintf(compassSysFs.compass_z_fifo_enable, "%s%s", sysfs_path, "/scan_elements/in_magn_z_en");
528    sprintf(compassSysFs.compass_rate, "%s%s", sysfs_path, "/sampling_frequency");
529    sprintf(compassSysFs.compass_scale, "%s%s", sysfs_path, "/in_magn_scale");
530    sprintf(compassSysFs.compass_orient, "%s%s", sysfs_path, "/compass_matrix");
531
532    if(mYasCompass) {
533        sprintf(compassSysFs.compass_attr_1, "%s%s", sysfs_path, "/overunderflow");
534    }
535#endif
536
537#if 0
538    // test print sysfs paths
539    dptr = (char**)&compassSysFs;
540    LOGI("sysfs path base: %s", sysfs_path);
541    for (i = 0; i < COMPASS_MAX_SYSFS_ATTRB; i++) {
542        LOGE("HAL:sysfs path: %s", *dptr++);
543    }
544#endif
545    return 0;
546}
547
548int CompassSensor::isYasCompass(void)
549{
550    return mYasCompass;
551}
552