/* * Copyright (C) 2012 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define LOG_NDEBUG 0 #include <fcntl.h> #include <errno.h> #include <math.h> #include <unistd.h> #include <dirent.h> #include <sys/select.h> #include <cutils/log.h> #include <linux/input.h> #include <string.h> #include "CompassSensor.IIO.primary.h" #include "sensors.h" #include "MPLSupport.h" #include "sensor_params.h" #include "ml_sysfs_helper.h" #define COMPASS_MAX_SYSFS_ATTRB sizeof(compassSysFs) / sizeof(char*) #define COMPASS_NAME "USE_SYSFS" #if defined COMPASS_AK8975 #pragma message("HAL:build Invensense compass cal with AK8975 on primary bus") #define USE_MPL_COMPASS_HAL (1) #define COMPASS_NAME "INV_AK8975" #endif /******************************************************************************/ CompassSensor::CompassSensor() : SensorBase(COMPASS_NAME, NULL), mCompassTimestamp(0), mCompassInputReader(8), mCoilsResetFd(0) { FILE *fptr; VFUNC_LOG; mYasCompass = false; if(!strcmp(dev_name, "USE_SYSFS")) { char sensor_name[20]; find_name_by_sensor_type("in_magn_x_raw", "iio:device", sensor_name); strncpy(dev_full_name, sensor_name, sizeof(dev_full_name) / sizeof(dev_full_name[0])); if(!strncmp(dev_full_name, "yas", 3)) { mYasCompass = true; } } else { #ifdef COMPASS_YAS53x /* for YAS53x compasses, dev_name is just a prefix, we need to find the actual name */ if (fill_dev_full_name_by_prefix(dev_name, dev_full_name, sizeof(dev_full_name) / sizeof(dev_full_name[0]))) { LOGE("Cannot find Yamaha device with prefix name '%s' - " "magnetometer will likely not work.", dev_name); } else { mYasCompass = true; } #else strncpy(dev_full_name, dev_name, sizeof(dev_full_name) / sizeof(dev_full_name[0])); #endif } if (inv_init_sysfs_attributes()) { LOGE("Error Instantiating Compass\n"); return; } if (!strcmp(dev_full_name, "INV_COMPASS")) { mI2CBus = COMPASS_BUS_SECONDARY; } else { mI2CBus = COMPASS_BUS_PRIMARY; } memset(mCachedCompassData, 0, sizeof(mCachedCompassData)); if (!isIntegrated()) { enable(ID_M, 0); } LOGV_IF(SYSFS_VERBOSE, "HAL:compass name: %s", dev_full_name); enable_iio_sysfs(); LOGV_IF(SYSFS_VERBOSE, "HAL:sysfs:cat %s (%lld)", compassSysFs.compass_orient, getTimestamp()); fptr = fopen(compassSysFs.compass_orient, "r"); if (fptr != NULL) { int om[9]; if (fscanf(fptr, "%d,%d,%d,%d,%d,%d,%d,%d,%d", &om[0], &om[1], &om[2], &om[3], &om[4], &om[5], &om[6], &om[7], &om[8]) < 0 || fclose(fptr)) { LOGE("HAL:could not read compass mounting matrix"); } else { LOGV_IF(EXTRA_VERBOSE, "HAL:compass mounting matrix: " "%+d %+d %+d %+d %+d %+d %+d %+d %+d", om[0], om[1], om[2], om[3], om[4], om[5], om[6], om[7], om[8]); mCompassOrientation[0] = om[0]; mCompassOrientation[1] = om[1]; mCompassOrientation[2] = om[2]; mCompassOrientation[3] = om[3]; mCompassOrientation[4] = om[4]; mCompassOrientation[5] = om[5]; mCompassOrientation[6] = om[6]; mCompassOrientation[7] = om[7]; mCompassOrientation[8] = om[8]; } } if(mYasCompass) { mCoilsResetFd = fopen(compassSysFs.compass_attr_1, "r+"); if (fptr == NULL) { LOGE("HAL:Could not open compass overunderflow"); } } } void CompassSensor::enable_iio_sysfs() { VFUNC_LOG; int tempFd = 0; char iio_device_node[MAX_CHIP_ID_LEN]; FILE *tempFp = NULL; const char* compass = dev_full_name; LOGV_IF(SYSFS_VERBOSE, "HAL:sysfs:echo %d > %s (%lld)", 1, compassSysFs.in_timestamp_en, getTimestamp()); write_sysfs_int(compassSysFs.in_timestamp_en, 1); LOGV_IF(SYSFS_VERBOSE, "HAL:sysfs:echo %d > %s (%lld)", IIO_BUFFER_LENGTH, compassSysFs.buffer_length, getTimestamp()); tempFp = fopen(compassSysFs.buffer_length, "w"); if (tempFp == NULL) { LOGE("HAL:could not open buffer length"); } else { if (fprintf(tempFp, "%d", IIO_BUFFER_LENGTH) < 0 || fclose(tempFp) < 0) { LOGE("HAL:could not write buffer length"); } } sprintf(iio_device_node, "%s%d", "/dev/iio:device", find_type_by_name(compass, "iio:device")); compass_fd = open(iio_device_node, O_RDONLY); int res = errno; if (compass_fd < 0) { LOGE("HAL:could not open '%s' iio device node in path '%s' - " "error '%s' (%d)", compass, iio_device_node, strerror(res), res); } else { LOGV_IF(EXTRA_VERBOSE, "HAL:iio %s, compass_fd opened : %d", compass, compass_fd); } /* TODO: need further tests for optimization to reduce context-switch LOGV_IF(SYSFS_VERBOSE, "HAL:sysfs:echo 1 > %s (%lld)", compassSysFs.compass_x_fifo_enable, getTimestamp()); tempFd = open(compassSysFs.compass_x_fifo_enable, O_RDWR); res = errno; if (tempFd > 0) { res = enable_sysfs_sensor(tempFd, 1); } else { LOGE("HAL:open of %s failed with '%s' (%d)", compassSysFs.compass_x_fifo_enable, strerror(res), res); } LOGV_IF(SYSFS_VERBOSE, "HAL:sysfs:echo 1 > %s (%lld)", compassSysFs.compass_y_fifo_enable, getTimestamp()); tempFd = open(compassSysFs.compass_y_fifo_enable, O_RDWR); res = errno; if (tempFd > 0) { res = enable_sysfs_sensor(tempFd, 1); } else { LOGE("HAL:open of %s failed with '%s' (%d)", compassSysFs.compass_y_fifo_enable, strerror(res), res); } LOGV_IF(SYSFS_VERBOSE, "HAL:sysfs:echo 1 > %s (%lld)", compassSysFs.compass_z_fifo_enable, getTimestamp()); tempFd = open(compassSysFs.compass_z_fifo_enable, O_RDWR); res = errno; if (tempFd > 0) { res = enable_sysfs_sensor(tempFd, 1); } else { LOGE("HAL:open of %s failed with '%s' (%d)", compassSysFs.compass_z_fifo_enable, strerror(res), res); } */ } CompassSensor::~CompassSensor() { VFUNC_LOG; free(pathP); if( compass_fd > 0) close(compass_fd); if(mYasCompass) { if( mCoilsResetFd != NULL ) fclose(mCoilsResetFd); } } int CompassSensor::getFd(void) const { VHANDLER_LOG; LOGI_IF(0, "HAL:compass_fd=%d", compass_fd); return compass_fd; } /** * @brief This function will enable/disable sensor. * @param[in] handle * which sensor to enable/disable. * @param[in] en * en=1, enable; * en=0, disable * @return if the operation is successful. */ int CompassSensor::enable(int32_t handle, int en) { VFUNC_LOG; mEnable = en; int tempFd; int res = 0; /* reset master enable */ res = masterEnable(0); if (res < 0) { return res; } if (en) { LOGV_IF(SYSFS_VERBOSE, "HAL:sysfs:echo %d > %s (%lld)", en, compassSysFs.compass_x_fifo_enable, getTimestamp()); res = write_sysfs_int(compassSysFs.compass_x_fifo_enable, en); LOGV_IF(SYSFS_VERBOSE, "HAL:sysfs:echo %d > %s (%lld)", en, compassSysFs.compass_y_fifo_enable, getTimestamp()); res += write_sysfs_int(compassSysFs.compass_y_fifo_enable, en); LOGV_IF(SYSFS_VERBOSE, "HAL:sysfs:echo %d > %s (%lld)", en, compassSysFs.compass_z_fifo_enable, getTimestamp()); res += write_sysfs_int(compassSysFs.compass_z_fifo_enable, en); res = masterEnable(en); if (res < en) { return res; } } return res; } int CompassSensor::masterEnable(int en) { VFUNC_LOG; LOGV_IF(SYSFS_VERBOSE, "HAL:sysfs:echo %d > %s (%lld)", en, compassSysFs.chip_enable, getTimestamp()); return write_sysfs_int(compassSysFs.chip_enable, en); } int CompassSensor::setDelay(int32_t handle, int64_t ns) { VFUNC_LOG; int tempFd; int res; mDelay = ns; if (ns == 0) return -1; tempFd = open(compassSysFs.compass_rate, O_RDWR); LOGV_IF(SYSFS_VERBOSE, "HAL:sysfs:echo %.0f > %s (%lld)", 1000000000.f / ns, compassSysFs.compass_rate, getTimestamp()); res = write_attribute_sensor(tempFd, 1000000000.f / ns); if(res < 0) { LOGE("HAL:Compass update delay error"); } return res; } /** @brief This function will return the state of the sensor. @return 1=enabled; 0=disabled **/ int CompassSensor::getEnable(int32_t handle) { VFUNC_LOG; return mEnable; } /* use for Invensense compass calibration */ #define COMPASS_EVENT_DEBUG (0) void CompassSensor::processCompassEvent(const input_event *event) { VHANDLER_LOG; switch (event->code) { case EVENT_TYPE_ICOMPASS_X: LOGV_IF(COMPASS_EVENT_DEBUG, "EVENT_TYPE_ICOMPASS_X\n"); mCachedCompassData[0] = event->value; break; case EVENT_TYPE_ICOMPASS_Y: LOGV_IF(COMPASS_EVENT_DEBUG, "EVENT_TYPE_ICOMPASS_Y\n"); mCachedCompassData[1] = event->value; break; case EVENT_TYPE_ICOMPASS_Z: LOGV_IF(COMPASS_EVENT_DEBUG, "EVENT_TYPE_ICOMPASS_Z\n"); mCachedCompassData[2] = event->value; break; } mCompassTimestamp = (int64_t)event->time.tv_sec * 1000000000L + event->time.tv_usec * 1000L; } void CompassSensor::getOrientationMatrix(signed char *orient) { VFUNC_LOG; memcpy(orient, mCompassOrientation, sizeof(mCompassOrientation)); } long CompassSensor::getSensitivity() { VFUNC_LOG; long sensitivity; LOGV_IF(SYSFS_VERBOSE, "HAL:sysfs:cat %s (%lld)", compassSysFs.compass_scale, getTimestamp()); inv_read_data(compassSysFs.compass_scale, &sensitivity); return sensitivity; } /** @brief This function is called by sensors_mpl.cpp to read sensor data from the driver. @param[out] data sensor data is stored in this variable. Scaled such that 1 uT = 2^16 @para[in] timestamp data's timestamp @return 1, if 1 sample read, 0, if not, negative if error */ int CompassSensor::readSample(long *data, int64_t *timestamp) { VFUNC_LOG; int i; char *rdata = mIIOBuffer; size_t rsize = read(compass_fd, rdata, (8 * mEnable + 8) * 1); if (!mEnable) { rsize = read(compass_fd, rdata, (8 + 8) * IIO_BUFFER_LENGTH); // LOGI("clear buffer with size: %d", rsize); } /* LOGI("get one sample of AMI IIO data with size: %d", rsize); LOGI_IF(mEnable, "compass x/y/z: %d/%d/%d", *((short *) (rdata + 0)), *((short *) (rdata + 2)), *((short *) (rdata + 4))); */ if (mEnable) { for (i = 0; i < 3; i++) { data[i] = *((short *) (rdata + i * 2)); } *timestamp = *((long long *) (rdata + 8 * mEnable)); } return mEnable; } /** * @brief This function will return the current delay for this sensor. * @return delay in nanoseconds. */ int64_t CompassSensor::getDelay(int32_t handle) { VFUNC_LOG; return mDelay; } void CompassSensor::fillList(struct sensor_t *list) { VFUNC_LOG; const char *compass = dev_full_name; if (compass) { if(!strcmp(compass, "INV_COMPASS")) { list->maxRange = COMPASS_MPU9150_RANGE; list->resolution = COMPASS_MPU9150_RESOLUTION; list->power = COMPASS_MPU9150_POWER; list->minDelay = COMPASS_MPU9150_MINDELAY; mMinDelay = list->minDelay; return; } if(!strcmp(compass, "compass") || !strcmp(compass, "INV_AK8975") || !strncmp(compass, "ak89xx", 2)) { list->maxRange = COMPASS_AKM8975_RANGE; list->resolution = COMPASS_AKM8975_RESOLUTION; list->power = COMPASS_AKM8975_POWER; list->minDelay = COMPASS_AKM8975_MINDELAY; mMinDelay = list->minDelay; return; } if(!strcmp(compass, "ami306")) { list->maxRange = COMPASS_AMI306_RANGE; list->resolution = COMPASS_AMI306_RESOLUTION; list->power = COMPASS_AMI306_POWER; list->minDelay = COMPASS_AMI306_MINDELAY; mMinDelay = list->minDelay; return; } if(!strcmp(compass, "yas530") || !strcmp(compass, "yas532") || !strcmp(compass, "yas533")) { list->maxRange = COMPASS_YAS53x_RANGE; list->resolution = COMPASS_YAS53x_RESOLUTION; list->power = COMPASS_YAS53x_POWER; list->minDelay = COMPASS_YAS53x_MINDELAY; mMinDelay = list->minDelay; return; } } LOGE("HAL:unknown compass id %s -- " "params default to ak8975 and might be wrong.", compass); list->maxRange = COMPASS_AKM8975_RANGE; list->resolution = COMPASS_AKM8975_RESOLUTION; list->power = COMPASS_AKM8975_POWER; list->minDelay = COMPASS_AKM8975_MINDELAY; mMinDelay = list->minDelay; } /* Read sysfs entry to determine whether overflow had happend then write to sysfs to reset to zero */ int CompassSensor::checkCoilsReset() { int result=-1; VFUNC_LOG; if(mCoilsResetFd != NULL) { int attr; rewind(mCoilsResetFd); fscanf(mCoilsResetFd, "%d", &attr); if(attr == 0) return 0; else { LOGV_IF(SYSFS_VERBOSE, "HAL:overflow detected"); rewind(mCoilsResetFd); if(fprintf(mCoilsResetFd, "%d", 0) < 0) LOGE("HAL:could not write overunderflow"); else return 1; } } else { LOGE("HAL:could not read overunderflow"); } return result; } int CompassSensor::inv_init_sysfs_attributes(void) { VFUNC_LOG; unsigned char i = 0; char sysfs_path[MAX_SYSFS_NAME_LEN], tbuf[2]; char *sptr; char **dptr; int num; const char* compass = dev_full_name; pathP = (char*)malloc( sizeof(char[COMPASS_MAX_SYSFS_ATTRB][MAX_SYSFS_NAME_LEN])); sptr = pathP; dptr = (char**)&compassSysFs; if (sptr == NULL) return -1; do { *dptr++ = sptr; sptr += sizeof(char[MAX_SYSFS_NAME_LEN]); } while (++i < COMPASS_MAX_SYSFS_ATTRB); // get proper (in absolute/relative) IIO path & build sysfs paths sprintf(sysfs_path, "%s%d", "/sys/bus/iio/devices/iio:device", find_type_by_name(compass, "iio:device")); #if defined COMPASS_AK8975 inv_get_input_number(compass, &num); tbuf[0] = num + 0x30; tbuf[1] = 0; sprintf(sysfs_path, "%s%s", "sys/class/input/input", tbuf); strcat(sysfs_path, "/ak8975"); sprintf(compassSysFs.compass_enable, "%s%s", sysfs_path, "/enable"); sprintf(compassSysFs.compass_rate, "%s%s", sysfs_path, "/rate"); sprintf(compassSysFs.compass_scale, "%s%s", sysfs_path, "/scale"); sprintf(compassSysFs.compass_orient, "%s%s", sysfs_path, "/compass_matrix"); #else /* IIO */ sprintf(compassSysFs.chip_enable, "%s%s", sysfs_path, "/buffer/enable"); sprintf(compassSysFs.in_timestamp_en, "%s%s", sysfs_path, "/scan_elements/in_timestamp_en"); sprintf(compassSysFs.buffer_length, "%s%s", sysfs_path, "/buffer/length"); sprintf(compassSysFs.compass_x_fifo_enable, "%s%s", sysfs_path, "/scan_elements/in_magn_x_en"); sprintf(compassSysFs.compass_y_fifo_enable, "%s%s", sysfs_path, "/scan_elements/in_magn_y_en"); sprintf(compassSysFs.compass_z_fifo_enable, "%s%s", sysfs_path, "/scan_elements/in_magn_z_en"); sprintf(compassSysFs.compass_rate, "%s%s", sysfs_path, "/sampling_frequency"); sprintf(compassSysFs.compass_scale, "%s%s", sysfs_path, "/in_magn_scale"); sprintf(compassSysFs.compass_orient, "%s%s", sysfs_path, "/compass_matrix"); if(mYasCompass) { sprintf(compassSysFs.compass_attr_1, "%s%s", sysfs_path, "/overunderflow"); } #endif #if 0 // test print sysfs paths dptr = (char**)&compassSysFs; LOGI("sysfs path base: %s", sysfs_path); for (i = 0; i < COMPASS_MAX_SYSFS_ATTRB; i++) { LOGE("HAL:sysfs path: %s", *dptr++); } #endif return 0; } int CompassSensor::isYasCompass(void) { return mYasCompass; }