diff options
author | codeworkx <daniel.hillenbrand@codeworkx.de> | 2012-06-02 13:09:29 +0200 |
---|---|---|
committer | codeworkx <daniel.hillenbrand@codeworkx.de> | 2012-06-02 13:09:29 +0200 |
commit | c6da2cfeb05178a11c6d062a06f8078150ee492f (patch) | |
tree | f3b4021d252c52d6463a9b3c1bb7245e399b009c /drivers/misc/mpu3050/slaveirq.c | |
parent | c6d7c4dbff353eac7919342ae6b3299a378160a6 (diff) | |
download | kernel_samsung_smdk4412-c6da2cfeb05178a11c6d062a06f8078150ee492f.tar.gz kernel_samsung_smdk4412-c6da2cfeb05178a11c6d062a06f8078150ee492f.tar.bz2 kernel_samsung_smdk4412-c6da2cfeb05178a11c6d062a06f8078150ee492f.zip |
samsung update 1
Diffstat (limited to 'drivers/misc/mpu3050/slaveirq.c')
-rwxr-xr-x | drivers/misc/mpu3050/slaveirq.c | 270 |
1 files changed, 270 insertions, 0 deletions
diff --git a/drivers/misc/mpu3050/slaveirq.c b/drivers/misc/mpu3050/slaveirq.c new file mode 100755 index 00000000000..2ee53851e12 --- /dev/null +++ b/drivers/misc/mpu3050/slaveirq.c @@ -0,0 +1,270 @@ +/* + $License: + Copyright (C) 2010 InvenSense Corporation, All Rights Reserved. + + 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, see <http://www.gnu.org/licenses/>. + $ + */ +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/stat.h> +#include <linux/irq.h> +#include <linux/signal.h> +#include <linux/miscdevice.h> +#include <linux/i2c.h> +#include <linux/i2c-dev.h> +#include <linux/poll.h> + +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/mm.h> +#include <linux/sched.h> +#include <linux/wait.h> +#include <linux/uaccess.h> +#include <linux/io.h> +#include <linux/wait.h> +#include <linux/slab.h> + +#include "mpu.h" +#include "slaveirq.h" +#include "mldl_cfg.h" +#include "mpu-i2c.h" + +/* function which gets slave data and sends it to SLAVE */ + +struct slaveirq_dev_data { + struct miscdevice dev; + struct i2c_client *slave_client; + struct mpuirq_data data; + wait_queue_head_t slaveirq_wait; + int irq; + int pid; + int data_ready; + int timeout; +}; + +/* The following depends on patch fa1f68db6ca7ebb6fc4487ac215bffba06c01c28 + * drivers: misc: pass miscdevice pointer via file private data + */ +static int slaveirq_open(struct inode *inode, struct file *file) +{ + /* Device node is availabe in the file->private_data, this is + * exactly what we want so we leave it there */ + struct slaveirq_dev_data *data = + container_of(file->private_data, struct slaveirq_dev_data, dev); + + dev_dbg(data->dev.this_device, + "%s current->pid %d\n", __func__, current->pid); + data->pid = current->pid; + return 0; +} + +static int slaveirq_release(struct inode *inode, struct file *file) +{ + struct slaveirq_dev_data *data = + container_of(file->private_data, struct slaveirq_dev_data, dev); + dev_dbg(data->dev.this_device, "slaveirq_release\n"); + return 0; +} + +/* read function called when from /dev/slaveirq is read */ +static ssize_t slaveirq_read(struct file *file, + char *buf, size_t count, loff_t *ppos) +{ + int len, err; + struct slaveirq_dev_data *data = + container_of(file->private_data, struct slaveirq_dev_data, dev); + + if (!data->data_ready) { + wait_event_interruptible_timeout(data->slaveirq_wait, + data->data_ready, + data->timeout); + } + + if (data->data_ready && NULL != buf + && count >= sizeof(data->data)) { + err = copy_to_user(buf, &data->data, sizeof(data->data)); + data->data.data_type = 0; + } else { + return 0; + } + if (err != 0) { + dev_err(data->dev.this_device, + "Copy to user returned %d\n", err); + return -EFAULT; + } + data->data_ready = 0; + len = sizeof(data->data); + return len; +} + +unsigned int slaveirq_poll(struct file *file, struct poll_table_struct *poll) +{ + int mask = 0; + struct slaveirq_dev_data *data = + container_of(file->private_data, struct slaveirq_dev_data, dev); + + poll_wait(file, &data->slaveirq_wait, poll); + if (data->data_ready) + mask |= POLLIN | POLLRDNORM; + return mask; +} + +/* ioctl - I/O control */ +static long slaveirq_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + int retval = 0; + int tmp; + struct slaveirq_dev_data *data = + container_of(file->private_data, struct slaveirq_dev_data, dev); + + switch (cmd) { + case SLAVEIRQ_SET_TIMEOUT: + data->timeout = arg; + break; + + case SLAVEIRQ_GET_INTERRUPT_CNT: + tmp = data->data.interruptcount - 1; + if (data->data.interruptcount > 1) + data->data.interruptcount = 1; + + if (copy_to_user((int *) arg, &tmp, sizeof(int))) + return -EFAULT; + break; + case SLAVEIRQ_GET_IRQ_TIME: + if (copy_to_user((int *) arg, &data->data.irqtime, + sizeof(data->data.irqtime))) + return -EFAULT; + data->data.irqtime = 0; + break; + default: + retval = -EINVAL; + } + return retval; +} + +static irqreturn_t slaveirq_handler(int irq, void *dev_id) +{ + struct slaveirq_dev_data *data = (struct slaveirq_dev_data *)dev_id; + static int mycount; + struct timeval irqtime; + mycount++; + + data->data.interruptcount++; + + /* wake up (unblock) for reading data from userspace */ + /* and ignore first interrupt generated in module init */ + data->data_ready = 1; + + do_gettimeofday(&irqtime); + data->data.irqtime = (((long long) irqtime.tv_sec) << 32); + data->data.irqtime += irqtime.tv_usec; + data->data.data_type |= 1; + + wake_up_interruptible(&data->slaveirq_wait); + + return IRQ_HANDLED; + +} + +/* define which file operations are supported */ +static const struct file_operations slaveirq_fops = { + .owner = THIS_MODULE, + .read = slaveirq_read, + .poll = slaveirq_poll, + +#if HAVE_COMPAT_IOCTL + .compat_ioctl = slaveirq_ioctl, +#endif +#if HAVE_UNLOCKED_IOCTL + .unlocked_ioctl = slaveirq_ioctl, +#endif + .open = slaveirq_open, + .release = slaveirq_release, +}; + +int slaveirq_init(struct i2c_adapter *slave_adapter, + struct ext_slave_platform_data *pdata, + char *name) +{ + + int res; + struct slaveirq_dev_data *data; + + if (!pdata->irq) + return -EINVAL; + + pdata->irq_data = kzalloc(sizeof(*data), + GFP_KERNEL); + data = (struct slaveirq_dev_data *) pdata->irq_data; + if (!data) + return -ENOMEM; + + data->dev.minor = MISC_DYNAMIC_MINOR; + data->dev.name = name; + data->dev.fops = &slaveirq_fops; + data->irq = pdata->irq; + data->pid = 0; + data->data_ready = 0; + data->timeout = 0; + + res = request_irq(data->irq, slaveirq_handler, IRQF_TRIGGER_RISING, + data->dev.name, data); + + if (res) { + dev_err(&slave_adapter->dev, + "myirqtest: cannot register IRQ %d\n", + data->irq); + goto out_request_irq; + } + + res = misc_register(&data->dev); + if (res < 0) { + dev_err(&slave_adapter->dev, + "misc_register returned %d\n", + res); + goto out_misc_register; + } + + init_waitqueue_head(&data->slaveirq_wait); + return res; + +out_misc_register: + free_irq(data->irq, data); +out_request_irq: + kfree(pdata->irq_data); + pdata->irq_data = NULL; + + return res; +} + +void slaveirq_exit(struct ext_slave_platform_data *pdata) +{ + struct slaveirq_dev_data *data = pdata->irq_data; + + if (!pdata->irq_data || data->irq <= 0) + return; + + dev_info(data->dev.this_device, "Unregistering %s\n", + data->dev.name); + + free_irq(data->irq, data); + misc_deregister(&data->dev); + kfree(pdata->irq_data); + pdata->irq_data = NULL; +} |