/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define __LINUX_KERNEL_DRIVER__ #include #include #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 #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");