Home | History | Annotate | Download | only in libsensors_iio
      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 
     46 CompassSensor::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 
    140 void 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 
    213 CompassSensor::~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 
    226 int 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  */
    242 int 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 
    276 int 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 
    284 int 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 **/
    307 int 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)
    315 void 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 
    338 void CompassSensor::getOrientationMatrix(signed char *orient)
    339 {
    340     VFUNC_LOG;
    341     memcpy(orient, mCompassOrientation, sizeof(mCompassOrientation));
    342 }
    343 
    344 long 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  */
    363 int 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  */
    394 int64_t CompassSensor::getDelay(int32_t handle)
    395 {
    396     VFUNC_LOG;
    397     return mDelay;
    398 }
    399 
    400 void 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 */
    457 int 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 
    482 int 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 
    548 int CompassSensor::isYasCompass(void)
    549 {
    550     return mYasCompass;
    551 }
    552