--- /dev/null
+/*
+ * Maru Virtio Pedometer Sensor Device Driver
+ *
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact:
+ * Sooyoung Ha <yoosah.ha@samsung.com>
+ * Jinhyung Choi <jinh0.choi@samsung.com>
+ * SeokYeon Hwang <syeon.hwang@samsung.com>
+ *
+ * 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.
+ *
+ * Contributors:
+ * - S-Core Co., Ltd
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/cdev.h>
+#include <linux/uaccess.h>
+
+#include "maru_virtio_sensor.h"
+
+struct maru_pedo_data {
+ struct input_dev *input_data;
+ struct delayed_work work;
+
+ struct virtio_sensor *vs;
+
+ atomic_t pedo;
+ atomic_t enable;
+ atomic_t poll_delay;
+};
+
+static struct class *pedo_class;
+dev_t pedo_devno;
+int pedo_major = 0;
+struct maru_pedo_data *gdata = NULL;
+
+#define PEDO_MSG_LENGTH 18
+#define PEDO_CMD_LENGTH 5
+#define SHUB_LIB_PEDOMETER 3
+#define SHUB_INST_LIB_ADD ((char)-79)
+#define SHUB_INST_LIB_REMOVE ((char)-78)
+
+static char STOP_MSG[] = { 1, 1, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+static char WALK_SLOW_MSG[] = { 1, 1, 3, 1, 0, 0, 0, 1, 0, 56, 30, 3, 3, 15, 0, 0, 0, 0};
+static char WALK_MSG[] = { 1, 1, 3, 1, 0, 0, 0, 1, 0, 80, 60, 3, 3, 20, 0, 0, 0, 0};
+static char RUN_SLOW_MSG[] = { 1, 1, 3, 0, 1, 0, 0, 1, 0, 110, 120, 4, 8, 30, 0, 0, 0, 0};
+static char RUN_MSG[] = { 1, 1, 3, 0, 1, 0, 0, 1, 0, 120, 0xA0, 4, 8, 37, 0, 0, 0, 0};
+
+static char PEDO_ENABLE_CMD[] = {SHUB_INST_LIB_ADD, SHUB_LIB_PEDOMETER, 0, 0, 0};
+static char PEDO_DISABLE_CMD[] = {SHUB_INST_LIB_REMOVE, SHUB_LIB_PEDOMETER, 0, 0};
+
+static void maru_pedo_input_work_func(struct work_struct *work)
+{
+ int pedo = 0;
+ int poll_time = 0;
+ int enable = -1;
+ int ret = 0;
+ char sensor_data[__MAX_BUF_SENSOR];
+ struct maru_pedo_data *data = container_of((struct delayed_work *)work,
+ struct maru_pedo_data, work);
+
+ LOG(1, "maru_pedo_input_work_func starts");
+
+ memset(sensor_data, 0, __MAX_BUF_SENSOR);
+
+ poll_time = atomic_read(&data->poll_delay);
+ enable = atomic_read(&data->enable);
+
+ if (enable) {
+ mutex_lock(&data->vs->vqlock);
+ ret = get_sensor_data(sensor_type_pedo, sensor_data);
+ mutex_unlock(&data->vs->vqlock);
+ if (!ret) {
+ sscanf(sensor_data, "%d", &pedo);
+ atomic_set(&data->pedo, pedo);
+ LOG(1, "pedo_set %d", pedo);
+
+ input_report_rel(data->input_data, REL_RX, 3);
+ input_sync(data->input_data);
+ }
+ }
+
+ enable = atomic_read(&data->enable);
+
+ if (enable) {
+ if (poll_time > 0) {
+ schedule_delayed_work(&data->work, nsecs_to_jiffies(poll_time));
+ } else {
+ schedule_delayed_work(&data->work, 0);
+ }
+ }
+}
+
+static ssize_t maru_enable_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ char sensor_data[__MAX_BUF_SENSOR];
+ int ret;
+ struct input_dev *input_data = to_input_dev(dev);
+ struct maru_pedo_data *data = input_get_drvdata(input_data);
+
+ memset(sensor_data, 0, __MAX_BUF_SENSOR);
+ mutex_lock(&data->vs->vqlock);
+ ret = get_sensor_data(sensor_type_pedo_enable, sensor_data);
+ mutex_unlock(&data->vs->vqlock);
+ if (ret)
+ return sprintf(buf, "%d", -1);
+
+ return sprintf(buf, "%s", sensor_data);
+}
+
+static ssize_t maru_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 maru_pedo_data *data = input_get_drvdata(input_data);
+ int value = simple_strtoul(buf, NULL, 10);
+
+ if (value != 0 && value != 1)
+ return count;
+
+ mutex_lock(&data->vs->vqlock);
+ set_sensor_data(sensor_type_pedo_enable, buf);
+ mutex_unlock(&data->vs->vqlock);
+
+ if (value) {
+ if (atomic_read(&data->enable) != 1) {
+ atomic_set(&data->enable, 1);
+ schedule_delayed_work(&data->work, 0);
+
+ }
+ } else {
+ if (atomic_read(&data->enable) != 0) {
+ atomic_set(&data->enable, 0);
+ cancel_delayed_work(&data->work);
+ }
+ }
+
+ return strnlen(buf, __MAX_BUF_SENSOR);
+}
+
+static ssize_t maru_poll_delay_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ char sensor_data[__MAX_BUF_SENSOR];
+ int ret;
+ struct input_dev *input_data = to_input_dev(dev);
+ struct maru_pedo_data *data = input_get_drvdata(input_data);
+
+ memset(sensor_data, 0, __MAX_BUF_SENSOR);
+ mutex_lock(&data->vs->vqlock);
+ ret = get_sensor_data(sensor_type_pedo_delay, sensor_data);
+ mutex_unlock(&data->vs->vqlock);
+ if (ret)
+ return sprintf(buf, "%d", -1);
+
+ return sprintf(buf, "%s", sensor_data);
+}
+
+static ssize_t maru_poll_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 maru_pedo_data *data = input_get_drvdata(input_data);
+ int value = simple_strtoul(buf, NULL, 10);
+
+ if (value < __MIN_DELAY_SENSOR)
+ return count;
+
+ mutex_lock(&data->vs->vqlock);
+ set_sensor_data(sensor_type_pedo_delay, buf);
+ mutex_unlock(&data->vs->vqlock);
+ atomic_set(&data->poll_delay, value);
+
+ return strnlen(buf, __MAX_BUF_SENSOR);
+}
+
+static struct device_attribute attr_pedo[] = {
+ MARU_ATTR_RW(enable),
+ MARU_ATTR_RW(poll_delay),
+};
+
+static struct attribute *maru_pedo_attribute[] = {
+ &attr_pedo[0].attr,
+ &attr_pedo[1].attr,
+ NULL
+};
+
+static struct attribute_group maru_pedo_attribute_group = {
+ .attrs = maru_pedo_attribute
+};
+
+static void pedo_clear(struct maru_pedo_data *data)
+{
+ if (data == NULL)
+ return;
+
+ if (data->input_data) {
+ sysfs_remove_group(&data->input_data->dev.kobj,
+ &maru_pedo_attribute_group);
+ input_free_device(data->input_data);
+ }
+
+ kfree(data);
+ data = NULL;
+}
+
+static int set_initial_value(struct maru_pedo_data *data)
+{
+ int delay = 0;
+ int ret = 0;
+ int enable = 0;
+ char sensor_data[__MAX_BUF_SENSOR];
+
+ memset(sensor_data, 0, __MAX_BUF_SENSOR);
+
+ mutex_lock(&data->vs->vqlock);
+ ret = get_sensor_data(sensor_type_pedo_delay, sensor_data);
+ mutex_unlock(&data->vs->vqlock);
+ if (ret) {
+ ERR("failed to get initial delay time");
+ return ret;
+ }
+
+ delay = sensor_atoi(sensor_data);
+
+ mutex_lock(&data->vs->vqlock);
+ ret = get_sensor_data(sensor_type_pedo_enable, sensor_data);
+ mutex_unlock(&data->vs->vqlock);
+ if (ret) {
+ ERR("failed to get initial enable");
+ return ret;
+ }
+
+ enable = sensor_atoi(sensor_data);
+
+ if (delay < 0) {
+ ERR("weird value is set initial delay");
+ return ret;
+ }
+
+ atomic_set(&data->poll_delay, delay);
+
+ if (enable) {
+ atomic_set(&data->enable, 1);
+ schedule_delayed_work(&data->work, 0);
+ }
+
+ return ret;
+}
+
+static int create_input_device(struct maru_pedo_data *data)
+{
+ int ret = 0;
+ struct input_dev *input_data = NULL;
+
+ input_data = input_allocate_device();
+ if (input_data == NULL) {
+ ERR("failed initialing input handler");
+ pedo_clear(data);
+ return -ENOMEM;
+ }
+
+ input_data->name = SENSOR_PEDO_INPUT_NAME;
+ input_data->id.bustype = BUS_I2C;
+
+ set_bit(EV_REL, input_data->evbit);
+ set_bit(EV_SYN, input_data->evbit);
+ input_set_capability(input_data, EV_REL, REL_RX);
+
+ data->input_data = input_data;
+
+ ret = input_register_device(input_data);
+ if (ret) {
+ ERR("failed to register input data");
+ pedo_clear(data);
+ return ret;
+ }
+
+ input_set_drvdata(input_data, data);
+
+ ret = sysfs_create_group(&input_data->dev.kobj,
+ &maru_pedo_attribute_group);
+ if (ret) {
+ pedo_clear(data);
+ ERR("failed initialing devices");
+ return ret;
+ }
+
+ return ret;
+}
+
+static int pedo_open(struct inode *inode, struct file *filp)
+{
+ filp->private_data = inode->i_cdev;
+ LOG(1, "pedometer is opened");
+ return 0;
+}
+
+static int pedo_close(struct inode *inode, struct file *filp)
+{
+ LOG(1, "pedometer is closed");
+ return 0;
+}
+
+static ssize_t pedo_read(struct file *filp, char __user *ubuf, size_t len,
+ loff_t *f_pos)
+{
+ unsigned long ret = 0;
+ int pedo = 0;
+ LOG(1, "try to read pedometer node");
+
+ pedo = atomic_read(&gdata->pedo);
+ LOG(1, "selected pedometer state is %d", pedo);
+
+ switch (pedo) {
+ case 1:
+ ret = copy_to_user(ubuf, STOP_MSG, PEDO_MSG_LENGTH);
+ break;
+ case 2:
+ ret = copy_to_user(ubuf, WALK_SLOW_MSG, PEDO_MSG_LENGTH);
+ break;
+ case 3:
+ ret = copy_to_user(ubuf, WALK_MSG, PEDO_MSG_LENGTH);
+ break;
+ case 4:
+ ret = copy_to_user(ubuf, RUN_SLOW_MSG, PEDO_MSG_LENGTH);
+ break;
+ case 5:
+ ret = copy_to_user(ubuf, RUN_MSG, PEDO_MSG_LENGTH);
+ break;
+ default:
+ ERR("invalid case pedometer state %d!!", pedo);
+ return -EFAULT;
+ }
+ if (ret) {
+ ERR("pedometer copy_to_user failed, errno: %lu", ret);
+ return -EFAULT;
+ } else {
+ LOG(1, "pedometer copy_to_user success.");
+ return PEDO_MSG_LENGTH;
+ }
+}
+
+static ssize_t pedo_write(struct file *filp, const char __user *ubuf, size_t len,
+ loff_t *f_pos)
+{
+ unsigned long ret = 0;
+ char cmd[PEDO_CMD_LENGTH] = {'\0', };
+ char buf[2] = {'\0', };
+ int value = -1;
+
+ LOG(1, "try to write pedometer node %zi", len);
+
+ ret = copy_from_user(cmd, ubuf, len);
+ if (ret) {
+ ERR("pedometer copy_to_user failed, errno: %lu", ret);
+ return -EFAULT;
+ }
+
+ if (!memcmp(cmd, PEDO_ENABLE_CMD, 2)) {
+ LOG(1, "enable pedometer");
+ value = 1;
+ } else if (!memcmp(cmd, PEDO_DISABLE_CMD, 2)) {
+ LOG(1, "disable pedometer");
+ value = 0;
+ } else {
+ ERR("invalid command %s", cmd);
+ return -EFAULT;
+ }
+ sprintf(buf, "%d", value);
+ mutex_lock(&gdata->vs->vqlock);
+ set_sensor_data(sensor_type_pedo_enable, buf);
+ mutex_unlock(&gdata->vs->vqlock);
+
+ if (value) {
+ if (atomic_read(&gdata->enable) != 1) {
+ atomic_set(&gdata->enable, 1);
+ schedule_delayed_work(&gdata->work, 0);
+
+ }
+ } else {
+ if (atomic_read(&gdata->enable) != 0) {
+ atomic_set(&gdata->enable, 0);
+ cancel_delayed_work(&gdata->work);
+ }
+ }
+ return 1;
+}
+
+struct file_operations pedo_fops = {
+ .owner = THIS_MODULE,
+ .read = pedo_read,
+ .write = pedo_write,
+ .open = pedo_open,
+ .release = pedo_close,
+};
+
+static char *pedo_devnode(struct device *dev, umode_t *mode)
+{
+ return kasprintf(GFP_KERNEL, "%s", dev_name(dev));
+}
+
+int maru_pedo_init(struct virtio_sensor *vs)
+{
+ int ret = 0;
+ int err = 0;
+ dev_t dev = 0;
+ struct cdev *cdev = NULL;
+
+ INFO("maru_pedo device init starts.");
+
+ cdev = kzalloc(sizeof(struct cdev), GFP_KERNEL);
+ if (cdev == NULL) {
+ ERR("failed to create pedo cdev.");
+ return -ENOMEM;
+ }
+
+ gdata = kzalloc(sizeof(struct maru_pedo_data), GFP_KERNEL);
+ if (gdata == NULL) {
+ ERR("failed to create pedo data.");
+ return -ENOMEM;
+ }
+
+ vs->pedo_handle = gdata;
+ gdata->vs = vs;
+
+ INIT_DELAYED_WORK(&gdata->work, maru_pedo_input_work_func);
+ ret = alloc_chrdev_region(&dev, 0, 1, DRIVER_PEDO_NAME);
+ if (ret < 0) {
+ ERR("Unable to get pedo region, error %d", ret);
+ return -1;
+ }
+
+ pedo_major = MAJOR(dev);
+ LOG(1, "pedo device major num = %d\n", pedo_major);
+ pedo_devno = dev;
+
+ pedo_class = class_create(THIS_MODULE, DRIVER_PEDO_NAME);
+
+ if (IS_ERR(pedo_class)) {
+ ERR("Unable to create pedo_class, error %d", ret);
+ unregister_chrdev_region(dev, 1);
+ return PTR_ERR(pedo_class);
+ }
+ pedo_class->devnode = pedo_devnode;
+
+ cdev_init(cdev, &pedo_fops);
+ cdev->owner = THIS_MODULE;
+ err = cdev_add(cdev, MKDEV(pedo_major, 0), 1);
+ if (err) {
+ ERR("unable to add cdev with error %d for pedo device\n", err);
+ }
+
+ if (IS_ERR(device_create(pedo_class, NULL, MKDEV(pedo_major, 0), NULL,
+ kasprintf(GFP_KERNEL, "%s", DRIVER_PEDO_NAME)))) {
+ ERR("failed to create pedo device");
+ pedo_clear(gdata);
+ return -1;
+ }
+
+ ret = create_input_device(gdata);
+ if (ret) {
+ ERR("failed to create input device");
+ return ret;
+ }
+
+ ret = set_initial_value(gdata);
+ if (ret) {
+ ERR("failed to set initial value");
+ return ret;
+ }
+
+ INFO("maru_pedo device init ends.");
+
+ return ret;
+}
+
+int maru_pedo_exit(struct virtio_sensor *vs)
+{
+ struct maru_pedo_data *data = NULL;
+
+ data = (struct maru_pedo_data *)vs->pedo_handle;
+ pedo_clear(data);
+ INFO("maru_pedo device exit ends.");
+ return 0;
+}
};
enum sensor_types {
- sensor_type_list = 0,
- sensor_type_accel,
- sensor_type_accel_enable,
- sensor_type_accel_delay,
+ sensor_type_list = 0,
+ sensor_type_accel,
+ sensor_type_accel_enable,
+ sensor_type_accel_delay,
sensor_type_geo,
- sensor_type_geo_enable, // 5
- sensor_type_geo_delay,
+ sensor_type_geo_enable, // 5
+ sensor_type_geo_delay,
sensor_type_gyro,
- sensor_type_gyro_enable,
- sensor_type_gyro_delay,
- sensor_type_gyro_x, // 10
+ sensor_type_gyro_enable,
+ sensor_type_gyro_delay,
+ sensor_type_gyro_x, // 10
sensor_type_gyro_y,
sensor_type_gyro_z,
sensor_type_light,
sensor_type_light_enable,
- sensor_type_light_delay, // 15
+ sensor_type_light_delay, // 15
sensor_type_light_adc,
sensor_type_light_level,
sensor_type_proxi,
sensor_type_proxi_enable,
- sensor_type_proxi_delay, // 20
+ sensor_type_proxi_delay, // 20
sensor_type_rotation_vector,
sensor_type_rotation_vector_enable,
sensor_type_rotation_vector_delay,
sensor_type_mag,
- sensor_type_tilt, // 25
- sensor_type_pressure,
- sensor_type_pressure_enable,
- sensor_type_pressure_delay,
- sensor_type_uv,
- sensor_type_uv_enable,
- sensor_type_uv_delay,
- sensor_type_hrm,
- sensor_type_hrm_heart,
- sensor_type_hrm_rri,
- sensor_type_hrm_enable,
- sensor_type_hrm_delay,
+ sensor_type_tilt, // 25
+ sensor_type_pressure,
+ sensor_type_pressure_enable,
+ sensor_type_pressure_delay,
+ sensor_type_uv,
+ sensor_type_uv_enable, // 30
+ sensor_type_uv_delay,
+ sensor_type_hrm,
+ sensor_type_hrm_heart,
+ sensor_type_hrm_rri,
+ sensor_type_hrm_enable, // 35
+ sensor_type_hrm_delay,
+ sensor_type_pedo,
+ sensor_type_pedo_enable,
+ sensor_type_pedo_delay,
sensor_type_max
};
sensor_cap_gyro = 0x0004,
sensor_cap_light = 0x0008,
sensor_cap_proxi = 0x0010,
- sensor_cap_rotation_vector = 0x0020,
+ sensor_cap_rotation_vector = 0x0020,
sensor_cap_haptic = 0x0040,
sensor_cap_pressure = 0x0080,
sensor_cap_uv = 0x0100,
- sensor_cap_hrm = 0x0200
+ sensor_cap_hrm = 0x0200,
+ sensor_cap_pedo = 0x0400
};
#define __MAX_BUF_SIZE 1024
void* pressure_handle;
void* uv_handle;
void* hrm_handle;
+ void* pedo_handle;
};
#define MARU_DEVICE_ATTR(_name) \
#define MARU_ATTR_RONLY(_name) { \
.attr = { .name = __stringify(_name), .mode = 0444 }, \
- .show = maru_##_name##_show, \
+ .show = maru_##_name##_show, \
}
#define MARU_ATTR_RW(_name) { \
.attr = {.name = __stringify(_name), .mode = 0644 }, \
- .show = maru_##_name##_show, \
- .store = maru_##_name##_store, \
+ .show = maru_##_name##_show, \
+ .store = maru_##_name##_store, \
}
int sensor_atoi(const char *value);
void set_sensor_data(int type, const char* buf);
int get_sensor_data(int type, char* data);
-#define SENSOR_CLASS_NAME "sensors"
+#define SENSOR_CLASS_NAME "sensors"
#define MARU_SENSOR_DEVICE_VENDOR "Tizen_SDK"
-#define DRIVER_ACCEL_NAME "accel"
+#define DRIVER_ACCEL_NAME "accel"
#define SENSOR_ACCEL_INPUT_NAME "accelerometer_sensor"
#define MARU_ACCEL_DEVICE_NAME "maru_sensor_accel_1"
-#define DRIVER_GEO_NAME "geo"
+#define DRIVER_GEO_NAME "geo"
#define SENSOR_GEO_INPUT_NAME "geomagnetic_sensor"
#define MARU_GEO_DEVICE_NAME "maru_sensor_geo_1"
-#define DRIVER_GYRO_NAME "gyro"
+#define DRIVER_GYRO_NAME "gyro"
#define SENSOR_GYRO_INPUT_NAME "gyro_sensor"
#define MARU_GYRO_DEVICE_NAME "maru_sensor_gyro_1"
-#define DRIVER_LIGHT_NAME "light"
+#define DRIVER_LIGHT_NAME "light"
#define SENSOR_LIGHT_INPUT_NAME "light_sensor"
#define MARU_LIGHT_DEVICE_NAME "maru_sensor_light_1"
-#define DRIVER_PROXI_NAME "proxi"
+#define DRIVER_PROXI_NAME "proxi"
#define SENSOR_PROXI_INPUT_NAME "proximity_sensor"
#define MARU_PROXI_DEVICE_NAME "maru_sensor_proxi_1"
#define SENSOR_PRESSURE_INPUT_NAME "pressure_sensor"
#define MARU_PRESSURE_DEVICE_NAME "maru_sensor_pressure_1"
-#define DRIVER_UV_NAME "ultraviolet"
+#define DRIVER_UV_NAME "ultraviolet"
#define SENSOR_UV_INPUT_NAME "uv_sensor"
-#define MARU_UV_DEVICE_NAME "maru_sensor_uv_1"
+#define MARU_UV_DEVICE_NAME "maru_sensor_uv_1"
-#define DRIVER_HRM_NAME "hrm"
+#define DRIVER_HRM_NAME "hrm"
#define SENSOR_HRM_INPUT_NAME "hrm_lib_sensor"
#define MARU_HRM_DEVICE_NAME "maru_sensor_hrm_1"
+#define DRIVER_PEDO_NAME "ssp_sensorhub"
+#define SENSOR_PEDO_INPUT_NAME "ssp_context"
+#define MARU_PEDO_DEVICE_NAME "maru_sensor_pedo_1"
+
// It locates /sys/module/maru_virtio_sensor/parameters/sensor_driver_debug
extern int sensor_driver_debug;
int maru_hrm_init(struct virtio_sensor *vs);
int maru_hrm_exit(struct virtio_sensor *vs);
+/*
+ * Pedometer device
+ */
+int maru_pedo_init(struct virtio_sensor *vs);
+int maru_pedo_exit(struct virtio_sensor *vs);
+
#endif