blob: 0817e4af540799f5f3812af8a38485c366eae500 [file] [log] [blame]
/*
* Copyright (C) 2010-2013 Motorola Mobility LLC
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 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., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307, USA
*/
#include <linux/cdev.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/export.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/fs.h>
#include <linux/gpio.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/input-polldev.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/poll.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/switch.h>
#include <linux/time.h>
#include <linux/uaccess.h>
#include <linux/wakelock.h>
#include <linux/workqueue.h>
#include <linux/stm401.h>
int stm401_as_data_buffer_write(struct stm401_data *ps_stm401,
unsigned char type, unsigned char *data, int size,
unsigned char status)
{
int new_head;
struct stm401_android_sensor_data *buffer;
struct timespec ts;
static bool error_reported;
new_head = (ps_stm401->stm401_as_data_buffer_head + 1)
& STM401_AS_DATA_QUEUE_MASK;
if (new_head == ps_stm401->stm401_as_data_buffer_tail) {
if (!error_reported) {
dev_err(&ps_stm401->client->dev, "as data buffer full\n");
error_reported = true;
}
wake_up(&ps_stm401->stm401_as_data_wq);
return 0;
}
buffer = &(ps_stm401->stm401_as_data_buffer[new_head]);
buffer->type = type;
buffer->status = status;
if (data != NULL && size > 0) {
if (size > sizeof(buffer->data)) {
dev_err(&ps_stm401->client->dev,
"size %d exceeds as buffer\n", size);
return 0;
}
memcpy(buffer->data, data, size);
}
buffer->size = size;
ktime_get_ts(&ts);
buffer->timestamp = ts.tv_sec*1000000000LL + ts.tv_nsec;
ps_stm401->stm401_as_data_buffer_head = new_head;
wake_up(&ps_stm401->stm401_as_data_wq);
error_reported = false;
return 1;
}
int stm401_as_data_buffer_read(struct stm401_data *ps_stm401,
struct stm401_android_sensor_data *buff)
{
int new_tail;
if (ps_stm401->stm401_as_data_buffer_tail
== ps_stm401->stm401_as_data_buffer_head)
return 0;
new_tail = (ps_stm401->stm401_as_data_buffer_tail + 1)
& STM401_AS_DATA_QUEUE_MASK;
*buff = ps_stm401->stm401_as_data_buffer[new_tail];
ps_stm401->stm401_as_data_buffer_tail = new_tail;
return 1;
}
int stm401_ms_data_buffer_write(struct stm401_data *ps_stm401,
unsigned char type, unsigned char *data, int size)
{
int new_head;
struct stm401_moto_sensor_data *buffer;
struct timespec ts;
static bool error_reported;
new_head = (ps_stm401->stm401_ms_data_buffer_head + 1)
& STM401_MS_DATA_QUEUE_MASK;
if (new_head == ps_stm401->stm401_ms_data_buffer_tail) {
if (!error_reported) {
dev_err(&ps_stm401->client->dev, "ms data buffer full\n");
error_reported = true;
}
wake_up(&ps_stm401->stm401_ms_data_wq);
return 0;
}
buffer = &(ps_stm401->stm401_ms_data_buffer[new_head]);
buffer->type = type;
if (data != NULL && size > 0) {
if (size > sizeof(buffer->data)) {
dev_err(&ps_stm401->client->dev,
"size %d exceeds ms buffer\n", size);
return 0;
}
memcpy(buffer->data, data, size);
}
buffer->size = size;
ktime_get_ts(&ts);
buffer->timestamp
= ts.tv_sec*1000000000LL + ts.tv_nsec;
ps_stm401->stm401_ms_data_buffer_head = new_head;
wake_up(&ps_stm401->stm401_ms_data_wq);
error_reported = false;
return 1;
}
int stm401_ms_data_buffer_read(struct stm401_data *ps_stm401,
struct stm401_moto_sensor_data *buff)
{
int new_tail;
if (ps_stm401->stm401_ms_data_buffer_tail
== ps_stm401->stm401_ms_data_buffer_head)
return 0;
new_tail = (ps_stm401->stm401_ms_data_buffer_tail + 1)
& STM401_MS_DATA_QUEUE_MASK;
*buff = ps_stm401->stm401_ms_data_buffer[new_tail];
ps_stm401->stm401_ms_data_buffer_tail = new_tail;
return 1;
}
static int stm401_as_open(struct inode *inode, struct file *file)
{
int err = 0;
dev_dbg(&stm401_misc_data->client->dev, "stm401_as_open\n");
err = nonseekable_open(inode, file);
if (err < 0)
return err;
file->private_data = stm401_misc_data;
return err;
}
static ssize_t stm401_as_read(struct file *file, char __user *buffer,
size_t size, loff_t *ppos)
{
int ret;
struct stm401_android_sensor_data tmp_buff;
struct stm401_data *ps_stm401 = file->private_data;
ret = stm401_as_data_buffer_read(ps_stm401, &tmp_buff);
if (ret == 0)
return 0;
ret = copy_to_user(buffer, &tmp_buff,
sizeof(struct stm401_android_sensor_data));
if (ret != 0) {
dev_err(&ps_stm401->client->dev, "Copy error\n");
return 0;
}
return sizeof(struct stm401_android_sensor_data);
}
static unsigned int stm401_as_poll(struct file *file,
struct poll_table_struct *wait)
{
unsigned int mask = 0;
struct stm401_data *ps_stm401 = file->private_data;
poll_wait(file, &ps_stm401->stm401_as_data_wq, wait);
if (ps_stm401->stm401_as_data_buffer_head
!= ps_stm401->stm401_as_data_buffer_tail)
mask = POLLIN | POLLRDNORM;
return mask;
}
const struct file_operations stm401_as_fops = {
.owner = THIS_MODULE,
.open = stm401_as_open,
.read = stm401_as_read,
.poll = stm401_as_poll,
};
static int stm401_ms_open(struct inode *inode, struct file *file)
{
int err = 0;
dev_dbg(&stm401_misc_data->client->dev, "stm401_ms_open\n");
err = nonseekable_open(inode, file);
if (err < 0)
return err;
file->private_data = stm401_misc_data;
return err;
}
static ssize_t stm401_ms_read(struct file *file, char __user *buffer,
size_t size, loff_t *ppos)
{
int ret;
struct stm401_moto_sensor_data tmp_buff;
struct stm401_data *ps_stm401 = file->private_data;
ret = stm401_ms_data_buffer_read(ps_stm401, &tmp_buff);
if (copy_to_user(buffer, &tmp_buff,
sizeof(struct stm401_moto_sensor_data))
!= 0) {
dev_err(&ps_stm401->client->dev, "Copy error\n");
ret = 0;
}
return ret;
}
static unsigned int stm401_ms_poll(struct file *file,
struct poll_table_struct *wait)
{
unsigned int mask = 0;
struct stm401_data *ps_stm401 = file->private_data;
poll_wait(file, &ps_stm401->stm401_ms_data_wq, wait);
if (ps_stm401->stm401_ms_data_buffer_head
!= ps_stm401->stm401_ms_data_buffer_tail)
mask = POLLIN | POLLRDNORM;
return mask;
}
const struct file_operations stm401_ms_fops = {
.owner = THIS_MODULE,
.open = stm401_ms_open,
.read = stm401_ms_read,
.poll = stm401_ms_poll,
};