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
CompassSensor()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
enable_iio_sysfs()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
~CompassSensor()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
getFd(void) const226 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 */
enable(int32_t handle,int en)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
masterEnable(int en)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
setDelay(int32_t handle,int64_t ns)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 **/
getEnable(int32_t handle)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)
processCompassEvent(const input_event * event)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
getOrientationMatrix(signed char * orient)338 void CompassSensor::getOrientationMatrix(signed char *orient)
339 {
340 VFUNC_LOG;
341 memcpy(orient, mCompassOrientation, sizeof(mCompassOrientation));
342 }
343
getSensitivity()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 */
readSample(long * data,int64_t * timestamp)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 */
getDelay(int32_t handle)394 int64_t CompassSensor::getDelay(int32_t handle)
395 {
396 VFUNC_LOG;
397 return mDelay;
398 }
399
fillList(struct sensor_t * list)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 */
checkCoilsReset()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
inv_init_sysfs_attributes(void)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
isYasCompass(void)548 int CompassSensor::isYasCompass(void)
549 {
550 return mYasCompass;
551 }
552