1 /*
2  * Copyright (C) 2014 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.9150.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_YAS53x
39 #pragma message("HAL:build Invensense compass cal with YAS53x IIO on secondary bus")
40 #define USE_MPL_COMPASS_HAL (1)
41 #define COMPASS_NAME        "INV_YAS530"
42 
43 #elif defined COMPASS_AK8975
44 #pragma message("HAL:build Invensense compass cal with AK8975 on primary bus")
45 #define USE_MPL_COMPASS_HAL (1)
46 #define COMPASS_NAME        "INV_AK8975"
47 
48 #elif defined INVENSENSE_COMPASS_CAL
49 #   define COMPASS_NAME                 "USE_SYSFS"
50 #pragma message("HAL:build Invensense compass cal with compass IIO on secondary bus")
51 #define USE_MPL_COMPASS_HAL (1)
52 #else
53 #pragma message("HAL:build third party compass cal HAL")
54 #define USE_MPL_COMPASS_HAL (0)
55 // TODO: change to vendor's name
56 #define COMPASS_NAME        "AKM8975"
57 
58 #endif
59 
60 /*****************************************************************************/
61 
CompassSensor()62 CompassSensor::CompassSensor()
63                   : SensorBase(NULL, NULL),
64                     compass_fd(-1),
65                     mCompassTimestamp(0),
66                     mCompassInputReader(8)
67 {
68     VFUNC_LOG;
69 
70     if(!strcmp(COMPASS_NAME, "USE_SYSFS")) {
71         int result = find_name_by_sensor_type("in_magn_scale", "iio:device",
72                                               sensor_name);
73         if(result) {
74             LOGE("HAL:Cannot read secondary device name - (%d)", result);
75         }
76         dev_name = sensor_name;
77     }
78     LOGI_IF(PROCESS_VERBOSE, "HAL:Secondary Chip Id: %s", dev_name);
79 
80     if(inv_init_sysfs_attributes()) {
81         LOGE("Error Instantiating Compass\n");
82         return;
83     }
84 
85     memset(mCachedCompassData, 0, sizeof(mCachedCompassData));
86 
87     LOGV_IF(SYSFS_VERBOSE, "HAL:sysfs:cat %s (%lld)",
88             compassSysFs.compass_orient, getTimestamp());
89     FILE *fptr;
90     fptr = fopen(compassSysFs.compass_orient, "r");
91     if (fptr != NULL) {
92         int om[9];
93         if (fscanf(fptr, "%d,%d,%d,%d,%d,%d,%d,%d,%d",
94                &om[0], &om[1], &om[2], &om[3], &om[4], &om[5],
95                &om[6], &om[7], &om[8]) < 0 || fclose(fptr) < 0) {
96             LOGE("HAL:Could not read compass mounting matrix");
97         } else {
98             LOGV_IF(EXTRA_VERBOSE, "HAL:compass mounting matrix: "
99                     "%+d %+d %+d %+d %+d %+d %+d %+d %+d", om[0], om[1], om[2],
100                     om[3], om[4], om[5], om[6], om[7], om[8]);
101             mCompassOrientation[0] = om[0];
102             mCompassOrientation[1] = om[1];
103             mCompassOrientation[2] = om[2];
104             mCompassOrientation[3] = om[3];
105             mCompassOrientation[4] = om[4];
106             mCompassOrientation[5] = om[5];
107             mCompassOrientation[6] = om[6];
108             mCompassOrientation[7] = om[7];
109             mCompassOrientation[8] = om[8];
110         }
111     }
112 
113     if (!isIntegrated()) {
114         enable(ID_M, 0);
115     }
116 }
117 
~CompassSensor()118 CompassSensor::~CompassSensor()
119 {
120     VFUNC_LOG;
121     free(pathP);
122     if( compass_fd > 0)
123         close(compass_fd);
124 }
125 
getFd() const126 int CompassSensor::getFd() const
127 {
128     VFUNC_LOG;
129     return compass_fd;
130 }
131 
132 /**
133  *  @brief        This function will enable/disable sensor.
134  *  @param[in]    handle
135  *                  which sensor to enable/disable.
136  *  @param[in]    en
137  *                  en=1, enable;
138  *                  en=0, disable
139  *  @return       if the operation is successful.
140  */
enable(int32_t handle,int en)141 int CompassSensor::enable(int32_t handle, int en)
142 {
143     VFUNC_LOG;
144     int res = 0;
145     LOGV_IF(SYSFS_VERBOSE, "HAL:sysfs:echo %d > %s (%lld)",
146             en, compassSysFs.compass_enable, getTimestamp());
147     res = write_sysfs_int(compassSysFs.compass_enable, en);
148     return res;
149 }
150 
setDelay(int32_t handle,int64_t ns)151 int CompassSensor::setDelay(int32_t handle, int64_t ns)
152 {
153     VFUNC_LOG;
154     int tempFd;
155     int res;
156 
157     LOGV_IF(SYSFS_VERBOSE, "HAL:sysfs:echo %.0f > %s (%lld)",
158             1000000000.f / ns, compassSysFs.compass_rate, getTimestamp());
159     mDelay = ns;
160     if (ns == 0)
161         return -1;
162     tempFd = open(compassSysFs.compass_rate, O_RDWR);
163     res = write_attribute_sensor(tempFd, 1000000000.f / ns);
164     if(res < 0) {
165         LOGE("HAL:Compass update delay error");
166     }
167     return res;
168 }
169 
turnOffCompassFifo(void)170 int CompassSensor::turnOffCompassFifo(void)
171 {
172     int i, res = 0, tempFd;
173     LOGV_IF(SYSFS_VERBOSE, "HAL:sysfs:echo %d > %s (%lld)",
174                         0, compassSysFs.compass_fifo_enable, getTimestamp());
175     res += write_sysfs_int(compassSysFs.compass_fifo_enable, 0);
176     return res;
177 }
178 
turnOnCompassFifo(void)179 int CompassSensor::turnOnCompassFifo(void)
180 {
181     int i, res = 0, tempFd;
182     LOGV_IF(SYSFS_VERBOSE, "HAL:sysfs:echo %d > %s (%lld)",
183                         1, compassSysFs.compass_fifo_enable, getTimestamp());
184     res += write_sysfs_int(compassSysFs.compass_fifo_enable, 1);
185     return res;
186 }
187 
188 /**
189     @brief      This function will return the state of the sensor.
190     @return     1=enabled; 0=disabled
191 **/
getEnable(int32_t handle)192 int CompassSensor::getEnable(int32_t handle)
193 {
194     VFUNC_LOG;
195     return mEnable;
196 }
197 
198 /* use for Invensense compass calibration */
199 #define COMPASS_EVENT_DEBUG (0)
processCompassEvent(const input_event * event)200 void CompassSensor::processCompassEvent(const input_event *event)
201 {
202     VHANDLER_LOG;
203 
204     switch (event->code) {
205     case EVENT_TYPE_ICOMPASS_X:
206         LOGV_IF(COMPASS_EVENT_DEBUG, "EVENT_TYPE_ICOMPASS_X\n");
207         mCachedCompassData[0] = event->value;
208         break;
209     case EVENT_TYPE_ICOMPASS_Y:
210         LOGV_IF(COMPASS_EVENT_DEBUG, "EVENT_TYPE_ICOMPASS_Y\n");
211         mCachedCompassData[1] = event->value;
212         break;
213     case EVENT_TYPE_ICOMPASS_Z:
214         LOGV_IF(COMPASS_EVENT_DEBUG, "EVENT_TYPE_ICOMPASS_Z\n");
215         mCachedCompassData[2] = event->value;
216         break;
217     }
218 
219     mCompassTimestamp =
220         (int64_t)event->time.tv_sec * 1000000000L + event->time.tv_usec * 1000L;
221 }
222 
getOrientationMatrix(signed char * orient)223 void CompassSensor::getOrientationMatrix(signed char *orient)
224 {
225     VFUNC_LOG;
226     memcpy(orient, mCompassOrientation, sizeof(mCompassOrientation));
227 }
228 
getSensitivity()229 long CompassSensor::getSensitivity()
230 {
231     VFUNC_LOG;
232 
233     long sensitivity;
234     LOGV_IF(SYSFS_VERBOSE, "HAL:sysfs:cat %s (%lld)",
235             compassSysFs.compass_scale, getTimestamp());
236     inv_read_data(compassSysFs.compass_scale, &sensitivity);
237     return sensitivity;
238 }
239 
240 /**
241     @brief         This function is called by sensors_mpl.cpp
242                    to read sensor data from the driver.
243     @param[out]    data      sensor data is stored in this variable. Scaled such that
244                              1 uT = 2^16
245     @para[in]      timestamp data's timestamp
246     @return        1, if 1   sample read, 0, if not, negative if error
247  */
readSample(long * data,int64_t * timestamp)248 int CompassSensor::readSample(long *data, int64_t *timestamp)
249 {
250     VHANDLER_LOG;
251 
252     int numEventReceived = 0, done = 0;
253 
254     ssize_t n = mCompassInputReader.fill(compass_fd);
255     if (n < 0) {
256         LOGE("HAL:no compass events read");
257         return n;
258     }
259 
260     input_event const* event;
261 
262     while (done == 0 && mCompassInputReader.readEvent(&event)) {
263         int type = event->type;
264         if (type == EV_REL) {
265             processCompassEvent(event);
266         } else if (type == EV_SYN) {
267             *timestamp = mCompassTimestamp;
268             memcpy(data, mCachedCompassData, sizeof(mCachedCompassData));
269             done = 1;
270         } else {
271             LOGE("HAL:Compass Sensor: unknown event (type=%d, code=%d)",
272                  type, event->code);
273         }
274         mCompassInputReader.next();
275     }
276 
277     return done;
278 }
279 
280 /**
281  *  @brief  This function will return the current delay for this sensor.
282  *  @return delay in nanoseconds.
283  */
getDelay(int32_t handle)284 int64_t CompassSensor::getDelay(int32_t handle)
285 {
286     VFUNC_LOG;
287     return mDelay;
288 }
289 
fillList(struct sensor_t * list)290 void CompassSensor::fillList(struct sensor_t *list)
291 {
292     VFUNC_LOG;
293 
294     const char *compass = sensor_name;
295 
296     if (compass) {
297         if(!strcmp(compass, "INV_COMPASS")) {
298             list->maxRange = COMPASS_MPU9150_RANGE;
299             list->resolution = COMPASS_MPU9150_RESOLUTION;
300             list->power = COMPASS_MPU9150_POWER;
301             list->minDelay = COMPASS_MPU9150_MINDELAY;
302             return;
303         }
304         if(!strcmp(compass, "compass")
305                 || !strcmp(compass, "INV_AK8975")
306                 || !strcmp(compass, "AK8975")
307                 || !strcmp(compass, "ak8975")) {
308             list->maxRange = COMPASS_AKM8975_RANGE;
309             list->resolution = COMPASS_AKM8975_RESOLUTION;
310             list->power = COMPASS_AKM8975_POWER;
311             list->minDelay = COMPASS_AKM8975_MINDELAY;
312             return;
313         }
314         if(!strcmp(compass, "compass")
315                 || !strcmp(compass, "INV_AK8963")
316                 || !strcmp(compass, "AK8963")
317                 || !strcmp(compass, "ak8963")) {
318             list->maxRange = COMPASS_AKM8963_RANGE;
319             list->resolution = COMPASS_AKM8963_RESOLUTION;
320             list->power = COMPASS_AKM8963_POWER;
321             list->minDelay = COMPASS_AKM8963_MINDELAY;
322             return;
323         }
324         if(!strcmp(compass, "compass")
325                 || !strcmp(compass, "INV_AK09911")
326                 || !strcmp(compass, "AK09911")
327                 || !strcmp(compass, "ak09911")) {
328             list->maxRange = COMPASS_AKM9911_RANGE;
329             list->resolution = COMPASS_AKM9911_RESOLUTION;
330             list->power = COMPASS_AKM9911_POWER;
331             list->minDelay = COMPASS_AKM9911_MINDELAY;
332             return;
333         }
334         if(!strcmp(compass, "compass")
335                 || !strcmp(compass, "INV_AK09912")
336                 || !strcmp(compass, "AK09912")
337                 || !strcmp(compass, "ak09912")) {
338             list->maxRange = COMPASS_AKM9912_RANGE;
339             list->resolution = COMPASS_AKM9912_RESOLUTION;
340             list->power = COMPASS_AKM9912_POWER;
341             list->minDelay = COMPASS_AKM9912_MINDELAY;
342             return;
343         }
344         if(!strcmp(compass, "compass")
345                 || !strncmp(compass, "mlx90399",3)
346                 || !strncmp(compass, "MLX90399",3)) {
347             list->maxRange = COMPASS_MPU9350_RANGE;
348             list->resolution = COMPASS_MPU9350_RESOLUTION;
349             list->power = COMPASS_MPU9350_POWER;
350             list->minDelay = COMPASS_MPU9350_MINDELAY;
351             return;
352         }
353         if(!strcmp(compass, "INV_YAS530")) {
354             list->maxRange = COMPASS_YAS53x_RANGE;
355             list->resolution = COMPASS_YAS53x_RESOLUTION;
356             list->power = COMPASS_YAS53x_POWER;
357             list->minDelay = COMPASS_YAS53x_MINDELAY;
358             return;
359         }
360         if(!strcmp(compass, "INV_AMI306")) {
361             list->maxRange = COMPASS_AMI306_RANGE;
362             list->resolution = COMPASS_AMI306_RESOLUTION;
363             list->power = COMPASS_AMI306_POWER;
364             list->minDelay = COMPASS_AMI306_MINDELAY;
365             return;
366         }
367     }
368     LOGE("HAL:unknown compass id %s -- "
369          "params default to ak8975 and might be wrong.",
370          compass);
371     list->maxRange = COMPASS_AKM8975_RANGE;
372     list->resolution = COMPASS_AKM8975_RESOLUTION;
373     list->power = COMPASS_AKM8975_POWER;
374     list->minDelay = COMPASS_AKM8975_MINDELAY;
375 }
376 
inv_init_sysfs_attributes(void)377 int CompassSensor::inv_init_sysfs_attributes(void)
378 {
379     VFUNC_LOG;
380 
381     char sysfs_path[MAX_SYSFS_NAME_LEN];
382     char iio_trigger_path[MAX_SYSFS_NAME_LEN];
383 
384     pathP = (char*)calloc(COMPASS_MAX_SYSFS_ATTRB,
385                           sizeof(char[MAX_SYSFS_NAME_LEN]));
386     if (pathP == NULL)
387         return -1;
388 
389     memset(sysfs_path, 0, sizeof(sysfs_path));
390     memset(iio_trigger_path, 0, sizeof(iio_trigger_path));
391 
392     char *sptr = pathP;
393     char **dptr = reinterpret_cast<char **>(&compassSysFs);
394     for (size_t i = 0; i < COMPASS_MAX_SYSFS_ATTRB; i++) {
395       *dptr++ = sptr;
396       sptr += sizeof(char[MAX_SYSFS_NAME_LEN]);
397     }
398 
399     // get proper (in absolute/relative) IIO path & build MPU's sysfs paths
400     // inv_get_sysfs_abs_path(sysfs_path);
401     inv_get_sysfs_path(sysfs_path);
402     inv_get_iio_trigger_path(iio_trigger_path);
403 
404 #if defined COMPASS_AK8975
405     char tbuf[2];
406     int num;
407 
408     inv_get_input_number(dev_name, &num);
409     tbuf[0] = num + 0x30;
410     tbuf[1] = 0;
411     sprintf(sysfs_path, "%s%s", "sys/class/input/input", tbuf);
412     strcat(sysfs_path, "/ak8975");
413 
414     sprintf(compassSysFs.compass_enable, "%s%s", sysfs_path, "/enable");
415     sprintf(compassSysFs.compass_rate, "%s%s", sysfs_path, "/rate");
416     sprintf(compassSysFs.compass_scale, "%s%s", sysfs_path, "/scale");
417     sprintf(compassSysFs.compass_orient, "%s%s", sysfs_path, "/compass_matrix");
418 #else
419     sprintf(compassSysFs.compass_enable, "%s%s", sysfs_path, "/compass_enable");
420     sprintf(compassSysFs.compass_fifo_enable, "%s%s", sysfs_path, "/compass_fifo_enable");
421     sprintf(compassSysFs.compass_rate, "%s%s", sysfs_path, "/compass_rate");
422     sprintf(compassSysFs.compass_scale, "%s%s", sysfs_path, "/in_magn_scale");
423     sprintf(compassSysFs.compass_orient, "%s%s", sysfs_path, "/compass_matrix");
424 #endif
425 
426     return 0;
427 }
428