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