diff options
Diffstat (limited to 'drivers/sensor')
-rw-r--r-- | drivers/sensor/Kconfig | 54 | ||||
-rw-r--r-- | drivers/sensor/Makefile | 6 | ||||
-rw-r--r-- | drivers/sensor/al3201.c | 51 | ||||
-rw-r--r-- | drivers/sensor/k3dh_kona.c | 1080 | ||||
-rw-r--r-- | drivers/sensor/k3dh_reg.h | 1 | ||||
-rw-r--r-- | drivers/sensor/yas_mag_driver-yas532.c | 2909 | ||||
-rw-r--r-- | drivers/sensor/yas_mag_driver.c | 27 | ||||
-rw-r--r-- | drivers/sensor/yas_mag_kernel_driver.c | 2192 | ||||
-rw-r--r-- | drivers/sensor/yas_ori_kernel_driver.c | 695 | ||||
-rw-r--r-- | drivers/sensor/yas_pcb_test.c | 1282 | ||||
-rw-r--r-- | drivers/sensor/yas_pcb_test.h | 106 | ||||
-rw-r--r-- | drivers/sensor/yas_types.h | 48 |
12 files changed, 8444 insertions, 7 deletions
diff --git a/drivers/sensor/Kconfig b/drivers/sensor/Kconfig index f7888b9..36464eb 100644 --- a/drivers/sensor/Kconfig +++ b/drivers/sensor/Kconfig @@ -112,6 +112,17 @@ config SENSORS_AL3201 Say N here if you do not use AL3201. +config SENSORS_K2DH + tristate "K2DH acceleration sensor support" + depends on I2C + default n + help + Say Y here if you use K2DH. + This option enables accelerometer sensors using + STM K2DH in K2DH device driver. + + Say N here if you do not use K2DH. + config SENSORS_K3DH tristate "K3DH acceleration sensor support" depends on I2C @@ -119,6 +130,17 @@ config SENSORS_K3DH help Driver for STMicroelectronic K3DH accelerometer. +config SENSOR_K3DH_INPUTDEV + bool "K3DH acceleration sensor input dev support" + depends on SENSORS_K3DH + default n + help + Say Y here if you use K3DH. + This option enables accelerometer sensor using + K3DH device driver. + + Say N here if you do not use K3DH. + config SENSORS_K3G tristate "K3G driver for s5pc210" depends on I2C @@ -139,4 +161,36 @@ config SENSORS_LPS331 depends on I2C help Driver for STMicro LPS331 + +config SENSORS_YAS532 + depends on I2C + tristate "yas532 Sensor Support" + default n + help + Say Y to enable YAS532 Magnetic Sensor support. + This allows control of supported Magnetic Sensor. + +config SENSORS_YAS_ORI + depends on I2C + tristate "yas orientation Sensor Support" + default n + help + Say Y to enable YAS532 Magnetic Sensor support. + This allows control of supported Magnetic Sensor. + +config INPUT_YAS_MAGNETOMETER_POSITION + int "YAS Geomagnetic Sensor Mounting Position on Board" + depends on I2C + default "0" + help + Chip mounting position (pin 1). + 0: top, upper-left + 1: top, upper-right + 2: top, lower-right + 3: top, lower-left + 4: bottom, upper-left + 5: bottom, upper-right + 6: bottom, lower-right + 7: bottom, lower-left + endif diff --git a/drivers/sensor/Makefile b/drivers/sensor/Makefile index 44adf31..195f0f9 100644 --- a/drivers/sensor/Makefile +++ b/drivers/sensor/Makefile @@ -6,7 +6,11 @@ obj-$(CONFIG_SENSORS_CORE) += sensors_core.o # accelerometer_sensor +ifeq ($(CONFIG_MACH_KONA_SENSOR),y) +obj-$(CONFIG_SENSORS_K3DH) += k3dh_kona.o +else obj-$(CONFIG_SENSORS_K3DH) += k3dh.o +endif obj-$(CONFIG_SENSORS_BMA254) += bma254_driver.o # gyro_sensor obj-$(CONFIG_SENSORS_K3G) += k3g.o @@ -15,6 +19,8 @@ obj-$(CONFIG_SENSORS_LSM330DLC) += lsm330dlc_accel.o lsm330dlc_gyro.o # magnetic_sensor obj-$(CONFIG_SENSORS_AK8975C) += ak8975.o obj-$(CONFIG_SENSORS_AK8963C) += ak8963.o +obj-$(CONFIG_SENSORS_YAS532) += yas_mag_kernel_driver.o +obj-$(CONFIG_SENSORS_YAS_ORI) += yas_ori_kernel_driver.o # optical_sensor obj-$(CONFIG_SENSORS_CM3663) += cm3663.o obj-$(CONFIG_SENSORS_TAOS) += taos.o diff --git a/drivers/sensor/al3201.c b/drivers/sensor/al3201.c index a225b1a..df21515 100644 --- a/drivers/sensor/al3201.c +++ b/drivers/sensor/al3201.c @@ -304,10 +304,35 @@ static ssize_t get_chip_name(struct device *dev, { return sprintf(buf, "%s\n", CHIP_NAME); } +#if defined(CONFIG_MACH_KONA) +static ssize_t al3201_lux_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int result; + struct input_dev *input = to_input_dev(dev); + struct al3201_data *data = input_get_drvdata(input); + + /* No LUX data if not operational */ + if (data->state == OFF) { + al3201_set_power_state(data->client, ON); + msleep(180); + } + + result = al3201_get_adc_value(data->client); + + if (data->state == OFF) + al3201_set_power_state(data->client, OFF); + + return sprintf(buf, "%d\n", result); +} +#endif static DEVICE_ATTR(raw_data, 0644, al3201_raw_data_show, NULL); static DEVICE_ATTR(vendor, 0644, get_vendor_name, NULL); static DEVICE_ATTR(name, 0644, get_chip_name, NULL); +#if defined(CONFIG_MACH_KONA) +static DEVICE_ATTR(lux, 0644, al3201_lux_show, NULL); +#endif /* factory test*/ #ifdef LSC_DBG @@ -582,26 +607,38 @@ static int __devinit al3201_probe(struct i2c_client *client, dev_attr_name.attr.name); goto err_light_device_create_file3; } + +#if defined(CONFIG_MACH_KONA) + if (device_create_file(data->light_dev, &dev_attr_lux) < 0) { + pr_err("%s: could not create device file(%s)!\n", __func__, + dev_attr_lux.attr.name); + goto err_light_device_create_file4; + } +#endif dev_set_drvdata(data->light_dev, data); pr_info("%s: success!\n", __func__); goto done; - err_light_device_create_file3: +#if defined(CONFIG_MACH_KONA) +err_light_device_create_file4: + device_remove_file(data->light_dev, &dev_attr_lux); +#endif +err_light_device_create_file3: device_remove_file(data->light_dev, &dev_attr_vendor); err_light_device_create_file2: device_remove_file(data->light_dev, &dev_attr_raw_data); err_light_device_create_file1: sensors_classdev_unregister(data->light_dev); - err_light_device_create: +err_light_device_create: sysfs_remove_group(&data->input->dev.kobj, &al3201_attribute_group); - err_sysfs_create_group_light: +err_sysfs_create_group_light: input_unregister_device(data->input); - err_input_register_device_light: - err_input_allocate_device_light: +err_input_register_device_light: +err_input_allocate_device_light: destroy_workqueue(data->wq); - err_create_workqueue: - err_initializ_chip: +err_create_workqueue: +err_initializ_chip: mutex_destroy(&data->lock); kfree(data); done: diff --git a/drivers/sensor/k3dh_kona.c b/drivers/sensor/k3dh_kona.c new file mode 100644 index 0000000..04e1f8b --- /dev/null +++ b/drivers/sensor/k3dh_kona.c @@ -0,0 +1,1080 @@ +/* + * STMicroelectronics k3dh acceleration sensor driver + * + * Copyright (C) 2010 Samsung Electronics Co.Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/fs.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/miscdevice.h> +#include <linux/uaccess.h> +#include <linux/sensor/sensors_core.h> +#include <linux/sensor/k3dh.h> +#include "k3dh_reg.h" +#ifdef CONFIG_SENSOR_K3DH_INPUTDEV +#include <linux/input.h> +#endif + +/* For Debugging */ +#if 1 +#define k3dh_dbgmsg(str, args...) pr_debug("%s: " str, __func__, ##args) +#endif +#define k3dh_infomsg(str, args...) pr_info("%s: " str, __func__, ##args) + +#define VENDOR "STM" +#define CHIP_ID "K3DH" + +/* The default settings when sensor is on is for all 3 axis to be enabled + * and output data rate set to 400Hz. Output is via a ioctl read call. + */ +#define DEFAULT_POWER_ON_SETTING (ODR400 | ENABLE_ALL_AXES) +#define ACC_DEV_MAJOR 241 + +#define CALIBRATION_FILE_PATH "/efs/calibration_data" +#define CAL_DATA_AMOUNT 20 + +#ifdef CONFIG_SENSOR_K3DH_INPUTDEV +/* ABS axes parameter range [um/s^2] (for input event) */ +#define GRAVITY_EARTH 9806550 +#define ABSMAX_2G (GRAVITY_EARTH * 2) +#define ABSMIN_2G (-GRAVITY_EARTH * 2) +#define MIN_DELAY 5 +#define MAX_DELAY 200 +#endif + +static const struct odr_delay { + u8 odr; /* odr reg setting */ + s64 delay_ns; /* odr in ns */ +} odr_delay_table[] = { + { ODR1344, 744047LL }, /* 1344Hz */ + { ODR400, 2500000LL }, /* 400Hz */ + { ODR200, 5000000LL }, /* 200Hz */ + { ODR100, 10000000LL }, /* 100Hz */ + { ODR50, 20000000LL }, /* 50Hz */ + { ODR25, 40000000LL }, /* 25Hz */ + { ODR10, 100000000LL }, /* 10Hz */ + { ODR1, 1000000000LL }, /* 1Hz */ +}; + +/* K3DH acceleration data */ +struct k3dh_acc { + s16 x; + s16 y; + s16 z; +}; + +struct k3dh_data { + struct i2c_client *client; + struct miscdevice k3dh_device; + struct mutex read_lock; + struct mutex write_lock; + struct completion data_ready; +#if defined(CONFIG_MACH_U1) || defined(CONFIG_MACH_TRATS) + struct class *acc_class; +#else + struct device *dev; +#endif + struct k3dh_acc cal_data; + struct k3dh_acc acc_xyz; + u8 ctrl_reg1_shadow; + atomic_t opened; /* opened implies enabled */ +#ifdef CONFIG_SENSOR_K3DH_INPUTDEV + struct input_dev *input; + struct delayed_work work; + atomic_t delay; + atomic_t enable; +#endif + bool axis_adjust; + int position; +}; + +static struct k3dh_data *g_k3dh; + + +static void k3dh_xyz_position_adjust(struct k3dh_acc *acc, + int position) +{ + const int position_map[][3][3] = { + {{ 0, 1, 0}, {-1, 0, 0}, { 0, 0, 1} }, /* 0 top/upper-left */ + {{-1, 0, 0}, { 0, -1, 0}, { 0, 0, 1} }, /* 1 top/upper-right */ + {{ 0, -1, 0}, { 1, 0, 0}, { 0, 0, 1} }, /* 2 top/lower-right */ + {{ 1, 0, 0}, { 0, 1, 0}, { 0, 0, 1} }, /* 3 top/lower-left */ + {{ 0, -1, 0}, {-1, 0, 0}, { 0, 0, -1} }, /* 4 bottom/upper-left */ + {{ 1, 0, 0}, { 0, -1, 0}, { 0, 0, -1} }, /* 5 bottom/upper-right */ + {{ 0, 1, 0}, { 1, 0, 0}, { 0, 0, -1} }, /* 6 bottom/lower-right */ + {{-1, 0, 0}, { 0, 1, 0}, { 0, 0, -1} }, /* 7 bottom/lower-left*/ + }; + + struct k3dh_acc xyz_adjusted = {0,}; + s16 raw[3] = {0,}; + int j; + raw[0] = acc->x; + raw[1] = acc->y; + raw[2] = acc->z; + for (j = 0; j < 3; j++) { + xyz_adjusted.x += + (position_map[position][0][j] * raw[j]); + xyz_adjusted.y += + (position_map[position][1][j] * raw[j]); + xyz_adjusted.z += + (position_map[position][2][j] * raw[j]); + } + acc->x = xyz_adjusted.x; + acc->y = xyz_adjusted.y; + acc->z = xyz_adjusted.z; +} + +/* Read X,Y and Z-axis acceleration raw data */ +static int k3dh_read_accel_raw_xyz(struct k3dh_data *data, + struct k3dh_acc *acc) +{ + int err; + s8 reg = OUT_X_L | AC; /* read from OUT_X_L to OUT_Z_H by auto-inc */ + u8 acc_data[6]; + + err = i2c_smbus_read_i2c_block_data(data->client, reg, + sizeof(acc_data), acc_data); + if (err != sizeof(acc_data)) { + pr_err("%s : failed to read 6 bytes for getting x/y/z\n", + __func__); + return -EIO; + } + + acc->x = (acc_data[1] << 8) | acc_data[0]; + acc->y = (acc_data[3] << 8) | acc_data[2]; + acc->z = (acc_data[5] << 8) | acc_data[4]; + + acc->x = acc->x >> 4; + acc->y = acc->y >> 4; + +#if defined(CONFIG_MACH_U1_NA_SPR_REV05) \ + || defined(CONFIG_MACH_U1_NA_SPR_EPIC2_REV00) \ + || defined(CONFIG_MACH_U1_NA_USCC_REV05) \ + || defined(CONFIG_MACH_Q1_BD) \ + || defined(CONFIG_MACH_U1_NA_USCC) \ + || defined(CONFIG_MACH_U1_NA_SPR) + acc->z = -acc->z >> 4; +#else + acc->z = acc->z >> 4; +#endif + + if (data->axis_adjust) + k3dh_xyz_position_adjust(acc, data->position); + return 0; +} + +static int k3dh_read_accel_xyz(struct k3dh_data *data, + struct k3dh_acc *acc) +{ + int err = 0; + + mutex_lock(&data->read_lock); + err = k3dh_read_accel_raw_xyz(data, acc); + mutex_unlock(&data->read_lock); + if (err < 0) { + pr_err("%s: k3dh_read_accel_raw_xyz() failed\n", __func__); + return err; + } + + acc->x -= data->cal_data.x; + acc->y -= data->cal_data.y; + acc->z -= data->cal_data.z; + + return err; +} + +static int k3dh_open_calibration(struct k3dh_data *data) +{ + struct file *cal_filp = NULL; + int err = 0; + mm_segment_t old_fs; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + + cal_filp = filp_open(CALIBRATION_FILE_PATH, O_RDONLY, 0666); + if (IS_ERR(cal_filp)) { + err = PTR_ERR(cal_filp); + if (err != -ENOENT) + pr_err("%s: Can't open calibration file\n", __func__); + set_fs(old_fs); + return err; + } + + err = cal_filp->f_op->read(cal_filp, + (char *)&data->cal_data, 3 * sizeof(s16), &cal_filp->f_pos); + if (err != 3 * sizeof(s16)) { + pr_err("%s: Can't read the cal data from file\n", __func__); + err = -EIO; + } + + k3dh_dbgmsg("%s: (%u,%u,%u)\n", __func__, + data->cal_data.x, data->cal_data.y, data->cal_data.z); + + filp_close(cal_filp, current->files); + set_fs(old_fs); + + return err; +} + +static int k3dh_do_calibrate(struct device *dev, bool do_calib) +{ + struct k3dh_data *acc_data = dev_get_drvdata(dev); + struct k3dh_acc data = { 0, }; + struct file *cal_filp = NULL; + int sum[3] = { 0, }; + int err = 0; + int i; + mm_segment_t old_fs; + + if (do_calib) { + for (i = 0; i < CAL_DATA_AMOUNT; i++) { + mutex_lock(&acc_data->read_lock); + err = k3dh_read_accel_raw_xyz(acc_data, &data); + mutex_unlock(&acc_data->read_lock); + if (err < 0) { + pr_err("%s: k3dh_read_accel_raw_xyz() " + "failed in the %dth loop\n", + __func__, i); + return err; + } + + sum[0] += data.x; + sum[1] += data.y; + sum[2] += data.z; + } + + acc_data->cal_data.x = sum[0] / CAL_DATA_AMOUNT; + acc_data->cal_data.y = sum[1] / CAL_DATA_AMOUNT; + if (sum[2] >= 0) + acc_data->cal_data.z = (sum[2] / CAL_DATA_AMOUNT)-1024; + else + acc_data->cal_data.z = (sum[2] / CAL_DATA_AMOUNT)+1024; + } else { + acc_data->cal_data.x = 0; + acc_data->cal_data.y = 0; + acc_data->cal_data.z = 0; + } + + printk(KERN_INFO "%s: cal data (%d,%d,%d)\n", __func__, + acc_data->cal_data.x, acc_data->cal_data.y, acc_data->cal_data.z); + + old_fs = get_fs(); + set_fs(KERNEL_DS); + + cal_filp = filp_open(CALIBRATION_FILE_PATH, + O_CREAT | O_TRUNC | O_WRONLY, 0666); + if (IS_ERR(cal_filp)) { + pr_err("%s: Can't open calibration file\n", __func__); + set_fs(old_fs); + err = PTR_ERR(cal_filp); + return err; + } + + err = cal_filp->f_op->write(cal_filp, + (char *)&acc_data->cal_data, 3 * sizeof(s16), &cal_filp->f_pos); + if (err != 3 * sizeof(s16)) { + pr_err("%s: Can't write the cal data to file\n", __func__); + err = -EIO; + } + + filp_close(cal_filp, current->files); + set_fs(old_fs); + + return err; +} + +static int k3dh_accel_enable(struct k3dh_data *data) +{ + int err = 0; + + if (atomic_read(&data->opened) == 0) { + err = k3dh_open_calibration(data); + if (err < 0 && err != -ENOENT) + pr_err("%s: k3dh_open_calibration() failed\n", + __func__); + data->ctrl_reg1_shadow = DEFAULT_POWER_ON_SETTING; + err = i2c_smbus_write_byte_data(data->client, CTRL_REG1, + DEFAULT_POWER_ON_SETTING); + if (err) + pr_err("%s: i2c write ctrl_reg1 failed\n", __func__); + +#if defined(CONFIG_SENSORS_K2DH) + err = i2c_smbus_write_byte_data(data->client, CTRL_REG4, + CTRL_REG4_HR | CTRL_REG4_BDU); +#else + err = i2c_smbus_write_byte_data(data->client, CTRL_REG4, + CTRL_REG4_HR); +#endif + if (err) + pr_err("%s: i2c write ctrl_reg4 failed\n", __func__); + } + + atomic_add(1, &data->opened); + + return err; +} + +static int k3dh_accel_disable(struct k3dh_data *data) +{ + int err = 0; + + atomic_sub(1, &data->opened); + if (atomic_read(&data->opened) == 0) { + err = i2c_smbus_write_byte_data(data->client, CTRL_REG1, + PM_OFF); + data->ctrl_reg1_shadow = PM_OFF; + } + + return err; +} + +/* open command for K3DH device file */ +static int k3dh_open(struct inode *inode, struct file *file) +{ + k3dh_infomsg("is called.\n"); + return 0; +} + +/* release command for K3DH device file */ +static int k3dh_close(struct inode *inode, struct file *file) +{ + k3dh_infomsg("is called.\n"); + return 0; +} + +static s64 k3dh_get_delay(struct k3dh_data *data) +{ + int i; + u8 odr; + s64 delay = -1; + + odr = data->ctrl_reg1_shadow & ODR_MASK; + for (i = 0; i < ARRAY_SIZE(odr_delay_table); i++) { + if (odr == odr_delay_table[i].odr) { + delay = odr_delay_table[i].delay_ns; + break; + } + } + return delay; +} + +static int k3dh_set_delay(struct k3dh_data *data, s64 delay_ns) +{ + int odr_value = ODR1; + int res = 0; + int i; + + /* round to the nearest delay that is less than + * the requested value (next highest freq) + */ + for (i = 0; i < ARRAY_SIZE(odr_delay_table); i++) { + if (delay_ns < odr_delay_table[i].delay_ns) + break; + } + if (i > 0) + i--; + odr_value = odr_delay_table[i].odr; + delay_ns = odr_delay_table[i].delay_ns; + + k3dh_infomsg("old=%lldns, new=%lldns, odr=0x%x/0x%x\n", + k3dh_get_delay(data), delay_ns, odr_value, + data->ctrl_reg1_shadow); + mutex_lock(&data->write_lock); + if (odr_value != (data->ctrl_reg1_shadow & ODR_MASK)) { + u8 ctrl = (data->ctrl_reg1_shadow & ~ODR_MASK); + ctrl |= odr_value; + data->ctrl_reg1_shadow = ctrl; + res = i2c_smbus_write_byte_data(data->client, CTRL_REG1, ctrl); + } + mutex_unlock(&data->write_lock); + return res; +} + +/* ioctl command for K3DH device file */ +static long k3dh_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + int err = 0; + struct k3dh_data *data = container_of(file->private_data, + struct k3dh_data, k3dh_device); + s64 delay_ns; + int enable = 0; + + /* cmd mapping */ + switch (cmd) { + case K3DH_IOCTL_SET_ENABLE: + if (copy_from_user(&enable, (void __user *)arg, + sizeof(enable))) + return -EFAULT; + k3dh_infomsg("opened = %d, enable = %d\n", + atomic_read(&data->opened), enable); + if (enable) + err = k3dh_accel_enable(data); + else + err = k3dh_accel_disable(data); + break; + case K3DH_IOCTL_SET_DELAY: + if (copy_from_user(&delay_ns, (void __user *)arg, + sizeof(delay_ns))) + return -EFAULT; + err = k3dh_set_delay(data, delay_ns); + break; + case K3DH_IOCTL_GET_DELAY: + delay_ns = k3dh_get_delay(data); + if (put_user(delay_ns, (s64 __user *)arg)) + return -EFAULT; + break; + case K3DH_IOCTL_READ_ACCEL_XYZ: + err = k3dh_read_accel_xyz(data, &data->acc_xyz); + if (err) + break; + if (copy_to_user((void __user *)arg, + &data->acc_xyz, sizeof(data->acc_xyz))) + return -EFAULT; + break; + default: + err = -EINVAL; + break; + } + + return err; +} + +static int k3dh_suspend(struct device *dev) +{ + int res = 0; + struct k3dh_data *data = dev_get_drvdata(dev); +#ifdef CONFIG_SENSOR_K3DH_INPUTDEV + if (atomic_read(&data->enable)) + cancel_delayed_work_sync(&data->work); +#endif + if (atomic_read(&data->opened) > 0) + res = i2c_smbus_write_byte_data(data->client, + CTRL_REG1, PM_OFF); + + return res; +} + +static int k3dh_resume(struct device *dev) +{ + int res = 0; + struct k3dh_data *data = dev_get_drvdata(dev); + + if (atomic_read(&data->opened) > 0) + res = i2c_smbus_write_byte_data(data->client, CTRL_REG1, + data->ctrl_reg1_shadow); +#ifdef CONFIG_SENSOR_K3DH_INPUTDEV + if (atomic_read(&data->enable)) + schedule_delayed_work(&data->work, + msecs_to_jiffies(5)); +#endif + return res; +} + +static const struct dev_pm_ops k3dh_pm_ops = { + .suspend = k3dh_suspend, + .resume = k3dh_resume, +}; + +static const struct file_operations k3dh_fops = { + .owner = THIS_MODULE, + .open = k3dh_open, + .release = k3dh_close, + .unlocked_ioctl = k3dh_ioctl, +}; + +#ifdef CONFIG_SENSOR_K3DH_INPUTDEV +static ssize_t k3dh_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct input_dev *input = to_input_dev(dev); + struct k3dh_data *data = input_get_drvdata(input); + + return sprintf(buf, "%d\n", atomic_read(&data->enable)); +} + +static ssize_t k3dh_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct input_dev *input = to_input_dev(dev); + struct k3dh_data *data = input_get_drvdata(input); + unsigned long enable = 0; + int err; + + if (strict_strtoul(buf, 10, &enable)) + return -EINVAL; + k3dh_open_calibration(data); + + if (enable) { + err = k3dh_accel_enable(data); + if (err < 0) + goto done; + schedule_delayed_work(&data->work, + msecs_to_jiffies(5)); + } else { + cancel_delayed_work_sync(&data->work); + err = k3dh_accel_disable(data); + if (err < 0) + goto done; + } + atomic_set(&data->enable, enable); + pr_info("%s, enable = %ld\n", __func__, enable); +done: + return count; +} +static DEVICE_ATTR(enable, + S_IRUGO | S_IWUSR | S_IWGRP, + k3dh_enable_show, k3dh_enable_store); + +static ssize_t k3dh_delay_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct input_dev *input = to_input_dev(dev); + struct k3dh_data *data = input_get_drvdata(input); + + return sprintf(buf, "%d\n", atomic_read(&data->delay)); +} + +static ssize_t k3dh_delay_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct input_dev *input = to_input_dev(dev); + struct k3dh_data *data = input_get_drvdata(input); + unsigned long delay = 0; + if (strict_strtoul(buf, 10, &delay)) + return -EINVAL; + + if (delay > MAX_DELAY) + delay = MAX_DELAY; + if (delay < MIN_DELAY) + delay = MIN_DELAY; + atomic_set(&data->delay, delay); + k3dh_set_delay(data, delay * 1000000); + pr_info("%s, delay = %ld\n", __func__, delay); + return count; +} +static DEVICE_ATTR(poll_delay, + S_IRUGO | S_IWUSR | S_IWGRP, + k3dh_delay_show, k3dh_delay_store); + +static struct attribute *k3dh_attributes[] = { + &dev_attr_enable.attr, + &dev_attr_poll_delay.attr, + NULL +}; + +static struct attribute_group k3dh_attribute_group = { + .attrs = k3dh_attributes +}; +#endif + +static ssize_t k3dh_fs_read(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct k3dh_data *data = dev_get_drvdata(dev); + +#if defined(CONFIG_MACH_U1) || defined(CONFIG_MACH_TRATS) + int err = 0; + int on; + + mutex_lock(&data->write_lock); + on = atomic_read(&data->opened); + if (on == 0) { + err = i2c_smbus_write_byte_data(data->client, CTRL_REG1, + DEFAULT_POWER_ON_SETTING); + } + mutex_unlock(&data->write_lock); + + if (err < 0) { + pr_err("%s: i2c write ctrl_reg1 failed\n", __func__); + return err; + } + + err = k3dh_read_accel_xyz(data, &data->acc_xyz); + if (err < 0) { + pr_err("%s: k3dh_read_accel_xyz failed\n", __func__); + return err; + } + + if (on == 0) { + mutex_lock(&data->write_lock); + err = i2c_smbus_write_byte_data(data->client, CTRL_REG1, + PM_OFF); + mutex_unlock(&data->write_lock); + if (err) + pr_err("%s: i2c write ctrl_reg1 failed\n", __func__); + } +#endif + return sprintf(buf, "%d,%d,%d\n", + data->acc_xyz.x, data->acc_xyz.y, data->acc_xyz.z); +} + +static ssize_t k3dh_calibration_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int err; + struct k3dh_data *data = dev_get_drvdata(dev); + + err = k3dh_open_calibration(data); + if (err < 0) + pr_err("%s: k3dh_open_calibration() failed\n", __func__); + + if (!data->cal_data.x && !data->cal_data.y && !data->cal_data.z) + err = -1; + + return sprintf(buf, "%d %d %d %d\n", + err, data->cal_data.x, data->cal_data.y, data->cal_data.z); +} + +static ssize_t k3dh_calibration_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct k3dh_data *data = dev_get_drvdata(dev); + bool do_calib; + int err; + + if (sysfs_streq(buf, "1")) + do_calib = true; + else if (sysfs_streq(buf, "0")) + do_calib = false; + else { + pr_debug("%s: invalid value %d\n", __func__, *buf); + return -EINVAL; + } + + if (atomic_read(&data->opened) == 0) { + /* if off, turn on the device.*/ + err = i2c_smbus_write_byte_data(data->client, CTRL_REG1, + DEFAULT_POWER_ON_SETTING); + if (err) { + pr_err("%s: i2c write ctrl_reg1 failed(err=%d)\n", + __func__, err); + } + } + + err = k3dh_do_calibrate(dev, do_calib); + if (err < 0) { + pr_err("%s: k3dh_do_calibrate() failed\n", __func__); + return err; + } + + if (atomic_read(&data->opened) == 0) { + /* if off, turn on the device.*/ + err = i2c_smbus_write_byte_data(data->client, CTRL_REG1, + PM_OFF); + if (err) { + pr_err("%s: i2c write ctrl_reg1 failed(err=%d)\n", + __func__, err); + } + } + + return count; +} + +#if defined(CONFIG_MACH_U1) || defined(CONFIG_MACH_TRATS) +static DEVICE_ATTR(acc_file, 0664, k3dh_fs_read, NULL); +#else +static ssize_t +k3dh_accel_position_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct k3dh_data *data = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", data->position); +} + +static ssize_t +k3dh_accel_position_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct k3dh_data *data = dev_get_drvdata(dev); + int err = 0; + + err = kstrtoint(buf, 10, &data->position); + if (err < 0) + pr_err("%s, kstrtoint failed.", __func__); + + return count; +} + +static ssize_t k3dh_accel_vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s\n", VENDOR); +} + +static ssize_t k3dh_accel_name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s\n", CHIP_ID); +} + +static DEVICE_ATTR(name, 0664, k3dh_accel_name_show, NULL); +static DEVICE_ATTR(vendor, 0664, k3dh_accel_vendor_show, NULL); +static DEVICE_ATTR(raw_data, 0664, k3dh_fs_read, NULL); +static DEVICE_ATTR(position, 0664, + k3dh_accel_position_show, k3dh_accel_position_store); +#endif +static DEVICE_ATTR(calibration, 0664, + k3dh_calibration_show, k3dh_calibration_store); + +void k3dh_shutdown(struct i2c_client *client) +{ + int res = 0; + struct k3dh_data *data = i2c_get_clientdata(client); + + k3dh_infomsg("is called.\n"); + +#ifdef CONFIG_SENSOR_K3DH_INPUTDEV + if (atomic_read(&data->enable)) + cancel_delayed_work_sync(&data->work); +#endif + res = i2c_smbus_write_byte_data(data->client, + CTRL_REG1, PM_OFF); + if (res < 0) + pr_err("%s: pm_off failed %d\n", __func__, res); +} + +#ifdef CONFIG_SENSOR_K3DH_INPUTDEV +static void k3dh_work_func(struct work_struct *work) +{ + k3dh_read_accel_xyz(g_k3dh, &g_k3dh->acc_xyz); + pr_debug("%s: x: %d, y: %d, z: %d\n", __func__, + g_k3dh->acc_xyz.x, g_k3dh->acc_xyz.y, g_k3dh->acc_xyz.z); + input_report_abs(g_k3dh->input, ABS_X, g_k3dh->acc_xyz.x); + input_report_abs(g_k3dh->input, ABS_Y, g_k3dh->acc_xyz.y); + input_report_abs(g_k3dh->input, ABS_Z, g_k3dh->acc_xyz.z); + input_sync(g_k3dh->input); + schedule_delayed_work(&g_k3dh->work, msecs_to_jiffies( + atomic_read(&g_k3dh->delay))); +} + +/* ----------------- * + Input device interface + * ------------------ */ +static int k3dh_input_init(struct k3dh_data *data) +{ + struct input_dev *dev; + int err = 0; + + dev = input_allocate_device(); + if (!dev) + return -ENOMEM; + dev->name = "accelerometer"; + dev->id.bustype = BUS_I2C; + + input_set_capability(dev, EV_ABS, ABS_MISC); + input_set_abs_params(dev, ABS_X, ABSMIN_2G, ABSMAX_2G, 0, 0); + input_set_abs_params(dev, ABS_Y, ABSMIN_2G, ABSMAX_2G, 0, 0); + input_set_abs_params(dev, ABS_Z, ABSMIN_2G, ABSMAX_2G, 0, 0); + input_set_drvdata(dev, data); + + err = input_register_device(dev); + if (err < 0) + goto done; + data->input = dev; +done: + return 0; +} +#endif +static int k3dh_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct k3dh_data *data; +#if defined(CONFIG_MACH_U1) || defined(CONFIG_MACH_TRATS) + struct device *dev_t, *dev_cal; +#endif + struct accel_platform_data *pdata; + int err; + + k3dh_infomsg("is started.\n"); + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_WRITE_BYTE_DATA | + I2C_FUNC_SMBUS_READ_I2C_BLOCK)) { + pr_err("%s: i2c functionality check failed!\n", __func__); + err = -ENODEV; + goto exit; + } + + data = kzalloc(sizeof(struct k3dh_data), GFP_KERNEL); + if (data == NULL) { + dev_err(&client->dev, + "failed to allocate memory for module data\n"); + err = -ENOMEM; + goto exit; + } + + /* Checking device */ + err = i2c_smbus_write_byte_data(client, CTRL_REG1, + PM_OFF); + if (err) { + pr_err("%s: there is no such device, err = %d\n", + __func__, err); + goto err_read_reg; + } + + data->client = client; + g_k3dh = data; + i2c_set_clientdata(client, data); + + init_completion(&data->data_ready); + mutex_init(&data->read_lock); + mutex_init(&data->write_lock); + atomic_set(&data->opened, 0); + + /* sensor HAL expects to find /dev/accelerometer */ + data->k3dh_device.minor = MISC_DYNAMIC_MINOR; + data->k3dh_device.name = ACC_DEV_NAME; + data->k3dh_device.fops = &k3dh_fops; + + err = misc_register(&data->k3dh_device); + if (err) { + pr_err("%s: misc_register failed\n", __FILE__); + goto err_misc_register; + } + + pdata = client->dev.platform_data; + if (!pdata) { + /*Set by default position 3, it doesn't adjust raw value*/ + data->position = 3; + data->axis_adjust = false; + pr_err("using defualt position = %d\n", data->position); + } else { + if (pdata->accel_get_position) + data->position = pdata->accel_get_position(); + data->axis_adjust = pdata->axis_adjust; + pr_info("successful, position = %d\n", data->position); + } +#ifdef CONFIG_SENSOR_K3DH_INPUTDEV + atomic_set(&data->enable, 0); + atomic_set(&data->delay, 200); + k3dh_input_init(data); + + /* Setup sysfs */ + err = + sysfs_create_group(&data->input->dev.kobj, + &k3dh_attribute_group); + if (err < 0) + goto err_sysfs_create_group; + + /* Setup driver interface */ + INIT_DELAYED_WORK(&data->work, k3dh_work_func); +#endif +#if defined(CONFIG_MACH_U1) || defined(CONFIG_MACH_TRATS) + /* creating class/device for test */ + data->acc_class = class_create(THIS_MODULE, "accelerometer"); + if (IS_ERR(data->acc_class)) { + pr_err("%s: class create failed(accelerometer)\n", __func__); + err = PTR_ERR(data->acc_class); + goto err_class_create; + } + + dev_t = device_create(data->acc_class, NULL, + MKDEV(ACC_DEV_MAJOR, 0), "%s", "accelerometer"); + if (IS_ERR(dev_t)) { + pr_err("%s: class create failed(accelerometer)\n", __func__); + err = PTR_ERR(dev_t); + goto err_acc_device_create; + } + + err = device_create_file(dev_t, &dev_attr_acc_file); + if (err < 0) { + pr_err("%s: Failed to create device file(%s)\n", + __func__, dev_attr_acc_file.attr.name); + goto err_acc_device_create_file; + } + dev_set_drvdata(dev_t, data); + + /* creating device for calibration */ + dev_cal = device_create(sec_class, NULL, 0, NULL, "gsensorcal"); + if (IS_ERR(dev_cal)) { + pr_err("%s: class create failed(gsensorcal)\n", __func__); + err = PTR_ERR(dev_cal); + goto err_cal_device_create; + } + + err = device_create_file(dev_cal, &dev_attr_calibration); + if (err < 0) { + pr_err("%s: Failed to create device file(%s)\n", + __func__, dev_attr_calibration.attr.name); + goto err_cal_device_create_file; + } + dev_set_drvdata(dev_cal, data); +#else + /* creating device for test & calibration */ + data->dev = sensors_classdev_register("accelerometer_sensor"); + if (IS_ERR(data->dev)) { + pr_err("%s: class create failed(accelerometer_sensor)\n", + __func__); + err = PTR_ERR(data->dev); + goto err_acc_device_create; + } + + err = device_create_file(data->dev, &dev_attr_position); + if (err < 0) { + pr_err("%s: Failed to create device file(%s)\n", + __func__, dev_attr_position.attr.name); + goto err_position_device_create_file; + } + + err = device_create_file(data->dev, &dev_attr_raw_data); + if (err < 0) { + pr_err("%s: Failed to create device file(%s)\n", + __func__, dev_attr_raw_data.attr.name); + goto err_acc_device_create_file; + } + + err = device_create_file(data->dev, &dev_attr_calibration); + if (err < 0) { + pr_err("%s: Failed to create device file(%s)\n", + __func__, dev_attr_calibration.attr.name); + goto err_cal_device_create_file; + } + + err = device_create_file(data->dev, &dev_attr_vendor); + if (err < 0) { + pr_err("%s: Failed to create device file(%s)\n", + __func__, dev_attr_vendor.attr.name); + goto err_vendor_device_create_file; + } + + err = device_create_file(data->dev, &dev_attr_name); + if (err < 0) { + pr_err("%s: Failed to create device file(%s)\n", + __func__, dev_attr_name.attr.name); + goto err_name_device_create_file; + } + + dev_set_drvdata(data->dev, data); +#endif + + k3dh_infomsg("is successful.\n"); + + return 0; + +#if defined(CONFIG_MACH_U1) || defined(CONFIG_MACH_TRATS) +err_cal_device_create_file: + device_destroy(sec_class, 0); +err_cal_device_create: + device_remove_file(dev_t, &dev_attr_acc_file); +err_acc_device_create_file: + device_destroy(data->acc_class, MKDEV(ACC_DEV_MAJOR, 0)); +err_acc_device_create: + class_destroy(data->acc_class); +err_class_create: +#else +err_name_device_create_file: + device_remove_file(data->dev, &dev_attr_vendor); +err_vendor_device_create_file: + device_remove_file(data->dev, &dev_attr_calibration); +err_cal_device_create_file: + device_remove_file(data->dev, &dev_attr_raw_data); +err_acc_device_create_file: + device_remove_file(data->dev, &dev_attr_position); +err_position_device_create_file: + sensors_classdev_unregister(data->dev); + +err_acc_device_create: +#endif +#ifdef CONFIG_SENSOR_K3DH_INPUTDEV + input_free_device(data->input); +err_sysfs_create_group: +#endif +misc_deregister(&data->k3dh_device); +err_misc_register: + mutex_destroy(&data->read_lock); + mutex_destroy(&data->write_lock); +err_read_reg: + kfree(data); +exit: + return err; +} + +static int k3dh_remove(struct i2c_client *client) +{ + struct k3dh_data *data = i2c_get_clientdata(client); + int err = 0; + + if (atomic_read(&data->opened) > 0) { + err = i2c_smbus_write_byte_data(data->client, + CTRL_REG1, PM_OFF); + if (err < 0) + pr_err("%s: pm_off failed %d\n", __func__, err); + } + +#if defined(CONFIG_MACH_U1) || defined(CONFIG_MACH_TRATS) + device_destroy(sec_class, 0); + device_destroy(data->acc_class, MKDEV(ACC_DEV_MAJOR, 0)); + class_destroy(data->acc_class); +#else + device_remove_file(data->dev, &dev_attr_name); + device_remove_file(data->dev, &dev_attr_vendor); + device_remove_file(data->dev, &dev_attr_calibration); + device_remove_file(data->dev, &dev_attr_raw_data); + device_remove_file(data->dev, &dev_attr_position); + sensors_classdev_unregister(data->dev); +#endif + misc_deregister(&data->k3dh_device); + mutex_destroy(&data->read_lock); + mutex_destroy(&data->write_lock); + kfree(data); + + return 0; +} + +static const struct i2c_device_id k3dh_id[] = { + { "k3dh", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, k3dh_id); + +static struct i2c_driver k3dh_driver = { + .probe = k3dh_probe, + .shutdown = k3dh_shutdown, + .remove = __devexit_p(k3dh_remove), + .id_table = k3dh_id, + .driver = { + .pm = &k3dh_pm_ops, + .owner = THIS_MODULE, + .name = "k3dh", + }, +}; + +static int __init k3dh_init(void) +{ + return i2c_add_driver(&k3dh_driver); +} + +static void __exit k3dh_exit(void) +{ + i2c_del_driver(&k3dh_driver); +} + +module_init(k3dh_init); +module_exit(k3dh_exit); + +MODULE_DESCRIPTION("k3dh accelerometer driver"); +MODULE_AUTHOR("Samsung Electronics"); +MODULE_LICENSE("GPL"); diff --git a/drivers/sensor/k3dh_reg.h b/drivers/sensor/k3dh_reg.h index 846d0d7..e6c9fa4 100644 --- a/drivers/sensor/k3dh_reg.h +++ b/drivers/sensor/k3dh_reg.h @@ -102,6 +102,7 @@ #define I1_OVERRUN (1 << 1) /* CTRL_REG4 */ +#define CTRL_REG4_BDU (1 << 7) #define CTRL_REG4_BLE (1 << 6) #define CTRL_REG4_FS1 (1 << 5) #define CTRL_REG4_FS0 (1 << 4) diff --git a/drivers/sensor/yas_mag_driver-yas532.c b/drivers/sensor/yas_mag_driver-yas532.c new file mode 100644 index 0000000..14259b8 --- /dev/null +++ b/drivers/sensor/yas_mag_driver-yas532.c @@ -0,0 +1,2909 @@ +/* + * Copyright (c) 2010-2011 Yamaha Corporation + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +#include <linux/sensor/yas.h> + +struct utimeval { + int32_t tv_sec; + int32_t tv_msec; +}; + +struct utimer { + struct utimeval prev_time; + struct utimeval total_time; + struct utimeval delay_ms; +}; + +static int utimeval_init(struct utimeval *val); +static int utimeval_is_initial(struct utimeval *val); +static int utimeval_is_overflow(struct utimeval *val); +static struct utimeval utimeval_plus(struct utimeval *first, + struct utimeval *second); +static struct utimeval utimeval_minus(struct utimeval *first, + struct utimeval *second); +static int utimeval_greater_than(struct utimeval *first, + struct utimeval *second); +static int utimeval_greater_or_equal(struct utimeval *first, + struct utimeval *second); +static int utimeval_greater_than_zero(struct utimeval *val); +static int utimeval_less_than_zero(struct utimeval *val); +static struct utimeval *msec_to_utimeval(struct utimeval *result, + uint32_t msec); +static uint32_t utimeval_to_msec(struct utimeval *val); + +static struct utimeval utimer_calc_next_time(struct utimer *ut, + struct utimeval *cur); +static struct utimeval utimer_current_time(void); +static int utimer_is_timeout(struct utimer *ut); +static int utimer_clear_timeout(struct utimer *ut); +static uint32_t utimer_get_total_time(struct utimer *ut); +static uint32_t utimer_get_delay(struct utimer *ut); +static int utimer_set_delay(struct utimer *ut, uint32_t delay_ms); +static int utimer_update(struct utimer *ut); +static int utimer_update_with_curtime(struct utimer *ut, struct utimeval *cur); +static uint32_t utimer_sleep_time(struct utimer *ut); +static uint32_t utimer_sleep_time_with_curtime(struct utimer *ut, + struct utimeval *cur); +static int utimer_init(struct utimer *ut, uint32_t delay_ms); +static int utimer_clear(struct utimer *ut); +static void utimer_lib_init(void (*func) (int *sec, int *msec)); + +#define YAS_REGADDR_DEVICE_ID (0x80) +#define YAS_REGADDR_ACTUATE_INIT_COIL (0x81) +#define YAS_REGADDR_MEASURE_COMMAND (0x82) +#define YAS_REGADDR_CONFIG (0x83) +#define YAS_REGADDR_MEASURE_INTERVAL (0x84) +#define YAS_REGADDR_OFFSET_X (0x85) +#define YAS_REGADDR_OFFSET_Y1 (0x86) +#define YAS_REGADDR_OFFSET_Y2 (0x87) +#define YAS_REGADDR_TEST1 (0x88) +#define YAS_REGADDR_TEST2 (0x89) +#define YAS_REGADDR_CAL (0x90) +#define YAS_REGADDR_MEASURE_DATA (0xb0) +#define YAS_YAS530_DEVICE_ID (0x01) /* YAS530 (MS-3E) */ +#define YAS_YAS530_VERSION_A (0) /* YAS530 (MS-3E Aver) */ +#define YAS_YAS530_VERSION_B (1) /* YAS530B (MS-3E Bver) */ +#define YAS_YAS530_VERSION_A_COEF (380) +#define YAS_YAS530_VERSION_B_COEF (550) +#define YAS_YAS530_DATA_CENTER (2048) +#define YAS_YAS530_DATA_OVERFLOW (4095) + +#define YAS_YAS532_DEVICE_ID (0x02) /* YAS532 (MS-3R) */ +#define YAS_YAS532_VERSION_AB (0) /* YAS532AB (MS-3R ABver) */ +#define YAS_YAS532_VERSION_AC (1) /* YAS532AC (MS-3R ACver) */ +#define YAS_YAS532_VERSION_AB_COEF (1800) +#define YAS_YAS532_VERSION_AC_COEF (900) +#define YAS_YAS532_DATA_CENTER (4096) +#define YAS_YAS532_DATA_OVERFLOW (8190) + +#undef YAS_YAS530_CAL_SINGLE_READ + +struct yas_machdep_func { + int (*device_open) (void); + int (*device_close) (void); + int (*device_write) (uint8_t addr, const uint8_t *buf, int len); + int (*device_read) (uint8_t addr, uint8_t *buf, int len); + void (*msleep) (int msec); +}; + +static int yas_cdrv_actuate_initcoil(void); +static int yas_cdrv_set_offset(const int8_t *offset); +static int yas_cdrv_recalc_calib_offset(int32_t *prev_calib_offset, + int32_t *new_calib_offset, + int8_t *prev_offset, + int8_t *new_offset); +static int yas_cdrv_set_transformatiom_matrix(const int8_t *transform); +static int yas_cdrv_measure_and_set_offset(int8_t *offset); +static int yas_cdrv_measure(int32_t *msens, int32_t *raw, int16_t *t); +static int yas_cdrv_init(const int8_t *transform, + struct yas_machdep_func *func); +static int yas_cdrv_term(void); + +static void (*current_time) (int *sec, int *msec) = { +0}; + +static int utimeval_init(struct utimeval *val) +{ + if (unlikely(!val)) + return -1; + val->tv_sec = val->tv_msec = 0; + return 0; +} + +static int utimeval_is_initial(struct utimeval *val) +{ + if (unlikely(!val)) + return 0; + return val->tv_sec == 0 && val->tv_msec == 0; +} + +static int utimeval_is_overflow(struct utimeval *val) +{ + int32_t max; + + if (unlikely(!val)) + return 0; + + max = (int32_t) (0xffffffff / 1000); + if (val->tv_sec > max) { + return 1; /* overflow */ + } else if (val->tv_sec == max) { + if (val->tv_msec > + (int32_t) (0xffffffff % 1000)) { + return 1; /* overflow */ + } + } + + return 0; +} + +static struct utimeval +utimeval_plus(struct utimeval *first, struct utimeval *second) +{ + struct utimeval result = { 0, 0 }; + int32_t tmp; + + if (unlikely(!first || !second)) + return result; + + tmp = first->tv_sec + second->tv_sec; + if (first->tv_sec >= 0 && second->tv_sec >= 0 && tmp < 0) + goto overflow; + if (first->tv_sec < 0 && second->tv_sec < 0 && tmp >= 0) + goto underflow; + + result.tv_sec = tmp; + result.tv_msec = first->tv_msec + second->tv_msec; + if (1000 <= result.tv_msec) { + tmp = result.tv_sec + result.tv_msec / 1000; + if (result.tv_sec >= 0 && result.tv_msec >= 0 && tmp < 0) + goto overflow; + result.tv_sec = tmp; + result.tv_msec = result.tv_msec % 1000; + } + if (result.tv_msec < 0) { + tmp = result.tv_sec + result.tv_msec / 1000 - 1; + if (result.tv_sec < 0 && result.tv_msec < 0 && tmp >= 0) + goto underflow; + result.tv_sec = tmp; + result.tv_msec = result.tv_msec % 1000 + 1000; + } + + return result; + +overflow: + result.tv_sec = 0x7fffffff; + result.tv_msec = 999; + return result; + +underflow: + result.tv_sec = 0x80000000; + result.tv_msec = 0; + return result; +} + +static struct utimeval +utimeval_minus(struct utimeval *first, struct utimeval *second) +{ + struct utimeval result = { 0, 0 }, tmp; + + if (first == NULL || second == NULL + || second->tv_sec == (int)0x80000000) + return result; + + tmp.tv_sec = -second->tv_sec; + tmp.tv_msec = -second->tv_msec; + return utimeval_plus(first, &tmp); +} + +static int utimeval_less_than(struct utimeval *first, struct utimeval *second) +{ + if (unlikely(!first || !second)) + return 0; + + if (first->tv_sec > second->tv_sec) + return 1; + else if (first->tv_sec < second->tv_sec) + return 0; + else + if (first->tv_msec > second->tv_msec) + return 1; + else + return 0; +} + +static int +utimeval_greater_than(struct utimeval *first, struct utimeval *second) +{ + if (unlikely(!first || !second)) + return 0; + + if (first->tv_sec < second->tv_sec) + return 1; + else if (first->tv_sec > second->tv_sec) + return 0; + else + if (first->tv_msec < second->tv_msec) + return 1; + else + return 0; +} + +static int +utimeval_greater_or_equal(struct utimeval *first, struct utimeval *second) +{ + return !utimeval_less_than(first, second); +} + +static int utimeval_greater_than_zero(struct utimeval *val) +{ + struct utimeval zero = { 0, 0 }; + return utimeval_greater_than(&zero, val); +} + +static int utimeval_less_than_zero(struct utimeval *val) +{ + struct utimeval zero = { 0, 0 }; + return utimeval_less_than(&zero, val); +} + +static struct utimeval *msec_to_utimeval(struct utimeval *result, uint32_t msec) +{ + if (unlikely(!result)) + return result; + result->tv_sec = msec / 1000; + result->tv_msec = msec % 1000; + + return result; +} + +static uint32_t utimeval_to_msec(struct utimeval *val) +{ + if (unlikely(!val)) + return 0; + if (utimeval_less_than_zero(val)) + return 0; + + if (utimeval_is_overflow(val)) + return 0xffffffff; + + return val->tv_sec * 1000 + val->tv_msec; +} + +static struct utimeval +utimer_calc_next_time(struct utimer *ut, struct utimeval *cur) +{ + struct utimeval result = { 0, 0 }, delay; + + if (ut == NULL || cur == NULL) + return result; + utimer_update_with_curtime(ut, cur); + if (utimer_is_timeout(ut)) { + result = *cur; + } else { + delay = utimeval_minus(&ut->delay_ms, &ut->total_time); + result = utimeval_plus(cur, &delay); + } + + return result; +} + +static struct utimeval utimer_current_time(void) +{ + struct utimeval tv; + int sec, msec; + + if (current_time != NULL) + current_time(&sec, &msec); + else + sec = 0, msec = 0; + tv.tv_sec = sec; + tv.tv_msec = msec; + + return tv; +} + +static int utimer_clear(struct utimer *ut) +{ + if (unlikely(!ut)) + return -1; + utimeval_init(&ut->prev_time); + utimeval_init(&ut->total_time); + + return 0; +} + +static int utimer_update_with_curtime(struct utimer *ut, struct utimeval *cur) +{ + struct utimeval tmp; + + if (unlikely(!ut || !cur)) + return -1; + if (utimeval_is_initial(&ut->prev_time)) + ut->prev_time = *cur; + if (utimeval_greater_than_zero(&ut->delay_ms)) { + tmp = utimeval_minus(cur, &ut->prev_time); + if (utimeval_less_than_zero(&tmp)) + utimeval_init(&ut->total_time); + else { + ut->total_time = utimeval_plus(&tmp, &ut->total_time); + if (utimeval_is_overflow(&ut->total_time)) + utimeval_init(&ut->total_time); + } + ut->prev_time = *cur; + } + + return 0; +} + +static int utimer_update(struct utimer *ut) +{ + struct utimeval cur; + + if (unlikely(!ut)) + return -1; + + cur = utimer_current_time(); + utimer_update_with_curtime(ut, &cur); + return 0; +} + +static int utimer_is_timeout(struct utimer *ut) +{ + if (unlikely(!ut)) + return 0; + + if (utimeval_greater_than_zero(&ut->delay_ms)) + return utimeval_greater_or_equal(&ut->delay_ms, + &ut->total_time); + else + return 1; +} + +static int utimer_clear_timeout(struct utimer *ut) +{ + uint32_t delay, total; + + if (unlikely(!ut)) + return -1; + + delay = utimeval_to_msec(&ut->delay_ms); + if (delay == 0 || utimeval_is_overflow(&ut->total_time)) + total = 0; + else + if (utimeval_is_overflow(&ut->total_time)) + total = 0; + else { + total = utimeval_to_msec(&ut->total_time); + total = total % delay; + } + msec_to_utimeval(&ut->total_time, total); + + return 0; +} + +static uint32_t +utimer_sleep_time_with_curtime(struct utimer *ut, struct utimeval *cur) +{ + struct utimeval tv; + + if (unlikely(!ut || !cur)) + return 0; + + tv = utimer_calc_next_time(ut, cur); + tv = utimeval_minus(&tv, cur); + if (utimeval_less_than_zero(&tv)) + return 0; + + return utimeval_to_msec(&tv); +} + +static uint32_t utimer_sleep_time(struct utimer *ut) +{ + struct utimeval cur; + + if (unlikely(!ut)) + return 0; + + cur = utimer_current_time(); + return utimer_sleep_time_with_curtime(ut, &cur); +} + +static int utimer_init(struct utimer *ut, uint32_t delay_ms) +{ + if (unlikely(!ut)) + return -1; + utimer_clear(ut); + msec_to_utimeval(&ut->delay_ms, delay_ms); + + return 0; +} + +static uint32_t utimer_get_total_time(struct utimer *ut) +{ + return utimeval_to_msec(&ut->total_time); +} + +static uint32_t utimer_get_delay(struct utimer *ut) +{ + if (unlikely(!ut)) + return -1; + return utimeval_to_msec(&ut->delay_ms); +} + +static int utimer_set_delay(struct utimer *ut, uint32_t delay_ms) +{ + return utimer_init(ut, delay_ms); +} + +static void utimer_lib_init(void (*func) (int *sec, int *msec)) +{ + current_time = func; +} + +struct yas_cal_data { + uint8_t dx, dy1, dy2; + uint8_t d2, d3, d4, d5, d6, d7, d8, d9, d0; + uint8_t dck; + uint8_t ver; +}; +struct yas_correction_data { + int32_t Cx, Cy1, Cy2; + int32_t a2, a3, a4, a5, a6, a7, a8, a9, k; +}; +struct yas_cdriver { + struct yas_cal_data cal; + struct yas_correction_data correct; + struct yas_machdep_func func; + int8_t transform[9]; + int16_t temperature; + uint8_t dev_id; + int32_t coef; + int16_t center; + int16_t overflow; +}; +static struct yas_cdriver cdriver; + +static int device_open(void) +{ + if (cdriver.func.device_open == NULL) + return -1; + return cdriver.func.device_open(); +} + +static int device_close(void) +{ + if (cdriver.func.device_close == NULL) + return -1; + return cdriver.func.device_close(); +} + +static int device_write(uint8_t addr, const uint8_t *buf, int len) +{ + if (cdriver.func.device_write == NULL) + return -1; + return cdriver.func.device_write(addr, buf, len); +} + +static int device_read(uint8_t addr, uint8_t *buf, int len) +{ + if (cdriver.func.device_read == NULL) + return -1; + return cdriver.func.device_read(addr, buf, len); +} + +static void sleep(int millisec) +{ + if (cdriver.func.msleep == NULL) + return; + cdriver.func.msleep(millisec); +} + +static int init_test_register(void) +{ + uint8_t data; + + data = 0x00; + if (device_write(YAS_REGADDR_TEST1, &data, 1) < 0) + return YAS_ERROR_DEVICE_COMMUNICATION; + data = 0x00; + if (device_write(YAS_REGADDR_TEST2, &data, 1) < 0) + return YAS_ERROR_DEVICE_COMMUNICATION; + + return YAS_NO_ERROR; +} + +static int get_device_id(uint8_t *id) +{ + uint8_t data = 0; + + if (device_read(YAS_REGADDR_DEVICE_ID, &data, 1) < 0) + return YAS_ERROR_DEVICE_COMMUNICATION; + *id = data; + + return YAS_NO_ERROR; +} + +static int get_cal_data_yas530(struct yas_cal_data *cal) +{ + uint8_t data[16]; +#ifdef YAS_YAS530_CAL_SINGLE_READ + int i; + + for (i = 0; i < 16; i++) { /* dummy read */ + if (device_read(YAS_REGADDR_CAL + i, &data[i], 1) < 0) + return YAS_ERROR_DEVICE_COMMUNICATION; + } + for (i = 0; i < 16; i++) { + if (device_read(YAS_REGADDR_CAL + i, &data[i], 1) < 0) + return YAS_ERROR_DEVICE_COMMUNICATION; + } +#else + if (device_read(YAS_REGADDR_CAL, data, 16) < 0) + return YAS_ERROR_DEVICE_COMMUNICATION; + if (device_read(YAS_REGADDR_CAL, data, 16) < 0) + return YAS_ERROR_DEVICE_COMMUNICATION; +#endif + + cal->dx = data[0]; + cal->dy1 = data[1]; + cal->dy2 = data[2]; + cal->d2 = (data[3] >> 2) & 0x03f; + cal->d3 = ((data[3] << 2) & 0x0c) | ((data[4] >> 6) & 0x03); + cal->d4 = data[4] & 0x3f; + cal->d5 = (data[5] >> 2) & 0x3f; + cal->d6 = ((data[5] << 4) & 0x30) | ((data[6] >> 4) & 0x0f); + cal->d7 = ((data[6] << 3) & 0x78) | ((data[7] >> 5) & 0x07); + cal->d8 = ((data[7] << 1) & 0x3e) | ((data[8] >> 7) & 0x01); + cal->d9 = ((data[8] << 1) & 0xfe) | ((data[9] >> 7) & 0x01); + cal->d0 = (data[9] >> 2) & 0x1f; + cal->dck = ((data[9] << 1) & 0x06) | ((data[10] >> 7) & 0x01); + cal->ver = (data[15]) & 0x03; + + return YAS_NO_ERROR; +} + +static void +get_correction_value_yas530(struct yas_cal_data *cal, + struct yas_correction_data *correct) +{ + correct->Cx = cal->dx * 6 - 768; + correct->Cy1 = cal->dy1 * 6 - 768; + correct->Cy2 = cal->dy2 * 6 - 768; + correct->a2 = cal->d2 - 32; + correct->a3 = cal->d3 - 8; + correct->a4 = cal->d4 - 32; + correct->a5 = cal->d5 + 38; + correct->a6 = cal->d6 - 32; + correct->a7 = cal->d7 - 64; + correct->a8 = cal->d8 - 32; + correct->a9 = cal->d9; + correct->k = cal->d0 + 10; +} + +static int get_cal_data_yas532(struct yas_cal_data *cal) +{ + uint8_t data[14]; +#ifdef YAS_YAS530_CAL_SINGLE_READ + int i; + + for (i = 0; i < 14; i++) { /* dummy read */ + if (device_read(YAS_REGADDR_CAL + i, &data[i], 1) < 0) + return YAS_ERROR_DEVICE_COMMUNICATION; + } + for (i = 0; i < 14; i++) { + if (device_read(YAS_REGADDR_CAL + i, &data[i], 1) < 0) + return YAS_ERROR_DEVICE_COMMUNICATION; + } +#else + if (device_read(YAS_REGADDR_CAL, data, 14) < 0) + return YAS_ERROR_DEVICE_COMMUNICATION; + if (device_read(YAS_REGADDR_CAL, data, 14) < 0) + return YAS_ERROR_DEVICE_COMMUNICATION; +#endif + + cal->dx = data[0]; + cal->dy1 = data[1]; + cal->dy2 = data[2]; + cal->d2 = (data[3] >> 2) & 0x03f; + cal->d3 = ((data[3] << 2) & 0x0c) | ((data[4] >> 6) & 0x03); + cal->d4 = data[4] & 0x3f; + cal->d5 = (data[5] >> 2) & 0x3f; + cal->d6 = ((data[5] << 4) & 0x30) | ((data[6] >> 4) & 0x0f); + cal->d7 = ((data[6] << 3) & 0x78) | ((data[7] >> 5) & 0x07); + cal->d8 = ((data[7] << 1) & 0x3e) | ((data[8] >> 7) & 0x01); + cal->d9 = ((data[8] << 1) & 0xfe) | ((data[9] >> 7) & 0x01); + cal->d0 = (data[9] >> 2) & 0x1f; + cal->dck = ((data[9] << 1) & 0x06) | ((data[10] >> 7) & 0x01); + cal->ver = (data[13]) & 0x01; + + return YAS_NO_ERROR; +} + +static void +get_correction_value_yas532(struct yas_cal_data *cal, + struct yas_correction_data *correct) +{ + correct->Cx = cal->dx * 10 - 1280; + correct->Cy1 = cal->dy1 * 10 - 1280; + correct->Cy2 = cal->dy2 * 10 - 1280; + correct->a2 = cal->d2 - 32; + correct->a3 = cal->d3 - 8; + correct->a4 = cal->d4 - 32; + correct->a5 = cal->d5 + 38; + correct->a6 = cal->d6 - 32; + correct->a7 = cal->d7 - 64; + correct->a8 = cal->d8 - 32; + correct->a9 = cal->d9; + correct->k = cal->d0; +} + +static int set_configuration(int inton, int inthact, int cck) +{ + uint8_t data = 0; + + data |= (!!inton) & 0x01; + data |= ((!!inthact) << 1) & 0x02; + data |= (cck << 2) & 0x1c; + + if (device_write(YAS_REGADDR_CONFIG, &data, 1) < 0) + return YAS_ERROR_DEVICE_COMMUNICATION; + + return YAS_NO_ERROR; +} + +static int get_measure_interval(int32_t *msec) +{ + uint8_t data; + int mult = 7; + + if (device_read(YAS_REGADDR_MEASURE_INTERVAL, &data, 1) < 0) + return YAS_ERROR_DEVICE_COMMUNICATION; + switch (cdriver.dev_id) { + case YAS_YAS532_DEVICE_ID: + mult = 4; + break; + case YAS_YAS530_DEVICE_ID: + default: + mult = 7; + break; + } + + *msec = data * mult; + + return YAS_NO_ERROR; +} + +static int set_measure_interval(int32_t msec) +{ + uint8_t data = 0; + int mult = 7; + + switch (cdriver.dev_id) { + case YAS_YAS532_DEVICE_ID: + mult = 4; + break; + case YAS_YAS530_DEVICE_ID: + default: + mult = 7; + break; + } + + if (msec > mult*0xff) + data = 0xff; + else + if (msec % mult == 0) + data = (uint8_t)(msec / mult); + else + data = (uint8_t)(msec / mult + 1); + if (device_write(YAS_REGADDR_MEASURE_INTERVAL, &data, 1) < 0) + return YAS_ERROR_DEVICE_COMMUNICATION; + + return YAS_NO_ERROR; +} + +static int set_measure_command(int ldtc, int fors, int dlymes) +{ + uint8_t data = 0; + + data |= 0x01; /* bit 0 must be 1 */ + data |= ((!(!ldtc)) << 1) & 0x02; + data |= ((!(!fors)) << 2) & 0x04; + data |= ((!(!dlymes)) << 4) & 0x10; + + if (device_write(YAS_REGADDR_MEASURE_COMMAND, &data, 1) < 0) + return YAS_ERROR_DEVICE_COMMUNICATION; + + return YAS_NO_ERROR; +} + +static int +measure_normal_yas530(int *busy, int16_t *t, int16_t *x, int16_t *y1, + int16_t *y2) +{ + uint8_t data[8]; + + if (set_measure_command(0, 0, 0) < 0) + return YAS_ERROR_DEVICE_COMMUNICATION; + + sleep(2); + + if (device_read(YAS_REGADDR_MEASURE_DATA, data, 8) < 0) + return YAS_ERROR_DEVICE_COMMUNICATION; + + *busy = (data[0] >> 7) & 0x01; + *t = (((int32_t) data[0] << 2) & 0x1fc) | ((data[1] >> 6) & 0x03); + *x = (((int32_t) data[2] << 5) & 0xfe0) | ((data[3] >> 3) & 0x1f); + *y1 = (((int32_t) data[4] << 5) & 0xfe0) | ((data[5] >> 3) & 0x1f); + *y2 = (((int32_t) data[6] << 5) & 0xfe0) | ((data[7] >> 3) & 0x1f); + /*YLOGD(("f[%d] t[%d] x[%d] y1[%d] y2[%d]\n", + *busy, *t, *x, *y1, *y2)); */ + + return YAS_NO_ERROR; +} + +static int +measure_normal_yas532(int *busy, int16_t *t, int16_t *x, int16_t *y1, + int16_t *y2) +{ + uint8_t data[8]; + + if (set_measure_command(0, 0, 0) < 0) + return YAS_ERROR_DEVICE_COMMUNICATION; + + sleep(2); + + if (device_read(YAS_REGADDR_MEASURE_DATA, data, 8) < 0) + return YAS_ERROR_DEVICE_COMMUNICATION; + + *busy = (data[0] >> 7) & 0x01; + *t = (((int32_t) data[0] << 3) & 0x3f8) | ((data[1] >> 5) & 0x07); + *x = (((int32_t) data[2] << 6) & 0x1fc0) | ((data[3] >> 2) & 0x3f); + *y1 = (((int32_t) data[4] << 6) & 0x1fc0) | ((data[5] >> 2) & 0x3f); + *y2 = (((int32_t) data[6] << 6) & 0x1fc0) | ((data[7] >> 2) & 0x3f); + /*YLOGD(("f[%d] t[%d] x[%d] y1[%d] y2[%d]\n", + *busy, *t, *x, *y1, *y2)); */ + + return YAS_NO_ERROR; +} + +static int +measure_normal(int *busy, int16_t *t, int16_t *x, int16_t *y1, int16_t *y2) +{ + int result; + + switch (cdriver.dev_id) { + case YAS_YAS532_DEVICE_ID: + result = measure_normal_yas532(busy, t, x, y1, y2); + break; + case YAS_YAS530_DEVICE_ID: + default: + result = measure_normal_yas530(busy, t, x, y1, y2); + break; + } + + return result; +} + +static int +coordinate_conversion(int32_t x, int32_t y1, int32_t y2, int16_t t, + int32_t *xo, int32_t *yo, int32_t *zo, + struct yas_correction_data *c) +{ + int32_t sx, sy1, sy2, sy, sz; + int32_t hx, hy, hz; + + sx = x - (c->Cx * t) / 100; + sy1 = y1 - (c->Cy1 * t) / 100; + sy2 = y2 - (c->Cy2 * t) / 100; + + sy = sy1 - sy2; + sz = -sy1 - sy2; + + hx = c->k * ((100 * sx + c->a2 * sy + c->a3 * sz) / 10); + hy = c->k * ((c->a4 * sx + c->a5 * sy + c->a6 * sz) / 10); + hz = c->k * ((c->a7 * sx + c->a8 * sy + c->a9 * sz) / 10); + + *xo = cdriver.transform[0] * hx + + cdriver.transform[1] * hy + cdriver.transform[2] * hz; + *yo = cdriver.transform[3] * hx + + cdriver.transform[4] * hy + cdriver.transform[5] * hz; + *zo = cdriver.transform[6] * hx + + cdriver.transform[7] * hy + cdriver.transform[8] * hz; + + return YAS_NO_ERROR; +} + +static int +set_hardware_offset(int8_t offset_x, int8_t offset_y1, int8_t offset_y2) +{ + uint8_t data; + + data = offset_x & 0x3f; + if (device_write(YAS_REGADDR_OFFSET_X, &data, 1) < 0) + return YAS_ERROR_DEVICE_COMMUNICATION; + + data = offset_y1 & 0x3f; + if (device_write(YAS_REGADDR_OFFSET_Y1, &data, 1) < 0) + return YAS_ERROR_DEVICE_COMMUNICATION; + + data = offset_y2 & 0x3f; + if (device_write(YAS_REGADDR_OFFSET_Y2, &data, 1) < 0) + return YAS_ERROR_DEVICE_COMMUNICATION; + + return YAS_NO_ERROR; +} + +static int yas_cdrv_actuate_initcoil(void) +{ + uint8_t data = 0; + + if (device_write(YAS_REGADDR_ACTUATE_INIT_COIL, &data, 1) < 0) + return YAS_ERROR_DEVICE_COMMUNICATION; + return YAS_NO_ERROR; +} + +static int +check_offset(int8_t offset_x, int8_t offset_y1, int8_t offset_y2, + int *flag_x, int *flag_y1, int *flag_y2) +{ + int busy; + int16_t t, x, y1, y2; + + if (set_hardware_offset(offset_x, offset_y1, offset_y2) < 0) + return YAS_ERROR_DEVICE_COMMUNICATION; + if (measure_normal(&busy, &t, &x, &y1, &y2) < 0) + return YAS_ERROR_DEVICE_COMMUNICATION; + *flag_x = *flag_y1 = *flag_y2 = 0; + if (x > cdriver.center) + *flag_x = 1; + if (y1 > cdriver.center) + *flag_y1 = 1; + if (y2 > cdriver.center) + *flag_y2 = 1; + if (x < cdriver.center) + *flag_x = -1; + if (y1 < cdriver.center) + *flag_y1 = -1; + if (y2 < cdriver.center) + *flag_y2 = -1; + + return YAS_NO_ERROR; +} + +static int yas_cdrv_measure_and_set_offset(int8_t *offset) +{ + int i; + int8_t offset_x = 0, offset_y1 = 0, offset_y2 = 0; + int flag_x = 0, flag_y1 = 0, flag_y2 = 0; + static const int correct[5] = { 16, 8, 4, 2, 1 }; + + for (i = 0; i < 5; i++) { + if (check_offset(offset_x, offset_y1, offset_y2, + &flag_x, &flag_y1, &flag_y2) < 0) { + return YAS_ERROR_DEVICE_COMMUNICATION; + } + YLOGD(("offset[%d][%d][%d] flag[%d][%d][%d]\n", + offset_x, offset_y1, offset_y2, + flag_x, flag_y1, flag_y2)); + if (flag_x) + offset_x += flag_x * correct[i]; + if (flag_y1) + offset_y1 += flag_y1 * correct[i]; + if (flag_y2) + offset_y2 += flag_y2 * correct[i]; + } + if (set_hardware_offset(offset_x, offset_y1, offset_y2) < 0) + return YAS_ERROR_DEVICE_COMMUNICATION; + offset[0] = offset_x; + offset[1] = offset_y1; + offset[2] = offset_y2; + + return YAS_NO_ERROR; +} + +static int yas_cdrv_set_offset(const int8_t *offset) +{ + if (set_hardware_offset(offset[0], offset[1], offset[2]) < 0) + return YAS_ERROR_DEVICE_COMMUNICATION; + return YAS_NO_ERROR; +} + +static int +yas_cdrv_measure(int32_t *data, int32_t *raw, int16_t *temperature) +{ + int busy; + int16_t x, y1, y2, t; + int32_t xx, yy1, yy2; + int result = 0; + + if (measure_normal(&busy, &t, &x, &y1, &y2) < 0) + return YAS_ERROR_DEVICE_COMMUNICATION; + + if (x == 0) + result |= 0x01; + if (x == cdriver.overflow) + result |= 0x02; + if (y1 == 0) + result |= 0x04; + if (y1 == cdriver.overflow) + result |= 0x08; + if (y2 == 0) + result |= 0x10; + if (y2 == cdriver.overflow) + result |= 0x20; + + xx = x; + yy1 = y1; + yy2 = y2; + + if (coordinate_conversion(xx, yy1, yy2, t, &data[0], &data[1], + &data[2], &cdriver.correct) < 0) + return YAS_ERROR_DEVICE_COMMUNICATION; + cdriver.temperature = t; + + if (raw != NULL) + raw[0] = xx, raw[1] = yy1, raw[2] = yy2; + if (temperature != NULL) + *temperature = t; + + return result; +} + +static int +yas_cdrv_recalc_calib_offset(int32_t *prev_calib_offset, + int32_t *new_calib_offset, + int8_t *prev_offset, int8_t *new_offset) +{ + int32_t tmp[3], resolution[9], base[3]; + int32_t raw[3]; + int32_t diff, i; + + if (prev_calib_offset == NULL || new_calib_offset == NULL + || prev_offset == NULL || new_offset == NULL) + return YAS_ERROR_ARG; + + raw[0] = raw[1] = raw[2] = 0; + if (coordinate_conversion(raw[0], raw[1], raw[2], cdriver.temperature, + &base[0], &base[1], &base[2], + &cdriver.correct) < 0) + return YAS_ERROR_ERROR; + for (i = 0; i < 3; i++) { + raw[0] = raw[1] = raw[2] = 0; + raw[i] = cdriver.coef; + if (coordinate_conversion(raw[0], raw[1], raw[2], + cdriver.temperature, + &resolution[i * 3 + 0], + &resolution[i * 3 + 1], + &resolution[i * 3 + 2], + &cdriver.correct) < 0) + return YAS_ERROR_ERROR; + resolution[i * 3 + 0] -= base[0]; + resolution[i * 3 + 1] -= base[1]; + resolution[i * 3 + 2] -= base[2]; + } + + for (i = 0; i < 3; i++) + tmp[i] = prev_calib_offset[i]; + + for (i = 0; i < 3; i++) { + diff = (int32_t) new_offset[i] - (int32_t) prev_offset[i]; + while (diff > 0) { + tmp[0] -= resolution[i * 3 + 0]; + tmp[1] -= resolution[i * 3 + 1]; + tmp[2] -= resolution[i * 3 + 2]; + diff--; + } + while (diff < 0) { + tmp[0] += resolution[i * 3 + 0]; + tmp[1] += resolution[i * 3 + 1]; + tmp[2] += resolution[i * 3 + 2]; + diff++; + } + } + for (i = 0; i < 3; i++) + new_calib_offset[i] = tmp[i]; + + return YAS_NO_ERROR; +} + +static int yas_cdrv_set_transformatiom_matrix(const int8_t *transform) +{ + int i; + + if (transform == NULL) + return YAS_ERROR_ARG; + for (i = 0; i < 9; i++) + cdriver.transform[i] = transform[i]; + + return YAS_NO_ERROR; +} + +static int +yas_cdrv_init(const int8_t *transform, struct yas_machdep_func *func) +{ + int interval, i; + uint8_t id; + + if (transform == NULL || func == NULL) + return YAS_ERROR_ARG; + + for (i = 0; i < 9; i++) + cdriver.transform[i] = transform[i]; + + cdriver.func = *func; + + if (device_open() < 0) + return YAS_ERROR_DEVICE_COMMUNICATION; + + if (init_test_register() < 0) + return YAS_ERROR_DEVICE_COMMUNICATION; + + if (get_device_id(&id) < 0) + return YAS_ERROR_DEVICE_COMMUNICATION; + + YLOGD(("device id:%02x\n", id)); + + switch (id) { + case YAS_YAS530_DEVICE_ID: + if (get_cal_data_yas530(&cdriver.cal) < 0) + return YAS_ERROR_DEVICE_COMMUNICATION; + + get_correction_value_yas530(&cdriver.cal, &cdriver.correct); + cdriver.center = YAS_YAS530_DATA_CENTER; + cdriver.overflow = YAS_YAS530_DATA_OVERFLOW; + switch (cdriver.cal.ver) { + case YAS_YAS530_VERSION_B: + cdriver.coef = YAS_YAS530_VERSION_B_COEF; + break; + case YAS_YAS530_VERSION_A: + default: + cdriver.coef = YAS_YAS530_VERSION_A_COEF; + break; + } + break; + + case YAS_YAS532_DEVICE_ID: + if (get_cal_data_yas532(&cdriver.cal) < 0) + return YAS_ERROR_DEVICE_COMMUNICATION; + get_correction_value_yas532(&cdriver.cal, &cdriver.correct); + cdriver.center = YAS_YAS532_DATA_CENTER; + cdriver.overflow = YAS_YAS532_DATA_OVERFLOW; + switch (cdriver.cal.ver) { + case YAS_YAS532_VERSION_AC: + cdriver.coef = YAS_YAS532_VERSION_AC_COEF; + break; + case YAS_YAS532_VERSION_AB: + default: + cdriver.coef = YAS_YAS532_VERSION_AB_COEF; + break; + } + break; + + default: + return YAS_ERROR_DEVICE_COMMUNICATION; + } + cdriver.dev_id = id; + + if (set_configuration(0, 0, cdriver.cal.dck) < 0) + return YAS_ERROR_DEVICE_COMMUNICATION; + if (set_measure_interval(0) < 0) + return YAS_ERROR_DEVICE_COMMUNICATION; + if (get_measure_interval(&interval) < 0) + return YAS_ERROR_DEVICE_COMMUNICATION; + YLOGD(("interval[%d]\n", interval)); + + return YAS_NO_ERROR; +} + +static int yas_cdrv_term(void) +{ + device_close(); + return YAS_NO_ERROR; +} + +#define YAS_DEFAULT_CALIB_INTERVAL (50) /* 50 msecs */ +#define YAS_DEFAULT_DATA_INTERVAL (200) /* 200 msecs */ +#define YAS_INITCOIL_INTERVAL (200) /* 200 msec */ +#define YAS_INITCOIL_GIVEUP_INTERVAL (180000) /* 180 seconds */ +#define YAS_DETECT_OVERFLOW_INTERVAL (0) /* 0 second */ + +#define YAS_MAG_ERROR_DELAY (200) +#define YAS_MAG_STATE_NORMAL (0) +#define YAS_MAG_STATE_INIT_COIL (1) +#define YAS_MAG_STATE_MEASURE_OFFSET (2) + +static const int8_t YAS_TRANSFORMATION[][9] = { + {0, 1, 0, -1, 0, 0, 0, 0, 1}, + {-1, 0, 0, 0, -1, 0, 0, 0, 1}, + {0, -1, 0, 1, 0, 0, 0, 0, 1}, + {1, 0, 0, 0, 1, 0, 0, 0, 1}, + {0, -1, 0, -1, 0, 0, 0, 0, -1}, + {1, 0, 0, 0, -1, 0, 0, 0, -1}, + {0, 1, 0, 1, 0, 0, 0, 0, -1}, + {-1, 0, 0, 0, 1, 0, 0, 0, -1}, +}; + +static const int supported_data_interval[] = { 10, 20, 50, 60, 100, 200, 1000 }; +static const int supported_calib_interval[] = { 60, 60, 50, 60, 50, 50, 50 }; +static const int32_t INVALID_CALIB_OFFSET[] = { 0x7fffffff, + 0x7fffffff, 0x7fffffff }; +static const int8_t INVALID_OFFSET[] = { 0x7f, 0x7f, 0x7f }; + +struct yas_adaptive_filter { + int num; + int index; + int filter_len; + int filter_noise; + int32_t sequence[YAS_MAG_MAX_FILTER_LEN]; +}; + +struct yas_thresh_filter { + int32_t threshold; + int32_t last; +}; + +struct yas_driver { + int initialized; + struct yas_mag_driver_callback callback; + struct utimer data_timer; + struct utimer initcoil_timer; + struct utimer initcoil_giveup_timer; + struct utimer detect_overflow_timer; + int32_t prev_mag[3]; + int32_t prev_xy1y2[3]; + int32_t prev_mag_w_offset[3]; + int16_t prev_temperature; + int measure_state; + int active; + int overflow; + int initcoil_gaveup; + int position; + int delay_timer_use_data; + int delay_timer_interval; + int delay_timer_counter; + int filter_enable; + int filter_len; + int filter_thresh; + int filter_noise[3]; + struct yas_adaptive_filter adap_filter[3]; + struct yas_thresh_filter thresh_filter[3]; + struct yas_mag_offset offset; +#ifdef YAS_MAG_MANUAL_OFFSET + struct yas_vector manual_offset; +#endif + struct yas_matrix static_matrix; + struct yas_matrix dynamic_matrix; +}; + +static struct yas_driver this_driver; + +static int lock(void) +{ + if (this_driver.callback.lock != NULL) { + if (this_driver.callback.lock() < 0) + return YAS_ERROR_RESTARTSYS; + } + return 0; +} + +static int unlock(void) +{ + if (this_driver.callback.unlock != NULL) { + if (this_driver.callback.unlock() < 0) + return YAS_ERROR_RESTARTSYS; + } + return 0; +} + +static int32_t square(int32_t data) +{ + return data * data; +} + +static void +adaptive_filter_init(struct yas_adaptive_filter *adap_filter, int len, + int noise) +{ + int i; + + adap_filter->num = 0; + adap_filter->index = 0; + adap_filter->filter_noise = noise; + adap_filter->filter_len = len; + + for (i = 0; i < adap_filter->filter_len; ++i) + adap_filter->sequence[i] = 0; +} + +static int32_t +adaptive_filter_filter(struct yas_adaptive_filter *adap_filter, int32_t in) +{ + int32_t avg, sum; + int i; + + if (adap_filter->filter_len == 0) + return in; + + if (adap_filter->num < adap_filter->filter_len) { + adap_filter->sequence[adap_filter->index++] = in / 100; + adap_filter->num++; + return in; + } + if (adap_filter->filter_len <= adap_filter->index) + adap_filter->index = 0; + + adap_filter->sequence[adap_filter->index++] = in / 100; + + avg = 0; + for (i = 0; i < adap_filter->filter_len; i++) + avg += adap_filter->sequence[i]; + + avg /= adap_filter->filter_len; + + sum = 0; + for (i = 0; i < adap_filter->filter_len; i++) + sum += square(avg - adap_filter->sequence[i]); + + sum /= adap_filter->filter_len; + + if (sum <= adap_filter->filter_noise) + return avg * 100; + + return ((in / 100 - avg) * (sum - adap_filter->filter_noise) / sum + + avg) * 100; +} + +static void +thresh_filter_init(struct yas_thresh_filter *thresh_filter, int threshold) +{ + thresh_filter->threshold = threshold; + thresh_filter->last = 0; +} + +static int32_t +thresh_filter_filter(struct yas_thresh_filter *thresh_filter, int32_t in) +{ + if (in < thresh_filter->last - thresh_filter->threshold + || thresh_filter->last + thresh_filter->threshold < in) { + thresh_filter->last = in; + return in; + } else + return thresh_filter->last; +} + +static void filter_init(struct yas_driver *d) +{ + int i; + + for (i = 0; i < 3; i++) { + adaptive_filter_init(&d->adap_filter[i], d->filter_len, + d->filter_noise[i]); + thresh_filter_init(&d->thresh_filter[i], d->filter_thresh); + } +} + +static void +filter_filter(struct yas_driver *d, int32_t *orig, int32_t *filtered) +{ + int i; + + for (i = 0; i < 3; i++) { + filtered[i] = + adaptive_filter_filter(&d->adap_filter[i], orig[i]); + filtered[i] = + thresh_filter_filter(&d->thresh_filter[i], filtered[i]); + } +} + +static int is_valid_offset(const int8_t *p) +{ + return (p != NULL && (p[0] <= 31) && (p[1] <= 31) && (p[2] <= 31) + && (-31 <= p[0]) && (-31 <= p[1]) && (-31 <= p[2])); +} + +static int is_valid_calib_offset(const int32_t *p) +{ + int i; + for (i = 0; i < 3; i++) { + if (p[i] != INVALID_CALIB_OFFSET[i]) + return 1; + } + return 0; +} + +static int is_offset_differ(const int8_t *p0, const int8_t *p1) +{ + if (p0[0] != p1[0] || p0[1] != p1[1] || p0[2] != p1[2]) + return 1; + else + return 0; +} + +static int is_calib_offset_differ(const int32_t *p0, const int32_t *p1) +{ + if (p0[0] != p1[0] || p0[1] != p1[1] || p0[2] != p1[2]) + return 1; + else + return 0; +} + +static int get_overflow(struct yas_driver *d) +{ + return d->overflow; +} + +static void set_overflow(struct yas_driver *d, const int overflow) +{ + if (d->overflow != overflow) + d->overflow = overflow; +} + +static int get_initcoil_gaveup(struct yas_driver *d) +{ + return d->initcoil_gaveup; +} + +static void set_initcoil_gaveup(struct yas_driver *d, const int initcoil_gaveup) +{ + d->initcoil_gaveup = initcoil_gaveup; +} + +static int32_t *get_calib_offset(struct yas_driver *d) +{ + return d->offset.calib_offset.v; +} + +static void set_calib_offset(struct yas_driver *d, const int32_t *offset) +{ + int i; + + if (is_calib_offset_differ(d->offset.calib_offset.v, offset)) { + for (i = 0; i < 3; i++) + d->offset.calib_offset.v[i] = offset[i]; + } +} + +#ifdef YAS_MAG_MANUAL_OFFSET +static int32_t *get_manual_offset(struct yas_driver *d) +{ + return d->manual_offset.v; +} + +static void set_manual_offset(struct yas_driver *d, const int32_t *offset) +{ + int i; + + for (i = 0; i < 3; i++) + d->manual_offset.v[i] = offset[i]; +} +#endif + +static int32_t *get_static_matrix(struct yas_driver *d) +{ + return d->static_matrix.matrix; +} + +static void set_static_matrix(struct yas_driver *d, const int32_t *matrix) +{ + int i; + + for (i = 0; i < 9; i++) + d->static_matrix.matrix[i] = matrix[i]; +} + +static int32_t *get_dynamic_matrix(struct yas_driver *d) +{ + return d->dynamic_matrix.matrix; +} + +static void set_dynamic_matrix(struct yas_driver *d, const int32_t *matrix) +{ + int i; + + for (i = 0; i < 9; i++) + d->dynamic_matrix.matrix[i] = matrix[i]; +} + +static int8_t *get_offset(struct yas_driver *d) +{ + return d->offset.hard_offset; +} + +static void set_offset(struct yas_driver *d, const int8_t *offset) +{ + int i; + + if (is_offset_differ(d->offset.hard_offset, offset)) { + for (i = 0; i < 3; i++) + d->offset.hard_offset[i] = offset[i]; + } +} + +static int get_active(struct yas_driver *d) +{ + return d->active; +} + +static void set_active(struct yas_driver *d, const int active) +{ + d->active = active; +} + +static int get_position(struct yas_driver *d) +{ + return d->position; +} + +static void set_position(struct yas_driver *d, const int position) +{ + d->position = position; +} + +static int get_measure_state(struct yas_driver *d) +{ + return d->measure_state; +} + +static void set_measure_state(struct yas_driver *d, const int state) +{ + d->measure_state = state; +} + +static struct utimer *get_data_timer(struct yas_driver *d) +{ + return &d->data_timer; +} + +static struct utimer *get_initcoil_timer(struct yas_driver *d) +{ + return &d->initcoil_timer; +} + +static struct utimer *get_initcoil_giveup_timer(struct yas_driver *d) +{ + return &d->initcoil_giveup_timer; +} + +static struct utimer *get_detect_overflow_timer(struct yas_driver *d) +{ + return &d->detect_overflow_timer; +} + +static int get_delay_timer_use_data(struct yas_driver *d) +{ + return d->delay_timer_use_data; +} + +static void set_delay_timer_use_data(struct yas_driver *d, int flag) +{ + d->delay_timer_use_data = !(!flag); +} + +static int get_delay_timer_interval(struct yas_driver *d) +{ + return d->delay_timer_interval; +} + +static void set_delay_timer_interval(struct yas_driver *d, int interval) +{ + d->delay_timer_interval = interval; +} + +static int get_delay_timer_counter(struct yas_driver *d) +{ + return d->delay_timer_counter; +} + +static void set_delay_timer_counter(struct yas_driver *d, int counter) +{ + d->delay_timer_counter = counter; +} + +static int get_filter_enable(struct yas_driver *d) +{ + return d->filter_enable; +} + +static void set_filter_enable(struct yas_driver *d, int enable) +{ + if (enable) + filter_init(d); + + d->filter_enable = !(!enable); +} + +static int get_filter_len(struct yas_driver *d) +{ + return d->filter_len; +} + +static void set_filter_len(struct yas_driver *d, int len) +{ + if (len < 0) + return; + + if (len > YAS_MAG_MAX_FILTER_LEN) + return; + + d->filter_len = len; + filter_init(d); +} + +static int get_filter_noise(struct yas_driver *d, int *noise) +{ + int i; + + for (i = 0; i < 3; i++) + noise[i] = d->filter_noise[i]; + + return 0; +} + +static void set_filter_noise(struct yas_driver *d, int *noise) +{ + int i; + + if (noise == NULL) + return; + + for (i = 0; i < 3; i++) { + if (noise[i] < 0) + noise[i] = 0; + + d->filter_noise[i] = noise[i]; + } + filter_init(d); +} + +static int get_filter_thresh(struct yas_driver *d) +{ + return d->filter_thresh; +} + +static void set_filter_thresh(struct yas_driver *d, int threshold) +{ + if (threshold < 0) + return; + + d->filter_thresh = threshold; + filter_init(d); +} + +static int32_t *get_previous_mag(struct yas_driver *d) +{ + return d->prev_mag; +} + +static void set_previous_mag(struct yas_driver *d, int32_t *data) +{ + int i; + for (i = 0; i < 3; i++) + d->prev_mag[i] = data[i]; +} + +static int32_t *get_previous_xy1y2(struct yas_driver *d) +{ + return d->prev_xy1y2; +} + +static void set_previous_xy1y2(struct yas_driver *d, int32_t *data) +{ + int i; + for (i = 0; i < 3; i++) + d->prev_xy1y2[i] = data[i]; +} + +static int32_t *get_previous_mag_w_offset(struct yas_driver *d) +{ + return d->prev_mag_w_offset; +} + +static void set_previous_mag_w_offset(struct yas_driver *d, int32_t *data) +{ + int i; + for (i = 0; i < 3; i++) + d->prev_mag_w_offset[i] = data[i]; +} + +static int16_t get_previous_temperature(struct yas_driver *d) +{ + return d->prev_temperature; +} + +static void set_previous_temperature(struct yas_driver *d, int16_t temperature) +{ + d->prev_temperature = temperature; +} + +static int init_coil(struct yas_driver *d) +{ + int rt; + + YLOGD(("init_coil IN\n")); + + utimer_update(get_initcoil_timer(d)); + if (!get_initcoil_gaveup(d)) { + utimer_update(get_initcoil_giveup_timer(d)); + if (utimer_is_timeout(get_initcoil_giveup_timer(d))) { + utimer_clear_timeout(get_initcoil_giveup_timer(d)); + set_initcoil_gaveup(d, TRUE); + } + } + if (utimer_is_timeout(get_initcoil_timer(d)) && + !get_initcoil_gaveup(d)) { + utimer_clear_timeout(get_initcoil_timer(d)); + YLOGI(("init_coil!\n")); + rt = yas_cdrv_actuate_initcoil(); + if (rt < 0) { + YLOGE(("yas_cdrv_actuate_initcoil failed[%d]\n", rt)); + return rt; + } + if (get_overflow(d) || !is_valid_offset(get_offset(d))) + set_measure_state(d, YAS_MAG_STATE_MEASURE_OFFSET); + else + set_measure_state(d, YAS_MAG_STATE_NORMAL); + } + + YLOGD(("init_coil OUT\n")); + + return 0; +} + +static int measure_offset(struct yas_driver *d) +{ + int8_t offset[3]; + int32_t moffset[3]; + int rt, result = 0, i; + + YLOGI(("measure_offset IN\n")); + rt = yas_cdrv_measure_and_set_offset(offset); + if (rt < 0) { + YLOGE(("yas_cdrv_measure_offset failed[%d]\n", rt)); + return rt; + } + + YLOGI(("offset[%d][%d][%d]\n", offset[0], offset[1], offset[2])); + + for (i = 0; i < 3; i++) + moffset[i] = get_calib_offset(d)[i]; + + if (is_offset_differ(get_offset(d), offset)) { + if (is_valid_offset(get_offset(d)) + && is_valid_calib_offset(get_calib_offset(d))) { + yas_cdrv_recalc_calib_offset(get_calib_offset(d), + moffset, + get_offset(d), offset); + result |= YAS_REPORT_CALIB_OFFSET_CHANGED; + } + } + result |= YAS_REPORT_HARD_OFFSET_CHANGED; + + set_offset(d, offset); + if (is_valid_calib_offset(moffset)) + set_calib_offset(d, moffset); + + set_measure_state(d, YAS_MAG_STATE_NORMAL); + + YLOGI(("measure_offset OUT\n")); + + return result; +} + +static int +measure_msensor_normal(struct yas_driver *d, int32_t *magnetic, + int32_t *mag_w_offset, int32_t * xy1y2, + int16_t *temperature) +{ + int rt = 0, result, i; + int32_t tmp[3]; + + YLOGD(("measure_msensor_normal IN\n")); + + result = 0; + rt = yas_cdrv_measure(mag_w_offset, tmp, temperature); + if (rt < 0) { + YLOGE(("yas_cdrv_measure failed[%d]\n", rt)); + return rt; + } + for (i = 0; i < 3; i++) + xy1y2[i] = tmp[i]; + +#ifdef YAS_MAG_MANUAL_OFFSET + for (i = 0; i < 3; i++) + mag_w_offset[i] -= get_manual_offset(d)[i]; +#endif + if (rt > 0) { + utimer_update(get_detect_overflow_timer(d)); + set_overflow(d, TRUE); + if (utimer_is_timeout(get_detect_overflow_timer(d))) { + utimer_clear_timeout(get_detect_overflow_timer(d)); + result |= YAS_REPORT_OVERFLOW_OCCURED; + } + if (get_measure_state(d) == YAS_MAG_STATE_NORMAL) + set_measure_state(d, YAS_MAG_STATE_INIT_COIL); + + } else { + utimer_clear(get_detect_overflow_timer(d)); + set_overflow(d, FALSE); + if (get_measure_state(d) == YAS_MAG_STATE_NORMAL) { + utimer_clear(get_initcoil_timer(d)); + utimer_clear(get_initcoil_giveup_timer(d)); + } + } + + for (i = 0; i < 3; i++) { + tmp[i] + = (get_static_matrix(d)[i * 3 + 0] / 10 * + (mag_w_offset[0] / 10)) / 100 + + (get_static_matrix(d)[i * 3 + 1] / 10 * + (mag_w_offset[1] / 10)) / 100 + + (get_static_matrix(d)[i * 3 + 2] / 10 * + (mag_w_offset[2] / 10)) / 100; + } + for (i = 0; i < 3; i++) + magnetic[i] = mag_w_offset[i] = tmp[i]; + if (is_valid_calib_offset(get_calib_offset(d))) { + for (i = 0; i < 3; i++) + magnetic[i] -= get_calib_offset(d)[i]; + } + for (i = 0; i < 3; i++) { + tmp[i] + = (get_dynamic_matrix(d)[i * 3 + 0] / 10 * + (magnetic[0] / 10)) / 100 + + (get_dynamic_matrix(d)[i * 3 + 1] / 10 * + (magnetic[1] / 10)) / 100 + (get_dynamic_matrix(d)[i * 3 + + 2] / + 10 * (magnetic[2] / 10)) / + 100; + } + for (i = 0; i < 3; i++) + magnetic[i] = tmp[i]; + + if (get_filter_enable(d)) + filter_filter(d, magnetic, magnetic); + + YLOGD(("measure_msensor_normal OUT\n")); + + return result; +} + +static int +measure_msensor(struct yas_driver *d, int32_t *magnetic, + int32_t *mag_w_offset, int32_t *xy1y2, int16_t *temperature) +{ + int result, i; + + YLOGD(("measure_msensor IN\n")); + + for (i = 0; i < 3; i++) { + magnetic[i] = get_previous_mag(d)[i]; + mag_w_offset[i] = get_previous_mag_w_offset(d)[i]; + xy1y2[i] = get_previous_xy1y2(d)[i]; + *temperature = get_previous_temperature(d); + } + + result = 0; + switch (get_measure_state(d)) { + case YAS_MAG_STATE_INIT_COIL: + result = init_coil(d); + break; + case YAS_MAG_STATE_MEASURE_OFFSET: + result = measure_offset(d); + break; + case YAS_MAG_STATE_NORMAL: + result = 0; + break; + default: + result = -1; + break; + } + + if (result < 0) + return result; + + if (!(result & YAS_REPORT_OVERFLOW_OCCURED)) + result |= + measure_msensor_normal(d, magnetic, mag_w_offset, xy1y2, + temperature); + set_previous_mag(d, magnetic); + set_previous_xy1y2(d, xy1y2); + set_previous_mag_w_offset(d, mag_w_offset); + set_previous_temperature(d, *temperature); + + YLOGD(("measure_msensor OUT\n")); + + return result; +} + +static int +measure(struct yas_driver *d, int32_t *magnetic, int32_t *mag_w_offset, + int32_t *xy1y2, int16_t *temperature, uint32_t *time_delay) +{ + int result; + int counter; + uint32_t total = 0; + + YLOGD(("measure IN\n")); + + utimer_update(get_data_timer(d)); + result = measure_msensor(d, magnetic, mag_w_offset, xy1y2, + temperature); + if (result < 0) + return result; + + counter = get_delay_timer_counter(d); + total = utimer_get_total_time(get_data_timer(d)); + if (utimer_get_delay(get_data_timer(d)) > 0) + counter -= total / utimer_get_delay(get_data_timer(d)); + else + counter = 0; + + if (utimer_is_timeout(get_data_timer(d))) { + utimer_clear_timeout(get_data_timer(d)); + + if (get_delay_timer_use_data(d)) { + result |= YAS_REPORT_DATA; + if (counter <= 0) + result |= YAS_REPORT_CALIB; + } else { + result |= YAS_REPORT_CALIB; + if (counter <= 0) + result |= YAS_REPORT_DATA; + } + } + + if (counter <= 0) + set_delay_timer_counter(d, get_delay_timer_interval(d)); + else + set_delay_timer_counter(d, counter); + + *time_delay = utimer_sleep_time(get_data_timer(d)); + + YLOGD(("measure OUT [%d]\n", result)); + + return result; +} + +static int resume(struct yas_driver *d) +{ + int32_t zero[] = { 0, 0, 0 }; + struct yas_machdep_func func; + int rt; + + YLOGI(("resume IN\n")); + + func.device_open = d->callback.device_open; + func.device_close = d->callback.device_close; + func.device_write = d->callback.device_write; + func.device_read = d->callback.device_read; + func.msleep = d->callback.msleep; + + rt = + yas_cdrv_init(YAS_TRANSFORMATION[get_position(d)], &func); + + if (rt < 0) { + YLOGE(("yas_cdrv_init failed[%d]\n", rt)); + return rt; + } + + utimer_clear(get_data_timer(d)); + utimer_clear(get_initcoil_giveup_timer(d)); + utimer_clear(get_initcoil_timer(d)); + utimer_clear(get_detect_overflow_timer(d)); + + set_previous_mag(d, zero); + set_previous_xy1y2(d, zero); + set_previous_mag_w_offset(d, zero); + set_previous_temperature(d, 0); + set_overflow(d, FALSE); + set_initcoil_gaveup(d, FALSE); + + filter_init(d); + + if (is_valid_offset(d->offset.hard_offset)) { + yas_cdrv_set_offset(d->offset.hard_offset); + rt = yas_cdrv_actuate_initcoil(); + if (rt < 0) { + YLOGE(("yas_cdrv_actuate_initcoil failed[%d]\n", rt)); + set_measure_state(d, YAS_MAG_STATE_INIT_COIL); + } else + set_measure_state(d, YAS_MAG_STATE_NORMAL); + } else { + rt = yas_cdrv_actuate_initcoil(); + if (rt < 0) { + YLOGE(("yas_cdrv_actuate_initcoil failed[%d]\n", rt)); + set_measure_state(d, YAS_MAG_STATE_INIT_COIL); + } else + set_measure_state(d, YAS_MAG_STATE_MEASURE_OFFSET); + } + + YLOGI(("resume OUT\n")); + return 0; +} + +static int suspend(struct yas_driver *d) +{ + YLOGI(("suspend IN\n")); + + (void)d; + yas_cdrv_term(); + + YLOGI(("suspend OUT\n")); + return 0; +} + +static int check_interval(int ms) +{ + int index = -1; + + if (ms <= supported_data_interval[0]) + ms = supported_data_interval[0]; + for (index = 0; index < NELEMS(supported_data_interval); index++) { + if (ms == supported_data_interval[index]) + goto done; + else if (ms < supported_data_interval[index]) { + if (index != 0) + index -= 1; + goto done; + } + } +done: + return index; +} + +static int yas_get_delay_nolock(struct yas_driver *d, int *ms) +{ + if (unlikely(!d->initialized)) + return YAS_ERROR_NOT_INITIALIZED; + if (get_delay_timer_use_data(d)) + *ms = utimer_get_delay(get_data_timer(d)); + else { + *ms = + utimer_get_delay(get_data_timer(d)) * + get_delay_timer_interval(d); + } + return YAS_NO_ERROR; +} + +static int yas_set_delay_nolock(struct yas_driver *d, int ms) +{ + int index; + uint32_t delay_data, delay_calib; + + if (unlikely(!d->initialized)) + return YAS_ERROR_NOT_INITIALIZED; + index = check_interval(ms); + if (index < 0) + return YAS_ERROR_ARG; + delay_data = supported_data_interval[index]; + delay_calib = supported_calib_interval[index]; + set_delay_timer_use_data(d, delay_data < delay_calib); + if (delay_data < delay_calib) { + set_delay_timer_interval(d, delay_calib / delay_data); + set_delay_timer_counter(d, delay_calib / delay_data); + utimer_set_delay(get_data_timer(d), + supported_data_interval[index]); + } else { + set_delay_timer_interval(d, delay_data / delay_calib); + set_delay_timer_counter(d, delay_data / delay_calib); + utimer_set_delay(get_data_timer(d), + supported_calib_interval[index]); + } + + return YAS_NO_ERROR; +} + +static int +yas_get_offset_nolock(struct yas_driver *d, struct yas_mag_offset *offset) +{ + if (unlikely(!d->initialized)) + return YAS_ERROR_NOT_INITIALIZED; + *offset = d->offset; + return YAS_NO_ERROR; +} + +static int +yas_set_offset_nolock(struct yas_driver *d, struct yas_mag_offset *offset) +{ + int32_t zero[] = { 0, 0, 0 }; + int rt; + + if (unlikely(!d->initialized)) + return YAS_ERROR_NOT_INITIALIZED; + if (!get_active(d)) { + d->offset = *offset; + return YAS_NO_ERROR; + } + + if (!is_valid_offset(offset->hard_offset) + || is_offset_differ(offset->hard_offset, d->offset.hard_offset)) { + filter_init(d); + + utimer_clear(get_data_timer(d)); + utimer_clear(get_initcoil_giveup_timer(d)); + utimer_clear(get_initcoil_timer(d)); + utimer_clear(get_detect_overflow_timer(d)); + + set_previous_mag(d, zero); + set_previous_xy1y2(d, zero); + set_previous_mag_w_offset(d, zero); + set_previous_temperature(d, 0); + set_overflow(d, FALSE); + set_initcoil_gaveup(d, FALSE); + } + d->offset = *offset; + + if (is_valid_offset(d->offset.hard_offset)) + yas_cdrv_set_offset(d->offset.hard_offset); + else { + rt = yas_cdrv_actuate_initcoil(); + if (rt < 0) { + YLOGE(("yas_cdrv_actuate_initcoil failed[%d]\n", rt)); + set_measure_state(d, YAS_MAG_STATE_INIT_COIL); + } else + set_measure_state(d, YAS_MAG_STATE_MEASURE_OFFSET); + } + + return YAS_NO_ERROR; +} + +#ifdef YAS_MAG_MANUAL_OFFSET + +static int +yas_get_manual_offset_nolock(struct yas_driver *d, struct yas_vector *offset) +{ + if (unlikely(!d->initialized)) + return YAS_ERROR_NOT_INITIALIZED; + + *offset = d->manual_offset; + + return YAS_NO_ERROR; +} + +static int +yas_set_manual_offset_nolock(struct yas_driver *d, struct yas_vector *offset) +{ + if (unlikely(!d->initialized)) + return YAS_ERROR_NOT_INITIALIZED; + + set_manual_offset(d, offset->v); + + return YAS_NO_ERROR; +} + +#endif + +static int +yas_get_static_matrix_nolock(struct yas_driver *d, struct yas_matrix *matrix) +{ + int i; + + if (unlikely(!d->initialized)) + return YAS_ERROR_NOT_INITIALIZED; + + for (i = 0; i < 9; i++) + matrix->matrix[i] = get_static_matrix(d)[i]; + + return YAS_NO_ERROR; +} + +static int +yas_set_static_matrix_nolock(struct yas_driver *d, struct yas_matrix *matrix) +{ + if (unlikely(!d->initialized)) + return YAS_ERROR_NOT_INITIALIZED; + + set_static_matrix(d, matrix->matrix); + + return YAS_NO_ERROR; +} + +static int +yas_get_dynamic_matrix_nolock(struct yas_driver *d, struct yas_matrix *matrix) +{ + int i; + + if (unlikely(!d->initialized)) + return YAS_ERROR_NOT_INITIALIZED; + + for (i = 0; i < 9; i++) + matrix->matrix[i] = get_dynamic_matrix(d)[i]; + + return YAS_NO_ERROR; +} + +static int +yas_set_dynamic_matrix_nolock(struct yas_driver *d, struct yas_matrix *matrix) +{ + if (unlikely(!d->initialized)) + return YAS_ERROR_NOT_INITIALIZED; + + set_dynamic_matrix(d, matrix->matrix); + + return YAS_NO_ERROR; +} + +static int yas_get_enable_nolock(struct yas_driver *d) +{ + if (unlikely(!d->initialized)) + return YAS_ERROR_NOT_INITIALIZED; + return get_active(d); +} + +static int yas_set_enable_nolock(struct yas_driver *d, int active) +{ + int rt; + + if (unlikely(!d->initialized)) + return YAS_ERROR_NOT_INITIALIZED; + + if (active) { + if (get_active(d)) + return YAS_NO_ERROR; + rt = resume(d); + if (rt < 0) + return rt; + set_active(d, TRUE); + } else { + if (!get_active(d)) + return YAS_NO_ERROR; + rt = suspend(d); + if (rt < 0) + return rt; + set_active(d, FALSE); + } + return YAS_NO_ERROR; +} + +static int +yas_get_filter_nolock(struct yas_driver *d, struct yas_mag_filter *filter) +{ + if (unlikely(!d->initialized)) + return YAS_ERROR_NOT_INITIALIZED; + filter->len = get_filter_len(d); + get_filter_noise(d, filter->noise); + filter->threshold = get_filter_thresh(d); + return YAS_NO_ERROR; +} + +static int +yas_set_filter_nolock(struct yas_driver *d, struct yas_mag_filter *filter) +{ + if (unlikely(!d->initialized)) + return YAS_ERROR_NOT_INITIALIZED; + set_filter_len(d, filter->len); + set_filter_noise(d, filter->noise); + set_filter_thresh(d, filter->threshold); + return YAS_NO_ERROR; +} + +static int yas_get_filter_enable_nolock(struct yas_driver *d) +{ + if (unlikely(!d->initialized)) + return YAS_ERROR_NOT_INITIALIZED; + return get_filter_enable(d); +} + +static int yas_set_filter_enable_nolock(struct yas_driver *d, int enable) +{ + if (unlikely(!d->initialized)) + return YAS_ERROR_NOT_INITIALIZED; + set_filter_enable(d, enable); + return YAS_NO_ERROR; +} + +static int yas_get_position_nolock(struct yas_driver *d, int *position) +{ + if (unlikely(!d->initialized)) + return YAS_ERROR_NOT_INITIALIZED; + *position = get_position(d); + return YAS_NO_ERROR; +} + +static int yas_set_position_nolock(struct yas_driver *d, int position) +{ + if (unlikely(!d->initialized)) + return YAS_ERROR_NOT_INITIALIZED; + if (get_active(d)) + yas_cdrv_set_transformatiom_matrix(YAS_TRANSFORMATION + [position]); + set_position(d, position); + filter_init(d); + return YAS_NO_ERROR; +} + +static int +yas_read_reg_nolock(struct yas_driver *d, uint8_t addr, uint8_t * buf, int len) +{ + if (unlikely(!d->initialized)) + return YAS_ERROR_NOT_INITIALIZED; + if (!get_active(d)) { + if (d->callback.device_open() < 0) + return YAS_ERROR_DEVICE_COMMUNICATION; + } + if (d->callback.device_read(addr, buf, len) < 0) + return YAS_ERROR_DEVICE_COMMUNICATION; + if (!get_active(d)) { + if (d->callback.device_close() < 0) + return YAS_ERROR_DEVICE_COMMUNICATION; + } + + return YAS_NO_ERROR; +} + +static int +yas_write_reg_nolock(struct yas_driver *d, uint8_t addr, const uint8_t * buf, + int len) +{ + if (unlikely(!d->initialized)) + return YAS_ERROR_NOT_INITIALIZED; + if (!get_active(d)) { + if (d->callback.device_open() < 0) + return YAS_ERROR_DEVICE_COMMUNICATION; + } + if (d->callback.device_write(addr, buf, len) < 0) + return YAS_ERROR_DEVICE_COMMUNICATION; + if (!get_active(d)) { + if (d->callback.device_close() < 0) + return YAS_ERROR_DEVICE_COMMUNICATION; + } + + return YAS_NO_ERROR; +} + +static int +yas_measure_nolock(struct yas_driver *d, struct yas_mag_data *data, + int *time_delay_ms) +{ + uint32_t time_delay = YAS_MAG_ERROR_DELAY; + int rt, i; + + if (unlikely(!d->initialized)) + return YAS_ERROR_NOT_INITIALIZED; + *time_delay_ms = YAS_MAG_ERROR_DELAY; + + if (!get_active(d)) { + for (i = 0; i < 3; i++) { + data->xyz.v[i] = get_previous_mag(d)[i]; + data->raw.v[i] = get_previous_mag_w_offset(d)[i]; + data->xy1y2.v[i] = get_previous_xy1y2(d)[i]; + } + data->temperature = get_previous_temperature(d); + return YAS_NO_ERROR; + } + + rt = measure(d, data->xyz.v, data->raw.v, data->xy1y2.v, + &data->temperature, &time_delay); + if (rt >= 0) { + *time_delay_ms = time_delay; + if (*time_delay_ms > 0) + *time_delay_ms += 1; + } + + return rt; +} + +static int yas_init_nolock(struct yas_driver *d) +{ +#ifdef YAS_MAG_MANUAL_OFFSET + int32_t zero[] = { 0, 0, 0 }; +#endif + int32_t notransform[] = { 10000, 0, 0, 0, 10000, 0, 0, 0, 10000 }; + int noise[] = { + YAS_MAG_DEFAULT_FILTER_NOISE_X, + YAS_MAG_DEFAULT_FILTER_NOISE_Y, + YAS_MAG_DEFAULT_FILTER_NOISE_Z + }; + + YLOGI(("yas_init_nolock IN\n")); + + utimer_lib_init(this_driver.callback.current_time); + utimer_init(get_data_timer(d), 50); + utimer_init(get_initcoil_timer(d), YAS_INITCOIL_INTERVAL); + utimer_init(get_initcoil_giveup_timer(d), YAS_INITCOIL_GIVEUP_INTERVAL); + utimer_init(get_detect_overflow_timer(d), YAS_DETECT_OVERFLOW_INTERVAL); + + set_delay_timer_use_data(d, 0); + set_delay_timer_interval(d, + YAS_DEFAULT_DATA_INTERVAL / + YAS_DEFAULT_CALIB_INTERVAL); + set_delay_timer_counter(d, + YAS_DEFAULT_DATA_INTERVAL / + YAS_DEFAULT_CALIB_INTERVAL); + + set_filter_enable(d, FALSE); + set_filter_len(d, YAS_MAG_DEFAULT_FILTER_LEN); + set_filter_thresh(d, YAS_MAG_DEFAULT_FILTER_THRESH); + set_filter_noise(d, noise); + filter_init(d); + set_calib_offset(d, INVALID_CALIB_OFFSET); +#ifdef YAS_MAG_MANUAL_OFFSET + set_manual_offset(d, zero); +#endif + set_static_matrix(d, notransform); + set_dynamic_matrix(d, notransform); + set_offset(d, INVALID_OFFSET); + set_active(d, FALSE); + set_position(d, 0); + + d->initialized = 1; + + YLOGI(("yas_init_nolock OUT\n")); + + return YAS_NO_ERROR; +} + +static int yas_term_nolock(struct yas_driver *d) +{ + YLOGI(("yas_term_nolock\n")); + + if (unlikely(!d->initialized)) + return YAS_ERROR_NOT_INITIALIZED; + + if (get_active(d)) + suspend(d); + d->initialized = 0; + + YLOGI(("yas_term_nolock out\n")); + return YAS_NO_ERROR; +} + +static int yas_get_delay(void) +{ + int ms = 0, rt; + + YLOGI(("yas_get_delay\n")); + + if (lock() < 0) + return YAS_ERROR_RESTARTSYS; + + rt = yas_get_delay_nolock(&this_driver, &ms); + + if (unlock() < 0) + return YAS_ERROR_RESTARTSYS; + + YLOGI(("yas_get_delay[%d] OUT\n", ms)); + + return (rt < 0 ? rt : ms); +} + +static int yas_set_delay(int delay) +{ + int rt; + + YLOGI(("yas_set_delay\n")); + + if (lock() < 0) + return YAS_ERROR_RESTARTSYS; + + rt = yas_set_delay_nolock(&this_driver, delay); + + if (unlock() < 0) + return YAS_ERROR_RESTARTSYS; + + YLOGI(("yas_set_delay OUT\n")); + + return rt; +} + +static int yas_get_offset(struct yas_mag_offset *offset) +{ + int rt; + + YLOGI(("yas_get_offset\n")); + + if (offset == NULL) + return YAS_ERROR_ARG; + + if (lock() < 0) + return YAS_ERROR_RESTARTSYS; + + rt = yas_get_offset_nolock(&this_driver, offset); + + if (unlock() < 0) + return YAS_ERROR_RESTARTSYS; + + YLOGI(("yas_get_offset[%d] OUT\n", rt)); + + return rt; +} + +static int yas_set_offset(struct yas_mag_offset *offset) +{ + int rt; + + YLOGI(("yas_set_offset IN\n")); + + if (offset == NULL) + return YAS_ERROR_ARG; + + if (lock() < 0) + return YAS_ERROR_RESTARTSYS; + + rt = yas_set_offset_nolock(&this_driver, offset); + + if (unlock() < 0) + return YAS_ERROR_RESTARTSYS; + + YLOGI(("yas_set_offset OUT\n")); + + return rt; +} + +#ifdef YAS_MAG_MANUAL_OFFSET + +static int yas_get_manual_offset(struct yas_vector *offset) +{ + int rt; + + YLOGI(("yas_get_manual_offset\n")); + + if (offset == NULL) + return YAS_ERROR_ARG; + + if (lock() < 0) + return YAS_ERROR_RESTARTSYS; + + rt = yas_get_manual_offset_nolock(&this_driver, offset); + + if (unlock() < 0) + return YAS_ERROR_RESTARTSYS; + + YLOGI(("yas_get_manual_offset[%d] OUT\n", rt)); + + return rt; +} + +static int yas_set_manual_offset(struct yas_vector *offset) +{ + int rt; + + YLOGI(("yas_set_manual_offset IN\n")); + + if (offset == NULL) + return YAS_ERROR_ARG; + + if (lock() < 0) + return YAS_ERROR_RESTARTSYS; + + rt = yas_set_manual_offset_nolock(&this_driver, offset); + + if (unlock() < 0) + return YAS_ERROR_RESTARTSYS; + + YLOGI(("yas_set_manual_offset OUT\n")); + + return rt; +} + +#endif + +static int yas_get_static_matrix(struct yas_matrix *matrix) +{ + int rt; + + YLOGI(("yas_get_static_matrix\n")); + + if (matrix == NULL) + return YAS_ERROR_ARG; + + if (lock() < 0) + return YAS_ERROR_RESTARTSYS; + + rt = yas_get_static_matrix_nolock(&this_driver, matrix); + + if (unlock() < 0) + return YAS_ERROR_RESTARTSYS; + + YLOGI(("yas_get_static_matrix[%d] OUT\n", rt)); + + return rt; +} + +static int yas_set_static_matrix(struct yas_matrix *matrix) +{ + int rt; + + YLOGI(("yas_set_static_matrix IN\n")); + + if (matrix == NULL) + return YAS_ERROR_ARG; + + if (lock() < 0) + return YAS_ERROR_RESTARTSYS; + + rt = yas_set_static_matrix_nolock(&this_driver, matrix); + + if (unlock() < 0) + return YAS_ERROR_RESTARTSYS; + + YLOGI(("yas_set_static_matrix OUT\n")); + + return rt; +} + +static int yas_get_dynamic_matrix(struct yas_matrix *matrix) +{ + int rt; + + YLOGI(("yas_get_dynamic_matrix\n")); + + if (matrix == NULL) + return YAS_ERROR_ARG; + + if (lock() < 0) + return YAS_ERROR_RESTARTSYS; + + rt = yas_get_dynamic_matrix_nolock(&this_driver, matrix); + + if (unlock() < 0) + return YAS_ERROR_RESTARTSYS; + + YLOGI(("yas_get_dynamic_matrix[%d] OUT\n", rt)); + + return rt; +} + +static int yas_set_dynamic_matrix(struct yas_matrix *matrix) +{ + int rt; + + YLOGI(("yas_set_dynamic_matrix IN\n")); + + if (matrix == NULL) + return YAS_ERROR_ARG; + + if (lock() < 0) + return YAS_ERROR_RESTARTSYS; + + rt = yas_set_dynamic_matrix_nolock(&this_driver, matrix); + + if (unlock() < 0) + return YAS_ERROR_RESTARTSYS; + + YLOGI(("yas_set_dynamic_matrix OUT\n")); + + return rt; +} + +static int yas_get_enable(void) +{ + int rt; + + YLOGI(("yas_get_enable\n")); + + if (lock() < 0) + return YAS_ERROR_RESTARTSYS; + + rt = yas_get_enable_nolock(&this_driver); + + if (unlock() < 0) + return YAS_ERROR_RESTARTSYS; + + YLOGI(("yas_get_enable OUT[%d]\n", rt)); + + return rt; +} + +static int yas_set_enable(int enable) +{ + int rt; + + YLOGI(("yas_set_enable IN\n")); + + if (lock() < 0) + return YAS_ERROR_RESTARTSYS; + + rt = yas_set_enable_nolock(&this_driver, enable); + + if (unlock() < 0) + return YAS_ERROR_RESTARTSYS; + + YLOGI(("yas_set_enable OUT\n")); + + return rt; +} + +static int yas_get_filter(struct yas_mag_filter *filter) +{ + int rt; + + YLOGI(("yas_get_filter\n")); + + if (filter == NULL) + return YAS_ERROR_ARG; + + if (lock() < 0) + return YAS_ERROR_RESTARTSYS; + + rt = yas_get_filter_nolock(&this_driver, filter); + + if (unlock() < 0) + return YAS_ERROR_RESTARTSYS; + + YLOGI(("yas_get_filter[%d] OUT\n", rt)); + + return rt; +} + +static int yas_set_filter(struct yas_mag_filter *filter) +{ + int rt, i; + + YLOGI(("yas_set_filter IN\n")); + + if (filter == NULL + || filter->len < 0 + || YAS_MAG_MAX_FILTER_LEN < filter->len || filter->threshold < 0) { + return YAS_ERROR_ARG; + } + for (i = 0; i < 3; i++) { + if (filter->noise[i] < 0) + return YAS_ERROR_ARG; + } + + if (lock() < 0) + return YAS_ERROR_RESTARTSYS; + + rt = yas_set_filter_nolock(&this_driver, filter); + + if (unlock() < 0) + return YAS_ERROR_RESTARTSYS; + + YLOGI(("yas_set_filter OUT\n")); + + return rt; +} + +static int yas_get_filter_enable(void) +{ + int rt; + + YLOGI(("yas_get_filter_enable\n")); + + if (lock() < 0) + return YAS_ERROR_RESTARTSYS; + + rt = yas_get_filter_enable_nolock(&this_driver); + + if (unlock() < 0) + return YAS_ERROR_RESTARTSYS; + + YLOGI(("yas_get_filter_enable OUT[%d]\n", rt)); + + return rt; +} + +static int yas_set_filter_enable(int enable) +{ + int rt; + + YLOGI(("yas_set_filter_enable IN\n")); + + if (lock() < 0) + return YAS_ERROR_RESTARTSYS; + + rt = yas_set_filter_enable_nolock(&this_driver, enable); + + if (unlock() < 0) + return YAS_ERROR_RESTARTSYS; + + YLOGI(("yas_set_filter_enable OUT\n")); + + return rt; +} + +static int yas_get_position(void) +{ + int position = 0; + int rt; + + YLOGI(("yas_get_position\n")); + + if (lock() < 0) + return YAS_ERROR_RESTARTSYS; + + rt = yas_get_position_nolock(&this_driver, &position); + + if (unlock() < 0) + return YAS_ERROR_RESTARTSYS; + + YLOGI(("yas_get_position[%d] OUT\n", position)); + + return (rt < 0 ? rt : position); +} + +static int yas_set_position(int position) +{ + int rt; + + YLOGI(("yas_set_position\n")); + + if (position < 0 || 7 < position) + return YAS_ERROR_ARG; + + if (lock() < 0) + return YAS_ERROR_RESTARTSYS; + + rt = yas_set_position_nolock(&this_driver, position); + + if (unlock() < 0) + return YAS_ERROR_RESTARTSYS; + + YLOGI(("yas_set_position[%d] OUT\n", position)); + + return rt; +} + +static int yas_read_reg(uint8_t addr, uint8_t *buf, int len) +{ + int rt; + + YLOGI(("yas_read_reg\n")); + + if (buf == NULL || len <= 0) + return YAS_ERROR_ARG; + + if (lock() < 0) + return YAS_ERROR_RESTARTSYS; + + rt = yas_read_reg_nolock(&this_driver, addr, buf, len); + + if (unlock() < 0) + return YAS_ERROR_RESTARTSYS; + + YLOGI(("yas_read_reg[%d] OUT\n", rt)); + + return rt; +} + +static int yas_write_reg(uint8_t addr, const uint8_t *buf, int len) +{ + int rt; + + YLOGI(("yas_write_reg\n")); + + if (buf == NULL || len <= 0) + return YAS_ERROR_ARG; + + if (lock() < 0) + return YAS_ERROR_RESTARTSYS; + + rt = yas_write_reg_nolock(&this_driver, addr, buf, len); + + if (unlock() < 0) + return YAS_ERROR_RESTARTSYS; + + YLOGI(("yas_write_reg[%d] OUT\n", rt)); + + return rt; +} + +static int yas_measure(struct yas_mag_data *data, int *time_delay_ms) +{ + int rt; + + YLOGD(("yas_measure IN\n")); + + if (data == NULL || time_delay_ms == NULL) + return YAS_ERROR_ARG; + + if (lock() < 0) + return YAS_ERROR_RESTARTSYS; + + rt = yas_measure_nolock(&this_driver, data, time_delay_ms); + + if (unlock() < 0) + return YAS_ERROR_RESTARTSYS; + + YLOGD(("yas_measure OUT[%d]\n", rt)); + + return rt; +} + +static int yas_init(void) +{ + int rt; + + YLOGI(("yas_init\n")); + + if (lock() < 0) + return YAS_ERROR_RESTARTSYS; + + rt = yas_init_nolock(&this_driver); + + if (unlock() < 0) + return YAS_ERROR_RESTARTSYS; + + return rt; +} + +static int yas_term(void) +{ + int rt; + YLOGI(("yas_term\n")); + + if (lock() < 0) + return YAS_ERROR_RESTARTSYS; + + rt = yas_term_nolock(&this_driver); + + if (unlock() < 0) + return YAS_ERROR_RESTARTSYS; + + return rt; +} + +int yas_mag_driver_init(struct yas_mag_driver *f) +{ + if (f == NULL) + return YAS_ERROR_ARG; + if (f->callback.device_open == NULL + || f->callback.device_close == NULL + || f->callback.device_read == NULL + || f->callback.device_write == NULL + || f->callback.msleep == NULL || f->callback.current_time == NULL) + return YAS_ERROR_ARG; + + f->init = yas_init; + f->term = yas_term; + f->get_delay = yas_get_delay; + f->set_delay = yas_set_delay; + f->get_offset = yas_get_offset; + f->set_offset = yas_set_offset; +#ifdef YAS_MAG_MANUAL_OFFSET + f->get_manual_offset = yas_get_manual_offset; + f->set_manual_offset = yas_set_manual_offset; +#endif + f->get_static_matrix = yas_get_static_matrix; + f->set_static_matrix = yas_set_static_matrix; + f->get_dynamic_matrix = yas_get_dynamic_matrix; + f->set_dynamic_matrix = yas_set_dynamic_matrix; + f->get_enable = yas_get_enable; + f->set_enable = yas_set_enable; + f->get_filter = yas_get_filter; + f->set_filter = yas_set_filter; + f->get_filter_enable = yas_get_filter_enable; + f->set_filter_enable = yas_set_filter_enable; + f->get_position = yas_get_position; + f->set_position = yas_set_position; + f->read_reg = yas_read_reg; + f->write_reg = yas_write_reg; + f->measure = yas_measure; + + if ((f->callback.lock == NULL && f->callback.unlock != NULL) + || (f->callback.lock != NULL && f->callback.unlock == NULL)) { + this_driver.callback.lock = NULL; + this_driver.callback.unlock = NULL; + } else { + this_driver.callback.lock = f->callback.lock; + this_driver.callback.unlock = f->callback.unlock; + } + this_driver.callback.device_open = f->callback.device_open; + this_driver.callback.device_close = f->callback.device_close; + this_driver.callback.device_write = f->callback.device_write; + this_driver.callback.device_read = f->callback.device_read; + this_driver.callback.msleep = f->callback.msleep; + this_driver.callback.current_time = f->callback.current_time; + yas_term(); + + return YAS_NO_ERROR; +} diff --git a/drivers/sensor/yas_mag_driver.c b/drivers/sensor/yas_mag_driver.c new file mode 100644 index 0000000..805ec32 --- /dev/null +++ b/drivers/sensor/yas_mag_driver.c @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2010 Yamaha Corporation + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +#include <linux/sensor/yas.h> + +#if CONFIG_SENSORS_YAS532 +#include "yas_mag_driver-yas532.c" +#else +#include "yas_mag_driver-none.c" +#endif diff --git a/drivers/sensor/yas_mag_kernel_driver.c b/drivers/sensor/yas_mag_kernel_driver.c new file mode 100644 index 0000000..0106958 --- /dev/null +++ b/drivers/sensor/yas_mag_kernel_driver.c @@ -0,0 +1,2192 @@ +/* + * Copyright (c) 2010-2011 Yamaha Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#include <linux/atomic.h> +#include <linux/types.h> +#include <linux/i2c.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/input.h> +#include <linux/mutex.h> +#include <linux/delay.h> +#include <linux/poll.h> +#include <linux/slab.h> +#include <linux/uaccess.h> +#include <linux/workqueue.h> + +#define __LINUX_KERNEL_DRIVER__ +#include <linux/sensor/yas.h> +#include <linux/sensor/sensors_core.h> +#include "yas_mag_driver.c" + +#define SYSFS_PCBTEST +#ifdef SYSFS_PCBTEST +#include "yas_pcb_test.h" +#include "yas_pcb_test.c" +#endif + +#define GEOMAGNETIC_I2C_DEVICE_NAME "yas532" +#define GEOMAGNETIC_INPUT_NAME "geomagnetic" +#define GEOMAGNETIC_INPUT_RAW_NAME "geomagnetic_raw" +#undef GEOMAGNETIC_PLATFORM_API + +#define ABS_STATUS (ABS_BRAKE) +#define ABS_WAKE (ABS_MISC) + +#define ABS_RAW_DISTORTION (ABS_THROTTLE) +#define ABS_RAW_THRESHOLD (ABS_RUDDER) +#define ABS_RAW_SHAPE (ABS_WHEEL) +#define ABS_RAW_MODE (ABS_HAT0X) +#define ABS_RAW_REPORT (ABS_GAS) + +struct geomagnetic_data { + struct input_dev *input_data; + struct input_dev *input_raw; + struct delayed_work work; + struct semaphore driver_lock; + struct semaphore multi_lock; + atomic_t last_data[3]; + atomic_t last_status; + atomic_t enable; + int filter_enable; + int filter_len; + int32_t filter_noise[3]; + int32_t filter_threshold; + int delay; + int32_t threshold; + int32_t distortion[3]; + int32_t shape; + int32_t ellipsoid_mode; + struct yas_mag_offset driver_offset; +#if DEBUG + int suspend; +#endif +#ifdef YAS_MAG_MANUAL_OFFSET + struct yas_vector manual_offset; +#endif + struct yas_matrix static_matrix; + struct yas_matrix dynamic_matrix; +#ifdef YAS_SENSOR_KERNEL_DEVFILE_INTERFACE + struct list_head devfile_list; + struct list_head raw_devfile_list; +#endif + struct device *magnetic_sensor_device; + struct mag_platform_data *mag_pdata; + uint8_t dev_id; + int noise_test_init; +}; + +static struct i2c_client *this_client; + +#ifdef YAS_SENSOR_KERNEL_DEVFILE_INTERFACE + +#include <linux/miscdevice.h> +#define MAX_COUNT (64) +#define SENSOR_NAME "geomagnetic" +#define SENSOR_RAW_NAME "geomagnetic_raw" + +struct sensor_device { + struct list_head list; + struct mutex lock; + wait_queue_head_t waitq; + struct input_event events[MAX_COUNT]; + int head, num_event; +}; + +static void get_time_stamp(struct timeval *tv) +{ + struct timespec ts; + ktime_get_ts(&ts); + tv->tv_sec = ts.tv_sec; + tv->tv_usec = ts.tv_nsec / 1000; +} + +static void make_event(struct input_event *ev, int type, int code, int value) +{ + struct timeval tv; + get_time_stamp(&tv); + ev->type = type; + ev->code = code; + ev->value = value; + ev->time = tv; +} + +static void +make_event_w_time(struct input_event *ev, int type, int code, int value, + struct timeval *tv) +{ + ev->type = type; + ev->code = code; + ev->value = value; + ev->time = *tv; +} + +static void sensor_enq(struct sensor_device *kdev, struct input_event *ev) +{ + int idx; + + idx = kdev->head + kdev->num_event; + if (MAX_COUNT <= idx) + idx -= MAX_COUNT; + kdev->events[idx] = *ev; + kdev->num_event++; + if (MAX_COUNT < kdev->num_event) { + kdev->num_event = MAX_COUNT; + kdev->head++; + if (MAX_COUNT <= kdev->head) + kdev->head -= MAX_COUNT; + } +} + +static int sensor_deq(struct sensor_device *kdev, struct input_event *ev) +{ + if (kdev->num_event == 0) + return 0; + + *ev = kdev->events[kdev->head]; + kdev->num_event--; + kdev->head++; + if (MAX_COUNT <= kdev->head) + kdev->head -= MAX_COUNT; + return 1; +} + +static void +sensor_event(struct list_head *devlist, struct input_event *ev, int num) +{ + struct sensor_device *kdev; + int i; + + list_for_each_entry(kdev, devlist, list) { + mutex_lock(&kdev->lock); + for (i = 0; i < num; i++) + sensor_enq(kdev, &ev[i]); + mutex_unlock(&kdev->lock); + wake_up_interruptible(&kdev->waitq); + } +} + +static ssize_t +sensor_write(struct file *f, const char __user *buf, size_t count, + loff_t *pos) +{ + struct geomagnetic_data *data = i2c_get_clientdata(this_client); + struct sensor_device *kdev; + struct input_event ev[MAX_COUNT]; + int num, i; + + if (count < sizeof(struct input_event)) + return -EINVAL; + num = count / sizeof(struct input_event); + if (MAX_COUNT < num) + num = MAX_COUNT; + + if (copy_from_user(ev, buf, num * sizeof(struct input_event))) + return -EFAULT; + + list_for_each_entry(kdev, &data->devfile_list, list) { + mutex_lock(&kdev->lock); + for (i = 0; i < num; i++) + sensor_enq(kdev, &ev[i]); + mutex_unlock(&kdev->lock); + wake_up_interruptible(&kdev->waitq); + } + + return count; +} + +static ssize_t +sensor_read(struct file *f, char __user *buf, size_t count, loff_t *pos) +{ + struct sensor_device *kdev = f->private_data; + int rt, num; + struct input_event ev[MAX_COUNT]; + + if (count < sizeof(struct input_event)) + return -EINVAL; + + rt = wait_event_interruptible(kdev->waitq, kdev->num_event != 0); + if (rt) + return rt; + + mutex_lock(&kdev->lock); + for (num = 0; num < count / sizeof(struct input_event); num++) { + if (!sensor_deq(kdev, &ev[num])) + break; + } + mutex_unlock(&kdev->lock); + + if (copy_to_user(buf, ev, num * sizeof(struct input_event))) + return -EFAULT; + + return num * sizeof(struct input_event); +} + +static unsigned int sensor_poll(struct file *f, struct poll_table_struct *wait) +{ + struct sensor_device *kdev = f->private_data; + + poll_wait(f, &kdev->waitq, wait); + if (kdev->num_event != 0) + return POLLIN | POLLRDNORM; + + return 0; +} + +static int sensor_open(struct inode *inode, struct file *f) +{ + struct geomagnetic_data *data = i2c_get_clientdata(this_client); + struct sensor_device *kdev; + + kdev = kzalloc(sizeof(struct sensor_device), GFP_KERNEL); + if (!kdev) + return -ENOMEM; + + mutex_init(&kdev->lock); + init_waitqueue_head(&kdev->waitq); + f->private_data = kdev; + kdev->head = 0; + kdev->num_event = 0; + list_add(&kdev->list, &data->devfile_list); + + return 0; +} + +static int sensor_release(struct inode *inode, struct file *f) +{ + struct sensor_device *kdev = f->private_data; + + list_del(&kdev->list); + kfree(kdev); + + return 0; +} + +static int sensor_raw_open(struct inode *inode, struct file *f) +{ + struct geomagnetic_data *data = i2c_get_clientdata(this_client); + struct sensor_device *kdev; + + kdev = kzalloc(sizeof(struct sensor_device), GFP_KERNEL); + if (!kdev) + return -ENOMEM; + + mutex_init(&kdev->lock); + init_waitqueue_head(&kdev->waitq); + f->private_data = kdev; + kdev->head = 0; + kdev->num_event = 0; + list_add(&kdev->list, &data->raw_devfile_list); + + return 0; +} + +const struct file_operations sensor_fops = { + .owner = THIS_MODULE, + .open = sensor_open, + .release = sensor_release, + .write = sensor_write, + .read = sensor_read, + .poll = sensor_poll, +}; + +static struct miscdevice sensor_devfile = { + .name = SENSOR_NAME, + .fops = &sensor_fops, + .minor = MISC_DYNAMIC_MINOR, +}; + +const struct file_operations sensor_raw_fops = { + .owner = THIS_MODULE, + .open = sensor_raw_open, + .release = sensor_release, + .write = sensor_write, + .read = sensor_read, + .poll = sensor_poll, +}; + +static struct miscdevice sensor_raw_devfile = { + .name = SENSOR_RAW_NAME, + .fops = &sensor_raw_fops, + .minor = MISC_DYNAMIC_MINOR, +}; + +#endif + +static int geomagnetic_i2c_open(void) +{ + return 0; +} + +static int geomagnetic_i2c_close(void) +{ + return 0; +} + +#if YAS_MAG_DRIVER == YAS_MAG_DRIVER_YAS529 +static int geomagnetic_i2c_write(const uint8_t *buf, int len) +{ + if (i2c_master_send(this_client, buf, len) < 0) + return -1; +#if DEBUG + YLOGD(("[W] [%02x]\n", buf[0])); +#endif + + return 0; +} + +static int geomagnetic_i2c_read(uint8_t *buf, int len) +{ + if (i2c_master_recv(this_client, buf, len) < 0) + return -1; + return 0; +} + +#else + +static int geomagnetic_i2c_write(uint8_t addr, const uint8_t *buf, int len) +{ + uint8_t tmp[16]; + + if (sizeof(tmp) - 1 < len) + return -1; + + tmp[0] = addr; + memcpy(&tmp[1], buf, len); + + if (i2c_master_send(this_client, tmp, len + 1) < 0) + return -1; +#if DEBUG + YLOGD(("[W] addr[%02x] [%02x]\n", addr, buf[0])); +#endif + + return 0; +} + +static int geomagnetic_i2c_read(uint8_t addr, uint8_t *buf, int len) +{ + struct i2c_msg msg[2]; + int err; + + msg[0].addr = this_client->addr; + msg[0].flags = 0; + msg[0].len = 1; + msg[0].buf = &addr; + msg[1].addr = this_client->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = len; + msg[1].buf = buf; + + err = i2c_transfer(this_client->adapter, msg, 2); + if (err != 2) { + dev_err(&this_client->dev, + "i2c_transfer() read error: slave_addr=%02x, reg_addr=%02x, err=%d\n", + this_client->addr, addr, err); + return err; + } + + return 0; +} + +#endif + +static int geomagnetic_lock(void) +{ + struct geomagnetic_data *data = NULL; + int rt; + + if (this_client == NULL) + return -1; + + data = i2c_get_clientdata(this_client); + rt = down_interruptible(&data->driver_lock); + if (rt < 0) + up(&data->driver_lock); + return rt; +} + +static int geomagnetic_unlock(void) +{ + struct geomagnetic_data *data = NULL; + + if (this_client == NULL) + return -1; + + data = i2c_get_clientdata(this_client); + up(&data->driver_lock); + return 0; +} + +static void geomagnetic_msleep(int ms) +{ + usleep_range(ms * 999, ms * 1000); +} + +static void geomagnetic_current_time(int32_t *sec, int32_t *msec) +{ + struct timeval tv; + + do_gettimeofday(&tv); + + *sec = tv.tv_sec; + *msec = tv.tv_usec / 1000; +} + +static struct yas_mag_driver hwdep_driver = { + .callback = { + .lock = geomagnetic_lock, + .unlock = geomagnetic_unlock, + .device_open = geomagnetic_i2c_open, + .device_close = geomagnetic_i2c_close, + .device_read = geomagnetic_i2c_read, + .device_write = geomagnetic_i2c_write, + .msleep = geomagnetic_msleep, + .current_time = geomagnetic_current_time, + }, +}; + +static int geomagnetic_multi_lock(void) +{ + struct geomagnetic_data *data = NULL; + int rt; + + if (this_client == NULL) + return -1; + + data = i2c_get_clientdata(this_client); + rt = down_interruptible(&data->multi_lock); + if (rt < 0) + up(&data->multi_lock); + return rt; +} + +static int geomagnetic_multi_unlock(void) +{ + struct geomagnetic_data *data = NULL; + + if (this_client == NULL) + return -1; + + data = i2c_get_clientdata(this_client); + up(&data->multi_lock); + return 0; +} + +static int geomagnetic_enable(struct geomagnetic_data *data) +{ + if (!atomic_cmpxchg(&data->enable, 0, 1)) + schedule_delayed_work(&data->work, 0); + + return 0; +} + +static int geomagnetic_disable(struct geomagnetic_data *data) +{ + if (atomic_cmpxchg(&data->enable, 1, 0)) + cancel_delayed_work_sync(&data->work); + + return 0; +} + +/* Sysfs interface */ +static ssize_t +geomagnetic_delay_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct input_dev *input_data = to_input_dev(dev); + struct geomagnetic_data *data = input_get_drvdata(input_data); + int delay; + + geomagnetic_multi_lock(); + + delay = data->delay; + + geomagnetic_multi_unlock(); + + return sprintf(buf, "%d\n", delay); +} + +static ssize_t +geomagnetic_delay_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct input_dev *input_data = to_input_dev(dev); + struct geomagnetic_data *data = input_get_drvdata(input_data); + unsigned long value; + int error; + + error = strict_strtoul(buf, 10, &value); + if (unlikely(error)) + return error; + + if (hwdep_driver.set_delay == NULL) + return -ENOTTY; + + geomagnetic_multi_lock(); + + error = strict_strtoul(buf, 10, &value); + if (unlikely(error)) + return error; + if (hwdep_driver.set_delay(value) == 0) + data->delay = value; + + geomagnetic_multi_unlock(); + + return count; +} + +static ssize_t +geomagnetic_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct input_dev *input_data = to_input_dev(dev); + struct geomagnetic_data *data = input_get_drvdata(input_data); + + return sprintf(buf, "%d\n", atomic_read(&data->enable)); +} + +static ssize_t +geomagnetic_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct input_dev *input_data = to_input_dev(dev); + struct geomagnetic_data *data = input_get_drvdata(input_data); + unsigned long value; + int error; + + error = strict_strtoul(buf, 10, &value); + if (unlikely(error)) + return error; + value = !(!value); + + if (hwdep_driver.set_enable == NULL) + return -ENOTTY; + + if (geomagnetic_multi_lock() < 0) + return count; + + if (hwdep_driver.set_enable(value) == 0) { + if (value) + geomagnetic_enable(data); + else + geomagnetic_disable(data); + } + + geomagnetic_multi_unlock(); + + return count; +} + +static ssize_t +geomagnetic_filter_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct input_dev *input_data = to_input_dev(dev); + struct geomagnetic_data *data = input_get_drvdata(input_data); + int filter_enable; + + geomagnetic_multi_lock(); + + filter_enable = data->filter_enable; + + geomagnetic_multi_unlock(); + + return sprintf(buf, "%d\n", filter_enable); +} + +static ssize_t +geomagnetic_filter_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct input_dev *input_data = to_input_dev(dev); + struct geomagnetic_data *data = input_get_drvdata(input_data); + unsigned long value; + int error; + + if (hwdep_driver.set_filter_enable == NULL) + return -ENOTTY; + + error = strict_strtoul(buf, 10, &value); + if (unlikely(error)) + return error; + + if (geomagnetic_multi_lock() < 0) + return count; + + if (hwdep_driver.set_filter_enable(value) == 0) + data->filter_enable = !!value; + + geomagnetic_multi_unlock(); + + return count; +} + +static ssize_t +geomagnetic_filter_len_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct input_dev *input_data = to_input_dev(dev); + struct geomagnetic_data *data = input_get_drvdata(input_data); + int filter_len; + + geomagnetic_multi_lock(); + + filter_len = data->filter_len; + + geomagnetic_multi_unlock(); + + return sprintf(buf, "%d\n", filter_len); +} + +static ssize_t +geomagnetic_filter_len_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct input_dev *input_data = to_input_dev(dev); + struct geomagnetic_data *data = input_get_drvdata(input_data); + struct yas_mag_filter filter; + unsigned long value; + int error; + + if (hwdep_driver.get_filter == NULL || hwdep_driver.set_filter == NULL) + return -ENOTTY; + + error = strict_strtoul(buf, 10, &value); + if (unlikely(error)) + return error; + + if (geomagnetic_multi_lock() < 0) + return count; + + hwdep_driver.get_filter(&filter); + filter.len = value; + if (hwdep_driver.set_filter(&filter) == 0) + data->filter_len = value; + + geomagnetic_multi_unlock(); + + return count; +} + +static ssize_t +geomagnetic_filter_noise_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct input_dev *input_raw = to_input_dev(dev); + struct geomagnetic_data *data = input_get_drvdata(input_raw); + int rt; + + geomagnetic_multi_lock(); + + rt = sprintf(buf, "%d %d %d\n", + data->filter_noise[0], + data->filter_noise[1], data->filter_noise[2]); + + geomagnetic_multi_unlock(); + + return rt; +} + +static ssize_t +geomagnetic_filter_noise_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct input_dev *input_raw = to_input_dev(dev); + struct yas_mag_filter filter; + struct geomagnetic_data *data = input_get_drvdata(input_raw); + int32_t filter_noise[3]; + + geomagnetic_multi_lock(); + + sscanf(buf, "%d %d %d", + &filter_noise[0], &filter_noise[1], &filter_noise[2]); + hwdep_driver.get_filter(&filter); + memcpy(filter.noise, filter_noise, sizeof(filter.noise)); + if (hwdep_driver.set_filter(&filter) == 0) + memcpy(data->filter_noise, filter_noise, + sizeof(data->filter_noise)); + + geomagnetic_multi_unlock(); + + return count; +} + +static ssize_t +geomagnetic_filter_threshold_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct input_dev *input_data = to_input_dev(dev); + struct geomagnetic_data *data = input_get_drvdata(input_data); + int32_t filter_threshold; + + geomagnetic_multi_lock(); + + filter_threshold = data->filter_threshold; + + geomagnetic_multi_unlock(); + + return sprintf(buf, "%d\n", filter_threshold); +} + +static ssize_t +geomagnetic_filter_threshold_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct input_dev *input_data = to_input_dev(dev); + struct geomagnetic_data *data = input_get_drvdata(input_data); + struct yas_mag_filter filter; + unsigned long value; + int error; + + if (hwdep_driver.get_filter == NULL || hwdep_driver.set_filter == NULL) + return -ENOTTY; + + error = strict_strtoul(buf, 10, &value); + if (unlikely(error)) + return error; + + if (geomagnetic_multi_lock() < 0) + return count; + + hwdep_driver.get_filter(&filter); + filter.threshold = value; + if (hwdep_driver.set_filter(&filter) == 0) + data->filter_threshold = value; + + geomagnetic_multi_unlock(); + + return count; +} + +static ssize_t +geomagnetic_position_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + if (hwdep_driver.get_position == NULL) + return -ENOTTY; + return sprintf(buf, "%d\n", hwdep_driver.get_position()); +} + +static ssize_t +geomagnetic_position_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long value; + int error; + + error = strict_strtoul(buf, 10, &value); + if (unlikely(error)) + return error; + + if (hwdep_driver.set_position == NULL) + return -ENOTTY; + hwdep_driver.set_position(value); + + return count; +} + +static ssize_t +geomagnetic_data_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct input_dev *input_data = to_input_dev(dev); + struct geomagnetic_data *data = input_get_drvdata(input_data); + int rt; + + rt = sprintf(buf, "%d %d %d\n", + atomic_read(&data->last_data[0]), + atomic_read(&data->last_data[1]), + atomic_read(&data->last_data[2])); + + return rt; +} + +static ssize_t +geomagnetic_status_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct input_dev *input_data = to_input_dev(dev); + struct geomagnetic_data *data = input_get_drvdata(input_data); + int rt; + + rt = sprintf(buf, "%d\n", atomic_read(&data->last_status)); + + return rt; +} + +static ssize_t +geomagnetic_status_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct input_dev *input_data = to_input_dev(dev); + struct geomagnetic_data *data = input_get_drvdata(input_data); + static int16_t cnt = 1; +#ifdef YAS_SENSOR_KERNEL_DEVFILE_INTERFACE + struct input_event ev; +#endif + int accuracy = 0; + int code = 0; + int value = 0; + + geomagnetic_multi_lock(); + + sscanf(buf, "%d", &accuracy); + if (0 <= accuracy && accuracy <= 3) + atomic_set(&data->last_status, accuracy); + code |= YAS_REPORT_CALIB_OFFSET_CHANGED; + value = (cnt++ << 16) | (code); + +#ifdef YAS_SENSOR_KERNEL_DEVFILE_INTERFACE + mkev(&ev, EV_ABS, ABS_RAW_REPORT, value); + sensor_event(&data->raw_devfile_list, &ev, 1); +#else + input_report_abs(data->input_raw, ABS_RAW_REPORT, value); + input_sync(data->input_raw); +#endif + + geomagnetic_multi_unlock(); + + return count; +} + +static ssize_t +geomagnetic_wake_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct input_dev *input_data = to_input_dev(dev); + struct geomagnetic_data *data = input_get_drvdata(input_data); + static int16_t cnt = 1; +#ifdef YAS_SENSOR_KERNEL_DEVFILE_INTERFACE + struct input_event ev[1]; + make_event(ev, EV_ABS, ABS_WAKE, cnt++); + sensor_event(&data->devfile_list, ev, 1); +#else + input_report_abs(data->input_data, ABS_WAKE, cnt++); + input_sync(data->input_data); +#endif + + return count; +} + +#if DEBUG + +static int geomagnetic_suspend(struct i2c_client *client, pm_message_t mesg); +static int geomagnetic_resume(struct i2c_client *client); + +static ssize_t +geomagnetic_debug_suspend_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct input_dev *input = to_input_dev(dev); + struct geomagnetic_data *data = input_get_drvdata(input); + + return sprintf(buf, "%d\n", data->suspend); +} + +static ssize_t +geomagnetic_debug_suspend_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long suspend; + int error; + + error = strict_strtoul(buf, 10, &suspend); + if (unlikely(error)) + return error; + + if (suspend) { + pm_message_t msg; + memset(&msg, 0, sizeof(msg)); + geomagnetic_suspend(this_client, msg); + } else + geomagnetic_resume(this_client); + + return count; +} + +#endif /* DEBUG */ + +static DEVICE_ATTR(delay, S_IRUGO | S_IWUSR | S_IWGRP, + geomagnetic_delay_show, geomagnetic_delay_store); +static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR | S_IWGRP, + geomagnetic_enable_show, geomagnetic_enable_store); +static DEVICE_ATTR(filter_enable, S_IRUGO | S_IWUSR | S_IWGRP, + geomagnetic_filter_enable_show, + geomagnetic_filter_enable_store); +static DEVICE_ATTR(filter_len, S_IRUGO | S_IWUSR | S_IWGRP, + geomagnetic_filter_len_show, geomagnetic_filter_len_store); +static DEVICE_ATTR(filter_threshold, S_IRUGO | S_IWUSR | S_IWGRP, + geomagnetic_filter_threshold_show, + geomagnetic_filter_threshold_store); +static DEVICE_ATTR(filter_noise, S_IRUGO | S_IWUSR | S_IWGRP, + geomagnetic_filter_noise_show, + geomagnetic_filter_noise_store); +static DEVICE_ATTR(data, S_IRUGO, geomagnetic_data_show, NULL); +static DEVICE_ATTR(status, S_IRUGO|S_IWUSR|S_IWGRP, geomagnetic_status_show, + geomagnetic_status_store); +static DEVICE_ATTR(wake, S_IWUSR | S_IWGRP, NULL, geomagnetic_wake_store); +static DEVICE_ATTR(position, S_IRUGO | S_IWUSR, + geomagnetic_position_show, geomagnetic_position_store); +#if DEBUG +static DEVICE_ATTR(debug_suspend, S_IRUGO | S_IWUSR, + geomagnetic_debug_suspend_show, + geomagnetic_debug_suspend_store); +#endif /* DEBUG */ + +static struct attribute *geomagnetic_attributes[] = { + &dev_attr_delay.attr, + &dev_attr_enable.attr, + &dev_attr_filter_enable.attr, + &dev_attr_filter_len.attr, + &dev_attr_filter_threshold.attr, + &dev_attr_filter_noise.attr, + &dev_attr_data.attr, + &dev_attr_status.attr, + &dev_attr_wake.attr, + &dev_attr_position.attr, +#if DEBUG + &dev_attr_debug_suspend.attr, +#endif /* DEBUG */ + NULL +}; + +static struct attribute_group geomagnetic_attribute_group = { + .attrs = geomagnetic_attributes +}; + +static ssize_t +geomagnetic_raw_threshold_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct input_dev *input_raw = to_input_dev(dev); + struct geomagnetic_data *data = input_get_drvdata(input_raw); + int threshold; + + geomagnetic_multi_lock(); + + threshold = data->threshold; + + geomagnetic_multi_unlock(); + + return sprintf(buf, "%d\n", threshold); +} + +static ssize_t +geomagnetic_raw_threshold_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct input_dev *input_raw = to_input_dev(dev); + struct geomagnetic_data *data = input_get_drvdata(input_raw); + unsigned long value; + int error; + + error = strict_strtoul(buf, 10, &value); + if (unlikely(error)) + return error; + + geomagnetic_multi_lock(); + + if (0 <= value && value <= 2) { +#ifdef YAS_SENSOR_KERNEL_DEVFILE_INTERFACE + struct input_event ev[1]; + make_event(ev, EV_ABS, ABS_RAW_THRESHOLD, value); + sensor_event(&data->raw_devfile_list, ev, 1); +#endif + data->threshold = value; +#ifndef YAS_SENSOR_KERNEL_DEVFILE_INTERFACE + input_report_abs(data->input_raw, ABS_RAW_THRESHOLD, value); + input_sync(data->input_raw); +#endif + } + + geomagnetic_multi_unlock(); + + return count; +} + +static ssize_t +geomagnetic_raw_distortion_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct input_dev *input_raw = to_input_dev(dev); + struct geomagnetic_data *data = input_get_drvdata(input_raw); + int rt; + + geomagnetic_multi_lock(); + + rt = sprintf(buf, "%d %d %d\n", + data->distortion[0], + data->distortion[1], data->distortion[2]); + + geomagnetic_multi_unlock(); + + return rt; +} + +static ssize_t +geomagnetic_raw_distortion_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct input_dev *input_raw = to_input_dev(dev); + struct geomagnetic_data *data = input_get_drvdata(input_raw); + int32_t distortion[3]; + static int32_t val = 1; + int i; + + geomagnetic_multi_lock(); + + sscanf(buf, "%d %d %d", &distortion[0], &distortion[1], &distortion[2]); + if (distortion[0] > 0 && distortion[1] > 0 && distortion[2] > 0) { +#ifdef YAS_SENSOR_KERNEL_DEVFILE_INTERFACE + struct input_event ev[1]; + make_event(ev, EV_ABS, ABS_RAW_DISTORTION, val++); + sensor_event(&data->raw_devfile_list, ev, 1); +#endif + for (i = 0; i < 3; i++) + data->distortion[i] = distortion[i]; +#ifndef YAS_SENSOR_KERNEL_DEVFILE_INTERFACE + input_report_abs(data->input_raw, ABS_RAW_DISTORTION, val++); + input_sync(data->input_raw); +#endif + } + + geomagnetic_multi_unlock(); + + return count; +} + +static ssize_t +geomagnetic_raw_shape_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct input_dev *input_raw = to_input_dev(dev); + struct geomagnetic_data *data = input_get_drvdata(input_raw); + int shape; + + geomagnetic_multi_lock(); + + shape = data->shape; + + geomagnetic_multi_unlock(); + + return sprintf(buf, "%d\n", shape); +} + +static ssize_t +geomagnetic_raw_shape_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct input_dev *input_raw = to_input_dev(dev); + struct geomagnetic_data *data = input_get_drvdata(input_raw); + unsigned long value; + int error; + + error = strict_strtoul(buf, 10, &value); + if (unlikely(error)) + return error; + + geomagnetic_multi_lock(); + + if (0 == value || value == 1) { +#ifdef YAS_SENSOR_KERNEL_DEVFILE_INTERFACE + struct input_event ev[1]; + make_event(ev, EV_ABS, ABS_RAW_SHAPE, value); + sensor_event(&data->raw_devfile_list, ev, 1); +#endif + data->shape = value; +#ifndef YAS_SENSOR_KERNEL_DEVFILE_INTERFACE + input_report_abs(data->input_raw, ABS_RAW_SHAPE, value); + input_sync(data->input_raw); +#endif + } + + geomagnetic_multi_unlock(); + + return count; +} + +static ssize_t +geomagnetic_raw_offsets_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct input_dev *input_raw = to_input_dev(dev); + struct geomagnetic_data *data = input_get_drvdata(input_raw); + struct yas_mag_offset offset; + int accuracy; + + geomagnetic_multi_lock(); + + offset = data->driver_offset; + accuracy = atomic_read(&data->last_status); + + geomagnetic_multi_unlock(); + + return sprintf(buf, "%d %d %d %d %d %d %d\n", + offset.hard_offset[0], + offset.hard_offset[1], + offset.hard_offset[2], + offset.calib_offset.v[0], + offset.calib_offset.v[1], + offset.calib_offset.v[2], accuracy); +} + +static ssize_t +geomagnetic_raw_offsets_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct input_dev *input_raw = to_input_dev(dev); + struct geomagnetic_data *data = input_get_drvdata(input_raw); + struct yas_mag_offset offset; + int32_t hard_offset[3]; + int i, accuracy; + + geomagnetic_multi_lock(); + + sscanf(buf, "%d %d %d %d %d %d %d", + &hard_offset[0], + &hard_offset[1], + &hard_offset[2], + &offset.calib_offset.v[0], + &offset.calib_offset.v[1], &offset.calib_offset.v[2], &accuracy); + if (0 <= accuracy && accuracy <= 3) { + for (i = 0; i < 3; i++) + offset.hard_offset[i] = (int8_t) hard_offset[i]; + + if (hwdep_driver.set_offset(&offset) == 0) { + atomic_set(&data->last_status, accuracy); + data->driver_offset = offset; + } + } + + geomagnetic_multi_unlock(); + + return count; +} + +#ifdef YAS_MAG_MANUAL_OFFSET +static ssize_t +geomagnetic_raw_manual_offsets_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct input_dev *input_raw = to_input_dev(dev); + struct geomagnetic_data *data = input_get_drvdata(input_raw); + struct yas_vector offset; + + geomagnetic_multi_lock(); + + offset = data->manual_offset; + + geomagnetic_multi_unlock(); + + return sprintf(buf, "%d %d %d\n", offset.v[0], offset.v[1], + offset.v[2]); +} + +static ssize_t +geomagnetic_raw_manual_offsets_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct input_dev *input_raw = to_input_dev(dev); + struct geomagnetic_data *data = input_get_drvdata(input_raw); + struct yas_vector offset; + + geomagnetic_multi_lock(); + + sscanf(buf, "%d %d %d", &offset.v[0], &offset.v[1], &offset.v[2]); + if (hwdep_driver.set_manual_offset(&offset) == 0) + data->manual_offset = offset; + + geomagnetic_multi_unlock(); + + return count; +} +#endif + +static ssize_t +geomagnetic_raw_static_matrix_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct input_dev *input_raw = to_input_dev(dev); + struct geomagnetic_data *data = input_get_drvdata(input_raw); + struct yas_matrix matrix; + + geomagnetic_multi_lock(); + + matrix = data->static_matrix; + + geomagnetic_multi_unlock(); + + return sprintf(buf, "%d %d %d %d %d %d %d %d %d\n", + matrix.matrix[0], matrix.matrix[1], matrix.matrix[2], + matrix.matrix[3], matrix.matrix[4], matrix.matrix[5], + matrix.matrix[6], matrix.matrix[7], matrix.matrix[8]); +} + +static ssize_t +geomagnetic_raw_static_matrix_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct input_dev *input_raw = to_input_dev(dev); + struct geomagnetic_data *data = input_get_drvdata(input_raw); + struct yas_matrix matrix; + + geomagnetic_multi_lock(); + + sscanf(buf, "%d %d %d %d %d %d %d %d %d", + &matrix.matrix[0], &matrix.matrix[1], &matrix.matrix[2], + &matrix.matrix[3], &matrix.matrix[4], &matrix.matrix[5], + &matrix.matrix[6], &matrix.matrix[7], &matrix.matrix[8]); + if (hwdep_driver.set_static_matrix(&matrix) == 0) + data->static_matrix = matrix; + + geomagnetic_multi_unlock(); + + return count; +} + +static ssize_t +geomagnetic_raw_dynamic_matrix_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct input_dev *input_raw = to_input_dev(dev); + struct geomagnetic_data *data = input_get_drvdata(input_raw); + struct yas_matrix matrix; + + geomagnetic_multi_lock(); + + matrix = data->dynamic_matrix; + + geomagnetic_multi_unlock(); + + return sprintf(buf, "%d %d %d %d %d %d %d %d %d\n", + matrix.matrix[0], matrix.matrix[1], matrix.matrix[2], + matrix.matrix[3], matrix.matrix[4], matrix.matrix[5], + matrix.matrix[6], matrix.matrix[7], matrix.matrix[8]); +} + +static ssize_t +geomagnetic_raw_dynamic_matrix_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct input_dev *input_raw = to_input_dev(dev); + struct geomagnetic_data *data = input_get_drvdata(input_raw); + struct yas_matrix matrix; + + geomagnetic_multi_lock(); + + sscanf(buf, "%d %d %d %d %d %d %d %d %d", + &matrix.matrix[0], &matrix.matrix[1], &matrix.matrix[2], + &matrix.matrix[3], &matrix.matrix[4], &matrix.matrix[5], + &matrix.matrix[6], &matrix.matrix[7], &matrix.matrix[8]); + if (hwdep_driver.set_dynamic_matrix(&matrix) == 0) + data->dynamic_matrix = matrix; + + geomagnetic_multi_unlock(); + + return count; +} + +static ssize_t +geomagnetic_raw_ellipsoid_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct input_dev *input_raw = to_input_dev(dev); + struct geomagnetic_data *data = input_get_drvdata(input_raw); + int ellipsoid_mode; + + geomagnetic_multi_lock(); + + ellipsoid_mode = data->ellipsoid_mode; + + geomagnetic_multi_unlock(); + + return sprintf(buf, "%d\n", ellipsoid_mode); +} + +static ssize_t +geomagnetic_raw_ellipsoid_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct input_dev *input_raw = to_input_dev(dev); + struct geomagnetic_data *data = input_get_drvdata(input_raw); + unsigned long value; + int error; + + error = strict_strtoul(buf, 10, &value); + if (unlikely(error)) + return error; + value = !(!value); +#ifdef YAS_SENSOR_KERNEL_DEVFILE_INTERFACE + struct input_event ev[1]; +#endif + + geomagnetic_multi_lock(); + +#ifdef YAS_SENSOR_KERNEL_DEVFILE_INTERFACE + make_event(ev, EV_ABS, ABS_RAW_MODE, value); + sensor_event(&data->raw_devfile_list, ev, 1); +#endif + data->ellipsoid_mode = value; +#ifndef YAS_SENSOR_KERNEL_DEVFILE_INTERFACE + input_report_abs(data->input_raw, ABS_RAW_MODE, value); + input_sync(data->input_raw); +#endif + + geomagnetic_multi_unlock(); + + return count; +} + +#ifdef SYSFS_PCBTEST + +static int +pcbtest_i2c_write(uint8_t slave, uint8_t addr, const uint8_t *buf, int len) +{ + return geomagnetic_i2c_write(addr, buf, len); +} + +static int +pcbtest_i2c_read(uint8_t slave, uint8_t addr, uint8_t *buf, int len) +{ + return geomagnetic_i2c_read(addr, buf, len); +} + +static struct yas_pcb_test pcbtest = { + .callback = { + .power_on = NULL, + .power_off = NULL, + .i2c_open = geomagnetic_i2c_open, + .i2c_close = geomagnetic_i2c_close, + .i2c_read = pcbtest_i2c_read, + .i2c_write = pcbtest_i2c_write, + .msleep = geomagnetic_msleep, + .read_intpin = NULL, + }, +}; + + +static ssize_t +geomagnetic_raw_self_test_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct geomagnetic_data *data = i2c_get_clientdata(this_client); + int id, x, y1, y2, dir, sx, sy, ohx, ohy, ohz; + int err1, err2, err3, err4, err5, err6, err7; + + if (data->noise_test_init) + pcbtest.power_off(); + err1 = pcbtest.power_on_and_device_check(&id); + err3 = pcbtest.initialization(); + err4 = + pcbtest.offset_control_measurement_and_set_offset_register( + &x, &y1, &y2); + err5 = pcbtest.direction_measurement(&dir); + err6 = + pcbtest.sensitivity_measurement_of_magnetic_sensor_by_test_coil( + &sx, &sy); + err7 = pcbtest.magnetic_field_level_check(&ohx, &ohy, &ohz); + err2 = pcbtest.power_off(); + data->noise_test_init = 0; + if (unlikely(id != 0x2)) + err1 = -1; + if (unlikely(x < -30 || x > 30)) + err4 = -1; + if (unlikely(y1 < -30 || y1 > 30)) + err4 = -1; + if (unlikely(y2 < -30 || y2 > 30)) + err4 = -1; + if (unlikely(sx < 17 || sy < 22)) + err6 = -1; + if (unlikely(ohx < -600 || ohx > 600)) + err7 = -1; + if (unlikely(ohy < -600 || ohy > 600)) + err7 = -1; + if (unlikely(ohz < -600 || ohz > 600)) + err7 = -1; + + pr_info("%s\n" + "Test1 - err = %d, id = %d\n" + "Test3 - err = %d\n" + "Test4 - err = %d, offset = %d,%d,%d\n" + "Test5 - err = %d, direction = %d\n" + "Test6 - err = %d, sensitivity = %d,%d\n" + "Test7 - err = %d, offset = %d,%d,%d\n" + "Test2 - err = %d\n", __func__, + err1, id, err3, err4, x, y1, y2, err5, dir, err6, sx, sy, + err7, ohx, ohy, ohz, err2); + + return sprintf(buf, + "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n", + err1, id, err3, err4, x, y1, y2, err5, dir, err6, sx, + sy, err7, ohx, ohy, ohz, err2); +} + +static ssize_t +geomagnetic_raw_self_test_noise_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct geomagnetic_data *data = i2c_get_clientdata(this_client); + int id, x, y1, y2, dir, hx0, hy0, hz0; + int err8; +#if CONFIG_MACH_KONA_SENSOR + pcbtest.power_on_and_device_check(&id); + pcbtest.initialization(); + pcbtest.offset_control_measurement_and_set_offset_register( + &x, &y1, &y2); +#else + if (!data->noise_test_init) { + pcbtest.power_on_and_device_check(&id); + pcbtest.initialization(); + pcbtest.offset_control_measurement_and_set_offset_register( + &x, &y1, &y2); + } +#endif + pcbtest.direction_measurement(&dir); + err8 = pcbtest.noise_level_check(&hx0, &hy0, &hz0); + if (err8 < 0) { + pr_err("%s: err8=%d\n", __func__, err8); + hx0 = 0; + hy0 = 0; + hz0 = 0; + } + usleep_range(3000, 3100); + data->noise_test_init = 1; + pr_debug("%s: %d, %d, %d\n", __func__, hx0, hy0, hz0); + return snprintf(buf, PAGE_SIZE, "%d,%d,%d\n", hx0, hy0, hz0); +} + +static DEVICE_ATTR(self_test, S_IRUSR, geomagnetic_raw_self_test_show, NULL); +static DEVICE_ATTR(self_test_noise, S_IRUSR, + geomagnetic_raw_self_test_noise_show, NULL); + +#endif + + +static DEVICE_ATTR(threshold, S_IRUGO | S_IWUSR, + geomagnetic_raw_threshold_show, + geomagnetic_raw_threshold_store); +static DEVICE_ATTR(distortion, S_IRUGO | S_IWUSR, + geomagnetic_raw_distortion_show, + geomagnetic_raw_distortion_store); +static DEVICE_ATTR(shape, S_IRUGO | S_IWUSR, geomagnetic_raw_shape_show, + geomagnetic_raw_shape_store); +static DEVICE_ATTR(offsets, S_IRUGO | S_IWUSR, geomagnetic_raw_offsets_show, + geomagnetic_raw_offsets_store); +#ifdef YAS_MAG_MANUAL_OFFSET +static DEVICE_ATTR(manual_offsets, S_IRUGO | S_IWUSR, + geomagnetic_raw_manual_offsets_show, + geomagnetic_raw_manual_offsets_store); +#endif +static DEVICE_ATTR(static_matrix, S_IRUGO | S_IWUSR, + geomagnetic_raw_static_matrix_show, + geomagnetic_raw_static_matrix_store); +static DEVICE_ATTR(dynamic_matrix, S_IRUGO | S_IWUSR, + geomagnetic_raw_dynamic_matrix_show, + geomagnetic_raw_dynamic_matrix_store); +static DEVICE_ATTR(ellipsoid_mode, S_IRUGO | S_IWUSR, + geomagnetic_raw_ellipsoid_mode_show, + geomagnetic_raw_ellipsoid_mode_store); +static struct attribute *geomagnetic_raw_attributes[] = { +#ifdef SYSFS_PCBTEST + &dev_attr_self_test.attr, + &dev_attr_self_test_noise.attr, +#endif + &dev_attr_threshold.attr, + &dev_attr_distortion.attr, + &dev_attr_shape.attr, + &dev_attr_offsets.attr, +#ifdef YAS_MAG_MANUAL_OFFSET + &dev_attr_manual_offsets.attr, +#endif + &dev_attr_static_matrix.attr, + &dev_attr_dynamic_matrix.attr, + &dev_attr_ellipsoid_mode.attr, + NULL +}; + +static struct attribute_group geomagnetic_raw_attribute_group = { + .attrs = geomagnetic_raw_attributes +}; +static struct device_attribute dev_attr_magnetic_sensor_selftest = + __ATTR(selftest, S_IRUSR | S_IRGRP, + geomagnetic_raw_self_test_show, NULL); + +static struct device_attribute dev_attr_magnetic_sensor_raw_data = + __ATTR(raw_data, S_IRUSR | S_IRGRP, + geomagnetic_raw_self_test_noise_show, NULL); + +static ssize_t magnetic_vendor_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%s\n", "YAMAHA"); +} +static struct device_attribute dev_attr_magnetic_sensor_vendor = + __ATTR(vendor, S_IRUSR | S_IRGRP, magnetic_vendor_show, NULL); + +static ssize_t magnetic_name_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct geomagnetic_data *data = i2c_get_clientdata(this_client); + int ret; + if (data->dev_id == YAS_YAS532_DEVICE_ID) + ret = sprintf(buf, "%s\n", "YAS532"); + else + ret = sprintf(buf, "%s\n", "YAS530C"); + return ret; +} +static struct device_attribute dev_attr_magnetic_sensor_name = + __ATTR(name, S_IRUSR | S_IRGRP, magnetic_name_show, NULL); + +static struct device_attribute *magnetic_sensor_attrs[] = { + &dev_attr_magnetic_sensor_selftest, + &dev_attr_magnetic_sensor_raw_data, + &dev_attr_magnetic_sensor_vendor, + &dev_attr_magnetic_sensor_name, + NULL, +}; + + +/* Interface Functions for Lower Layer */ +#ifdef YAS_MAG_MANUAL_OFFSET +void geomagnetic_manual_offset(void) +{ + struct geomagnetic_data *data = i2c_get_clientdata(this_client); + struct yas_vector offset; + if (data->mag_pdata->chg_status == CABLE_TYPE_NONE) { + offset = data->mag_pdata->full_offset; + if (hwdep_driver.set_manual_offset(&offset) == 0) + data->manual_offset = offset; + } else if (data->mag_pdata->chg_status == CABLE_TYPE_USB) { + offset = data->mag_pdata->usb_offset; + if (hwdep_driver.set_manual_offset(&offset) == 0) + data->manual_offset = offset; + } else if (data->mag_pdata->chg_status == CABLE_TYPE_AC) { + offset = data->mag_pdata->ta_offset; + if (hwdep_driver.set_manual_offset(&offset) == 0) + data->manual_offset = offset; + } else { + offset = data->mag_pdata->full_offset; + if (hwdep_driver.set_manual_offset(&offset) == 0) + data->manual_offset = offset; + } + data->mag_pdata->offset_enable = 0; +} +#endif + +static int geomagnetic_work(struct yas_mag_data *magdata) +{ + struct geomagnetic_data *data = i2c_get_clientdata(this_client); + uint32_t time_delay_ms = 100; + static int cnt; + int rt, i, accuracy; +#ifdef YAS_SENSOR_KERNEL_DEVFILE_INTERFACE + struct input_event ev[5]; + struct timeval tv; +#endif + +#ifdef YAS_MAG_MANUAL_OFFSET + if (data->mag_pdata) { + if (data->mag_pdata->offset_enable) + geomagnetic_manual_offset(); + } +#endif + + if (hwdep_driver.measure == NULL || hwdep_driver.get_offset == NULL) + return time_delay_ms; + + rt = hwdep_driver.measure(magdata, &time_delay_ms); + if (rt < 0) + YLOGE(("measure failed[%d]\n", rt)); + YLOGD(("xy1y2 [%d][%d][%d] raw[%d][%d][%d]\n", + magdata->xy1y2.v[0], magdata->xy1y2.v[1], magdata->xy1y2.v[2], + magdata->xyz.v[0], magdata->xyz.v[1], magdata->xyz.v[2])); + + if (rt >= 0) { + accuracy = atomic_read(&data->last_status); + + if ((rt & YAS_REPORT_OVERFLOW_OCCURED) + || (rt & YAS_REPORT_HARD_OFFSET_CHANGED) + || (rt & YAS_REPORT_CALIB_OFFSET_CHANGED)) { + static uint16_t count = 1; + int code = 0; + int value = 0; + + hwdep_driver.get_offset(&data->driver_offset); + if (rt & YAS_REPORT_OVERFLOW_OCCURED) { + atomic_set(&data->last_status, 0); + accuracy = 0; + } + + /* report event */ + code |= (rt & YAS_REPORT_OVERFLOW_OCCURED); + code |= (rt & YAS_REPORT_HARD_OFFSET_CHANGED); + code |= (rt & YAS_REPORT_CALIB_OFFSET_CHANGED); + value = (count++ << 16) | (code); +#ifdef YAS_SENSOR_KERNEL_DEVFILE_INTERFACE + make_event(ev, EV_ABS, ABS_RAW_REPORT, value); + sensor_event(&data->raw_devfile_list, ev, 1); +#else + input_report_abs(data->input_raw, ABS_RAW_REPORT, + value); + input_sync(data->input_raw); +#endif + } + + if (rt & YAS_REPORT_DATA) { +#ifdef YAS_SENSOR_KERNEL_DEVFILE_INTERFACE + get_time_stamp(&tv); + make_event_w_time(&ev[0], EV_ABS, ABS_X, + magdata->xyz.v[0], &tv); + make_event_w_time(&ev[1], EV_ABS, ABS_Y, + magdata->xyz.v[1], &tv); + make_event_w_time(&ev[2], EV_ABS, ABS_Z, + magdata->xyz.v[2], &tv); + make_event_w_time(&ev[3], EV_ABS, ABS_STATUS, accuracy, + &tv); + make_event_w_time(&ev[4], EV_SYN, 0, 0, &tv); + sensor_event(&data->devfile_list, ev, 5); +#else + /* report magnetic data in [nT] */ + input_report_abs(data->input_data, ABS_X, + magdata->xyz.v[0]); + input_report_abs(data->input_data, ABS_Y, + magdata->xyz.v[1]); + input_report_abs(data->input_data, ABS_Z, + magdata->xyz.v[2]); + if (atomic_read(&data->last_data[0]) == + magdata->xyz.v[0] + && atomic_read(&data->last_data[1]) == + magdata->xyz.v[1] + && atomic_read(&data->last_data[2]) == + magdata->xyz.v[2]) { + input_report_abs(data->input_data, ABS_RUDDER, + cnt++); + } + input_report_abs(data->input_data, ABS_STATUS, + accuracy); + input_sync(data->input_data); +#endif + + for (i = 0; i < 3; i++) + atomic_set(&data->last_data[i], + magdata->xyz.v[i]); + } + + if (rt & YAS_REPORT_CALIB) { +#ifdef YAS_SENSOR_KERNEL_DEVFILE_INTERFACE + get_time_stamp(&tv); + make_event_w_time(&ev[0], EV_ABS, ABS_X, + magdata->raw.v[0], &tv); + make_event_w_time(&ev[1], EV_ABS, ABS_Y, + magdata->raw.v[1], &tv); + make_event_w_time(&ev[2], EV_ABS, ABS_Z, + magdata->raw.v[2], &tv); + make_event_w_time(&ev[3], EV_SYN, 0, 0, &tv); + sensor_event(&data->raw_devfile_list, ev, 4); +#else + /* report raw magnetic data */ + input_report_abs(data->input_raw, ABS_X, + magdata->raw.v[0]); + input_report_abs(data->input_raw, ABS_Y, + magdata->raw.v[1]); + input_report_abs(data->input_raw, ABS_Z, + magdata->raw.v[2]); + input_sync(data->input_raw); +#endif + } + } else { + time_delay_ms = 100; + } + + return time_delay_ms; + +} + +static void geomagnetic_input_work_func(struct work_struct *work) +{ + struct geomagnetic_data *data = + container_of((struct delayed_work *)work, + struct geomagnetic_data, work); + uint32_t time_delay_ms; + struct yas_mag_data magdata; + + time_delay_ms = geomagnetic_work(&magdata); + + if (time_delay_ms > 60) + schedule_delayed_work(&data->work, + msecs_to_jiffies(time_delay_ms)); + else { + if (time_delay_ms > 0) + usleep_range(time_delay_ms * 1000, + time_delay_ms * 1100); + schedule_delayed_work(&data->work, 0); + } +} + +static int geomagnetic_suspend(struct i2c_client *client, pm_message_t mesg) +{ + struct geomagnetic_data *data = i2c_get_clientdata(client); + + if (atomic_read(&data->enable)) + cancel_delayed_work_sync(&data->work); +#if DEBUG + data->suspend = 1; +#endif + + return 0; +} + +static int geomagnetic_resume(struct i2c_client *client) +{ + struct geomagnetic_data *data = i2c_get_clientdata(client); + + if (atomic_read(&data->enable)) + schedule_delayed_work(&data->work, 0); +#if DEBUG + data->suspend = 0; +#endif + + return 0; +} + +static int +geomagnetic_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct geomagnetic_data *data = NULL; + struct input_dev *input_data = NULL, *input_raw = NULL; + int rt, sysfs_created = 0, sysfs_raw_created = 0; + int data_registered = 0, raw_registered = 0, i; + struct yas_mag_filter filter; + struct mag_platform_data *pdata; + + pr_err("%s, is called\n", __func__); + + i2c_set_clientdata(client, NULL); + data = kzalloc(sizeof(struct geomagnetic_data), GFP_KERNEL); + if (data == NULL) { + rt = -ENOMEM; + goto err; + } + + pdata = (struct mag_platform_data *) client->dev.platform_data; + data->mag_pdata = pdata; + + if (pdata) { + if (pdata->power_on) + pdata->power_on(true); + } + data->threshold = YAS_DEFAULT_MAGCALIB_THRESHOLD; + for (i = 0; i < 3; i++) + data->distortion[i] = YAS_DEFAULT_MAGCALIB_DISTORTION; + data->shape = 0; + atomic_set(&data->enable, 0); + for (i = 0; i < 3; i++) + atomic_set(&data->last_data[i], 0); + atomic_set(&data->last_status, 0); + INIT_DELAYED_WORK(&data->work, geomagnetic_input_work_func); + sema_init(&data->driver_lock, 1); + sema_init(&data->multi_lock, 1); + + input_data = input_allocate_device(); + if (input_data == NULL) { + rt = -ENOMEM; + YLOGE(("mag Failed to allocate input_data device\n")); + goto err; + } + + input_data->name = GEOMAGNETIC_INPUT_NAME; + input_data->id.bustype = BUS_I2C; + set_bit(EV_ABS, input_data->evbit); + input_set_abs_params(input_data, ABS_X, 0x80000000, 0x7fffffff, 0, 0); + input_set_abs_params(input_data, ABS_Y, 0x80000000, 0x7fffffff, 0, 0); + input_set_abs_params(input_data, ABS_Z, 0x80000000, 0x7fffffff, 0, 0); + input_set_abs_params(input_data, ABS_RUDDER, 0x80000000, 0x7fffffff, 0, + 0); + input_set_abs_params(input_data, ABS_STATUS, 0, 3, 0, 0); + input_set_abs_params(input_data, ABS_WAKE, 0x80000000, 0x7fffffff, 0, + 0); + input_data->dev.parent = &client->dev; + + rt = input_register_device(input_data); + if (rt) { + YLOGE(("mag: Unable to reg input_data %s\n", input_data->name)); + goto err; + } + data_registered = 1; + + rt = sysfs_create_group(&input_data->dev.kobj, + &geomagnetic_attribute_group); + if (rt) { + YLOGE(("mag sysfs_create failed[%s]\n", input_data->name)); + goto err; + } + sysfs_created = 1; + + input_raw = input_allocate_device(); + if (input_raw == NULL) { + rt = -ENOMEM; + YLOGE(("mag Failed to alloc input_raw dev\n")); + goto err; + } + + input_raw->name = GEOMAGNETIC_INPUT_RAW_NAME; + input_raw->id.bustype = BUS_I2C; + set_bit(EV_ABS, input_raw->evbit); + input_set_abs_params(input_raw, ABS_X, 0x80000000, 0x7fffffff, 0, 0); + input_set_abs_params(input_raw, ABS_Y, 0x80000000, 0x7fffffff, 0, 0); + input_set_abs_params(input_raw, ABS_Z, 0x80000000, 0x7fffffff, 0, 0); + input_set_abs_params(input_raw, ABS_RAW_DISTORTION, 0, 0x7fffffff, 0, + 0); + input_set_abs_params(input_raw, ABS_RAW_THRESHOLD, 0, 2, 0, 0); + input_set_abs_params(input_raw, ABS_RAW_SHAPE, 0, 1, 0, 0); + input_set_abs_params(input_raw, ABS_RAW_MODE, 0, 1, 0, 0); + input_set_abs_params(input_raw, ABS_RAW_REPORT, 0x80000000, 0x7fffffff, + 0, 0); + input_raw->dev.parent = &client->dev; + + rt = input_register_device(input_raw); + if (rt) { + YLOGE(("mag: Unable to reg input_raw dev\n")); + goto err; + } + raw_registered = 1; + + rt = sysfs_create_group(&input_raw->dev.kobj, + &geomagnetic_raw_attribute_group); + if (rt) { + YLOGE(("geomagnetic_probe: sysfs_create_group failed[%s]\n", + input_data->name)); + goto err; + } + sysfs_raw_created = 1; + + this_client = client; + data->input_raw = input_raw; + data->input_data = input_data; + input_set_drvdata(input_data, data); + input_set_drvdata(input_raw, data); + i2c_set_clientdata(client, data); + rt = yas_mag_driver_init(&hwdep_driver); + if (rt < 0) { + YLOGE(("yas_mag_driver_init failed[%d]\n", rt)); + goto err; + } + if (hwdep_driver.init != NULL) { + rt = hwdep_driver.init(); + if (rt < 0) { + YLOGE(("hwdep_driver.init() failed[%d]\n", rt)); + goto err; + } + } + if (hwdep_driver.set_position != NULL) { + if (pdata) { + if (pdata->orientation) { + pr_info("%s: set from board file.\n", __func__); + if (hwdep_driver. + set_position(pdata->orientation + - YAS532_POSITION_OFFSET) < 0) { + pr_err("set_position failed %d\n", rt); + goto err; + } + } else { + pr_info("%s: set from defconfig.\n", __func__); + if (hwdep_driver. + set_position( + CONFIG_INPUT_YAS_MAGNETOMETER_POSITION) + < 0) { + pr_err("set_position failed %d\n", rt); + goto err; + } + } + } else { + if (hwdep_driver. + set_position( + CONFIG_INPUT_YAS_MAGNETOMETER_POSITION) + < 0) { + pr_err("set_position() failed[%d]\n", rt); + goto err; + } + } + pr_info("%s: yas magnetic position is %d\n", __func__, + hwdep_driver.get_position()); + } + if (hwdep_driver.get_offset != NULL) { + if (hwdep_driver.get_offset(&data->driver_offset) < 0) { + YLOGE(("hwdep_driver get_driver_state failed\n")); + goto err; + } + } + if (hwdep_driver.get_delay != NULL) + data->delay = hwdep_driver.get_delay(); + + if (hwdep_driver.set_filter_enable != NULL) { + /* default to enable */ + if (hwdep_driver.set_filter_enable(1) == 0) + data->filter_enable = 1; + } + if (hwdep_driver.get_filter != NULL) { + if (hwdep_driver.get_filter(&filter) < 0) { + YLOGE(("hwdep_driver get_filter failed\n")); + goto err; + } + data->filter_len = filter.len; + for (i = 0; i < 3; i++) + data->filter_noise[i] = filter.noise[i]; + data->filter_threshold = filter.threshold; + } + if (hwdep_driver.get_static_matrix != NULL) + hwdep_driver.get_static_matrix(&data->static_matrix); + if (hwdep_driver.get_dynamic_matrix != NULL) + hwdep_driver.get_dynamic_matrix(&data->dynamic_matrix); +#ifdef SYSFS_PCBTEST + rt = yas_pcb_test_init(&pcbtest); + if (rt < 0) { + YLOGE(("yas_pcb_test_init failed[%d]\n", rt)); + goto err; + } +#endif +#ifdef YAS_SENSOR_KERNEL_DEVFILE_INTERFACE + INIT_LIST_HEAD(&data->devfile_list); + INIT_LIST_HEAD(&data->raw_devfile_list); + if (misc_register(&sensor_devfile) < 0) + goto err; + if (misc_register(&sensor_raw_devfile) < 0) { + misc_deregister(&sensor_devfile); + goto err; + } +#endif + + rt = + geomagnetic_i2c_read(YAS_REGADDR_DEVICE_ID, &data->dev_id, 1); + if (rt) { + pr_err("%s: cound not read device id(%d).\n", + __func__, rt); + goto err; + } else + pr_info("%s: yamaha magnetic sensor id = %x\n", + __func__, data->dev_id); + + rt = sensors_register(data->magnetic_sensor_device, + NULL, magnetic_sensor_attrs, + "magnetic_sensor"); + if (rt) { + pr_err("%s: cound not register magnetic sensor device(%d).\n", + __func__, rt); + goto out_sensor_register_failed; + } + + return 0; + +out_sensor_register_failed: +err: + if (data != NULL) { + if (input_raw != NULL) { + if (sysfs_raw_created) + sysfs_remove_group(&input_raw->dev.kobj, + &geomagnetic_raw_attribute_group); + + if (raw_registered) + input_unregister_device(input_raw); + else + input_free_device(input_raw); + } + if (input_data != NULL) { + if (sysfs_created) + sysfs_remove_group(&input_data->dev.kobj, + &geomagnetic_attribute_group); + if (data_registered) + input_unregister_device(input_data); + else + input_free_device(input_data); + } + if (pdata) { + if (pdata->power_on) + pdata->power_on(false); + } + kfree(data); + } + + return rt; +} + +static int geomagnetic_remove(struct i2c_client *client) +{ + struct geomagnetic_data *data = i2c_get_clientdata(client); + +#ifdef YAS_SENSOR_KERNEL_DEVFILE_INTERFACE + misc_deregister(&sensor_devfile); + misc_deregister(&sensor_raw_devfile); +#endif + if (data != NULL) { + geomagnetic_disable(data); + if (hwdep_driver.term != NULL) + hwdep_driver.term(); + + input_unregister_device(data->input_raw); + sysfs_remove_group(&data->input_data->dev.kobj, + &geomagnetic_attribute_group); + sysfs_remove_group(&data->input_raw->dev.kobj, + &geomagnetic_raw_attribute_group); + input_unregister_device(data->input_data); + kfree(data); + } + + return 0; +} + +static void geomagnetic_shutdown(struct i2c_client *client) +{ + struct geomagnetic_data *data = i2c_get_clientdata(client); + + if (data != NULL) { + geomagnetic_disable(data); + if (data->mag_pdata) { + if (data->mag_pdata->power_on) + data->mag_pdata->power_on(false); + } + } +} + +/* I2C Device Driver */ +static struct i2c_device_id geomagnetic_idtable[] = { + {GEOMAGNETIC_I2C_DEVICE_NAME, 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, geomagnetic_idtable); + +static struct i2c_driver geomagnetic_i2c_driver = { + .driver = { + .name = GEOMAGNETIC_I2C_DEVICE_NAME, + .owner = THIS_MODULE, + }, + + .id_table = geomagnetic_idtable, + .probe = geomagnetic_probe, + .remove = geomagnetic_remove, + .suspend = geomagnetic_suspend, + .resume = geomagnetic_resume, + .shutdown = geomagnetic_shutdown, +}; + +static int __init geomagnetic_init(void) +{ + return i2c_add_driver(&geomagnetic_i2c_driver); +} + +static void __exit geomagnetic_term(void) +{ + i2c_del_driver(&geomagnetic_i2c_driver); +} + +#ifdef GEOMAGNETIC_PLATFORM_API +static int geomagnetic_api_enable(int enable) +{ + struct geomagnetic_data *data = i2c_get_clientdata(this_client); + int rt; + + if (geomagnetic_multi_lock() < 0) + return -1; + enable = !!enable; + rt = hwdep_driver.set_enable(enable); + if (rt == 0) { + atomic_set(&data->enable, enable); + if (enable) + rt = hwdep_driver.set_delay(20); + } + + geomagnetic_multi_unlock(); + + return rt; +} + +int geomagnetic_api_resume(void) +{ + return geomagnetic_api_enable(1); +} +EXPORT_SYMBOL(geomagnetic_api_resume); + +int geomagnetic_api_suspend(void) +{ + return geomagnetic_api_enable(0); +} +EXPORT_SYMBOL(geomagnetic_api_suspend); + +int geomagnetic_api_read(int *xyz, int *raw, int *xy1y2, int *accuracy) +{ + struct geomagnetic_data *data = i2c_get_clientdata(this_client); + struct yas_mag_data magdata; + int i; + + geomagnetic_work(&magdata); + if (xyz != NULL) { + for (i = 0; i < 3; i++) + xyz[i] = magdata.xyz.v[i]; + } + if (raw != NULL) { + for (i = 0; i < 3; i++) + raw[i] = magdata.raw.v[i]; + } + if (xy1y2 != NULL) { + for (i = 0; i < 3; i++) + xy1y2[i] = magdata.xy1y2.v[i]; + } + if (accuracy != NULL) + *accuracy = atomic_read(&data->last_status); + + return 0; +} +EXPORT_SYMBOL(geomagnetic_api_read); +#endif + +module_init(geomagnetic_init); +module_exit(geomagnetic_term); + +MODULE_AUTHOR("Yamaha Corporation"); +#if YAS_MAG_DRIVER == YAS_MAG_DRIVER_YAS529 +MODULE_DESCRIPTION("YAS529 Geomagnetic Sensor Driver"); +#elif YAS_MAG_DRIVER == YAS_MAG_DRIVER_YAS530 +MODULE_DESCRIPTION("YAS530 Geomagnetic Sensor Driver"); +#elif CONFIG_SENSORS_YAS532 +MODULE_DESCRIPTION("YAS532 Geomagnetic Sensor Driver"); +#endif +MODULE_LICENSE("GPL"); +MODULE_VERSION("4.4.702a"); diff --git a/drivers/sensor/yas_ori_kernel_driver.c b/drivers/sensor/yas_ori_kernel_driver.c new file mode 100644 index 0000000..20003aa --- /dev/null +++ b/drivers/sensor/yas_ori_kernel_driver.c @@ -0,0 +1,695 @@ +/* + * Copyright (c) 2010-2011 Yamaha Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/input.h> +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/platform_device.h> +#include <linux/poll.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <linux/uaccess.h> +#include <linux/version.h> +#include <linux/workqueue.h> + +#define __LINUX_KERNEL_DRIVER__ +#include <linux/sensor/yas.h> + +#define SENSOR_NAME "orientation" +#define SENSOR_DEFAULT_DELAY (200) /* 200 ms */ +#define SENSOR_MAX_DELAY (2000) /* 2000 ms */ +#define ABS_STATUS (ABS_BRAKE) +#define ABS_WAKE (ABS_MISC) +#define ABS_CONTROL_REPORT (ABS_THROTTLE) + +static int suspend(void); +static int resume(void); + +struct sensor_data { + struct mutex mutex; + int enabled; + int delay; +#if DEBUG + int suspend; +#endif +#ifdef YAS_SENSOR_KERNEL_DEVFILE_INTERFACE + struct list_head devfile_list; +#endif +}; + +static struct platform_device *sensor_pdev; +static struct input_dev *this_data; + +#ifdef YAS_SENSOR_KERNEL_DEVFILE_INTERFACE +#include <linux/miscdevice.h> +#define MAX_COUNT (64) + +struct sensor_device { + struct list_head list; + struct mutex lock; + wait_queue_head_t waitq; + struct input_event events[MAX_COUNT]; + int head, num_event; +}; + +static void get_time_stamp(struct timeval *tv) +{ + struct timespec ts; + ktime_get_ts(&ts); + tv->tv_sec = ts.tv_sec; + tv->tv_usec = ts.tv_nsec / 1000; +} + +static void make_event(struct input_event *ev, int type, int code, int value) +{ + struct timeval tv; + get_time_stamp(&tv); + ev->type = type; + ev->code = code; + ev->value = value; + ev->time = tv; +} + +static void +make_event_w_time(struct input_event *ev, int type, int code, int value, + struct timeval *tv) +{ + ev->type = type; + ev->code = code; + ev->value = value; + ev->time = *tv; +} + +static void sensor_enq(struct sensor_device *kdev, struct input_event *ev) +{ + int idx; + + idx = kdev->head + kdev->num_event; + if (MAX_COUNT <= idx) + idx -= MAX_COUNT; + kdev->events[idx] = *ev; + kdev->num_event++; + if (MAX_COUNT < kdev->num_event) { + kdev->num_event = MAX_COUNT; + kdev->head++; + if (MAX_COUNT <= kdev->head) + kdev->head -= MAX_COUNT; + } +} + +static int sensor_deq(struct sensor_device *kdev, struct input_event *ev) +{ + if (kdev->num_event == 0) + return 0; + + *ev = kdev->events[kdev->head]; + kdev->num_event--; + kdev->head++; + if (MAX_COUNT <= kdev->head) + kdev->head -= MAX_COUNT; + return 1; +} + +static void +sensor_event(struct list_head *devlist, struct input_event *ev, int num) +{ + struct sensor_device *kdev; + int i; + + list_for_each_entry(kdev, devlist, list) { + mutex_lock(&kdev->lock); + for (i = 0; i < num; i++) + sensor_enq(kdev, &ev[i]); + + mutex_unlock(&kdev->lock); + wake_up_interruptible(&kdev->waitq); + } +} + +static ssize_t +sensor_write(struct file *f, const char __user *buf, size_t count, + loff_t *pos) +{ + struct sensor_data *data = input_get_drvdata(this_data); + struct sensor_device *kdev; + struct input_event ev[MAX_COUNT]; + int num, i; + + if (count < sizeof(struct input_event)) + return -EINVAL; + + num = count / sizeof(struct input_event); + if (MAX_COUNT < num) + num = MAX_COUNT; + + if (copy_from_user(ev, buf, num * sizeof(struct input_event))) + return -EFAULT; + + list_for_each_entry(kdev, &data->devfile_list, list) { + mutex_lock(&kdev->lock); + for (i = 0; i < num; i++) + sensor_enq(kdev, &ev[i]); + mutex_unlock(&kdev->lock); + wake_up_interruptible(&kdev->waitq); + } + + return count; +} + +static ssize_t +sensor_read(struct file *f, char __user *buf, size_t count, loff_t *pos) +{ + struct sensor_device *kdev = f->private_data; + int rt, num; + struct input_event ev[MAX_COUNT]; + + if (count < sizeof(struct input_event)) + return -EINVAL; + + rt = wait_event_interruptible(kdev->waitq, kdev->num_event != 0); + if (rt) + return rt; + + mutex_lock(&kdev->lock); + for (num = 0; num < count / sizeof(struct input_event); num++) { + if (!sensor_deq(kdev, &ev[num])) + break; + } + mutex_unlock(&kdev->lock); + + if (copy_to_user(buf, ev, num * sizeof(struct input_event))) + return -EFAULT; + + return num * sizeof(struct input_event); +} + +static unsigned int sensor_poll(struct file *f, struct poll_table_struct *wait) +{ + struct sensor_device *kdev = f->private_data; + + poll_wait(f, &kdev->waitq, wait); + if (kdev->num_event != 0) + return POLLIN | POLLRDNORM; + + return 0; +} + +static int sensor_open(struct inode *inode, struct file *f) +{ + struct sensor_data *data = input_get_drvdata(this_data); + struct sensor_device *kdev; + + kdev = kzalloc(sizeof(struct sensor_device), GFP_KERNEL); + if (!kdev) + return -ENOMEM; + + mutex_init(&kdev->lock); + init_waitqueue_head(&kdev->waitq); + f->private_data = kdev; + kdev->head = 0; + kdev->num_event = 0; + list_add(&kdev->list, &data->devfile_list); + + return 0; +} + +static int sensor_release(struct inode *inode, struct file *f) +{ + struct sensor_device *kdev = f->private_data; + + list_del(&kdev->list); + kfree(kdev); + + return 0; +} + +const struct file_operations sensor_fops = { + .owner = THIS_MODULE, + .open = sensor_open, + .release = sensor_release, + .write = sensor_write, + .read = sensor_read, + .poll = sensor_poll, +}; + +static struct miscdevice sensor_devfile = { + .name = SENSOR_NAME, + .fops = &sensor_fops, + .minor = MISC_DYNAMIC_MINOR, +}; + +#endif + +static int suspend(void) +{ + /* implement suspend of the sensor */ + YLOGD(("%s: suspend\n", SENSOR_NAME)); + + return 0; +} + +static int resume(void) +{ + /* implement resume of the sensor */ + YLOGD(("%s: resume\n", SENSOR_NAME)); + + return 0; +} + +/* Sysfs interface */ +static ssize_t +sensor_delay_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct input_dev *input_data = to_input_dev(dev); + struct sensor_data *data = input_get_drvdata(input_data); + int delay; + + mutex_lock(&data->mutex); + + delay = data->delay; + + mutex_unlock(&data->mutex); + + return sprintf(buf, "%d\n", delay); +} + +static ssize_t +sensor_delay_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct input_dev *input_data = to_input_dev(dev); + struct sensor_data *data = input_get_drvdata(input_data); + long value; + int error; + + error = strict_strtoul(buf, 10, &value); + if (unlikely(error)) + return error; + +#ifdef YAS_SENSOR_KERNEL_DEVFILE_INTERFACE + struct input_event ev[1]; +#endif + + if (value < 0) + return count; + + if (SENSOR_MAX_DELAY < value) + value = SENSOR_MAX_DELAY; + + mutex_lock(&data->mutex); + +#ifdef YAS_SENSOR_KERNEL_DEVFILE_INTERFACE + make_event(ev, EV_ABS, ABS_CONTROL_REPORT, + (data->enabled << 16) | value); + sensor_event(&data->devfile_list, ev, 1); +#endif + data->delay = value; +#ifndef YAS_SENSOR_KERNEL_DEVFILE_INTERFACE + input_report_abs(input_data, ABS_CONTROL_REPORT, + (data->enabled << 16) | value); +#endif + + mutex_unlock(&data->mutex); + + return count; +} + +static ssize_t +sensor_enable_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct input_dev *input_data = to_input_dev(dev); + struct sensor_data *data = input_get_drvdata(input_data); + int enabled; + + mutex_lock(&data->mutex); + + enabled = data->enabled; + + mutex_unlock(&data->mutex); + + return sprintf(buf, "%d\n", enabled); +} + +static ssize_t +sensor_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct input_dev *input_data = to_input_dev(dev); + struct sensor_data *data = input_get_drvdata(input_data); + unsigned long value; + int error; + + error = strict_strtoul(buf, 10, &value); + if (unlikely(error)) + return error; + +#ifdef YAS_SENSOR_KERNEL_DEVFILE_INTERFACE + struct input_event ev[1]; +#endif + + value = !(!value); + + mutex_lock(&data->mutex); + +#ifdef YAS_SENSOR_KERNEL_DEVFILE_INTERFACE + make_event(ev, EV_ABS, ABS_CONTROL_REPORT, (value << 16) | data->delay); + sensor_event(&data->devfile_list, ev, 1); +#else + input_report_abs(input_data, ABS_CONTROL_REPORT, + (value << 16) | data->delay); + input_sync(input_data); +#endif + + if (data->enabled && !value) + suspend(); + if (!data->enabled && value) + resume(); + data->enabled = value; + + mutex_unlock(&data->mutex); + + return count; +} + +static ssize_t +sensor_wake_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct input_dev *input_data = to_input_dev(dev); + static int cnt = 1; +#ifdef YAS_SENSOR_KERNEL_DEVFILE_INTERFACE + struct sensor_data *data = input_get_drvdata(input_data); + struct input_event ev[2]; + struct timeval tv; + get_time_stamp(&tv); + make_event_w_time(&ev[0], EV_ABS, ABS_WAKE, cnt++, &tv); + make_event_w_time(&ev[1], EV_SYN, 0, 0, &tv); + sensor_event(&data->devfile_list, ev, 2); +#else + input_report_abs(input_data, ABS_WAKE, cnt++); + input_sync(input_data); +#endif + + return count; +} + +#if DEBUG + +static int sensor_suspend(struct platform_device *pdev, pm_message_t state); +static int sensor_resume(struct platform_device *pdev); + +static ssize_t +sensor_debug_suspend_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct input_dev *input = to_input_dev(dev); + struct sensor_data *data = input_get_drvdata(input); + + return sprintf(buf, "%d\n", data->suspend); +} + +static ssize_t +sensor_debug_suspend_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long suspend; + int error; + + error = strict_strtoul(buf, 10, &suspend); + if (unlikely(error)) + return error; + + + if (suspend) { + pm_message_t msg; + memset(&msg, 0, sizeof(msg)); + sensor_suspend(sensor_pdev, msg); + } else + sensor_resume(sensor_pdev); + + return count; +} + +#endif /* DEBUG */ + +static ssize_t +sensor_data_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct input_dev *input_data = to_input_dev(dev); + int x, y, z; + + x = input_abs_get_val(input_data, ABS_X); + y = input_abs_get_val(input_data, ABS_Y); + z = input_abs_get_val(input_data, ABS_Z); + + return sprintf(buf, "%d %d %d\n", x, y, z); +} + +static ssize_t +sensor_status_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct input_dev *input_data = to_input_dev(dev); + int status; + + status = input_abs_get_val(input_data, ABS_STATUS); + + return sprintf(buf, "%d\n", status); +} + +static DEVICE_ATTR(delay, S_IRUGO | S_IWUSR | S_IWGRP, + sensor_delay_show, sensor_delay_store); +static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR | S_IWGRP, + sensor_enable_show, sensor_enable_store); +static DEVICE_ATTR(wake, S_IWUSR | S_IWGRP, NULL, sensor_wake_store); +static DEVICE_ATTR(data, S_IRUGO, sensor_data_show, NULL); +static DEVICE_ATTR(status, S_IRUGO, sensor_status_show, NULL); + +#if DEBUG +static DEVICE_ATTR(debug_suspend, S_IRUGO | S_IWUSR, + sensor_debug_suspend_show, sensor_debug_suspend_store); +#endif /* DEBUG */ + +static struct attribute *sensor_attributes[] = { + &dev_attr_delay.attr, + &dev_attr_enable.attr, + &dev_attr_wake.attr, + &dev_attr_data.attr, + &dev_attr_status.attr, +#if DEBUG + &dev_attr_debug_suspend.attr, +#endif /* DEBUG */ + NULL +}; + +static struct attribute_group sensor_attribute_group = { + .attrs = sensor_attributes +}; + +static int sensor_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct sensor_data *data = input_get_drvdata(this_data); + int rt = 0; + + mutex_lock(&data->mutex); + + if (data->enabled) { +#ifdef YAS_SENSOR_KERNEL_DEVFILE_INTERFACE + struct input_event ev[1]; + make_event(ev, EV_ABS, ABS_CONTROL_REPORT, + (0 << 16) | data->delay); + sensor_event(&data->devfile_list, ev, 1); +#else + input_report_abs(this_data, ABS_CONTROL_REPORT, + (0 << 16) | data->delay); +#endif + rt = suspend(); + } + + mutex_unlock(&data->mutex); + + return rt; +} + +static int sensor_resume(struct platform_device *pdev) +{ + struct sensor_data *data = input_get_drvdata(this_data); + int rt = 0; + + mutex_lock(&data->mutex); + + if (data->enabled) { +#ifdef YAS_SENSOR_KERNEL_DEVFILE_INTERFACE + struct input_event ev[1]; + make_event(ev, EV_ABS, ABS_CONTROL_REPORT, + (1 << 16) | data->delay); + sensor_event(&data->devfile_list, ev, 1); +#endif + rt = resume(); +#ifndef YAS_SENSOR_KERNEL_DEVFILE_INTERFACE + input_report_abs(this_data, ABS_CONTROL_REPORT, + (1 << 16) | data->delay); +#endif + } + + mutex_unlock(&data->mutex); + + return rt; +} + +static int sensor_probe(struct platform_device *pdev) +{ + struct sensor_data *data = NULL; + struct input_dev *input_data = NULL; + int input_registered = 0, sysfs_created = 0; + int rt; + + data = kzalloc(sizeof(struct sensor_data), GFP_KERNEL); + if (!data) { + rt = -ENOMEM; + goto err; + } + data->enabled = 0; + data->delay = SENSOR_DEFAULT_DELAY; + + input_data = input_allocate_device(); + if (!input_data) { + rt = -ENOMEM; + YLOGE(("sensor_probe: Failed to allocate input_data device\n")); + goto err; + } + + set_bit(EV_ABS, input_data->evbit); + input_set_abs_params(input_data, ABS_X, 0x80000000, 0x7fffffff, 0, 0); + input_set_abs_params(input_data, ABS_Y, 0x80000000, 0x7fffffff, 0, 0); + input_set_abs_params(input_data, ABS_Z, 0x80000000, 0x7fffffff, 0, 0); + input_set_abs_params(input_data, ABS_RUDDER, 0x80000000, 0x7fffffff, 0, + 0); + input_set_abs_params(input_data, ABS_STATUS, 0, 3, 0, 0); + input_set_abs_params(input_data, ABS_WAKE, 0x80000000, 0x7fffffff, 0, + 0); + input_set_abs_params(input_data, ABS_CONTROL_REPORT, 0x80000000, + 0x7fffffff, 0, 0); + input_data->name = SENSOR_NAME; + + rt = input_register_device(input_data); + if (rt) { + YLOGE(("ori Unable to reg input_data %s\n", input_data->name)); + goto err; + } + input_set_drvdata(input_data, data); + input_registered = 1; + + rt = sysfs_create_group(&input_data->dev.kobj, &sensor_attribute_group); + if (rt) { + YLOGE(("sensor_probe: sysfs_create_group failed[%s]\n", + input_data->name)); + goto err; + } + sysfs_created = 1; + mutex_init(&data->mutex); + this_data = input_data; + +#ifdef YAS_SENSOR_KERNEL_DEVFILE_INTERFACE + INIT_LIST_HEAD(&data->devfile_list); + if (misc_register(&sensor_devfile) < 0) + goto err; +#endif + + return 0; + +err: + if (data != NULL) { + if (input_data != NULL) { + if (sysfs_created) + sysfs_remove_group(&input_data->dev.kobj, + &sensor_attribute_group); + if (input_registered) + input_unregister_device(input_data); + else + input_free_device(input_data); + input_data = NULL; + } + kfree(data); + } + + return rt; +} + +static int sensor_remove(struct platform_device *pdev) +{ + struct sensor_data *data; + +#ifdef YAS_SENSOR_KERNEL_DEVFILE_INTERFACE + misc_deregister(&sensor_devfile); +#endif + if (this_data != NULL) { + data = input_get_drvdata(this_data); + sysfs_remove_group(&this_data->dev.kobj, + &sensor_attribute_group); + input_unregister_device(this_data); + if (data != NULL) + kfree(data); + } + + return 0; +} + +/* + * Module init and exit + */ +static struct platform_driver sensor_driver = { + .probe = sensor_probe, + .remove = sensor_remove, + .suspend = sensor_suspend, + .resume = sensor_resume, + .driver = { + .name = SENSOR_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init sensor_init(void) +{ + sensor_pdev = platform_device_register_simple(SENSOR_NAME, 0, NULL, 0); + if (IS_ERR(sensor_pdev)) + return -1; + return platform_driver_register(&sensor_driver); +} + +module_init(sensor_init); + +static void __exit sensor_exit(void) +{ + platform_driver_unregister(&sensor_driver); + platform_device_unregister(sensor_pdev); +} + +module_exit(sensor_exit); + +MODULE_AUTHOR("Yamaha Corporation"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("4.4.702a"); diff --git a/drivers/sensor/yas_pcb_test.c b/drivers/sensor/yas_pcb_test.c new file mode 100644 index 0000000..686c8c6 --- /dev/null +++ b/drivers/sensor/yas_pcb_test.c @@ -0,0 +1,1282 @@ +/* + * Copyright (c) 2010-2011 Yamaha Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +/* + * File yas_pcb_test.c + * Brief pcb test program for yas530/yas532 + * Date 2013/1/22 + * Revision 1.4.3 + */ +#include "yas_pcb_test.h" + +/* define */ +/* reg num */ +#define YAS530_CAL_REG_NUM (16) +#define YAS532_CAL_REG_NUM (14) +#define YAS_PCB_MEASURE_DATA_REG_NUM (8) + +/* default value */ +#define YAS_PCB_TEST1_DEFAULT (0x00) +#define YAS_PCB_TEST2_DEFAULT (0x00) +#define YAS_PCB_INTERVAL_DEFAULT (0x00) +#define YAS_PCB_CONFIG_DEFAULT (0x01) /* INTON = 1 */ +#define YAS_PCB_COIL_DEFAULT (0x00) + +/* measure command */ +#define YAS_PCB_MEASURE_COMMAND_START (0x01) +#define YAS_PCB_MEASURE_COMMAND_LDTC (0x02) +#define YAS_PCB_MEASURE_COMMAND_FORS (0x04) + +#define YAS_PCB_MEASURE_BUSY (0x80) + +#define YAS_PCB_MEASURE_WAIT_TIME (2) /* ms */ +#define YAS_PCB_HARD_OFFSET_CORRECT (16) +#define YAS_PCB_COIL_INIT_CALC_NUM (5) + +#define YAS_PCB_HARD_OFFSET_MASK (0x3F) + +#define YAS_PCB_INT_CHECK (1) +#define YAS_PCB_INT_NOTCHECK (0) +#define YAS_PCB_INT_HIGH (1) +#define YAS_PCB_INT_LOW (0) + +#define YAS_PCB_ACC_Z (9806550L) /* m/s2 */ + +#define YAS530_DEVICE_ID (0x01) /* MS-3E */ +#define YAS532_DEVICE_ID (0x02) /* MS-3R */ + +#define YAS530_VERSION_A (0) /* MS-3E Aver */ +#define YAS530_VERSION_B (1) /* MS-3E Bver */ +/*#define YAS530_VERSION_AB (0)*/ /* MS-3R ABver */ +#define YAS532_VERSION_AC (1) /* MS-3R ACver */ + +#define YAS530_COEF_VERSION_A (380) +#define YAS530_COEF_VERSION_B (550) +/*#define YAS532_COEF_VERSION_AB (1800)*/ +/*#define YAS532_COEF_VERSION_AC (900)*/ +#define YAS532_COEFX_VERSION_AC (850) +#define YAS532_COEFY1_VERSION_AC (750) +#define YAS532_COEFY2_VERSION_AC (750) + +#define YAS530_RAWDATA_CENTER (2048) +#define YAS530_RAWDATA_OVERFLOW (4095) +#define YAS532_RAWDATA_CENTER (4096) +#define YAS532_RAWDATA_OVERFLOW (8190) + +#define YAS_PCB_DIR_DIVIDER (400) + +#define YAS_PCB_TEST1 (0) +#define YAS_PCB_TEST3 (1) +#define YAS_PCB_TEST4 (2) +#define YAS_PCB_TEST5 (3) +#define YAS_PCB_TEST6 (4) +#define YAS_PCB_TEST7 (5) +#define YAS_PCB_TEST8 (6) +#define YAS_PCB_TEST2 (7) +#define YAS_PCB_TEST_NUM (8) + +/* typedef */ +struct yas_pcb_vector { + int32_t v[3]; +}; + +struct yas_pcb_correction { + int32_t s32Cx, s32Cy1, s32Cy2; + int32_t s32A2, s32A3, s32A4, s32A5, s32A6, s32A7, s32A8, s32A9, s32K; + int32_t s32ZFlag; + int32_t s32Rx, s32Ry1, s32Ry2; + int32_t s32Fx, s32Fy1, s32Fy2; + int32_t s32Ver; +}; + +struct yas_pcb_sensitivity { + int32_t s32Sx, s32Sy, s32Sz; +}; + +/* values */ +static uint16_t gu16State; +static struct yas_pcb_test_callback g_callback; +static struct yas_pcb_vector gstXy1y2; +static int8_t gs08HardOffset[3]; +static struct yas_pcb_correction gstCorrect; +static uint8_t gu08DevId; +static int32_t gs32Center; +static int32_t gs32Overflow; +#ifdef YAS_PCBTEST_EXTRA +static uint8_t gu08Recalc; +static int32_t gs32RecalcWait; +#endif + +/* functions */ +static int Ms3AxesLibAtan8(int, int, short *); +static int Ms3AxesLibDir8(int, int, unsigned short *); +static int yas_pcb_check_state(int); +static void yas_pcb_update_state(int); +static int yas_pcb_power_on(void); +static int yas_pcb_power_off(void); +static int yas_pcb_reset_coil(void); +static int yas530_read_cal(uint8_t *); +static int yas532_read_cal(uint8_t *); +static void yas530_calc_correction(const uint8_t *); +static void yas532_calc_correction(const uint8_t *); +static int yas_pcb_set_offset(const int8_t *); +static int yas_pcb_measure(struct yas_pcb_vector *, int *, uint8_t, uint8_t); +static int yas_pcb_is_flow_occued(struct yas_pcb_vector *, int32_t, int32_t); +static void yas_pcb_calc_sensitivity(struct yas_pcb_vector *, + int, struct yas_pcb_sensitivity *); +static void yas_pcb_calc_position(struct yas_pcb_vector *, + struct yas_pcb_vector *, int); +static int yas_pcb_calc_magnetic_field(struct yas_pcb_vector *, + struct yas_pcb_vector *); +static int yas_pcb_test1(int *); +static int yas_pcb_test2(void); +static int yas_pcb_test3(void); +static int yas_pcb_test4(int *, int *, int *); +static int yas_pcb_test5(int *); +static int yas_pcb_test6(int *, int *); +#ifdef YAS_PCBTEST_EXTRA +static int yas_pcb_test7(int *, int *, int *); +#endif + +static int Ms3AxesLibAtan8(int ss, int cc, short *ans) +{ + static const unsigned char AtanTable[] = { + 0, 1, 3, 5, 6, 8, 11, 13, + 15, 18, 21, 24, 27, 31, 34, 39, + 43, 48, 53, 58, 63, 69, 75, 82, + 89, 96, 103, 110, 118, 126, 134, 143, + 152 + }; + + unsigned char idx; + unsigned short idx_mul64; + signed char sign = 1; + unsigned int ucc; + unsigned int uss; + unsigned short ans_mul8; + unsigned char idx_mod64; + unsigned short ans_diff8; + + if (cc < 0) { + sign = -sign; + ucc = -cc; + } else { + ucc = cc; + } + + if (ss < 0) { + sign = -sign; + uss = -ss; + } else { + uss = ss; + } + + while (ucc >= 0x400) { + ucc >>= 1; + uss >>= 1; + } + + if (ucc == 0) + return -1; + + idx_mul64 = (uss << 11) / ucc; + + idx = idx_mul64 >> 6; + ans_mul8 = (idx << 4) - AtanTable[idx]; + + idx_mod64 = (unsigned char)idx_mul64 & 0x3f; + + if (idx < 32) { + idx++; + ans_diff8 = (idx << 4) - AtanTable[idx] - ans_mul8; + ans_mul8 += (ans_diff8 * idx_mod64) >> 6; + } + + *ans = (sign == 1) ? ans_mul8 : (-ans_mul8); + + return 0; +} + +static int Ms3AxesLibDir8(int ss, int cc, unsigned short *ans) +{ + short temp_ans = 0; + int ucc = cc; + int uss = ss; + int ret = 0; + + if (cc < -2147483647) + cc++; + if (ss < -2147483647) + ss++; + + if (cc < 0) + ucc = -cc; + + if (ss < 0) + uss = -ss; + + if (uss <= ucc) { + ret = Ms3AxesLibAtan8(ss, cc, &temp_ans); + if (ret < 0) + return 1; + + if (cc > 0) { + if (temp_ans < 0) + temp_ans += 2880; + } else + temp_ans += 1440; + } else { + ret = Ms3AxesLibAtan8(cc, ss, &temp_ans); + if (ret < 0) + return 1; + + if (ss > 0) + temp_ans = 720 - temp_ans; + else + temp_ans = 2160 - temp_ans; + } + + *ans = temp_ans; + + return 0; +} + +static int yas_pcb_check_state(int id) +{ + int result = YAS_PCB_ERROR_TEST_ORDER; + uint16_t u16Mask; + const uint16_t u16TestTable[] = { + 0x0000, /* 1 */ + 0x0000, /* 3 */ + (1 << YAS_PCB_TEST1) | (1 << YAS_PCB_TEST3), /* 4 */ + (1 << YAS_PCB_TEST1) | (1 << YAS_PCB_TEST3) + | (1 << YAS_PCB_TEST4), /* 5 */ + (1 << YAS_PCB_TEST1) | (1 << YAS_PCB_TEST3) + | (1 << YAS_PCB_TEST4), /* 6 */ + (1 << YAS_PCB_TEST1) | (1 << YAS_PCB_TEST3) + | (1 << YAS_PCB_TEST4) /* 7 */ + | (1 << YAS_PCB_TEST5), + (1 << YAS_PCB_TEST1) | (1 << YAS_PCB_TEST3) + | (1 << YAS_PCB_TEST4), /* 8 */ + (1 << YAS_PCB_TEST1), /* 2 */ + }; + + if ((YAS_PCB_TEST1 <= id) && (id < YAS_PCB_TEST_NUM)) { + u16Mask = u16TestTable[id]; + if (u16Mask == 0) { + switch (id) { + case YAS_PCB_TEST1: + if ((gu16State == 0) + || (gu16State == (1 << YAS_PCB_TEST1))) + result = YAS_PCB_NO_ERROR; + break; + + case YAS_PCB_TEST3: + if ((gu16State == (1 << YAS_PCB_TEST1)) + || (gu16State == + ((1 << YAS_PCB_TEST1) + | (1 << YAS_PCB_TEST3)))) + result = YAS_PCB_NO_ERROR; + break; + + default: + break; + } + } else { + if ((gu16State & u16Mask) == u16Mask) + result = YAS_PCB_NO_ERROR; + } + } + + return result; +} + +static void yas_pcb_update_state(int id) +{ + if ((YAS_PCB_TEST1 <= id) && (id < YAS_PCB_TEST2)) + gu16State |= (uint16_t)(1 << id); + else + gu16State = 0; +} + +static int yas_pcb_power_on(void) +{ + int result = YAS_PCB_NO_ERROR; + int ret; + + if (NULL != g_callback.power_on) { + ret = g_callback.power_on(); + if (0 != ret) + result = YAS_PCB_ERROR_POWER; + } + + return result; +} + +static int yas_pcb_power_off(void) +{ + int result = YAS_PCB_NO_ERROR; + int ret; + + if (NULL != g_callback.power_off) { + ret = g_callback.power_off(); + if (0 != ret) + result = YAS_PCB_ERROR_POWER; + } + + return result; +} + +static int yas_pcb_reset_coil(void) +{ + int ret; + uint8_t u08Data; + u08Data = YAS_PCB_COIL_DEFAULT; + ret = g_callback.i2c_write(YAS_PCB_ADDR_SLAVE, YAS_PCB_ADDR_COIL, + &u08Data, 1); + if (0 != ret) + return YAS_PCB_ERROR_I2C; + + return YAS_PCB_NO_ERROR; +} +static int yas530_read_cal(uint8_t *pu08Buf) +{ + int i; + int ret; + int size = YAS530_CAL_REG_NUM; + + /* Dummy read */ + ret = g_callback.i2c_read(YAS_PCB_ADDR_SLAVE, YAS_PCB_ADDR_CAL, + pu08Buf, size); + if (0 != ret) + return YAS_PCB_ERROR_I2C; + + ret = g_callback.i2c_read(YAS_PCB_ADDR_SLAVE, YAS_PCB_ADDR_CAL, + pu08Buf, size); + if (0 != ret) + return YAS_PCB_ERROR_I2C; + + /* cal register is all 0 */ + for (i = 0; i < size; i++) { + if (pu08Buf[i] != 0x00) + return YAS_PCB_NO_ERROR; + } + + return YAS_PCB_ERROR_CALREG; +} + + +static int yas532_read_cal(uint8_t *pu08Buf) +{ + int i; + int ret; + int size = YAS532_CAL_REG_NUM; + int len = size - 1; + + /* Dummy read */ + ret = g_callback.i2c_read(YAS_PCB_ADDR_SLAVE, YAS_PCB_ADDR_CAL, + pu08Buf, size); + if (0 != ret) + return YAS_PCB_ERROR_I2C; + + ret = g_callback.i2c_read(YAS_PCB_ADDR_SLAVE, YAS_PCB_ADDR_CAL, + pu08Buf, size); + if (0 != ret) + return YAS_PCB_ERROR_I2C; + + /* cal register is all 0 */ + for (i = 0; i < len; i++) { + if (pu08Buf[i] != 0x00) + return YAS_PCB_NO_ERROR; + } + + /* MSB is not 0 */ + if (pu08Buf[len] & 0x80) + return YAS_PCB_NO_ERROR; + + return YAS_PCB_ERROR_CALREG; +} + +static void yas530_calc_correction(const uint8_t *pu08Data) +{ + uint8_t u08Dx = pu08Data[0]; + uint8_t u08Dy1 = pu08Data[1]; + uint8_t u08Dy2 = pu08Data[2]; + uint8_t u08D2 = (uint8_t)((pu08Data[3] >> 2) & 0x3F); + uint8_t u08D3 = (uint8_t)(((pu08Data[3] << 2) & 0x0C) + | ((pu08Data[4] >> 6) & 0x03)); + uint8_t u08D4 = (uint8_t)(pu08Data[4] & 0x3F); + uint8_t u08D5 = (uint8_t)((pu08Data[5] >> 2) & 0x3f); + uint8_t u08D6 = (uint8_t)(((pu08Data[5] << 4) & 0x30) + | ((pu08Data[6] >> 4) & 0x0F)); + uint8_t u08D7 = (uint8_t)(((pu08Data[6] << 3) & 0x78) + | ((pu08Data[7] >> 5) & 0x07)); + uint8_t u08D8 = (uint8_t)(((pu08Data[7] << 1) & 0x3E) + | ((pu08Data[8] >> 7) & 0x01)); + uint8_t u08D9 = (uint8_t)(((pu08Data[8] << 1) & 0xFE) + | ((pu08Data[9] >> 7) & 0x01)); + uint8_t u08D0 = (uint8_t)((pu08Data[9] >> 2) & 0x1F); + uint8_t u08ZFlag = (uint8_t)((pu08Data[11] >> 5) & 0x01); + uint8_t u08Rx = (uint8_t)(((pu08Data[11] << 1) & 0x3E) + | ((pu08Data[12] >> 7) & 0x01)); + uint8_t u08Fx = (uint8_t)((pu08Data[12] >> 5) & 0x03); + uint8_t u08Ry1 = (uint8_t)(((pu08Data[12] << 1) & 0x3E) + | ((pu08Data[13] >> 7) & 0x01)); + uint8_t u08Fy1 = (uint8_t)((pu08Data[13] >> 5) & 0x03); + uint8_t u08Ry2 = (uint8_t)(((pu08Data[13] << 1) & 0x3E) + | ((pu08Data[14] >> 7) & 0x01)); + uint8_t u08Fy2 = (uint8_t)((pu08Data[14] >> 5) & 0x03); + uint8_t u08Ver = pu08Data[15] & 0x07; + + gstCorrect.s32Cx = (int32_t)((u08Dx * 6) - 768); + gstCorrect.s32Cy1 = (int32_t)((u08Dy1 * 6) - 768); + gstCorrect.s32Cy2 = (int32_t)((u08Dy2 * 6) - 768); + gstCorrect.s32A2 = (int32_t)(u08D2 - 32); + gstCorrect.s32A3 = (int32_t)(u08D3 - 8); + gstCorrect.s32A4 = (int32_t)(u08D4 - 32); + gstCorrect.s32A5 = (int32_t)(u08D5 + 38); + gstCorrect.s32A6 = (int32_t)(u08D6 - 32); + gstCorrect.s32A7 = (int32_t)(u08D7 - 64); + gstCorrect.s32A8 = (int32_t)(u08D8 - 32); + gstCorrect.s32A9 = (int32_t)u08D9; + gstCorrect.s32K = (int32_t)(u08D0) + 10; + gstCorrect.s32ZFlag = (int32_t)u08ZFlag; + gstCorrect.s32Rx = (int32_t)((int8_t)(u08Rx << 2) >> 2); + gstCorrect.s32Fx = (int32_t)u08Fx; + gstCorrect.s32Ry1 = (int32_t)((int8_t)(u08Ry1 << 2) >> 2); + gstCorrect.s32Fy1 = (int32_t)u08Fy1; + gstCorrect.s32Ry2 = (int32_t)((int8_t)(u08Ry2 << 2) >> 2); + gstCorrect.s32Fy2 = (int32_t)u08Fy2; + gstCorrect.s32Ver = (int32_t)u08Ver; +} + +static void yas532_calc_correction(const uint8_t *pu08Data) +{ + uint8_t u08Dx = pu08Data[0]; + uint8_t u08Dy1 = pu08Data[1]; + uint8_t u08Dy2 = pu08Data[2]; + uint8_t u08D2 = (uint8_t)((pu08Data[3] >> 2) & 0x3F); + uint8_t u08D3 = (uint8_t)(((pu08Data[3] << 2) & 0x0C) + | ((pu08Data[4] >> 6) & 0x03)); + uint8_t u08D4 = (uint8_t)(pu08Data[4] & 0x3F); + uint8_t u08D5 = (uint8_t)((pu08Data[5] >> 2) & 0x3f); + uint8_t u08D6 = (uint8_t)(((pu08Data[5] << 4) & 0x30) + | ((pu08Data[6] >> 4) & 0x0F)); + uint8_t u08D7 = (uint8_t)(((pu08Data[6] << 3) & 0x78) + | ((pu08Data[7] >> 5) & 0x07)); + uint8_t u08D8 = (uint8_t)(((pu08Data[7] << 1) & 0x3E) + | ((pu08Data[8] >> 7) & 0x01)); + uint8_t u08D9 = (uint8_t)(((pu08Data[8] << 1) & 0xFE) + | ((pu08Data[9] >> 7) & 0x01)); + uint8_t u08D0 = (uint8_t)((pu08Data[9] >> 2) & 0x1F); + uint8_t u08Rx = (uint8_t)((pu08Data[10] >> 1) & 0x3F); + uint8_t u08Fx = (uint8_t)(((pu08Data[10] & 0x01) << 1) + | ((pu08Data[11] >> 7) & 0x01)); + uint8_t u08Ry1 = (uint8_t)((pu08Data[11] >> 1) & 0x3F); + uint8_t u08Fy1 = (uint8_t)(((pu08Data[11] & 0x01) << 1) + | ((pu08Data[12] >> 7) & 0x01)); + uint8_t u08Ry2 = (uint8_t)((pu08Data[12] >> 1) & 0x3F); + uint8_t u08Fy2 = (uint8_t)(((pu08Data[12] & 0x01) << 1) + | ((pu08Data[13] >> 7) & 0x01)); + uint8_t u08Ver = pu08Data[13] & 0x01; + + gstCorrect.s32Cx = (int32_t)((u08Dx * 10) - 1280); + gstCorrect.s32Cy1 = (int32_t)((u08Dy1 * 10) - 1280); + gstCorrect.s32Cy2 = (int32_t)((u08Dy2 * 10) - 1280); + gstCorrect.s32A2 = (int32_t)(u08D2 - 32); + gstCorrect.s32A3 = (int32_t)(u08D3 - 8); + gstCorrect.s32A4 = (int32_t)(u08D4 - 32); + gstCorrect.s32A5 = (int32_t)(u08D5 + 38); + gstCorrect.s32A6 = (int32_t)(u08D6 - 32); + gstCorrect.s32A7 = (int32_t)(u08D7 - 64); + gstCorrect.s32A8 = (int32_t)(u08D8 - 32); + gstCorrect.s32A9 = (int32_t)u08D9; + gstCorrect.s32K = (int32_t)u08D0; + gstCorrect.s32ZFlag = (int32_t)1; + gstCorrect.s32Rx = (int32_t)((int8_t)(u08Rx << 2) >> 2); + gstCorrect.s32Fx = (int32_t)u08Fx; + gstCorrect.s32Ry1 = (int32_t)((int8_t)(u08Ry1 << 2) >> 2); + gstCorrect.s32Fy1 = (int32_t)u08Fy1; + gstCorrect.s32Ry2 = (int32_t)((int8_t)(u08Ry2 << 2) >> 2); + gstCorrect.s32Fy2 = (int32_t)u08Fy2; + gstCorrect.s32Ver = (int32_t)u08Ver; +} + +static int yas_pcb_set_offset(const int8_t *ps08Offset) +{ + int result = YAS_PCB_NO_ERROR; + int ret; + uint8_t u08Addr; + uint8_t u08Data; + uint8_t i; + + for (i = 0; i < 3; i++) { + u08Addr = (uint8_t)(YAS_PCB_ADDR_OFFSET + i); + u08Data = (uint8_t)ps08Offset[i] & YAS_PCB_HARD_OFFSET_MASK; + ret = g_callback.i2c_write(YAS_PCB_ADDR_SLAVE, + u08Addr, &u08Data, 1); + if (0 != ret) { + result = YAS_PCB_ERROR_I2C; + break; + } + } + + return result; +} + +static int yas_pcb_measure(struct yas_pcb_vector *pstXy1y2, int *temperature, + uint8_t u08Command, uint8_t u08CheckIni) +{ + int ret; + uint8_t u08Buf[YAS_PCB_MEASURE_DATA_REG_NUM]; + int low_or_high; + + if ((YAS_PCB_INT_CHECK == u08CheckIni) + && (NULL != g_callback.read_intpin)) { + ret = g_callback.read_intpin(&low_or_high); + if ((0 != ret) || (YAS_PCB_INT_HIGH != low_or_high)) + return YAS_PCB_ERROR_INTERRUPT; + } + + ret = g_callback.i2c_write(YAS_PCB_ADDR_SLAVE, + YAS_PCB_ADDR_MEASURE_COMMAND, + &u08Command, 1); + if (0 != ret) + return YAS_PCB_ERROR_I2C; + + g_callback.msleep(YAS_PCB_MEASURE_WAIT_TIME); + + if ((YAS_PCB_INT_CHECK == u08CheckIni) + && (NULL != g_callback.read_intpin)) { + ret = g_callback.read_intpin(&low_or_high); + if ((0 != ret) || (YAS_PCB_INT_LOW != low_or_high)) + return YAS_PCB_ERROR_INTERRUPT; + } + + ret = g_callback.i2c_read(YAS_PCB_ADDR_SLAVE, YAS_PCB_ADDR_MEASURE_DATA, + u08Buf, YAS_PCB_MEASURE_DATA_REG_NUM); + if (0 != ret) + return YAS_PCB_ERROR_I2C; + + /* calc measure data */ + if (YAS532_DEVICE_ID == gu08DevId) { + *temperature = (((int32_t)(u08Buf[0] & 0x7F) << 3) + | ((u08Buf[1] >> 5) & 0x07)); + pstXy1y2->v[0] = (int32_t)(((int32_t)(u08Buf[2] & 0x7F) << 6) + | ((u08Buf[3] >> 2) & 0x3F)); + pstXy1y2->v[1] = (int32_t)(((int32_t)(u08Buf[4] & 0x7F) << 6) + | ((u08Buf[5] >> 2) & 0x3F)); + pstXy1y2->v[2] = (int32_t)(((int32_t)(u08Buf[6] & 0x7F) << 6) + | ((u08Buf[7] >> 2) & 0x3F)); + } else { + *temperature = (((int32_t)(u08Buf[0] & 0x7F) << 2) + | ((u08Buf[1] >> 6) & 0x03)); + pstXy1y2->v[0] = (int32_t)(((int32_t)(u08Buf[2] & 0x7F) << 5) + | ((u08Buf[3] >> 3) & 0x1F)); + pstXy1y2->v[1] = (int32_t)(((int32_t)(u08Buf[4] & 0x7F) << 5) + | ((u08Buf[5] >> 3) & 0x1F)); + pstXy1y2->v[2] = (int32_t)(((int32_t)(u08Buf[6] & 0x7F) << 5) + | ((u08Buf[7] >> 3) & 0x1F)); + } + + if (YAS_PCB_MEASURE_BUSY == (u08Buf[0] & YAS_PCB_MEASURE_BUSY)) + return YAS_PCB_ERROR_BUSY; + + return YAS_PCB_NO_ERROR; +} + +static int yas_pcb_is_flow_occued(struct yas_pcb_vector *pstXy1y2, + int32_t underflow, int32_t overflow) +{ + int result = YAS_PCB_NO_ERROR; + int32_t s32Tmp; + uint8_t i; + + for (i = 0; i < 3; i++) { + s32Tmp = pstXy1y2->v[i]; + if (s32Tmp <= underflow) + result = YAS_PCB_ERROR_UNDERFLOW; + else + if (overflow <= s32Tmp) + result = YAS_PCB_ERROR_OVERFLOW; + } + + return result; +} + +static void yas_pcb_calc_sensitivity(struct yas_pcb_vector *pstXy1y2, + int temperature, struct yas_pcb_sensitivity *pstYasSensitivity) +{ + /* calc XYZ data from xy1y2 data */ + int32_t s32Sx = pstXy1y2->v[0] + - ((gstCorrect.s32Cx * temperature) / 100); + int32_t s32Sy1 = pstXy1y2->v[1] + - ((gstCorrect.s32Cy1 * temperature) / 100); + int32_t s32Sy2 = pstXy1y2->v[2] + - ((gstCorrect.s32Cy2 * temperature) / 100); + int32_t s32Sy = s32Sy1 - s32Sy2; + int32_t s32Sz = -s32Sy1 - s32Sy2; + + pstYasSensitivity->s32Sx = s32Sx; + pstYasSensitivity->s32Sy = s32Sy; + pstYasSensitivity->s32Sz = s32Sz; +} + +static void yas_pcb_calc_position(struct yas_pcb_vector *pstXy1y2, + struct yas_pcb_vector *pstXyz, int temperature) +{ + struct yas_pcb_sensitivity stSensitivity; + struct yas_pcb_sensitivity *pst; + + yas_pcb_calc_sensitivity(pstXy1y2, temperature, &stSensitivity); + + pst = &stSensitivity; + pstXyz->v[0] = (gstCorrect.s32K + * ((100 * pst->s32Sx) + (gstCorrect.s32A2 * pst->s32Sy) + + (gstCorrect.s32A3 * pst->s32Sz))) / 10; + pstXyz->v[1] = (gstCorrect.s32K * ((gstCorrect.s32A4 * pst->s32Sx) + + (gstCorrect.s32A5 * pst->s32Sy) + + (gstCorrect.s32A6 * pst->s32Sz))) / 10; + pstXyz->v[2] = (gstCorrect.s32K * ((gstCorrect.s32A7 * pst->s32Sx) + + (gstCorrect.s32A8 * pst->s32Sy) + + (gstCorrect.s32A9 * pst->s32Sz))) / 10; +} + +static void yas530_calc_magnetic_field(struct yas_pcb_vector *pstXy1y2, + struct yas_pcb_vector *pstXyz, int32_t s32Coef) +{ + int32_t s32Oy; + int32_t s32Oz; + static const int32_t s32HTbl[] = { + 1748, 1948, 2148, 2348 + }; + + pstXy1y2->v[0] = gstXy1y2.v[0] - s32HTbl[gstCorrect.s32Fx] + + (gs08HardOffset[0] - gstCorrect.s32Rx) * s32Coef; + pstXy1y2->v[1] = gstXy1y2.v[1] - s32HTbl[gstCorrect.s32Fy1] + + (gs08HardOffset[1] - gstCorrect.s32Ry1) * s32Coef; + pstXy1y2->v[2] = gstXy1y2.v[2] - s32HTbl[gstCorrect.s32Fy2] + + (gs08HardOffset[2] - gstCorrect.s32Ry2) * s32Coef; + s32Oy = pstXy1y2->v[1] - pstXy1y2->v[2]; + s32Oz = -pstXy1y2->v[1] - pstXy1y2->v[2]; + + pstXyz->v[0] = (gstCorrect.s32K + * ((100 * pstXy1y2->v[0]) + + (gstCorrect.s32A2 * s32Oy) + + (gstCorrect.s32A3 * s32Oz))) / 10; + pstXyz->v[1] = (gstCorrect.s32K + * ((gstCorrect.s32A4 * pstXy1y2->v[0]) + + (gstCorrect.s32A5 * s32Oy) + + (gstCorrect.s32A6 * s32Oz))) / 10; + pstXyz->v[2] = (gstCorrect.s32K + * ((gstCorrect.s32A7 * pstXy1y2->v[0]) + + (gstCorrect.s32A8 * s32Oy) + + (gstCorrect.s32A9 * s32Oz))) / 10; +} + +static void yas532_calc_magnetic_field(struct yas_pcb_vector *pstXy1y2, + struct yas_pcb_vector *pstXyz, + int32_t s32CoefX, int32_t s32CoefY1, int32_t s32CoefY2) +{ + int32_t s32Oy; + int32_t s32Oz; + static const int32_t s32HTbl[] = { + 3721, 3971, 4221, 4471 + }; + + pstXy1y2->v[0] = gstXy1y2.v[0] - s32HTbl[gstCorrect.s32Fx] + + (gs08HardOffset[0] - gstCorrect.s32Rx) * s32CoefX; + pstXy1y2->v[1] = gstXy1y2.v[1] - s32HTbl[gstCorrect.s32Fy1] + + (gs08HardOffset[1] - gstCorrect.s32Ry1) * s32CoefY1; + pstXy1y2->v[2] = gstXy1y2.v[2] - s32HTbl[gstCorrect.s32Fy2] + + (gs08HardOffset[2] - gstCorrect.s32Ry2) * s32CoefY2; + s32Oy = pstXy1y2->v[1] - pstXy1y2->v[2]; + s32Oz = -pstXy1y2->v[1] - pstXy1y2->v[2]; + + pstXyz->v[0] = (gstCorrect.s32K + * ((100 * pstXy1y2->v[0]) + + (gstCorrect.s32A2 * s32Oy) + + (gstCorrect.s32A3 * s32Oz))) / 10; + pstXyz->v[1] = (gstCorrect.s32K + * ((gstCorrect.s32A4 * pstXy1y2->v[0]) + + (gstCorrect.s32A5 * s32Oy) + + (gstCorrect.s32A6 * s32Oz))) / 10; + pstXyz->v[2] = (gstCorrect.s32K + * ((gstCorrect.s32A7 * pstXy1y2->v[0]) + + (gstCorrect.s32A8 * s32Oy) + + (gstCorrect.s32A9 * s32Oz))) / 10; +} + +static int yas_pcb_calc_magnetic_field(struct yas_pcb_vector *pstXy1y2, + struct yas_pcb_vector *pstXyz) +{ + int32_t s32Coef; + + if (YAS532_DEVICE_ID == gu08DevId) { + switch (gstCorrect.s32Ver) { + case YAS532_VERSION_AC: + break; + + default: + return YAS_PCB_ERROR_I2C; + /* break; */ + } + + /* calculate Ohx/y/z[nT] */ + yas532_calc_magnetic_field(pstXy1y2, pstXyz, + YAS532_COEFX_VERSION_AC, + YAS532_COEFY1_VERSION_AC, + YAS532_COEFY2_VERSION_AC); + } else { + switch (gstCorrect.s32Ver) { + case YAS530_VERSION_A: + s32Coef = YAS530_COEF_VERSION_A; + break; + + case YAS530_VERSION_B: + s32Coef = YAS530_COEF_VERSION_B; + break; + + default: + return YAS_PCB_ERROR_I2C; + /* break; */ + } + + /* calculate Ohx/y/z[nT] */ + yas530_calc_magnetic_field(pstXy1y2, pstXyz, s32Coef); + } + + return YAS_PCB_NO_ERROR; +} + +static int yas_pcb_test1(int *id) +{ + int result; + int ret; + + result = yas_pcb_power_on(); + if (YAS_PCB_NO_ERROR == result) { + result = YAS_PCB_ERROR_I2C; + ret = g_callback.i2c_read(YAS_PCB_ADDR_SLAVE, + YAS_PCB_ADDR_ID, &gu08DevId, 1); + if (0 == ret) { + *id = (int)gu08DevId; + result = YAS_PCB_NO_ERROR; + } + } + + return result; +} + +static int yas_pcb_test2(void) +{ + return yas_pcb_power_off(); +} + +static int yas_pcb_test3(void) +{ + int result; + int ret; + uint8_t u08Data; + uint8_t pu08Buf[YAS530_CAL_REG_NUM]; + + u08Data = YAS_PCB_TEST1_DEFAULT; + ret = g_callback.i2c_write(YAS_PCB_ADDR_SLAVE, YAS_PCB_ADDR_TEST1, + &u08Data, 1); + if (0 != ret) + return YAS_PCB_ERROR_I2C; + + u08Data = YAS_PCB_TEST2_DEFAULT; + ret = g_callback.i2c_write(YAS_PCB_ADDR_SLAVE, YAS_PCB_ADDR_TEST2, + &u08Data, 1); + if (0 != ret) + return YAS_PCB_ERROR_I2C; + + u08Data = YAS_PCB_INTERVAL_DEFAULT; + ret = g_callback.i2c_write(YAS_PCB_ADDR_SLAVE, + YAS_PCB_ADDR_MEASURE_INTERVAL, &u08Data, 1); + if (0 != ret) + return YAS_PCB_ERROR_I2C; + + if (YAS532_DEVICE_ID == gu08DevId) { + gs32Center = YAS532_RAWDATA_CENTER; + gs32Overflow = YAS532_RAWDATA_OVERFLOW; + result = yas532_read_cal(pu08Buf); + if (YAS_PCB_NO_ERROR == result) + yas532_calc_correction(pu08Buf); + } else { + gs32Center = YAS530_RAWDATA_CENTER; + gs32Overflow = YAS530_RAWDATA_OVERFLOW; + result = yas530_read_cal(pu08Buf); + if (YAS_PCB_NO_ERROR == result) + yas530_calc_correction(pu08Buf); + } + + if (YAS_PCB_NO_ERROR != result) + return result; + + u08Data = (uint8_t)(YAS_PCB_CONFIG_DEFAULT + | (uint8_t)((pu08Buf[9] & 0x03) << 3) + | (uint8_t)((pu08Buf[10] & 0x80) >> 5)); + ret = g_callback.i2c_write(YAS_PCB_ADDR_SLAVE, + YAS_PCB_ADDR_CONFIG, &u08Data, 1); + if (0 != ret) + return YAS_PCB_ERROR_I2C; + + ret = yas_pcb_reset_coil(); + if (YAS_PCB_NO_ERROR != ret) + return ret; + return YAS_PCB_NO_ERROR; +} + +static int yas_pcb_test4(int *x, int *y1, int *y2) +{ + int result; + struct yas_pcb_vector stXy1y2; + int temperature; + int32_t s32Tmp; + int8_t s08Correct = YAS_PCB_HARD_OFFSET_CORRECT; + uint8_t i; + uint8_t k; + + gs08HardOffset[0] = 0; + gs08HardOffset[1] = 0; + gs08HardOffset[2] = 0; + result = yas_pcb_set_offset(&gs08HardOffset[0]); + if (YAS_PCB_NO_ERROR == result) { + /* calc hard offset */ + for (i = 0; i < YAS_PCB_COIL_INIT_CALC_NUM; i++) { + result = yas_pcb_measure(&stXy1y2, &temperature, + YAS_PCB_MEASURE_COMMAND_START, + YAS_PCB_INT_NOTCHECK); + if (YAS_PCB_NO_ERROR != result) + break; + + for (k = 0; k < 3; k++) { + s32Tmp = stXy1y2.v[k]; + if (gs32Center < s32Tmp) + gs08HardOffset[k] += s08Correct; + else { + if (s32Tmp < gs32Center) + gs08HardOffset[k] -= s08Correct; + } + } + + result = yas_pcb_set_offset(&gs08HardOffset[0]); + if (YAS_PCB_NO_ERROR != result) + break; + + s08Correct = (int8_t)((uint8_t)s08Correct >> 1); + } + + if (YAS_PCB_NO_ERROR == result) { + *x = (int)gs08HardOffset[0]; + *y1 = (int)gs08HardOffset[1]; + *y2 = (int)gs08HardOffset[2]; + result = yas_pcb_is_flow_occued(&stXy1y2, + 0, gs32Overflow); + } + } + + return result; +} + +static int yas_pcb_test5(int *direction) +{ + uint16_t dir; + int result; + int ret; + int x; + int y; + int nTemp; + struct yas_pcb_vector stXyz; + + result = yas_pcb_measure(&gstXy1y2, &nTemp, + YAS_PCB_MEASURE_COMMAND_START, YAS_PCB_INT_NOTCHECK); + if (YAS_PCB_NO_ERROR == result) { + result = YAS_PCB_ERROR_DIRCALC; + yas_pcb_calc_position(&gstXy1y2, &stXyz, nTemp); + + x = -stXyz.v[0] / YAS_PCB_DIR_DIVIDER; + y = stXyz.v[1] / YAS_PCB_DIR_DIVIDER; + ret = Ms3AxesLibDir8(x, y, &dir); + if (0 == ret) { + *direction = (int)(dir / 8); + result = yas_pcb_is_flow_occued(&gstXy1y2, + 0, gs32Overflow); + } + } + + return result; +} + +static int yas_pcb_test6(int *sx, int *sy) +{ + int result; + struct yas_pcb_vector stXy1y2P; + struct yas_pcb_vector stXy1y2N; + int temperature; + uint8_t u08Command; + struct yas_pcb_vector *pP = &stXy1y2P; + struct yas_pcb_vector *pN = &stXy1y2N; + struct yas_pcb_correction *pC = &gstCorrect; + + u08Command = YAS_PCB_MEASURE_COMMAND_START + | YAS_PCB_MEASURE_COMMAND_LDTC; + result = yas_pcb_measure(pP, &temperature, + u08Command, YAS_PCB_INT_CHECK); + + if (YAS_PCB_NO_ERROR == result) { + u08Command = YAS_PCB_MEASURE_COMMAND_START + | YAS_PCB_MEASURE_COMMAND_LDTC + | YAS_PCB_MEASURE_COMMAND_FORS; + result = yas_pcb_measure(pN, &temperature, u08Command, + YAS_PCB_INT_NOTCHECK); + + if (YAS_PCB_NO_ERROR == result) { + if (YAS532_DEVICE_ID == gu08DevId) { + *sx = (int)(pC->s32K * 100 + * (pP->v[0] - pN->v[0])); + *sx /= 1000; + *sx /= YAS_VCORE; + *sy = (int)(pC->s32K * pC->s32A5 + * ((pP->v[1] - pN->v[1]) + - (pP->v[2] - pN->v[2]))); + *sy /= 1000; + *sy /= YAS_VCORE; + } else { + *sx = (int)(pN->v[0] - pP->v[0]); + *sy = (int)((pN->v[1] - pP->v[1]) + - (pN->v[2] - pP->v[2])); + } + + result = yas_pcb_is_flow_occued(pP, 0, gs32Overflow); + if (YAS_PCB_NO_ERROR == result) + result = yas_pcb_is_flow_occued(pN, + 0, gs32Overflow); + } + } + + return result; +} + +#ifdef YAS_PCBTEST_EXTRA +static int yas_pcb_test7(int *ohx, int *ohy, int *ohz) +{ + int nRet = YAS_PCB_ERROR_NOT_SUPPORTED; + struct yas_pcb_vector stOhxy1y2, stOhxyz; + + if (0 != gstCorrect.s32ZFlag) { + nRet = yas_pcb_calc_magnetic_field(&stOhxy1y2, &stOhxyz); + if (YAS_PCB_NO_ERROR == nRet) { + /* [nT]->[uT] */ + *ohx = stOhxyz.v[0] / 1000; + *ohy = stOhxyz.v[1] / 1000; + *ohz = stOhxyz.v[2] / 1000; + } + } + + return nRet; +} + +static int yas_pcb_test8(int *hx0, int *hy0, int *hz0) +{ + int nRet; + int nTemp; + int nX, nY1, nY2; + int32_t s32Underflow = 0; + int32_t s32Overflow = gs32Overflow; + struct yas_pcb_vector stOhxy1y2, stOhxyz; + + if (YAS532_DEVICE_ID == gu08DevId) { + s32Underflow = YAS_PCB_NOISE_UNDERFLOW; + s32Overflow = YAS_PCB_NOISE_OVERFLOW; + } + + if (gu08Recalc != 0) { + gs32RecalcWait++; + if (YAS_PCB_NOISE_INTERVAL <= gs32RecalcWait) { + nRet = yas_pcb_reset_coil(); + if (YAS_PCB_NO_ERROR == nRet) + nRet = yas_pcb_test4(&nX, &nY1, &nY2); + + if (YAS_PCB_NO_ERROR == nRet) + gu08Recalc = 0; + + gs32RecalcWait = 0; + } + } + + if (0 != gstCorrect.s32ZFlag) { + nRet = yas_pcb_measure(&gstXy1y2, &nTemp, + YAS_PCB_MEASURE_COMMAND_START, + YAS_PCB_INT_NOTCHECK); + if (YAS_PCB_NO_ERROR == nRet) { + nRet = yas_pcb_calc_magnetic_field(&stOhxy1y2, + &stOhxyz); + if (YAS_PCB_NO_ERROR == nRet) { + *hx0 = stOhxy1y2.v[0]; + *hy0 = stOhxy1y2.v[1] - stOhxy1y2.v[2]; + *hz0 = -stOhxy1y2.v[1] - stOhxy1y2.v[2]; + nRet = yas_pcb_is_flow_occued(&gstXy1y2, + s32Underflow, s32Overflow); + if (YAS_PCB_NO_ERROR != nRet) { + if (gu08Recalc == 0) { + gu08Recalc++; + gs32RecalcWait = 0; + } + } + } + } + } else + nRet = YAS_PCB_ERROR_NOT_SUPPORTED; + + return nRet; +} + +#endif + +/* test 1 */ +static int power_on_and_device_check(int *id) +{ + int ret; + int result = yas_pcb_check_state(YAS_PCB_TEST1); + + if (YAS_PCB_NO_ERROR == result) { + if (id != NULL) { + result = YAS_PCB_ERROR_I2C; + ret = g_callback.i2c_open(); + if (0 == ret) { + result = yas_pcb_test1(id); + ret = g_callback.i2c_close(); + if (0 != ret) + result = YAS_PCB_ERROR_I2C; + } + if (YAS_PCB_NO_ERROR == result) + yas_pcb_update_state(YAS_PCB_TEST1); + } else + result = YAS_PCB_ERROR_ARG; + } + + return result; +} + +/* test 2 */ +static int power_off(void) +{ + int result = yas_pcb_check_state(YAS_PCB_TEST2); + + if (YAS_PCB_NO_ERROR == result) { + result = yas_pcb_test2(); + + if (YAS_PCB_NO_ERROR == result) + yas_pcb_update_state(YAS_PCB_TEST2); + } + + return result; +} + +/* test 3 */ +static int initialization(void) +{ + int ret; + int result = yas_pcb_check_state(YAS_PCB_TEST3); + + if (YAS_PCB_NO_ERROR == result) { + result = YAS_PCB_ERROR_I2C; + ret = g_callback.i2c_open(); + if (0 == ret) { + result = yas_pcb_test3(); + ret = g_callback.i2c_close(); + if (0 != ret) + result = YAS_PCB_ERROR_I2C; + } + + if (YAS_PCB_NO_ERROR == result) + yas_pcb_update_state(YAS_PCB_TEST3); + } + + return result; +} + +/* test 4 */ +static int offset_control_measurement_and_set_offset_register(int *x, + int *y1, int *y2) +{ + int ret; + int result = yas_pcb_check_state(YAS_PCB_TEST4); + + if (YAS_PCB_NO_ERROR == result) { + if ((x != NULL) && (y1 != NULL) && (y2 != NULL)) { + result = YAS_PCB_ERROR_I2C; + ret = g_callback.i2c_open(); + if (0 == ret) { + result = yas_pcb_test4(x, y1, y2); + ret = g_callback.i2c_close(); + if (0 != ret) + result = YAS_PCB_ERROR_I2C; + } + if (YAS_PCB_NO_ERROR == result) + yas_pcb_update_state(YAS_PCB_TEST4); + } else + result = YAS_PCB_ERROR_ARG; + } + + return result; +} + +/* test 5 */ +static int direction_measurement(int *direction) +{ + int ret; + int result = yas_pcb_check_state(YAS_PCB_TEST5); + + if (YAS_PCB_NO_ERROR == result) { + if (direction != NULL) { + result = YAS_PCB_ERROR_I2C; + ret = g_callback.i2c_open(); + if (0 == ret) { + result = yas_pcb_test5(direction); + ret = g_callback.i2c_close(); + if (0 != ret) + result = YAS_PCB_ERROR_I2C; + } + if (YAS_PCB_NO_ERROR == result) + yas_pcb_update_state(YAS_PCB_TEST5); + } else + result = YAS_PCB_ERROR_ARG; + } + + return result; +} + +/* test 6 */ +static int sensitivity_measurement_of_magnetic_sensor_by_test_coil( + int *sx, int *sy) +{ + int ret; + int result = yas_pcb_check_state(YAS_PCB_TEST6); + + if (YAS_PCB_NO_ERROR == result) { + if ((sx != NULL) && (sy != NULL)) { + result = YAS_PCB_ERROR_I2C; + ret = g_callback.i2c_open(); + if (0 == ret) { + result = yas_pcb_test6(sx, sy); + ret = g_callback.i2c_close(); + if (0 != ret) + result = YAS_PCB_ERROR_I2C; + } + if (YAS_PCB_NO_ERROR == result) + yas_pcb_update_state(YAS_PCB_TEST6); + } else + result = YAS_PCB_ERROR_ARG; + } + + return result; +} + +/* test 7 */ +static int magnetic_field_level_check(int *ohx, int *ohy, int *ohz) +{ +#ifdef YAS_PCBTEST_EXTRA + int result = yas_pcb_check_state(YAS_PCB_TEST7); + + if (YAS_PCB_NO_ERROR == result) { + if ((ohx != NULL) && (ohy != NULL) && (ohz != NULL)) { + result = yas_pcb_test7(ohx, ohy, ohz); + if (YAS_PCB_NO_ERROR == result) + yas_pcb_update_state(YAS_PCB_TEST7); + } else + result = YAS_PCB_ERROR_ARG; + } + + return result; +#else + return YAS_PCB_ERROR_NOT_SUPPORTED; +#endif +} + +/* test 8 */ +static int noise_level_check(int *hx0, int *hy0, int *hz0) +{ +#ifdef YAS_PCBTEST_EXTRA + int ret; + int result = yas_pcb_check_state(YAS_PCB_TEST8); + + if (YAS_PCB_NO_ERROR == result) { + if ((hx0 != NULL) && (hy0 != NULL) && (hz0 != NULL)) { + result = YAS_PCB_ERROR_I2C; + ret = g_callback.i2c_open(); + if (0 == ret) { + result = yas_pcb_test8(hx0, hy0, hz0); + ret = g_callback.i2c_close(); + if (0 != ret) + result = YAS_PCB_ERROR_I2C; + } + if (YAS_PCB_NO_ERROR == result) + yas_pcb_update_state(YAS_PCB_TEST8); + } else + result = YAS_PCB_ERROR_ARG; + } + + return result; +#else + return YAS_PCB_ERROR_NOT_SUPPORTED; +#endif +} + +/* pcb test module initialize */ +int yas_pcb_test_init(struct yas_pcb_test *func) +{ + int result = YAS_PCB_ERROR_ARG; + + if ((NULL != func) + && (NULL != func->callback.i2c_open) + && (NULL != func->callback.i2c_close) + && (NULL != func->callback.i2c_write) + && (NULL != func->callback.i2c_read) + && (NULL != func->callback.msleep)) { + func->power_on_and_device_check = power_on_and_device_check; + func->initialization = initialization; + func->offset_control_measurement_and_set_offset_register + = offset_control_measurement_and_set_offset_register; + func->direction_measurement = direction_measurement; + func->sensitivity_measurement_of_magnetic_sensor_by_test_coil + = sensitivity_measurement_of_magnetic_sensor_by_test_coil; + func->magnetic_field_level_check = magnetic_field_level_check; + func->noise_level_check = noise_level_check; + func->power_off = power_off; + + g_callback = func->callback; + + if (0 != gu16State) { + gu16State = 0; + yas_pcb_power_off(); + } +#ifdef YAS_PCBTEST_EXTRA + gu08Recalc = 0; + gs32RecalcWait = 0; +#endif + result = YAS_PCB_NO_ERROR; + } + + return result; +} +/* end of file */ diff --git a/drivers/sensor/yas_pcb_test.h b/drivers/sensor/yas_pcb_test.h new file mode 100644 index 0000000..40d9dbd --- /dev/null +++ b/drivers/sensor/yas_pcb_test.h @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2010-2011 Yamaha Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +/* + * File yas_pcb_test.h + * Date 2013/1/22 + * Revision 1.4.3 + */ + +#ifndef __YAS_PCB_TEST_H__ +#define __YAS_PCB_TEST_H__ + +#include "yas_types.h" + +/* extra */ +#define YAS_PCBTEST_EXTRA + +/* error code */ +#define YAS_PCB_NO_ERROR (0) +#define YAS_PCB_ERROR_I2C (-1) +#define YAS_PCB_ERROR_POWER (-2) +#define YAS_PCB_ERROR_TEST_ORDER (-3) +#define YAS_PCB_ERROR_INTERRUPT (-4) +#define YAS_PCB_ERROR_BUSY (-5) +#define YAS_PCB_ERROR_OVERFLOW (-6) +#define YAS_PCB_ERROR_UNDERFLOW (-7) +#define YAS_PCB_ERROR_DIRCALC (-8) +#define YAS_PCB_ERROR_NOT_SUPPORTED (-9) +#define YAS_PCB_ERROR_CALREG (-10) +#define YAS_PCB_ERROR_ARG (-128) + +/* addr */ +#define YAS_PCB_ADDR_SLAVE (0x2E) + +#define YAS_PCB_ADDR_ID (0x80) +#define YAS_PCB_ADDR_COIL (0x81) +#define YAS_PCB_ADDR_MEASURE_COMMAND (0x82) +#define YAS_PCB_ADDR_CONFIG (0x83) +#define YAS_PCB_ADDR_MEASURE_INTERVAL (0x84) +#define YAS_PCB_ADDR_OFFSET (0x85) +#define YAS_PCB_ADDR_TEST1 (0x88) +#define YAS_PCB_ADDR_TEST2 (0x89) +#define YAS_PCB_ADDR_CAL (0x90) +#define YAS_PCB_ADDR_MEASURE_DATA (0xB0) + +/* V Core */ +#define YAS_VCORE (18) +#define YAS_PCB_NOISE_OVERFLOW (6000) +#define YAS_PCB_NOISE_UNDERFLOW (2000) +#define YAS_PCB_NOISE_INTERVAL (50) + +struct yas_pcb_test_callback { + int (*power_on)(void); + int (*power_off)(void); + int (*i2c_open)(void); + int (*i2c_close)(void); + int (*i2c_write)(uint8_t, uint8_t, const uint8_t *, int); + int (*i2c_read)(uint8_t, uint8_t, uint8_t *, int); + void (*msleep)(int); + int (*read_intpin)(int *); +}; + +struct yas_pcb_test { + int (*power_on_and_device_check)(int *); + int (*initialization)(void); + int (*offset_control_measurement_and_set_offset_register) + (int *, int *, int *); + int (*direction_measurement)(int *); + int (*sensitivity_measurement_of_magnetic_sensor_by_test_coil) + (int *, int *); + int (*magnetic_field_level_check)(int *, int *, int *); + int (*noise_level_check)(int *, int *, int *); + int (*power_off)(void); + struct yas_pcb_test_callback callback; +}; + +/* prototype functions */ +#ifdef __cplusplus +extern "C" { +#endif + +int yas_pcb_test_init(struct yas_pcb_test *); + +#ifdef __cplusplus +} +#endif + +#endif /* ! __YAS_PCB_TEST_H__ */ + +/* end of file */ diff --git a/drivers/sensor/yas_types.h b/drivers/sensor/yas_types.h new file mode 100644 index 0000000..97aa3f3 --- /dev/null +++ b/drivers/sensor/yas_types.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2010-2011 Yamaha Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +/* + * File yas_types.h + * Date 2012/10/05 + * Revision 1.4.1 + */ + +#ifndef __YAS_TYPES_H__ +#define __YAS_TYPES_H__ + +/* macro */ +#ifndef NULL +#define NULL ((void *)0) +#endif + +#if defined(__KERNEL__) +#include <linux/types.h> +#else +#include <stdint.h> +/*typedef signed char int8_t;*/ +/*typedef unsigned char uint8_t;*/ +/*typedef signed short int16_t;*/ +/*typedef unsigned short uint16_t;*/ +/*typedef signed int int32_t;*/ +/*typedef unsigned int uint32_t;*/ +#endif + +#endif /* __YASTYPES_H__ */ + +/* end of file */ |