From: Jinhyung Choi Date: Mon, 16 Jun 2014 01:37:27 +0000 (+0900) Subject: sensors: created virtual device for accel, gyro, geo, light, and proxi X-Git-Tag: submit/tizen_common/20140905.094502~16 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=89a8fbc79ce4175e3c7a756c3d76015907132c4a;p=sdk%2Femulator%2Femulator-kernel.git sensors: created virtual device for accel, gyro, geo, light, and proxi Change-Id: I554643e382212dcb06ef82e8b7cc00424b321e42 Signed-off-by: Jinhyung Choi --- diff --git a/drivers/maru/Makefile b/drivers/maru/Makefile index 9001653b030e..ff7408b1078a 100644 --- a/drivers/maru/Makefile +++ b/drivers/maru/Makefile @@ -7,6 +7,6 @@ obj-$(CONFIG_MARU_POWER_SUPPLY) += maru_power_supply.o obj-$(CONFIG_MARU_VIRTIO_KEYBOARD) += maru_virtio_keyboard.o obj-$(CONFIG_MARU_VIRTIO_NFC) += maru_virtio_nfc.o obj-$(CONFIG_MARU_VIRTIO_EVDI) += maru_virtio_evdi.o -obj-$(CONFIG_MARU_VIRTIO_SENSOR) += maru_virtio_sensor.o +obj-$(CONFIG_MARU_VIRTIO_SENSOR) += sensors/ #maru_virtio_sensor.o obj-$(CONFIG_MARU_BRILLCODEC) += maru_brillcodec.o obj-$(CONFIG_MARU_VIRTIO_VMODEM) += maru_virtio_vmodem.o diff --git a/drivers/maru/maru_virtio_sensor.c b/drivers/maru/maru_virtio_sensor.c deleted file mode 100644 index c7d9eaa899fc..000000000000 --- a/drivers/maru/maru_virtio_sensor.c +++ /dev/null @@ -1,659 +0,0 @@ -/* - * Maru Virtio Sensor Device Driver - * - * Copyright (c) 2012 Samsung Electronics Co., Ltd. All rights reserved. - * - * Contact: - * Jinhyung Choi - * Daiyoung Kim - * YeongKyoon Lee - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -#define LOG(log_level, fmt, ...) \ - printk(log_level "%s: " fmt "\n", SENSOR_CLASS_NAME, ##__VA_ARGS__) - -#define SENSOR_CLASS_NAME "sensor" - -#define DRIVER_ACCEL_NAME "accel" -#define DRIVER_GEO_NAME "geo" -#define DRIVER_GYRO_NAME "gyro" -#define DRIVER_LIGHT_NAME "light" -#define DRIVER_PROXI_NAME "proxi" - -#define __MAX_BUF_SIZE 1024 -#define __MAX_BUF_SENSOR 32 - -#define DEVICE_COUNT 5 - -enum sensor_types { - sensor_type_list = 0, - sensor_type_accel, - sensor_type_geo, - sensor_type_gyro, - sensor_type_gyro_x, - sensor_type_gyro_y, - sensor_type_gyro_z, - sensor_type_light, - sensor_type_light_adc, - sensor_type_light_level, - sensor_type_proxi, - sensor_type_mag, - sensor_type_tilt, - sensor_type_max -}; - -enum sensor_capabilities { - sensor_cap_accel = 0x01, - sensor_cap_geo = 0x02, - sensor_cap_gyro = 0x04, - sensor_cap_light = 0x08, - sensor_cap_proxi = 0x10 -}; - -enum sensor_attributes { - sensor_attr_accel = 0, - sensor_attr_geo, - sensor_attr_gyro, - sensor_attr_light, - sensor_attr_proxi -}; - -enum request_cmd { - request_get = 0, - request_set, - request_answer -}; - -struct msg_info { - char buf[__MAX_BUF_SIZE]; - - uint16_t type; - uint16_t req; -}; - -struct virtio_sensor { - struct virtio_device* vdev; - struct virtqueue* vq; - - struct msg_info msginfo; - - struct scatterlist sg_vq[2]; - - int flags; - struct mutex lock; -}; - -static struct virtio_device_id id_table[] = { { VIRTIO_ID_SENSOR, - VIRTIO_DEV_ANY_ID }, { 0 }, }; - -struct virtio_sensor *vs; - -static struct class* sensor_class; - -static DECLARE_WAIT_QUEUE_HEAD(wq); - -#define ___ATTR_RONLY(_name,_show) { \ - .attr = { .name = __stringify(_name), .mode = 0444 }, \ - .show = _show, \ -} - -#define ___ATTR_RW(_name) { \ - .attr = {.name = __stringify(_name), .mode = 0644 }, \ - .show = _name##_show, \ - .store = _name##_store, \ -} - -static char sensor_data [PAGE_SIZE]; -static int sensor_capability = 0; - -static void sensor_vq_done(struct virtqueue *rvq) { - unsigned int len; - struct msg_info* msg; - - msg = (struct msg_info*) virtqueue_get_buf(vs->vq, &len); - if (msg == NULL) { - LOG(KERN_ERR, "failed to virtqueue_get_buf"); - return; - } - - if (msg->req != request_answer || msg->buf == NULL) { - LOG(KERN_ERR, "message from host is cracked."); - return; - } - - LOG(KERN_DEBUG, "msg buf: %s, req: %d, type: %d", msg->buf, msg->req, msg->type); - - mutex_lock(&vs->lock); - strcpy(sensor_data, msg->buf); - vs->flags = 1; - mutex_unlock(&vs->lock); - - wake_up_interruptible(&wq); -} - -static int sensor_atoi(const char *name) -{ - int val = 0; - - for (;; name++) { - switch (*name) { - case '0' ... '9': - val = 10*val+(*name-'0'); - break; - default: - return val; - } - } -} - -static void set_sensor_data(int type, const char* buf) -{ - int err = 0; - - if (buf == NULL) { - LOG(KERN_ERR, "set_sensor buf is NULL."); - return; - } - - if (vs == NULL) { - LOG(KERN_ERR, "Invalid sensor handle"); - return; - } - - mutex_lock(&vs->lock); - memset(sensor_data, 0, PAGE_SIZE); - memset(&vs->msginfo, 0, sizeof(vs->msginfo)); - - strcpy(sensor_data, buf); - - vs->msginfo.req = request_set; - vs->msginfo.type = type; - strcpy(vs->msginfo.buf, buf); - mutex_unlock(&vs->lock); - - LOG(KERN_DEBUG, "set_sensor_data type: %d, req: %d, buf: %s", - vs->msginfo.type, vs->msginfo.req, vs->msginfo.buf); - - err = virtqueue_add_outbuf(vs->vq, vs->sg_vq, 1, &vs->msginfo, GFP_ATOMIC); - if (err < 0) { - LOG(KERN_ERR, "failed to add out buffer to virtqueue (err = %d)", err); - return; - } - - virtqueue_kick(vs->vq); -} - -static void get_sensor_data(int type) -{ - struct scatterlist *sgs[2]; - int err = 0; - - if (vs == NULL) { - LOG(KERN_ERR, "Invalid sensor handle"); - return; - } - - mutex_lock(&vs->lock); - memset(sensor_data, 0, PAGE_SIZE); - memset(&vs->msginfo, 0, sizeof(vs->msginfo)); - - vs->msginfo.req = request_get; - vs->msginfo.type = type; - - mutex_unlock(&vs->lock); - - LOG(KERN_DEBUG, "get_sensor_data type: %d, req: %d", - vs->msginfo.type, vs->msginfo.req); - - sgs[0] = &vs->sg_vq[0]; - sgs[1] = &vs->sg_vq[1]; - err = virtqueue_add_sgs(vs->vq, sgs, 1, 1, &vs->msginfo, GFP_ATOMIC); - if (err < 0) { - LOG(KERN_ERR, "failed to add in buffer to virtqueue (err = %d)", err); - return; - } - - virtqueue_kick(vs->vq); - - wait_event_interruptible(wq, vs->flags != 0); - - mutex_lock(&vs->lock); - vs->flags = 0; - mutex_unlock(&vs->lock); -} - -/* - * Accelerometer - */ -#define ACCEL_NAME_STR "accel_sim" -#define ACCEL_FILE_NUM 2 - -static ssize_t accel_name_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - return snprintf(buf, PAGE_SIZE, ACCEL_NAME_STR); -} - -static ssize_t xyz_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - get_sensor_data(sensor_type_accel); - return snprintf(buf, PAGE_SIZE, "%s", sensor_data); -} - -static ssize_t xyz_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) -{ - set_sensor_data(sensor_type_accel, buf); - return strnlen(buf, PAGE_SIZE); -} - -static struct device_attribute da_accel [] = -{ - ___ATTR_RONLY(name, accel_name_show), - ___ATTR_RW(xyz), -}; - -/* - * GeoMagnetic - */ -#define GEO_NAME_STR "geo_sim" -#define GEO_FILE_NUM 3 - -static ssize_t geo_name_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - return snprintf(buf, PAGE_SIZE, GEO_NAME_STR); -} - -static ssize_t raw_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - get_sensor_data(sensor_type_tilt); - return snprintf(buf, PAGE_SIZE, "%s", sensor_data); -} - -static ssize_t raw_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) -{ - set_sensor_data(sensor_type_tilt, buf); - return strnlen(buf, PAGE_SIZE); -} - -static ssize_t tesla_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - get_sensor_data(sensor_type_mag); - return snprintf(buf, PAGE_SIZE, "%s", sensor_data); -} - -static ssize_t tesla_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) -{ - set_sensor_data(sensor_type_mag, buf); - return strnlen(buf, PAGE_SIZE); -} - -static struct device_attribute da_geo [] = -{ - ___ATTR_RONLY(name, geo_name_show), - ___ATTR_RW(raw), - ___ATTR_RW(tesla), -}; - - -/* - * Gyroscope - */ - -#define GYRO_NAME_STR "gyro_sim" -#define GYRO_FILE_NUM 4 - -static ssize_t gyro_name_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - return snprintf(buf, PAGE_SIZE, GYRO_NAME_STR); -} - -static ssize_t gyro_x_raw_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - get_sensor_data(sensor_type_gyro_x); - return snprintf(buf, PAGE_SIZE, "%s", sensor_data); -} - -static ssize_t gyro_x_raw_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) -{ - set_sensor_data(sensor_type_gyro_x, buf); - return strnlen(buf, PAGE_SIZE); -} - -static ssize_t gyro_y_raw_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - get_sensor_data(sensor_type_gyro_y); - return snprintf(buf, PAGE_SIZE, "%s", sensor_data); -} - -static ssize_t gyro_y_raw_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) -{ - set_sensor_data(sensor_type_gyro_y, buf); - return strnlen(buf, PAGE_SIZE); -} - -static ssize_t gyro_z_raw_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - get_sensor_data(sensor_type_gyro_z); - return snprintf(buf, PAGE_SIZE, "%s", sensor_data); -} - -static ssize_t gyro_z_raw_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) -{ - set_sensor_data(sensor_type_gyro_z, buf); - return strnlen(buf, PAGE_SIZE); -} - -static struct device_attribute da_gyro [] = -{ - ___ATTR_RONLY(name, gyro_name_show), - ___ATTR_RW(gyro_x_raw), - ___ATTR_RW(gyro_y_raw), - ___ATTR_RW(gyro_z_raw), -}; - -/* - * Light - */ - -#define LIGHT_NAME_STR "light_sim" -#define LIGHT_FILE_NUM 3 - -static ssize_t light_name_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - return snprintf(buf, PAGE_SIZE, LIGHT_NAME_STR); -} - -static ssize_t adc_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - get_sensor_data(sensor_type_light_adc); - return snprintf(buf, PAGE_SIZE, "%s", sensor_data); -} - -static ssize_t adc_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) -{ - set_sensor_data(sensor_type_light_adc, buf); - return strnlen(buf, PAGE_SIZE); -} - -static ssize_t level_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - get_sensor_data(sensor_type_light_level); - return snprintf(buf, PAGE_SIZE, "%s", sensor_data); -} - -static ssize_t level_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) -{ - set_sensor_data(sensor_type_light_level, buf); - return strnlen(buf, PAGE_SIZE); -} - -static struct device_attribute da_light [] = -{ - ___ATTR_RONLY(name, light_name_show), - ___ATTR_RW(adc), - ___ATTR_RW(level), -}; - - -/* - * Proxi - */ - -#define PROXI_NAME_STR "proxi_sim" -#define PROXI_FILE_NUM 3 - -static int proxi_enable = 1; - -static ssize_t proxi_name_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - return snprintf(buf, PAGE_SIZE, PROXI_NAME_STR); -} - -static ssize_t enable_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - return snprintf(buf, PAGE_SIZE, "%d", proxi_enable); -} - -static ssize_t enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) -{ - sscanf(buf, "%d", &proxi_enable); - return strnlen(buf, PAGE_SIZE); -} - -static ssize_t vo_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - get_sensor_data(sensor_type_proxi); - return snprintf(buf, PAGE_SIZE, "%s", sensor_data); -} - -static ssize_t vo_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) -{ - set_sensor_data(sensor_type_proxi, buf); - return strnlen(buf, PAGE_SIZE); -} - -static struct device_attribute da_proxi [] = -{ - ___ATTR_RONLY(name, proxi_name_show), - ___ATTR_RW(enable), - ___ATTR_RW(vo), -}; - -/* - * Initialize Devices - */ - -static struct device* device_list[DEVICE_COUNT]; - -const char *device_name_list[] = { DRIVER_ACCEL_NAME, DRIVER_GEO_NAME, DRIVER_GYRO_NAME, DRIVER_LIGHT_NAME, DRIVER_PROXI_NAME }; - -const int device_file_num[] = {ACCEL_FILE_NUM, GEO_FILE_NUM, GYRO_FILE_NUM, LIGHT_FILE_NUM, PROXI_FILE_NUM}; - -static struct device_attribute* da_list[] = {da_accel, da_geo, da_gyro, da_light, da_proxi }; - -static void class_cleanup (void) -{ - int i = 0, j = 0; - - for (i = 0; i < DEVICE_COUNT; i++) { - - if (device_list[i] == NULL) - continue; - - for (j = 0; j < device_file_num[i]; j++) { - device_remove_file(device_list[i], &da_list[i][j]); - } - - device_destroy(sensor_class, (dev_t)NULL); - device_list[i] = NULL; - } - - class_destroy(sensor_class); - sensor_class = NULL; -} - -static int init_device(void) -{ - int i = 0; - int j = 0; - int ret = 0; - - for (i = 0; i < DEVICE_COUNT; i++) { - if (i == sensor_attr_accel && !(sensor_capability & sensor_cap_accel)) { - continue; - } else if (i == sensor_attr_geo && !(sensor_capability & sensor_cap_geo)) { - continue; - } else if (i == sensor_attr_gyro && !(sensor_capability & sensor_cap_gyro)) { - continue; - } else if (i == sensor_attr_light && !(sensor_capability & sensor_cap_light)) { - continue; - } else if (i == sensor_attr_proxi && !(sensor_capability & sensor_cap_proxi)) { - continue; - } - device_list[i] = device_create(sensor_class, NULL, (dev_t)NULL, NULL, device_name_list[i]); - if (device_list[i] < 0) { - LOG(KERN_ERR, "%dth sensor device creation is failed.", i); - goto device_err; - } - - for (j = 0; j < device_file_num[i]; j++) { - ret = device_create_file(device_list[i], &da_list[i][j]); - if (ret) { - LOG(KERN_ERR, "%dth file creation is failed from %dth sensor device.", i, j); - goto device_err; - } - } - } - - return ret; -device_err: - class_cleanup(); - return -1; -} - -static void cleanup(struct virtio_device* dev) { - dev->config->del_vqs(dev); - - if (vs) { - kfree(vs); - vs = NULL; - } - - class_cleanup(); -} - -static int sensor_probe(struct virtio_device* dev) -{ - int err = 0; - int ret = 0; - int index = 0; - - LOG(KERN_INFO, "Sensor probe starts"); - - vs = kmalloc(sizeof(struct virtio_sensor), GFP_KERNEL); - - vs->vdev = dev; - dev->priv = vs; - - sensor_class = class_create(THIS_MODULE, SENSOR_CLASS_NAME); - if (sensor_class == NULL) { - LOG(KERN_ERR, "sensor class creation is failed."); - return -1; - } - - vs->vq = virtio_find_single_vq(dev, sensor_vq_done, "sensor"); - if (IS_ERR(vs->vq)) { - cleanup(dev); - LOG(KERN_ERR, "failed to init virt queue"); - return ret; - } - - virtqueue_enable_cb(vs->vq); - - memset(&vs->msginfo, 0x00, sizeof(vs->msginfo)); - - sg_init_table(vs->sg_vq, 2); - for (; index < 2; index++) { - sg_set_buf(&vs->sg_vq[index], &vs->msginfo, sizeof(vs->msginfo)); - } - - mutex_init(&vs->lock); - - LOG(KERN_INFO, "Sensor probe completes"); - - get_sensor_data(sensor_type_list); - sensor_capability = sensor_atoi(sensor_data); - LOG(KERN_INFO, "sensor capability is %02x", sensor_capability); - - ret = init_device(); - if (ret) { - LOG(KERN_ERR, "failed initialing devices"); - cleanup(dev); - return ret; - } - - return err; -} - -static void sensor_remove(struct virtio_device* dev) -{ - struct virtio_sensor* vs = dev->priv; - if (!vs) - { - LOG(KERN_ERR, "vs is NULL"); - return; - } - - dev->config->reset(dev); - - cleanup(dev); - - LOG(KERN_INFO, "Sensor driver is removed."); -} - -MODULE_DEVICE_TABLE(virtio, id_table); - -static struct virtio_driver virtio_sensor_driver = { - .driver = { - .name = KBUILD_MODNAME, - .owner = THIS_MODULE , - }, - .id_table = id_table, - .probe = sensor_probe, - .remove = sensor_remove, -}; - - -static int __init sensor_init(void) -{ - LOG(KERN_INFO, "Sensor driver initialized."); - - return register_virtio_driver(&virtio_sensor_driver); -} - -static void __exit sensor_exit(void) -{ - unregister_virtio_driver(&virtio_sensor_driver); - - LOG(KERN_INFO, "Sensor driver is destroyed."); -} - -module_init(sensor_init); -module_exit(sensor_exit); - -MODULE_LICENSE("GPL2"); -MODULE_AUTHOR("Jinhyung Choi "); -MODULE_DESCRIPTION("Emulator Virtio Sensor Driver"); - diff --git a/drivers/maru/sensors/Makefile b/drivers/maru/sensors/Makefile new file mode 100644 index 000000000000..57259aa78063 --- /dev/null +++ b/drivers/maru/sensors/Makefile @@ -0,0 +1,6 @@ +obj-$(CONFIG_MARU_VIRTIO_SENSOR) += maru_virtio_sensor.o \ + maru_accel.o \ + maru_geo.o \ + maru_gyro.o \ + maru_light.o \ + maru_proxi.o diff --git a/drivers/maru/sensors/maru_accel.c b/drivers/maru/sensors/maru_accel.c new file mode 100644 index 000000000000..442fcbbeddfa --- /dev/null +++ b/drivers/maru/sensors/maru_accel.c @@ -0,0 +1,381 @@ +/* + * Maru Virtio Accelerometer Sensor Device Driver + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: + * Jinhyung Choi + * Sangho Park + * YeongKyoon Lee + * + * 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 +#include + +#include "maru_virtio_sensor.h" + +struct maru_accel_data { + struct input_dev *input_data; + struct delayed_work work; + struct mutex data_mutex; + + struct virtio_sensor* vs; + + atomic_t enable; + atomic_t poll_delay; +}; + +static struct device *accel_sensor_device; + +#ifdef SUPPORT_LEGACY_SENSOR +static struct device *l_accel_sensor_device; +#endif + +static void maru_accel_input_work_func(struct work_struct *work) { + + int poll_time = 200000000; + int enable = 0; + int ret = 0; + int accel_x, accel_y, accel_z; + char sensor_data[__MAX_BUF_SENSOR]; + struct maru_accel_data *data = container_of((struct delayed_work *)work, + struct maru_accel_data, work); + + LOG(KERN_DEBUG, "maru_accel_input_work_func starts"); + + memset(sensor_data, 0, __MAX_BUF_SENSOR); + poll_time = atomic_read(&data->poll_delay); + + mutex_lock(&data->data_mutex); + enable = atomic_read(&data->enable); + mutex_unlock(&data->data_mutex); + + if (enable) { + mutex_lock(&data->data_mutex); + ret = get_sensor_data(sensor_type_accel, sensor_data); + mutex_unlock(&data->data_mutex); + if (!ret) { + sscanf(sensor_data, "%d,%d,%d", &accel_x, &accel_y, &accel_z); + LOG(KERN_INFO, "accel_set %d, %d, %d", accel_x, accel_y, accel_z); + + input_report_rel(data->input_data, REL_RX, accel_x); + input_report_rel(data->input_data, REL_RY, accel_y); + input_report_rel(data->input_data, REL_RZ, accel_z); + input_sync(data->input_data); + } + } + + mutex_lock(&data->data_mutex); + enable = atomic_read(&data->enable); + mutex_unlock(&data->data_mutex); + + LOG(KERN_DEBUG, "enable: %d, poll_time: %d", enable, poll_time); + if (enable) { + if (poll_time > 0) { + schedule_delayed_work(&data->work, nsecs_to_jiffies(poll_time)); + } else { + schedule_delayed_work(&data->work, 0); + } + } + + LOG(KERN_DEBUG, "maru_accel_input_work_func ends"); + +} + +static ssize_t maru_name_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s", MARU_ACCEL_DEVICE_NAME); +} + +static ssize_t maru_vendor_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s", MARU_SENSOR_DEVICE_VENDOR); +} + +static ssize_t maru_enable_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return get_data_for_show(sensor_type_accel_enable, buf); +} + +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_accel_data *data = input_get_drvdata(input_data); + int value = simple_strtoul(buf, NULL, 10); + + if (value != 0 && value != 1) + return count; + + set_sensor_data(sensor_type_accel_enable, buf); + + 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) +{ + return get_data_for_show(sensor_type_accel_delay, buf); +} + +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_accel_data *data = input_get_drvdata(input_data); + int value = simple_strtoul(buf, NULL, 10); + + set_sensor_data(sensor_type_accel_delay, buf); + atomic_set(&data->poll_delay, value); + + return strnlen(buf, __MAX_BUF_SENSOR); +} + +static struct device_attribute dev_attr_sensor_name = + __ATTR(name, S_IRUGO, maru_name_show, NULL); + +static struct device_attribute dev_attr_sensor_vendor = + __ATTR(vendor, S_IRUGO, maru_vendor_show, NULL); + +static struct device_attribute *accel_sensor_attrs [] = { + &dev_attr_sensor_name, + &dev_attr_sensor_vendor, + NULL, +}; + +#ifdef SUPPORT_LEGACY_SENSOR +#define ACCEL_NAME_STR "accel_sim" + +static ssize_t accel_name_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s", ACCEL_NAME_STR); +} + +static ssize_t xyz_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return get_data_for_show(sensor_type_accel, buf); +} + +static ssize_t xyz_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + set_sensor_data(sensor_type_accel, buf); + return strnlen(buf, __MAX_BUF_SENSOR); +} + +static struct device_attribute dev_attr_l_sensor_name = + __ATTR(name, S_IRUGO, accel_name_show, NULL); + +static DEVICE_ATTR(xyz, 0644, xyz_show, xyz_store); + +static struct device_attribute *l_accel_sensor_attrs [] = { + &dev_attr_l_sensor_name, + &dev_attr_xyz, + NULL, +}; +#endif + +static struct device_attribute attr_accel [] = +{ + MARU_ATTR_RW(enable), + MARU_ATTR_RW(poll_delay), +}; + +static struct attribute *maru_accel_attribute[] = { + &attr_accel[0].attr, + &attr_accel[1].attr, + NULL +}; + +static struct attribute_group maru_accel_attribute_group = { + .attrs = maru_accel_attribute +}; + +static void accel_clear(struct maru_accel_data *data) { + if (data == NULL) + return; + + if (data->input_data) { + sysfs_remove_group(&data->input_data->dev.kobj, + &maru_accel_attribute_group); + input_free_device(data->input_data); + } + + kfree(data); + data = NULL; +} + +static int set_initial_value(struct maru_accel_data *data) +{ + int delay = 0; + int ret = 0; + int enable = 0; + char sensor_data [__MAX_BUF_SENSOR]; + + memset(sensor_data, 0, __MAX_BUF_SENSOR); + + ret = get_sensor_data(sensor_type_accel_delay, sensor_data); + if (ret) { + LOG(KERN_ERR, "failed to get initial delay time"); + return ret; + } + + delay = sensor_atoi(sensor_data); + + ret = get_sensor_data(sensor_type_accel_enable, sensor_data); + if (ret) { + LOG(KERN_ERR, "failed to get initial enable"); + return ret; + } + + enable = sensor_atoi(sensor_data); + + if (delay < 0) { + LOG(KERN_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_accel_data *data) +{ + int ret = 0; + struct input_dev *input_data = NULL; + + input_data = input_allocate_device(); + if (input_data == NULL) { + LOG(KERN_ERR, "failed initialing input handler"); + accel_clear(data); + return -ENOMEM; + } + + input_data->name = SENSOR_ACCEL_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); + input_set_capability(input_data, EV_REL, REL_RY); + input_set_capability(input_data, EV_REL, REL_RZ); + + data->input_data = input_data; + + ret = input_register_device(input_data); + if (ret) { + LOG(KERN_ERR, "failed to register input data"); + accel_clear(data); + return ret; + } + + input_set_drvdata(input_data, data); + + ret = sysfs_create_group(&input_data->dev.kobj, + &maru_accel_attribute_group); + if (ret) { + accel_clear(data); + LOG(KERN_ERR, "failed initialing devices"); + return ret; + } + + return ret; +} + +int maru_accel_init(struct virtio_sensor *vs) { + int ret = 0; + struct maru_accel_data *data = NULL; + + LOG(KERN_INFO, "maru_accel device init starts."); + + data = kmalloc(sizeof(struct maru_accel_data), GFP_KERNEL); + if (data == NULL) { + LOG(KERN_ERR, "failed to create accel data."); + return -ENOMEM; + } + + vs->accel_handle = data; + data->vs = vs; + + mutex_init(&data->data_mutex); + + INIT_DELAYED_WORK(&data->work, maru_accel_input_work_func); + + // create name & vendor + ret = register_sensor_device(accel_sensor_device, vs, + accel_sensor_attrs, DRIVER_ACCEL_NAME); + if (ret) { + LOG(KERN_ERR, "failed to register accel device"); + accel_clear(data); + return -1; + } + +#ifdef SUPPORT_LEGACY_SENSOR + ret = l_register_sensor_device(l_accel_sensor_device, vs, + l_accel_sensor_attrs, DRIVER_ACCEL_NAME); + if (ret) { + LOG(KERN_ERR, "failed to register legacy accel device"); + accel_clear(data); + return -1; + } +#endif + + // create input + ret = create_input_device(data); + if (ret) { + LOG(KERN_ERR, "failed to create input device"); + return ret; + } + + // set initial delay & enable + ret = set_initial_value(data); + if (ret) { + LOG(KERN_ERR, "failed to set initial value"); + return ret; + } + + LOG(KERN_INFO, "maru_accel device init ends."); + + return ret; +} + +int maru_accel_exit(struct virtio_sensor *vs) { + struct maru_accel_data *data = NULL; + + data = (struct maru_accel_data *)vs->accel_handle; + accel_clear(data); + return 0; +} diff --git a/drivers/maru/sensors/maru_geo.c b/drivers/maru/sensors/maru_geo.c new file mode 100644 index 000000000000..3406f8ccab1b --- /dev/null +++ b/drivers/maru/sensors/maru_geo.c @@ -0,0 +1,396 @@ +/* + * Maru Virtio Geomagnetic Sensor Device Driver + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: + * Jinhyung Choi + * Sangho Park + * YeongKyoon Lee + * + * 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 +#include + +#include "maru_virtio_sensor.h" + +struct maru_geo_data { + struct input_dev *input_data; + struct delayed_work work; + struct mutex data_mutex; + + struct virtio_sensor* vs; + + atomic_t enable; + atomic_t poll_delay; +}; + +static struct device *geo_sensor_device; + +#ifdef SUPPORT_LEGACY_SENSOR +static struct device *l_geo_sensor_device; +#endif + +static void maru_geo_input_work_func(struct work_struct *work) { + + int poll_time = 200000000; + int enable = 0; + int ret = 0; + int geo_x, geo_y, geo_z, hdst; + char sensor_data[__MAX_BUF_SENSOR]; + struct maru_geo_data *data = container_of((struct delayed_work *)work, + struct maru_geo_data, work); + + LOG(KERN_DEBUG, "maru_geo_input_work_func starts"); + + memset(sensor_data, 0, __MAX_BUF_SENSOR); + poll_time = atomic_read(&data->poll_delay); + + mutex_lock(&data->data_mutex); + enable = atomic_read(&data->enable); + mutex_unlock(&data->data_mutex); + + if (enable) { + mutex_lock(&data->data_mutex); + ret = get_sensor_data(sensor_type_tilt, sensor_data); + mutex_unlock(&data->data_mutex); + if (!ret) { + sscanf(sensor_data, "%d %d %d %d", &geo_x, &geo_y, &geo_z, &hdst); + LOG(KERN_INFO, "geo_set %d, %d, %d, %d", geo_x, geo_y, geo_z, hdst); + + input_report_rel(data->input_data, REL_RX, geo_x); + input_report_rel(data->input_data, REL_RY, geo_y); + input_report_rel(data->input_data, REL_RZ, geo_z); + input_report_rel(data->input_data, REL_HWHEEL, hdst); + input_sync(data->input_data); + } + } + + mutex_lock(&data->data_mutex); + enable = atomic_read(&data->enable); + mutex_unlock(&data->data_mutex); + + LOG(KERN_DEBUG, "enable: %d, poll_time: %d", enable, poll_time); + if (enable) { + if (poll_time > 0) { + schedule_delayed_work(&data->work, nsecs_to_jiffies(poll_time)); + } else { + schedule_delayed_work(&data->work, 0); + } + } + + LOG(KERN_DEBUG, "maru_geo_input_work_func ends"); + +} + +static ssize_t maru_name_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s", MARU_GEO_DEVICE_NAME); +} + +static ssize_t maru_vendor_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s", MARU_SENSOR_DEVICE_VENDOR); +} + +static ssize_t maru_enable_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return get_data_for_show(sensor_type_geo_enable, buf); +} + +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_geo_data *data = input_get_drvdata(input_data); + int value = simple_strtoul(buf, NULL, 10); + + if (value != 0 && value != 1) + return count; + + set_sensor_data(sensor_type_geo_enable, buf); + + 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) +{ + return get_data_for_show(sensor_type_geo_delay, buf); +} + +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_geo_data *data = input_get_drvdata(input_data); + int value = simple_strtoul(buf, NULL, 10); + + set_sensor_data(sensor_type_geo_delay, buf); + atomic_set(&data->poll_delay, value); + + return strnlen(buf, __MAX_BUF_SENSOR); +} + +static struct device_attribute dev_attr_sensor_name = + __ATTR(name, S_IRUGO, maru_name_show, NULL); + +static struct device_attribute dev_attr_sensor_vendor = + __ATTR(vendor, S_IRUGO, maru_vendor_show, NULL); + +static struct device_attribute *geo_sensor_attrs [] = { + &dev_attr_sensor_name, + &dev_attr_sensor_vendor, + NULL, +}; + +#ifdef SUPPORT_LEGACY_SENSOR +#define GEO_NAME_STR "geo_sim" + +static ssize_t geo_name_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s", GEO_NAME_STR); +} + +static ssize_t raw_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return get_data_for_show(sensor_type_tilt, buf); +} + +static ssize_t raw_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + set_sensor_data(sensor_type_tilt, buf); + return strnlen(buf, __MAX_BUF_SENSOR); +} + +static ssize_t tesla_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return get_data_for_show(sensor_type_mag, buf); +} + +static ssize_t tesla_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + set_sensor_data(sensor_type_mag, buf); + return strnlen(buf, __MAX_BUF_SENSOR); +} + +static struct device_attribute dev_attr_l_sensor_name = + __ATTR(name, S_IRUGO, geo_name_show, NULL); + +static DEVICE_ATTR(raw, 0644, raw_show, raw_store); +static DEVICE_ATTR(tesla, 0644, tesla_show, tesla_store); + +static struct device_attribute *l_geo_sensor_attrs [] = { + &dev_attr_l_sensor_name, + &dev_attr_raw, + &dev_attr_tesla, + NULL, +}; +#endif + +static struct device_attribute attr_geo [] = +{ + MARU_ATTR_RW(enable), + MARU_ATTR_RW(poll_delay), +}; + +static struct attribute *maru_geo_attribute[] = { + &attr_geo[0].attr, + &attr_geo[1].attr, + NULL +}; + +static struct attribute_group maru_geo_attribute_group = { + .attrs = maru_geo_attribute +}; + +static void geo_clear(struct maru_geo_data *data) { + if (data == NULL) + return; + + if (data->input_data) { + sysfs_remove_group(&data->input_data->dev.kobj, + &maru_geo_attribute_group); + input_free_device(data->input_data); + } + + kfree(data); + data = NULL; +} + +static int set_initial_value(struct maru_geo_data *data) +{ + int delay = 0; + int ret = 0; + int enable = 0; + char sensor_data [__MAX_BUF_SENSOR]; + + memset(sensor_data, 0, __MAX_BUF_SENSOR); + + ret = get_sensor_data(sensor_type_geo_delay, sensor_data); + if (ret) { + LOG(KERN_ERR, "failed to get initial delay time"); + return ret; + } + + delay = sensor_atoi(sensor_data); + + ret = get_sensor_data(sensor_type_geo_enable, sensor_data); + if (ret) { + LOG(KERN_ERR, "failed to get initial enable"); + return ret; + } + + enable = sensor_atoi(sensor_data); + + if (delay < 0) { + LOG(KERN_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_geo_data *data) +{ + int ret = 0; + struct input_dev *input_data = NULL; + + input_data = input_allocate_device(); + if (input_data == NULL) { + LOG(KERN_ERR, "failed initialing input handler"); + geo_clear(data); + return -ENOMEM; + } + + input_data->name = SENSOR_GEO_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); + input_set_capability(input_data, EV_REL, REL_RY); + input_set_capability(input_data, EV_REL, REL_RZ); + input_set_capability(input_data, EV_REL, REL_HWHEEL); + + data->input_data = input_data; + + ret = input_register_device(input_data); + if (ret) { + LOG(KERN_ERR, "failed to register input data"); + geo_clear(data); + return ret; + } + + input_set_drvdata(input_data, data); + + ret = sysfs_create_group(&input_data->dev.kobj, + &maru_geo_attribute_group); + if (ret) { + geo_clear(data); + LOG(KERN_ERR, "failed initialing devices"); + return ret; + } + + return ret; +} + +int maru_geo_init(struct virtio_sensor *vs) { + int ret = 0; + struct maru_geo_data *data = NULL; + + LOG(KERN_INFO, "maru_geo device init starts."); + + data = kmalloc(sizeof(struct maru_geo_data), GFP_KERNEL); + if (data == NULL) { + LOG(KERN_ERR, "failed to create geo data."); + return -ENOMEM; + } + + vs->geo_handle = data; + data->vs = vs; + + mutex_init(&data->data_mutex); + + INIT_DELAYED_WORK(&data->work, maru_geo_input_work_func); + + // create name & vendor + ret = register_sensor_device(geo_sensor_device, vs, + geo_sensor_attrs, DRIVER_GEO_NAME); + if (ret) { + LOG(KERN_ERR, "failed to register geo device"); + geo_clear(data); + return -1; + } + +#ifdef SUPPORT_LEGACY_SENSOR + ret = l_register_sensor_device(l_geo_sensor_device, vs, + l_geo_sensor_attrs, DRIVER_GEO_NAME); + if (ret) { + LOG(KERN_ERR, "failed to register legacy geo device"); + geo_clear(data); + return -1; + } +#endif + + // create input + ret = create_input_device(data); + if (ret) { + LOG(KERN_ERR, "failed to create input device"); + return ret; + } + + // set initial delay & enable + ret = set_initial_value(data); + if (ret) { + LOG(KERN_ERR, "failed to set initial value"); + return ret; + } + + LOG(KERN_INFO, "maru_geo device init ends."); + + return ret; +} + +int maru_geo_exit(struct virtio_sensor *vs) { + struct maru_geo_data *data = NULL; + + data = (struct maru_geo_data *)vs->geo_handle; + geo_clear(data); + return 0; +} diff --git a/drivers/maru/sensors/maru_gyro.c b/drivers/maru/sensors/maru_gyro.c new file mode 100644 index 000000000000..030032d7cf0c --- /dev/null +++ b/drivers/maru/sensors/maru_gyro.c @@ -0,0 +1,407 @@ +/* + * Maru Virtio Gyroscope Sensor Device Driver + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: + * Jinhyung Choi + * Sangho Park + * YeongKyoon Lee + * + * 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 +#include + +#include "maru_virtio_sensor.h" + +struct maru_gyro_data { + struct input_dev *input_data; + struct delayed_work work; + struct mutex data_mutex; + + struct virtio_sensor* vs; + + atomic_t enable; + atomic_t poll_delay; +}; + +static struct device *gyro_sensor_device; + +#ifdef SUPPORT_LEGACY_SENSOR +static struct device *l_gyro_sensor_device; +#endif + +static void maru_gyro_input_work_func(struct work_struct *work) { + + int poll_time = 200000000; + int enable = 0; + int ret = 0; + int gyro_x, gyro_y, gyro_z; + char sensor_data[__MAX_BUF_SENSOR]; + struct maru_gyro_data *data = container_of((struct delayed_work *)work, + struct maru_gyro_data, work); + + LOG(KERN_DEBUG, "maru_gyro_input_work_func starts"); + + memset(sensor_data, 0, __MAX_BUF_SENSOR); + poll_time = atomic_read(&data->poll_delay); + + mutex_lock(&data->data_mutex); + enable = atomic_read(&data->enable); + mutex_unlock(&data->data_mutex); + + if (enable) { + mutex_lock(&data->data_mutex); + ret = get_sensor_data(sensor_type_gyro, sensor_data); + mutex_unlock(&data->data_mutex); + if (!ret) { + sscanf(sensor_data, "%d,%d,%d", &gyro_x, &gyro_y, &gyro_z); + LOG(KERN_INFO, "gyro_set %d, %d, %d", gyro_x, gyro_y, gyro_z); + + input_report_rel(data->input_data, REL_RX, gyro_x); + input_report_rel(data->input_data, REL_RY, gyro_y); + input_report_rel(data->input_data, REL_RZ, gyro_z); + input_sync(data->input_data); + } + } + + mutex_lock(&data->data_mutex); + enable = atomic_read(&data->enable); + mutex_unlock(&data->data_mutex); + + LOG(KERN_DEBUG, "enable: %d, poll_time: %d", enable, poll_time); + if (enable) { + if (poll_time > 0) { + schedule_delayed_work(&data->work, nsecs_to_jiffies(poll_time)); + } else { + schedule_delayed_work(&data->work, 0); + } + } + + LOG(KERN_DEBUG, "maru_gyro_input_work_func ends"); + +} + +static ssize_t maru_name_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s", MARU_GYRO_DEVICE_NAME); +} + +static ssize_t maru_vendor_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s", MARU_SENSOR_DEVICE_VENDOR); +} + +static ssize_t maru_enable_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return get_data_for_show(sensor_type_gyro_enable, buf); +} + +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_gyro_data *data = input_get_drvdata(input_data); + int value = simple_strtoul(buf, NULL, 10); + + if (value != 0 && value != 1) + return count; + + set_sensor_data(sensor_type_gyro_enable, buf); + + 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) +{ + return get_data_for_show(sensor_type_gyro_delay, buf); +} + +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_gyro_data *data = input_get_drvdata(input_data); + int value = simple_strtoul(buf, NULL, 10); + + set_sensor_data(sensor_type_gyro_delay, buf); + atomic_set(&data->poll_delay, value); + + return strnlen(buf, __MAX_BUF_SENSOR); +} + +static struct device_attribute dev_attr_sensor_name = + __ATTR(name, S_IRUGO, maru_name_show, NULL); + +static struct device_attribute dev_attr_sensor_vendor = + __ATTR(vendor, S_IRUGO, maru_vendor_show, NULL); + +static struct device_attribute *gyro_sensor_attrs [] = { + &dev_attr_sensor_name, + &dev_attr_sensor_vendor, + NULL, +}; + +#ifdef SUPPORT_LEGACY_SENSOR +#define GYRO_NAME_STR "gyro_sim" + +static ssize_t gyro_name_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s", GYRO_NAME_STR); +} + +static ssize_t gyro_x_raw_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return get_data_for_show(sensor_type_gyro_x, buf); +} + +static ssize_t gyro_x_raw_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + set_sensor_data(sensor_type_gyro_x, buf); + return strnlen(buf, __MAX_BUF_SENSOR); +} + +static ssize_t gyro_y_raw_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return get_data_for_show(sensor_type_gyro_y, buf); +} + +static ssize_t gyro_y_raw_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + set_sensor_data(sensor_type_gyro_y, buf); + return strnlen(buf, __MAX_BUF_SENSOR); +} + +static ssize_t gyro_z_raw_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return get_data_for_show(sensor_type_gyro_z, buf); +} + +static ssize_t gyro_z_raw_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + set_sensor_data(sensor_type_gyro_z, buf); + return strnlen(buf, __MAX_BUF_SENSOR); +} + +static struct device_attribute dev_attr_l_sensor_name = + __ATTR(name, S_IRUGO, gyro_name_show, NULL); + +static DEVICE_ATTR(gyro_x_raw, 0644, gyro_x_raw_show, gyro_x_raw_store); +static DEVICE_ATTR(gyro_y_raw, 0644, gyro_y_raw_show, gyro_y_raw_store); +static DEVICE_ATTR(gyro_z_raw, 0644, gyro_z_raw_show, gyro_z_raw_store); + +static struct device_attribute *l_gyro_sensor_attrs [] = { + &dev_attr_l_sensor_name, + &dev_attr_gyro_x_raw, + &dev_attr_gyro_y_raw, + &dev_attr_gyro_z_raw, + NULL, +}; +#endif + +static struct device_attribute attr_gyro [] = +{ + MARU_ATTR_RW(enable), + MARU_ATTR_RW(poll_delay), +}; + +static struct attribute *maru_gyro_attribute[] = { + &attr_gyro[0].attr, + &attr_gyro[1].attr, + NULL +}; + +static struct attribute_group maru_gyro_attribute_group = { + .attrs = maru_gyro_attribute +}; + +static void gyro_clear(struct maru_gyro_data *data) { + if (data == NULL) + return; + + if (data->input_data) { + sysfs_remove_group(&data->input_data->dev.kobj, + &maru_gyro_attribute_group); + input_free_device(data->input_data); + } + + kfree(data); + data = NULL; +} + +static int set_initial_value(struct maru_gyro_data *data) +{ + int delay = 0; + int ret = 0; + int enable = 0; + char sensor_data [__MAX_BUF_SENSOR]; + + memset(sensor_data, 0, __MAX_BUF_SENSOR); + + ret = get_sensor_data(sensor_type_gyro_delay, sensor_data); + if (ret) { + LOG(KERN_ERR, "failed to get initial delay time"); + return ret; + } + + delay = sensor_atoi(sensor_data); + + ret = get_sensor_data(sensor_type_gyro_enable, sensor_data); + if (ret) { + LOG(KERN_ERR, "failed to get initial enable"); + return ret; + } + + enable = sensor_atoi(sensor_data); + + if (delay < 0) { + LOG(KERN_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_gyro_data *data) +{ + int ret = 0; + struct input_dev *input_data = NULL; + + input_data = input_allocate_device(); + if (input_data == NULL) { + LOG(KERN_ERR, "failed initialing input handler"); + gyro_clear(data); + return -ENOMEM; + } + + input_data->name = SENSOR_GYRO_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); + input_set_capability(input_data, EV_REL, REL_RY); + input_set_capability(input_data, EV_REL, REL_RZ); + + data->input_data = input_data; + + ret = input_register_device(input_data); + if (ret) { + LOG(KERN_ERR, "failed to register input data"); + gyro_clear(data); + return ret; + } + + input_set_drvdata(input_data, data); + + ret = sysfs_create_group(&input_data->dev.kobj, + &maru_gyro_attribute_group); + if (ret) { + gyro_clear(data); + LOG(KERN_ERR, "failed initialing devices"); + return ret; + } + + return ret; +} + +int maru_gyro_init(struct virtio_sensor *vs) { + int ret = 0; + struct maru_gyro_data *data = NULL; + + LOG(KERN_INFO, "maru_gyro device init starts."); + + data = kmalloc(sizeof(struct maru_gyro_data), GFP_KERNEL); + if (data == NULL) { + LOG(KERN_ERR, "failed to create gyro data."); + return -ENOMEM; + } + + vs->gyro_handle = data; + data->vs = vs; + + mutex_init(&data->data_mutex); + + INIT_DELAYED_WORK(&data->work, maru_gyro_input_work_func); + + // create name & vendor + ret = register_sensor_device(gyro_sensor_device, vs, + gyro_sensor_attrs, DRIVER_GYRO_NAME); + if (ret) { + LOG(KERN_ERR, "failed to register gyro device"); + gyro_clear(data); + return -1; + } + +#ifdef SUPPORT_LEGACY_SENSOR + ret = l_register_sensor_device(l_gyro_sensor_device, vs, + l_gyro_sensor_attrs, DRIVER_GYRO_NAME); + if (ret) { + LOG(KERN_ERR, "failed to register legacy gyro device"); + gyro_clear(data); + return -1; + } +#endif + + // create input + ret = create_input_device(data); + if (ret) { + LOG(KERN_ERR, "failed to create input device"); + return ret; + } + + // set initial delay & enable + ret = set_initial_value(data); + if (ret) { + LOG(KERN_ERR, "failed to set initial value"); + return ret; + } + + LOG(KERN_INFO, "maru_gyro device init ends."); + + return ret; +} + +int maru_gyro_exit(struct virtio_sensor *vs) { + struct maru_gyro_data *data = NULL; + + data = (struct maru_gyro_data *)vs->gyro_handle; + gyro_clear(data); + return 0; +} diff --git a/drivers/maru/sensors/maru_light.c b/drivers/maru/sensors/maru_light.c new file mode 100644 index 000000000000..7fc18544d7a2 --- /dev/null +++ b/drivers/maru/sensors/maru_light.c @@ -0,0 +1,397 @@ +/* + * Maru Virtio Light Sensor Device Driver + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: + * Jinhyung Choi + * Sangho Park + * YeongKyoon Lee + * + * 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 +#include + +#include "maru_virtio_sensor.h" + +struct maru_light_data { + struct input_dev *input_data; + struct delayed_work work; + struct mutex data_mutex; + + struct virtio_sensor* vs; + + atomic_t enable; + atomic_t poll_delay; +}; + +static struct device *light_sensor_device; + +#ifdef SUPPORT_LEGACY_SENSOR +static struct device *l_light_sensor_device; +#endif + +static void maru_light_input_work_func(struct work_struct *work) { + + int poll_time = 200000000; + int enable = 0; + int ret = 0; + int light = 0; + char sensor_data[__MAX_BUF_SENSOR]; + struct maru_light_data *data = container_of((struct delayed_work *)work, + struct maru_light_data, work); + + LOG(KERN_DEBUG, "maru_light_input_work_func starts"); + + memset(sensor_data, 0, __MAX_BUF_SENSOR); + poll_time = atomic_read(&data->poll_delay); + + mutex_lock(&data->data_mutex); + enable = atomic_read(&data->enable); + mutex_unlock(&data->data_mutex); + + if (enable) { + mutex_lock(&data->data_mutex); + ret = get_sensor_data(sensor_type_light, sensor_data); + mutex_unlock(&data->data_mutex); + if (!ret) { + sscanf(sensor_data, "%d", &light); + LOG(KERN_INFO, "light_set %d", light); + + input_report_rel(data->input_data, REL_RX, light); // LUX + input_report_rel(data->input_data, REL_HWHEEL, 0); // red + input_report_rel(data->input_data, REL_DIAL, 0); // green + input_report_rel(data->input_data, REL_WHEEL, 0); // blue + input_report_rel(data->input_data, REL_MISC, 0); // white + input_sync(data->input_data); + } + } + + mutex_lock(&data->data_mutex); + enable = atomic_read(&data->enable); + mutex_unlock(&data->data_mutex); + + LOG(KERN_DEBUG, "enable: %d, poll_time: %d", enable, poll_time); + if (enable) { + if (poll_time > 0) { + schedule_delayed_work(&data->work, nsecs_to_jiffies(poll_time)); + } else { + schedule_delayed_work(&data->work, 0); + } + } + + LOG(KERN_DEBUG, "maru_light_input_work_func ends"); + +} + +static ssize_t maru_name_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s", MARU_LIGHT_DEVICE_NAME); +} + +static ssize_t maru_vendor_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s", MARU_SENSOR_DEVICE_VENDOR); +} + +static ssize_t maru_enable_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return get_data_for_show(sensor_type_light_enable, buf); +} + +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_light_data *data = input_get_drvdata(input_data); + int value = simple_strtoul(buf, NULL, 10); + + if (value != 0 && value != 1) + return count; + + set_sensor_data(sensor_type_light_enable, buf); + + 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) +{ + return get_data_for_show(sensor_type_light_delay, buf); +} + +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_light_data *data = input_get_drvdata(input_data); + int value = simple_strtoul(buf, NULL, 10); + + set_sensor_data(sensor_type_light_delay, buf); + atomic_set(&data->poll_delay, value); + + return strnlen(buf, __MAX_BUF_SENSOR); +} + +static struct device_attribute dev_attr_sensor_name = + __ATTR(name, S_IRUGO, maru_name_show, NULL); + +static struct device_attribute dev_attr_sensor_vendor = + __ATTR(vendor, S_IRUGO, maru_vendor_show, NULL); + +static struct device_attribute *light_sensor_attrs [] = { + &dev_attr_sensor_name, + &dev_attr_sensor_vendor, + NULL, +}; + +#ifdef SUPPORT_LEGACY_SENSOR +#define LIGHT_NAME_STR "light_sim" + +static ssize_t light_name_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s", LIGHT_NAME_STR); +} + +static ssize_t adc_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return get_data_for_show(sensor_type_light_adc, buf); +} + +static ssize_t adc_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + set_sensor_data(sensor_type_light_adc, buf); + return strnlen(buf, __MAX_BUF_SENSOR); +} + +static ssize_t level_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return get_data_for_show(sensor_type_light_level, buf); +} + +static ssize_t level_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + set_sensor_data(sensor_type_light_level, buf); + return strnlen(buf, __MAX_BUF_SENSOR); +} + +static struct device_attribute dev_attr_l_sensor_name = + __ATTR(name, S_IRUGO, light_name_show, NULL); + +static DEVICE_ATTR(adc, 0644, adc_show, adc_store); +static DEVICE_ATTR(level, 0644, level_show, level_store); + +static struct device_attribute *l_light_sensor_attrs [] = { + &dev_attr_l_sensor_name, + &dev_attr_adc, + &dev_attr_level, + NULL, +}; +#endif + +static struct device_attribute attr_light [] = +{ + MARU_ATTR_RW(enable), + MARU_ATTR_RW(poll_delay), +}; + +static struct attribute *maru_light_attribute[] = { + &attr_light[0].attr, + &attr_light[1].attr, + NULL +}; + +static struct attribute_group maru_light_attribute_group = { + .attrs = maru_light_attribute +}; + +static void light_clear(struct maru_light_data *data) { + if (data == NULL) + return; + + if (data->input_data) { + sysfs_remove_group(&data->input_data->dev.kobj, + &maru_light_attribute_group); + input_free_device(data->input_data); + } + + kfree(data); + data = NULL; +} + +static int set_initial_value(struct maru_light_data *data) +{ + int delay = 0; + int ret = 0; + int enable = 0; + char sensor_data [__MAX_BUF_SENSOR]; + + memset(sensor_data, 0, __MAX_BUF_SENSOR); + + ret = get_sensor_data(sensor_type_light_delay, sensor_data); + if (ret) { + LOG(KERN_ERR, "failed to get initial delay time"); + return ret; + } + + delay = sensor_atoi(sensor_data); + + ret = get_sensor_data(sensor_type_light_enable, sensor_data); + if (ret) { + LOG(KERN_ERR, "failed to get initial enable"); + return ret; + } + + enable = sensor_atoi(sensor_data); + + if (delay < 0) { + LOG(KERN_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_light_data *data) +{ + int ret = 0; + struct input_dev *input_data = NULL; + + input_data = input_allocate_device(); + if (input_data == NULL) { + LOG(KERN_ERR, "failed initialing input handler"); + light_clear(data); + return -ENOMEM; + } + + input_data->name = SENSOR_LIGHT_INPUT_NAME; + input_data->id.bustype = BUS_I2C; + + set_bit(EV_REL, input_data->evbit); + input_set_capability(input_data, EV_REL, REL_RX); + input_set_capability(input_data, EV_REL, REL_HWHEEL); + input_set_capability(input_data, EV_REL, REL_DIAL); + input_set_capability(input_data, EV_REL, REL_WHEEL); + input_set_capability(input_data, EV_REL, REL_MISC); + + data->input_data = input_data; + + ret = input_register_device(input_data); + if (ret) { + LOG(KERN_ERR, "failed to register input data"); + light_clear(data); + return ret; + } + + input_set_drvdata(input_data, data); + + ret = sysfs_create_group(&input_data->dev.kobj, + &maru_light_attribute_group); + if (ret) { + light_clear(data); + LOG(KERN_ERR, "failed initialing devices"); + return ret; + } + + return ret; +} + +int maru_light_init(struct virtio_sensor *vs) { + int ret = 0; + struct maru_light_data *data = NULL; + + LOG(KERN_INFO, "maru_light device init starts."); + + data = kmalloc(sizeof(struct maru_light_data), GFP_KERNEL); + if (data == NULL) { + LOG(KERN_ERR, "failed to create light data."); + return -ENOMEM; + } + + vs->light_handle = data; + data->vs = vs; + + mutex_init(&data->data_mutex); + + INIT_DELAYED_WORK(&data->work, maru_light_input_work_func); + + // create name & vendor + ret = register_sensor_device(light_sensor_device, vs, + light_sensor_attrs, DRIVER_LIGHT_NAME); + if (ret) { + LOG(KERN_ERR, "failed to register light device"); + light_clear(data); + return -1; + } + +#ifdef SUPPORT_LEGACY_SENSOR + ret = l_register_sensor_device(l_light_sensor_device, vs, + l_light_sensor_attrs, DRIVER_LIGHT_NAME); + if (ret) { + LOG(KERN_ERR, "failed to register legacy light device"); + light_clear(data); + return -1; + } +#endif + + // create input + ret = create_input_device(data); + if (ret) { + LOG(KERN_ERR, "failed to create input device"); + return ret; + } + + // set initial delay & enable + ret = set_initial_value(data); + if (ret) { + LOG(KERN_ERR, "failed to set initial value"); + return ret; + } + + LOG(KERN_INFO, "maru_light device init ends."); + + return ret; +} + +int maru_light_exit(struct virtio_sensor *vs) { + struct maru_light_data *data = NULL; + + data = (struct maru_light_data *)vs->light_handle; + light_clear(data); + return 0; +} diff --git a/drivers/maru/sensors/maru_proxi.c b/drivers/maru/sensors/maru_proxi.c new file mode 100644 index 000000000000..ce3554a69cdb --- /dev/null +++ b/drivers/maru/sensors/maru_proxi.c @@ -0,0 +1,391 @@ +/* + * Maru Virtio Proximity Sensor Device Driver + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: + * Jinhyung Choi + * Sangho Park + * YeongKyoon Lee + * + * 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 +#include + +#include "maru_virtio_sensor.h" + +struct maru_proxi_data { + struct input_dev *input_data; + struct delayed_work work; + struct mutex data_mutex; + + struct virtio_sensor* vs; + + atomic_t enable; + atomic_t poll_delay; +}; + +static struct device *proxi_sensor_device; + +#ifdef SUPPORT_LEGACY_SENSOR +static struct device *l_proxi_sensor_device; +#endif + +static void maru_proxi_input_work_func(struct work_struct *work) { + + int poll_time = 200000000; + int enable = 0; + int ret = 0; + int proxi = 0; + char sensor_data[__MAX_BUF_SENSOR]; + struct maru_proxi_data *data = container_of((struct delayed_work *)work, + struct maru_proxi_data, work); + + LOG(KERN_DEBUG, "maru_proxi_input_work_func starts"); + + memset(sensor_data, 0, __MAX_BUF_SENSOR); + poll_time = atomic_read(&data->poll_delay); + + mutex_lock(&data->data_mutex); + enable = atomic_read(&data->enable); + mutex_unlock(&data->data_mutex); + + if (enable) { + mutex_lock(&data->data_mutex); + ret = get_sensor_data(sensor_type_proxi, sensor_data); + mutex_unlock(&data->data_mutex); + if (!ret) { + sscanf(sensor_data, "%d", &proxi); + if (proxi) + proxi = 1; + LOG(KERN_INFO, "proxi_set %d", proxi); + + input_report_rel(data->input_data, ABS_DISTANCE, proxi); + input_sync(data->input_data); + } + } + + mutex_lock(&data->data_mutex); + enable = atomic_read(&data->enable); + mutex_unlock(&data->data_mutex); + + LOG(KERN_DEBUG, "enable: %d, poll_time: %d", enable, poll_time); + if (enable) { + if (poll_time > 0) { + schedule_delayed_work(&data->work, nsecs_to_jiffies(poll_time)); + } else { + schedule_delayed_work(&data->work, 0); + } + } + + LOG(KERN_DEBUG, "maru_proxi_input_work_func ends"); + +} + +static ssize_t maru_name_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s", MARU_PROXI_DEVICE_NAME); +} + +static ssize_t maru_vendor_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s", MARU_SENSOR_DEVICE_VENDOR); +} + +static ssize_t maru_enable_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return get_data_for_show(sensor_type_proxi_enable, buf); +} + +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_proxi_data *data = input_get_drvdata(input_data); + int value = simple_strtoul(buf, NULL, 10); + + if (value != 0 && value != 1) + return count; + + set_sensor_data(sensor_type_proxi_enable, buf); + + 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) +{ + return get_data_for_show(sensor_type_proxi_delay, buf); +} + +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_proxi_data *data = input_get_drvdata(input_data); + int value = simple_strtoul(buf, NULL, 10); + + set_sensor_data(sensor_type_proxi_delay, buf); + atomic_set(&data->poll_delay, value); + + return strnlen(buf, __MAX_BUF_SENSOR); +} + +static struct device_attribute dev_attr_sensor_name = + __ATTR(name, S_IRUGO, maru_name_show, NULL); + +static struct device_attribute dev_attr_sensor_vendor = + __ATTR(vendor, S_IRUGO, maru_vendor_show, NULL); + +static struct device_attribute *proxi_sensor_attrs [] = { + &dev_attr_sensor_name, + &dev_attr_sensor_vendor, + NULL, +}; + +#ifdef SUPPORT_LEGACY_SENSOR +#define PROXI_NAME_STR "proxi_sim" + +static ssize_t proxi_name_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s", PROXI_NAME_STR); +} + +static ssize_t enable_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return get_data_for_show(sensor_type_proxi_enable, buf); +} + +static ssize_t enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + set_sensor_data(sensor_type_proxi_enable, buf); + return strnlen(buf, __MAX_BUF_SENSOR); +} + +static ssize_t vo_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return get_data_for_show(sensor_type_proxi, buf); +} + +static ssize_t vo_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + set_sensor_data(sensor_type_proxi, buf); + return strnlen(buf, __MAX_BUF_SENSOR); +} + +static struct device_attribute dev_attr_l_sensor_name = + __ATTR(name, S_IRUGO, proxi_name_show, NULL); + +static DEVICE_ATTR(enable, 0644, enable_show, enable_store); +static DEVICE_ATTR(vo, 0644, vo_show, vo_store); + +static struct device_attribute *l_proxi_sensor_attrs [] = { + &dev_attr_l_sensor_name, + &dev_attr_enable, + &dev_attr_vo, + NULL, +}; +#endif + +static struct device_attribute attr_proxi [] = +{ + MARU_ATTR_RW(enable), + MARU_ATTR_RW(poll_delay), +}; + +static struct attribute *maru_proxi_attribute[] = { + &attr_proxi[0].attr, + &attr_proxi[1].attr, + NULL +}; + +static struct attribute_group maru_proxi_attribute_group = { + .attrs = maru_proxi_attribute +}; + +static void proxi_clear(struct maru_proxi_data *data) { + if (data == NULL) + return; + + if (data->input_data) { + sysfs_remove_group(&data->input_data->dev.kobj, + &maru_proxi_attribute_group); + input_free_device(data->input_data); + } + + kfree(data); + data = NULL; +} + +static int set_initial_value(struct maru_proxi_data *data) +{ + int delay = 0; + int ret = 0; + int enable = 0; + char sensor_data [__MAX_BUF_SENSOR]; + + memset(sensor_data, 0, __MAX_BUF_SENSOR); + + ret = get_sensor_data(sensor_type_proxi_delay, sensor_data); + if (ret) { + LOG(KERN_ERR, "failed to get initial delay time"); + return ret; + } + + delay = sensor_atoi(sensor_data); + + ret = get_sensor_data(sensor_type_proxi_enable, sensor_data); + if (ret) { + LOG(KERN_ERR, "failed to get initial enable"); + return ret; + } + + enable = sensor_atoi(sensor_data); + + if (delay < 0) { + LOG(KERN_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_proxi_data *data) +{ + int ret = 0; + struct input_dev *input_data = NULL; + + input_data = input_allocate_device(); + if (input_data == NULL) { + LOG(KERN_ERR, "failed initialing input handler"); + proxi_clear(data); + return -ENOMEM; + } + + input_data->name = SENSOR_PROXI_INPUT_NAME; + input_data->id.bustype = BUS_I2C; + + set_bit(EV_ABS, input_data->evbit); + input_set_capability(input_data, EV_ABS, ABS_DISTANCE); + + data->input_data = input_data; + + ret = input_register_device(input_data); + if (ret) { + LOG(KERN_ERR, "failed to register input data"); + proxi_clear(data); + return ret; + } + + input_set_drvdata(input_data, data); + + ret = sysfs_create_group(&input_data->dev.kobj, + &maru_proxi_attribute_group); + if (ret) { + proxi_clear(data); + LOG(KERN_ERR, "failed initialing devices"); + return ret; + } + + return ret; +} + +int maru_proxi_init(struct virtio_sensor *vs) { + int ret = 0; + struct maru_proxi_data *data = NULL; + + LOG(KERN_INFO, "maru_proxi device init starts."); + + data = kmalloc(sizeof(struct maru_proxi_data), GFP_KERNEL); + if (data == NULL) { + LOG(KERN_ERR, "failed to create proxi data."); + return -ENOMEM; + } + + vs->proxi_handle = data; + data->vs = vs; + + mutex_init(&data->data_mutex); + + INIT_DELAYED_WORK(&data->work, maru_proxi_input_work_func); + + // create name & vendor + ret = register_sensor_device(proxi_sensor_device, vs, + proxi_sensor_attrs, DRIVER_PROXI_NAME); + if (ret) { + LOG(KERN_ERR, "failed to register proxi device"); + proxi_clear(data); + return -1; + } + +#ifdef SUPPORT_LEGACY_SENSOR + ret = l_register_sensor_device(l_proxi_sensor_device, vs, + l_proxi_sensor_attrs, DRIVER_PROXI_NAME); + if (ret) { + LOG(KERN_ERR, "failed to register legacy proxi device"); + proxi_clear(data); + return -1; + } +#endif + + // create input + ret = create_input_device(data); + if (ret) { + LOG(KERN_ERR, "failed to create input device"); + return ret; + } + + // set initial delay & enable + ret = set_initial_value(data); + if (ret) { + LOG(KERN_ERR, "failed to set initial value"); + return ret; + } + + LOG(KERN_INFO, "maru_proxi device init ends."); + + return ret; +} + +int maru_proxi_exit(struct virtio_sensor *vs) { + struct maru_proxi_data *data = NULL; + + data = (struct maru_proxi_data *)vs->proxi_handle; + proxi_clear(data); + return 0; +} diff --git a/drivers/maru/sensors/maru_virtio_sensor.c b/drivers/maru/sensors/maru_virtio_sensor.c new file mode 100644 index 000000000000..c17de0ac5851 --- /dev/null +++ b/drivers/maru/sensors/maru_virtio_sensor.c @@ -0,0 +1,459 @@ +/* + * Maru Virtio Sensor Device Driver + * + * Copyright (c) 2012 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: + * Jinhyung Choi + * Daiyoung Kim + * YeongKyoon Lee + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "maru_virtio_sensor.h" + +static struct virtio_device_id id_table[] = { { VIRTIO_ID_SENSOR, + VIRTIO_DEV_ANY_ID }, { 0 }, }; + +static char sensor_data[__MAX_BUF_SENSOR]; + +struct virtio_sensor *vs; + +static DECLARE_WAIT_QUEUE_HEAD(wq); + +int sensor_atoi(const char *value) +{ + int val = 0; + + for (;; value++) { + switch (*value) { + case '0' ... '9': + val = 10*val+(*value-'0'); + break; + default: + return val; + } + } +} + +int register_sensor_device(struct device *dev, struct virtio_sensor *vs, + struct device_attribute *attributes[], const char* name) +{ + int i = 0, err = 0; + + if (!vs->sensor_class) { + LOG(KERN_ERR, "sensor class is not created before make device"); + return -1; + } + + LOG(KERN_INFO, "device creation: %s.", name); + + dev = device_create(vs->sensor_class, NULL, 0, NULL, "%s", name); + if (dev < 0) { + LOG(KERN_ERR, "register_device_create failed!"); + return -1; + } + + if (attributes == NULL) { + LOG(KERN_ERR, "attributes is NULL."); + return -1; + } + + for (i = 0; attributes[i] != NULL; i++) { + if ((err = device_create_file(dev, attributes[i])) < 0) { + LOG(KERN_ERR, "failed to create device file with attribute[%d - %d]", i, err); + return -1; + } + } + + LOG(KERN_INFO, "register_sensor_device ends: %s.", name); + + return 0; +} + +#ifdef SUPPORT_LEGACY_SENSOR + +int l_register_sensor_device(struct device *dev, struct virtio_sensor *vs, + struct device_attribute *attributes[], const char* name) +{ + int i = 0, err = 0; + + if (!vs->l_sensor_class) { + LOG(KERN_ERR, "l sensor class is not created before make device"); + return -1; + } + + dev = device_create(vs->l_sensor_class, NULL, 0, NULL, "%s", name); + if (dev < 0) { + LOG(KERN_ERR, "legacy register_device_create failed!"); + return -1; + } + + if (attributes == NULL) { + LOG(KERN_ERR, "l sensor attributes is NULL."); + return -1; + } + + for (i = 0; attributes[i] != NULL; i++) { + if ((err = device_create_file(dev, attributes[i])) < 0) { + LOG(KERN_ERR, "failed to create legacy device file with attribute[%d - %d]", i, err); + return -1; + } + } + + return 0; +} + +#endif + +static void sensor_vq_done(struct virtqueue *rvq) { + unsigned int len; + struct msg_info* msg; + + msg = (struct msg_info*) virtqueue_get_buf(vs->vq, &len); + if (msg == NULL) { + LOG(KERN_ERR, "failed to virtqueue_get_buf"); + return; + } + + if (msg->req != request_answer) { + LOG(KERN_DEBUG, "receive queue- not an answer message: %d", msg->req); + return; + } + if (msg->buf == NULL) { + LOG(KERN_ERR, "receive queue- message from host is NULL."); + return; + } + + LOG(KERN_DEBUG, "msg buf: %s, req: %d, type: %d", msg->buf, msg->req, msg->type); + + mutex_lock(&vs->lock); + memset(sensor_data, 0, __MAX_BUF_SENSOR); + strcpy(sensor_data, msg->buf); + vs->flags = 1; + mutex_unlock(&vs->lock); + + wake_up_interruptible(&wq); +} + +void set_sensor_data(int type, const char* buf) +{ + int err = 0; + + if (buf == NULL) { + LOG(KERN_ERR, "set_sensor buf is NULL."); + return; + } + + if (vs == NULL) { + LOG(KERN_ERR, "Invalid sensor handle"); + return; + } + + mutex_lock(&vs->lock); + memset(&vs->msginfo, 0, sizeof(vs->msginfo)); + + vs->msginfo.req = request_set; + vs->msginfo.type = type; + strcpy(vs->msginfo.buf, buf); + mutex_unlock(&vs->lock); + + LOG(KERN_DEBUG, "set_sensor_data type: %d, req: %d, buf: %s", + vs->msginfo.type, vs->msginfo.req, vs->msginfo.buf); + + err = virtqueue_add_outbuf(vs->vq, vs->sg_vq, 1, &vs->msginfo, GFP_ATOMIC); + if (err < 0) { + LOG(KERN_ERR, "failed to add buffer to virtqueue (err = %d)", err); + return; + } + + virtqueue_kick(vs->vq); +} + +int get_sensor_data(int type, char* data) +{ + struct scatterlist *sgs[2]; + int err = 0; + + if (vs == NULL || data == NULL) { + LOG(KERN_ERR, "Invalid sensor handle or data is NULL."); + return -1; + } + + mutex_lock(&vs->lock); + memset(&vs->msginfo, 0, sizeof(vs->msginfo)); + + vs->msginfo.req = request_get; + vs->msginfo.type = type; + mutex_unlock(&vs->lock); + + LOG(KERN_DEBUG, "get_sensor_data type: %d, req: %d", + vs->msginfo.type, vs->msginfo.req); + + sgs[0] = &vs->sg_vq[0]; + sgs[1] = &vs->sg_vq[1]; + err = virtqueue_add_sgs(vs->vq, sgs, 1, 1, &vs->msginfo, GFP_ATOMIC); + if (err < 0) { + LOG(KERN_ERR, "failed to add buffer to virtqueue (err = %d)", err); + return err; + } + + virtqueue_kick(vs->vq); + + wait_event_interruptible(wq, vs->flags != 0); + + mutex_lock(&vs->lock); + vs->flags = 0; + memcpy(data, sensor_data, strlen(sensor_data)); + mutex_unlock(&vs->lock); + + return 0; +} + +int get_data_for_show(int type, char* buf) +{ + char sensor_data[__MAX_BUF_SENSOR]; + int ret; + + memset(sensor_data, 0, __MAX_BUF_SENSOR); + ret = get_sensor_data(type, sensor_data); + if (ret) + return sprintf(buf, "%d", -1); + + return sprintf(buf, "%s", sensor_data); +} + +static int device_init(struct virtio_sensor *vs) +{ + int ret = 0; + + if (vs->sensor_capability & sensor_cap_accel) { + ret = maru_accel_init(vs); + if (ret) + return ret; + } + + if (vs->sensor_capability & sensor_cap_geo) { + ret = maru_geo_init(vs); + if (ret) + return ret; + } + + if (vs->sensor_capability & sensor_cap_gyro) { + ret = maru_gyro_init(vs); + if (ret) + return ret; + } + + if (vs->sensor_capability & sensor_cap_light) { + ret = maru_light_init(vs); + if (ret) + return ret; + } + + if (vs->sensor_capability & sensor_cap_proxi) { + ret = maru_proxi_init(vs); + if (ret) + return ret; + } + + return ret; +} + +static void device_exit(struct virtio_sensor *vs) +{ + if (vs->sensor_capability & sensor_cap_accel) { + maru_accel_exit(vs); + } + + if (vs->sensor_capability & sensor_cap_geo) { + maru_geo_exit(vs); + } + + if (vs->sensor_capability & sensor_cap_gyro) { + maru_gyro_exit(vs); + } + + if (vs->sensor_capability & sensor_cap_light) { + maru_light_exit(vs); + } + + if (vs->sensor_capability & sensor_cap_proxi) { + maru_proxi_exit(vs); + } +} + +static void cleanup(struct virtio_device* dev) { + dev->config->del_vqs(dev); + + if (vs == NULL) + return; + + if (vs->sensor_class) { + device_destroy(vs->sensor_class, (dev_t)NULL); + class_destroy(vs->sensor_class); + } + +#ifdef SUPPORT_LEGACY_SENSOR + if (vs->l_sensor_class) { + device_destroy(vs->l_sensor_class, (dev_t)NULL); + class_destroy(vs->l_sensor_class); + } +#endif + + kfree(vs); + vs = NULL; +} + +static int sensor_probe(struct virtio_device* dev) +{ + int ret = 0; + int index = 0; + char sensor_data[__MAX_BUF_SENSOR]; + + LOG(KERN_INFO, "Sensor probe starts"); + + vs = kmalloc(sizeof(struct virtio_sensor), GFP_KERNEL); + if (!vs) { + LOG(KERN_ERR, "failed to allocate sensor structure."); + return -ENOMEM; + } + + vs->vdev = dev; + dev->priv = vs; + + vs->sensor_class = class_create(THIS_MODULE, SENSOR_CLASS_NAME); + if (IS_ERR(vs->sensor_class)) { + LOG(KERN_ERR, "sensor class creation is failed."); + return PTR_ERR(vs->sensor_class); + } + +#ifdef SUPPORT_LEGACY_SENSOR + vs->l_sensor_class = class_create(THIS_MODULE, L_SENSOR_CLASS_NAME); + if (IS_ERR(vs->sensor_class)) { + LOG(KERN_ERR, "sensor class creation is failed."); + return PTR_ERR(vs->sensor_class); + } +#endif + + vs->vq = virtio_find_single_vq(dev, sensor_vq_done, "sensor"); + if (IS_ERR(vs->vq)) { + cleanup(dev); + LOG(KERN_ERR, "failed to init virt queue"); + return ret; + } + + virtqueue_enable_cb(vs->vq); + + sg_init_table(vs->sg_vq, 2); + for (; index < 2; index++) { + sg_set_buf(&vs->sg_vq[index], &vs->msginfo, sizeof(vs->msginfo)); + } + + mutex_init(&vs->lock); + + memset(sensor_data, 0, __MAX_BUF_SENSOR); + ret = get_sensor_data(sensor_type_list, sensor_data); + if (ret) { + LOG(KERN_ERR, "sensor capability data is null."); + cleanup(dev); + return ret; + } + + vs->sensor_capability = sensor_atoi(sensor_data); + LOG(KERN_INFO, "sensor capability is %02x", vs->sensor_capability); + + ret = device_init(vs); + if (ret) { + LOG(KERN_ERR, "failed initialing devices"); + cleanup(dev); + return ret; + } + + LOG(KERN_INFO, "Sensor probe completes"); + + return ret; +} + +static void sensor_remove(struct virtio_device* dev) +{ + struct virtio_sensor* vs = dev->priv; + if (!vs) + { + LOG(KERN_ERR, "vs is NULL"); + return; + } + + dev->config->reset(dev); + + device_exit(vs); + + cleanup(dev); + + LOG(KERN_INFO, "Sensor driver is removed."); +} + +MODULE_DEVICE_TABLE(virtio, id_table); + +static struct virtio_driver virtio_sensor_driver = { + .driver = { + .name = KBUILD_MODNAME, + .owner = THIS_MODULE , + }, + .id_table = id_table, + .probe = sensor_probe, + .remove = sensor_remove, +}; + + +static int __init sensor_init(void) +{ + LOG(KERN_INFO, "Sensor driver initialized."); + + return register_virtio_driver(&virtio_sensor_driver); +} + +static void __exit sensor_exit(void) +{ + unregister_virtio_driver(&virtio_sensor_driver); + + LOG(KERN_INFO, "Sensor driver is destroyed."); +} + +module_init(sensor_init); +module_exit(sensor_exit); + +MODULE_LICENSE("GPL2"); +MODULE_AUTHOR("Jinhyung Choi "); +MODULE_DESCRIPTION("Emulator Virtio Sensor Driver"); + diff --git a/drivers/maru/sensors/maru_virtio_sensor.h b/drivers/maru/sensors/maru_virtio_sensor.h new file mode 100644 index 000000000000..f0c5b8dd9432 --- /dev/null +++ b/drivers/maru/sensors/maru_virtio_sensor.h @@ -0,0 +1,203 @@ +/* + * Maru Virtio Sensor Device Driver + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: + * Jinhyung Choi + * Sangho Park + * YeongKyoon Lee + * + * 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 + * + */ + +#ifndef _MARU_VIRTIO_SENSOR_H +#define _MARU_VIRTIO_SENSOR_H + +#include +#include +#include + +#define SUPPORT_LEGACY_SENSOR 1 + +enum request_cmd { + request_get = 0, + request_set, + request_answer +}; + +enum sensor_types { + sensor_type_list = 0, + sensor_type_accel, + sensor_type_accel_enable, + sensor_type_accel_delay, + sensor_type_geo, + sensor_type_geo_enable, + sensor_type_geo_delay, + sensor_type_gyro, + sensor_type_gyro_enable, + sensor_type_gyro_delay, + sensor_type_gyro_x, + sensor_type_gyro_y, + sensor_type_gyro_z, + sensor_type_light, + sensor_type_light_enable, + sensor_type_light_delay, + sensor_type_light_adc, + sensor_type_light_level, + sensor_type_proxi, + sensor_type_proxi_enable, + sensor_type_proxi_delay, + sensor_type_mag, + sensor_type_tilt, + sensor_type_max +}; + +enum sensor_capabilities { + sensor_cap_accel = 0x01, + sensor_cap_geo = 0x02, + sensor_cap_gyro = 0x04, + sensor_cap_light = 0x08, + sensor_cap_proxi = 0x10 +}; + +#define __MAX_BUF_SIZE 1024 +#define __MAX_BUF_SENSOR 128 + +struct msg_info { + char buf[__MAX_BUF_SIZE]; + + uint16_t type; + uint16_t req; +}; + +#ifdef SUPPORT_LEGACY_SENSOR +# define L_SENSOR_CLASS_NAME "sensor" +#endif + +struct virtio_sensor { + struct virtio_device* vdev; + struct virtqueue* vq; + + struct msg_info msginfo; + struct scatterlist sg_vq[2]; + + int flags; + struct mutex lock; + + struct class* sensor_class; + +#ifdef SUPPORT_LEGACY_SENSOR + struct class* l_sensor_class; +#endif + + int sensor_capability; + + void* accel_handle; + void* geo_handle; + void* gyro_handle; + void* light_handle; + void* proxi_handle; +}; + +#define MARU_DEVICE_ATTR(_name) \ + struct device_attribute dev_attr_##_name = MARU_ATTR_RONLY(_name) + +#define MARU_ATTR_RONLY(_name) { \ + .attr = { .name = __stringify(_name), .mode = 0444 }, \ + .show = maru_##_name##_show, \ +} + +#define MARU_ATTR_RW(_name) { \ + .attr = {.name = __stringify(_name), .mode = 0644 }, \ + .show = maru_##_name##_show, \ + .store = maru_##_name##_store, \ +} + +int sensor_atoi(const char *value); + +int get_data_for_show(int type, char* buf); +int register_sensor_device(struct device *dev, struct virtio_sensor *vs, + struct device_attribute *attributes[], const char* name); + +#ifdef SUPPORT_LEGACY_SENSOR +int l_register_sensor_device(struct device *dev, struct virtio_sensor *vs, + struct device_attribute *attributes[], const char* name); +#endif + +void set_sensor_data(int type, const char* buf); +int get_sensor_data(int type, char* data); + +#define SENSOR_CLASS_NAME "sensors" +#define MARU_SENSOR_DEVICE_VENDOR "Tizen" + +#define DRIVER_ACCEL_NAME "accel" +#define SENSOR_ACCEL_INPUT_NAME "accelerometer_sensor" +#define MARU_ACCEL_DEVICE_NAME "EMULATOR_ACCEL" + +#define DRIVER_GEO_NAME "geo" +#define SENSOR_GEO_INPUT_NAME "geomagnetic_sensor" +#define MARU_GEO_DEVICE_NAME "EMULATOR_GEO" + +#define DRIVER_GYRO_NAME "gyro" +#define SENSOR_GYRO_INPUT_NAME "gyro_sensor" +#define MARU_GYRO_DEVICE_NAME "EMULATOR_GYRO" + +#define DRIVER_LIGHT_NAME "light" +#define SENSOR_LIGHT_INPUT_NAME "light_sensor" +#define MARU_LIGHT_DEVICE_NAME "EMULATOR_LIGHT" + +#define DRIVER_PROXI_NAME "proxi" +#define SENSOR_PROXI_INPUT_NAME "proximity_sensor" +#define MARU_PROXI_DEVICE_NAME "EMULATOR_PROXI" + +#define LOG(log_level, fmt, ...) \ + printk(log_level "%s: " fmt "\n", SENSOR_CLASS_NAME, ##__VA_ARGS__) + +/* + * Accelerometer device + */ +int maru_accel_init(struct virtio_sensor *vs); +int maru_accel_exit(struct virtio_sensor *vs); + +/* + * Geomagnetic device + */ +int maru_geo_init(struct virtio_sensor *vs); +int maru_geo_exit(struct virtio_sensor *vs); + +/* + * Gyroscope device + */ +int maru_gyro_init(struct virtio_sensor *vs); +int maru_gyro_exit(struct virtio_sensor *vs); + +/* + * Light device + */ +int maru_light_init(struct virtio_sensor *vs); +int maru_light_exit(struct virtio_sensor *vs); + +/* + * Proximity device + */ +int maru_proxi_init(struct virtio_sensor *vs); +int maru_proxi_exit(struct virtio_sensor *vs); + +#endif