lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:	Thu, 30 Jun 2011 19:18:19 -0700
From:	Nathan Royer <nroyer@...ensense.com>
To:	Nathan Royer <nroyer@...ensense.com>
Cc:	Andrew Morton <akpm@...ux-foundation.org>,
	Greg Kroah-Hartman <gregkh@...e.de>,
	Jonathan Cameron <jic23@....ac.uk>,
	Jiri Kosina <jkosina@...e.cz>, Alan Cox <alan@...ux.intel.com>,
	Jean Delvare <khali@...ux-fr.org>,
	linux-kernel@...r.kernel.org, linux-input@...r.kernel.org
Subject: [PATCH 03/11] misc: mpu3050 /dev/mpu implementation.

Implements i2c probing, receives registration of slaves, handles file
operations, and power management.

Signed-off-by: Nathan Royer <nroyer@...ensense.com>
---
 drivers/misc/inv_mpu/mpu-dev.c | 1247 ++++++++++++++++++++++++++++++++++++++++
 drivers/misc/inv_mpu/mpu-dev.h |   36 ++
 2 files changed, 1283 insertions(+), 0 deletions(-)
 create mode 100644 drivers/misc/inv_mpu/mpu-dev.c
 create mode 100644 drivers/misc/inv_mpu/mpu-dev.h

diff --git a/drivers/misc/inv_mpu/mpu-dev.c b/drivers/misc/inv_mpu/mpu-dev.c
new file mode 100644
index 0000000..977631d
--- /dev/null
+++ b/drivers/misc/inv_mpu/mpu-dev.c
@@ -0,0 +1,1247 @@
+/*
+	$License:
+	Copyright (C) 2011 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/i2c.h>
+#include <linux/i2c-dev.h>
+#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/gpio.h>
+#include <linux/signal.h>
+#include <linux/miscdevice.h>
+#include <linux/slab.h>
+#include <linux/version.h>
+#include <linux/pm.h>
+#include <linux/mutex.h>
+#include <linux/suspend.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 "mpuirq.h"
+#include "slaveirq.h"
+#include "mlsl.h"
+#include "mldl_cfg.h"
+#include <linux/mpu.h>
+
+
+/* Platform data for the MPU */
+struct mpu_private_data {
+	struct miscdevice dev;
+	struct i2c_client *client;
+
+	/* mldl_cfg data */
+	struct mldl_cfg mldl_cfg;
+	struct mpu_ram		mpu_ram;
+	struct mpu_gyro_cfg	mpu_gyro_cfg;
+	struct mpu_offsets	mpu_offsets;
+	struct mpu_chip_info	mpu_chip_info;
+	struct inv_mpu_cfg	inv_mpu_cfg;
+	struct inv_mpu_state	inv_mpu_state;
+
+	struct mutex mutex;
+	wait_queue_head_t mpu_event_wait;
+	struct completion completion;
+	struct timer_list timeout;
+	struct notifier_block nb;
+	struct mpuirq_data mpu_pm_event;
+	int response_timeout;	/* In seconds */
+	unsigned long event;
+	int pid;
+	struct module *slave_modules[EXT_SLAVE_NUM_TYPES];
+};
+
+struct mpu_private_data *mpu_private_data;
+
+static void mpu_pm_timeout(u_long data)
+{
+	struct mpu_private_data *mpu = (struct mpu_private_data *)data;
+	struct i2c_client *client = mpu->client;
+	dev_dbg(&client->adapter->dev, "%s\n", __func__);
+	complete(&mpu->completion);
+}
+
+static int mpu_pm_notifier_callback(struct notifier_block *nb,
+				    unsigned long event, void *unused)
+{
+	struct mpu_private_data *mpu =
+	    container_of(nb, struct mpu_private_data, nb);
+	struct i2c_client *client = mpu->client;
+	struct timeval event_time;
+	dev_dbg(&client->adapter->dev, "%s: %ld\n", __func__, event);
+
+	/* Prevent the file handle from being closed before we initialize
+	   the completion event */
+	mutex_lock(&mpu->mutex);
+	if (!(mpu->pid) ||
+	    (event != PM_SUSPEND_PREPARE && event != PM_POST_SUSPEND)) {
+		mutex_unlock(&mpu->mutex);
+		return NOTIFY_OK;
+	}
+
+	do_gettimeofday(&event_time);
+	mpu->mpu_pm_event.interruptcount++;
+	mpu->mpu_pm_event.irqtime =
+	    (((long long)event_time.tv_sec) << 32) + event_time.tv_usec;
+	mpu->mpu_pm_event.data_type = MPUIRQ_DATA_TYPE_PM_EVENT;
+	mpu->mpu_pm_event.data = mpu->event;
+
+	if (event == PM_SUSPEND_PREPARE)
+		mpu->event = MPU_PM_EVENT_SUSPEND_PREPARE;
+	if (event == PM_POST_SUSPEND)
+		mpu->event = MPU_PM_EVENT_POST_SUSPEND;
+
+	if (mpu->response_timeout > 0) {
+		mpu->timeout.expires = jiffies + mpu->response_timeout * HZ;
+		add_timer(&mpu->timeout);
+	}
+	INIT_COMPLETION(mpu->completion);
+	mutex_unlock(&mpu->mutex);
+
+	wake_up_interruptible(&mpu->mpu_event_wait);
+	wait_for_completion(&mpu->completion);
+	del_timer_sync(&mpu->timeout);
+	dev_dbg(&client->adapter->dev, "%s: %ld DONE\n", __func__, event);
+	return NOTIFY_OK;
+}
+
+static int mpu_dev_open(struct inode *inode, struct file *file)
+{
+	struct mpu_private_data *mpu =
+	    container_of(file->private_data, struct mpu_private_data, dev);
+	struct i2c_client *client = mpu->client;
+	int result;
+	int ii;
+	dev_dbg(&client->adapter->dev, "%s\n", __func__);
+	dev_dbg(&client->adapter->dev, "current->pid %d\n", current->pid);
+
+	result = mutex_lock_interruptible(&mpu->mutex);
+	if (mpu->pid) {
+		mutex_unlock(&mpu->mutex);
+		return -EBUSY;
+	}
+	mpu->pid = current->pid;
+
+	/* Reset the sensors to the default */
+	if (result) {
+		dev_err(&client->adapter->dev,
+			"%s: mutex_lock_interruptible returned %d\n",
+			__func__, result);
+		return result;
+	}
+
+	for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++)
+		__module_get(mpu->slave_modules[ii]);
+
+	mutex_unlock(&mpu->mutex);
+	return 0;
+}
+
+/* close function - called when the "file" /dev/mpu is closed in userspace   */
+static int mpu_release(struct inode *inode, struct file *file)
+{
+	struct mpu_private_data *mpu =
+	    container_of(file->private_data, struct mpu_private_data, dev);
+	struct i2c_client *client = mpu->client;
+	struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
+	int result = 0;
+	int ii;
+	struct i2c_adapter *slave_adapter[EXT_SLAVE_NUM_TYPES];
+	struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave;
+
+	for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) {
+		if (!pdata_slave[ii])
+			slave_adapter[ii] = NULL;
+		else
+			slave_adapter[ii] =
+				i2c_get_adapter(pdata_slave[ii]->adapt_num);
+	}
+	slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE] = client->adapter;
+
+	mutex_lock(&mpu->mutex);
+	mldl_cfg->inv_mpu_cfg->requested_sensors = 0;
+	result = inv_mpu_suspend(mldl_cfg,
+				slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
+				slave_adapter[EXT_SLAVE_TYPE_ACCEL],
+				slave_adapter[EXT_SLAVE_TYPE_COMPASS],
+				slave_adapter[EXT_SLAVE_TYPE_PRESSURE],
+				INV_ALL_SENSORS);
+	mpu->pid = 0;
+	for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++)
+		module_put(mpu->slave_modules[ii]);
+
+	mutex_unlock(&mpu->mutex);
+	complete(&mpu->completion);
+	dev_dbg(&client->adapter->dev, "mpu_release\n");
+
+	return result;
+}
+
+/* read function called when from /dev/mpu is read.  Read from the FIFO */
+static ssize_t mpu_read(struct file *file,
+			char __user *buf, size_t count, loff_t *offset)
+{
+	struct mpu_private_data *mpu =
+	    container_of(file->private_data, struct mpu_private_data, dev);
+	struct i2c_client *client = mpu->client;
+	size_t len = sizeof(mpu->mpu_pm_event) + sizeof(unsigned long);
+	int err;
+
+	if (!mpu->event && (!(file->f_flags & O_NONBLOCK)))
+		wait_event_interruptible(mpu->mpu_event_wait, mpu->event);
+
+	if (!mpu->event || !buf
+	    || count < sizeof(mpu->mpu_pm_event) + sizeof(unsigned long))
+		return 0;
+
+	err = copy_to_user(buf, &mpu->mpu_pm_event, sizeof(mpu->mpu_pm_event));
+	if (err) {
+		dev_err(&client->adapter->dev,
+			"Copy to user returned %d\n", err);
+		return -EFAULT;
+	}
+	mpu->event = 0;
+	return len;
+}
+
+static unsigned int mpu_poll(struct file *file, struct poll_table_struct *poll)
+{
+	struct mpu_private_data *mpu =
+	    container_of(file->private_data, struct mpu_private_data, dev);
+	int mask = 0;
+
+	poll_wait(file, &mpu->mpu_event_wait, poll);
+	if (mpu->event)
+		mask |= POLLIN | POLLRDNORM;
+	return mask;
+}
+
+static int mpu_dev_ioctl_get_ext_slave_platform_data(
+	struct i2c_client *client,
+	struct ext_slave_platform_data __user *arg)
+{
+	struct mpu_private_data *mpu =
+	    (struct mpu_private_data *)i2c_get_clientdata(client);
+	struct ext_slave_platform_data *pdata_slave;
+	struct ext_slave_platform_data local_pdata_slave;
+
+	if (copy_from_user(&local_pdata_slave, arg, sizeof(local_pdata_slave)))
+		return -EFAULT;
+
+	if (local_pdata_slave.type >= EXT_SLAVE_NUM_TYPES)
+		return -EINVAL;
+
+	pdata_slave = mpu->mldl_cfg.pdata_slave[local_pdata_slave.type];
+	/* All but private data and irq_data */
+	if (!pdata_slave)
+		return -ENODEV;
+	if (copy_to_user(arg, pdata_slave, sizeof(*pdata_slave)))
+		return -EFAULT;
+	return 0;
+}
+
+static int mpu_dev_ioctl_get_mpu_platform_data(
+	struct i2c_client *client,
+	struct mpu_platform_data __user *arg)
+{
+	struct mpu_private_data *mpu =
+	    (struct mpu_private_data *)i2c_get_clientdata(client);
+	struct mpu_platform_data *pdata = mpu->mldl_cfg.pdata;
+
+	if (copy_to_user(arg, pdata, sizeof(pdata)))
+		return -EFAULT;
+	return 0;
+}
+
+static int mpu_dev_ioctl_get_ext_slave_descr(
+	struct i2c_client *client,
+	struct ext_slave_descr __user *arg)
+{
+	struct mpu_private_data *mpu =
+	    (struct mpu_private_data *)i2c_get_clientdata(client);
+	struct ext_slave_descr *slave;
+	struct ext_slave_descr local_slave;
+
+	if (copy_from_user(&local_slave, arg, sizeof(local_slave)))
+		return -EFAULT;
+
+	if (local_slave.type >= EXT_SLAVE_NUM_TYPES)
+		return -EINVAL;
+
+	slave = mpu->mldl_cfg.slave[local_slave.type];
+	/* All but private data and irq_data */
+	if (!slave)
+		return -ENODEV;
+	if (copy_to_user(arg, slave, sizeof(*slave)))
+		return -EFAULT;
+	return 0;
+}
+
+
+/**
+ * slave_config() - Pass a requested slave configuration to the slave sensor
+ *
+ * @adapter the adaptor to use to communicate with the slave
+ * @mldl_cfg the mldl configuration structuer
+ * @slave pointer to the slave descriptor
+ * @usr_config The configuration to pass to the slave sensor
+ *
+ * returns 0 or non-zero error code
+ */
+static int inv_mpu_config(struct mldl_cfg *mldl_cfg,
+			void *gyro_adapter,
+			struct ext_slave_config __user *usr_config)
+{
+	int retval = 0;
+	struct ext_slave_config config;
+
+	retval = copy_from_user(&config, usr_config, sizeof(config));
+	if (retval)
+		return -EFAULT;
+
+	if (config.len && config.data) {
+		void *data;
+		data = kmalloc(config.len, GFP_KERNEL);
+		if (!data)
+			return -ENOMEM;
+
+		retval = copy_from_user(data,
+					(void __user *)config.data, config.len);
+		if (retval) {
+			retval = -EFAULT;
+			kfree(data);
+			return retval;
+		}
+		config.data = data;
+	}
+	retval = gyro_config(gyro_adapter, mldl_cfg, &config);
+	kfree(config.data);
+	return retval;
+}
+
+static int inv_mpu_get_config(struct mldl_cfg *mldl_cfg,
+			    void *gyro_adapter,
+			    struct ext_slave_config __user *usr_config)
+{
+	int retval = 0;
+	struct ext_slave_config config;
+	void *user_data;
+
+	retval = copy_from_user(&config, usr_config, sizeof(config));
+	if (retval)
+		return -EFAULT;
+
+	user_data = config.data;
+	if (config.len && config.data) {
+		void *data;
+		data = kmalloc(config.len, GFP_KERNEL);
+		if (!data)
+			return -ENOMEM;
+
+		retval = copy_from_user(data,
+					(void __user *)config.data, config.len);
+		if (retval) {
+			retval = -EFAULT;
+			kfree(data);
+			return retval;
+		}
+		config.data = data;
+	}
+	retval = gyro_get_config(gyro_adapter, mldl_cfg, &config);
+	if (!retval)
+		retval = copy_to_user((unsigned char __user *)user_data,
+				config.data, config.len);
+	kfree(config.data);
+	return retval;
+}
+
+static int slave_config(struct mldl_cfg *mldl_cfg,
+			void *gyro_adapter,
+			void *slave_adapter,
+			struct ext_slave_descr *slave,
+			struct ext_slave_platform_data *pdata,
+			struct ext_slave_config __user *usr_config)
+{
+	int retval = 0;
+	struct ext_slave_config config;
+	if ((!slave) || (!slave->config))
+		return -ENODEV;
+
+	retval = copy_from_user(&config, usr_config, sizeof(config));
+	if (retval)
+		return -EFAULT;
+
+	if (config.len && config.data) {
+		void *data;
+		data = kmalloc(config.len, GFP_KERNEL);
+		if (!data)
+			return -ENOMEM;
+
+		retval = copy_from_user(data,
+					(void __user *)config.data, config.len);
+		if (retval) {
+			retval = -EFAULT;
+			kfree(data);
+			return retval;
+		}
+		config.data = data;
+	}
+	retval = inv_mpu_slave_config(mldl_cfg, gyro_adapter, slave_adapter,
+				      &config, slave, pdata);
+	kfree(config.data);
+	return retval;
+}
+
+static int slave_get_config(struct mldl_cfg *mldl_cfg,
+			    void *gyro_adapter,
+			    void *slave_adapter,
+			    struct ext_slave_descr *slave,
+			    struct ext_slave_platform_data *pdata,
+			    struct ext_slave_config __user *usr_config)
+{
+	int retval = 0;
+	struct ext_slave_config config;
+	void *user_data;
+	if (!(slave) || !(slave->get_config))
+		return -ENODEV;
+
+	retval = copy_from_user(&config, usr_config, sizeof(config));
+	if (retval)
+		return -EFAULT;
+
+	user_data = config.data;
+	if (config.len && config.data) {
+		void *data;
+		data = kmalloc(config.len, GFP_KERNEL);
+		if (!data)
+			return -ENOMEM;
+
+		retval = copy_from_user(data,
+					(void __user *)config.data, config.len);
+		if (retval) {
+			retval = -EFAULT;
+			kfree(data);
+			return retval;
+		}
+		config.data = data;
+	}
+	retval = inv_mpu_get_slave_config(mldl_cfg, gyro_adapter,
+					  slave_adapter, &config, slave, pdata);
+	if (retval) {
+		kfree(config.data);
+		return retval;
+	}
+	retval = copy_to_user((unsigned char __user *)user_data,
+			      config.data, config.len);
+	kfree(config.data);
+	return retval;
+}
+
+static int inv_slave_read(struct mldl_cfg *mldl_cfg,
+			  void *gyro_adapter,
+			  void *slave_adapter,
+			  struct ext_slave_descr *slave,
+			  struct ext_slave_platform_data *pdata,
+			  void __user *usr_data)
+{
+	int retval;
+	unsigned char *data;
+	data = kzalloc(slave->read_len, GFP_KERNEL);
+	if (!data)
+		return -EFAULT;
+
+	retval = inv_mpu_slave_read(mldl_cfg, gyro_adapter, slave_adapter,
+				    slave, pdata, data);
+
+	if ((!retval) &&
+	    (copy_to_user((unsigned char __user *)usr_data,
+			  data, slave->read_len)))
+		retval = -EFAULT;
+
+	kfree(data);
+	return retval;
+}
+
+static int mpu_handle_mlsl(void *sl_handle,
+			   unsigned char addr,
+			   unsigned int cmd,
+			   struct mpu_read_write __user *usr_msg)
+{
+	int retval = 0;
+	struct mpu_read_write msg;
+	unsigned char *user_data;
+	retval = copy_from_user(&msg, usr_msg, sizeof(msg));
+	if (retval)
+		return -EFAULT;
+
+	user_data = msg.data;
+	if (msg.length && msg.data) {
+		unsigned char *data;
+		data = kmalloc(msg.length, GFP_KERNEL);
+		if (!data)
+			return -ENOMEM;
+
+		retval = copy_from_user(data,
+					(void __user *)msg.data, msg.length);
+		if (retval) {
+			retval = -EFAULT;
+			kfree(data);
+			return retval;
+		}
+		msg.data = data;
+	} else {
+		return -EPERM;
+	}
+
+	switch (cmd) {
+	case MPU_READ:
+		retval = inv_serial_read(sl_handle, addr,
+					 msg.address, msg.length, msg.data);
+		break;
+	case MPU_WRITE:
+		retval = inv_serial_write(sl_handle, addr,
+					  msg.length, msg.data);
+		break;
+	case MPU_READ_MEM:
+		retval = inv_serial_read_mem(sl_handle, addr,
+					     msg.address, msg.length, msg.data);
+		break;
+	case MPU_WRITE_MEM:
+		retval = inv_serial_write_mem(sl_handle, addr,
+					      msg.address, msg.length,
+					      msg.data);
+		break;
+	case MPU_READ_FIFO:
+		retval = inv_serial_read_fifo(sl_handle, addr,
+					      msg.length, msg.data);
+		break;
+	case MPU_WRITE_FIFO:
+		retval = inv_serial_write_fifo(sl_handle, addr,
+					       msg.length, msg.data);
+		break;
+
+	};
+	if (retval) {
+		dev_err(&((struct i2c_adapter *)sl_handle)->dev,
+			"%s: i2c %d error %d\n",
+			__func__, cmd, retval);
+		kfree(msg.data);
+		return retval;
+	}
+	retval = copy_to_user((unsigned char __user *)user_data,
+			      msg.data, msg.length);
+	kfree(msg.data);
+	return retval;
+}
+
+/* ioctl - I/O control */
+static long mpu_dev_ioctl(struct file *file,
+			  unsigned int cmd, unsigned long arg)
+{
+	struct mpu_private_data *mpu =
+	    container_of(file->private_data, struct mpu_private_data, dev);
+	struct i2c_client *client = mpu->client;
+	struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
+	int retval = 0;
+	struct i2c_adapter *slave_adapter[EXT_SLAVE_NUM_TYPES];
+	struct ext_slave_descr **slave = mldl_cfg->slave;
+	struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave;
+	int ii;
+
+	for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) {
+		if (!pdata_slave[ii])
+			slave_adapter[ii] = NULL;
+		else
+			slave_adapter[ii] =
+				i2c_get_adapter(pdata_slave[ii]->adapt_num);
+	}
+	slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE] = client->adapter;
+
+	retval = mutex_lock_interruptible(&mpu->mutex);
+	if (retval) {
+		dev_err(&client->adapter->dev,
+			"%s: mutex_lock_interruptible returned %d\n",
+			__func__, retval);
+		return retval;
+	}
+
+	switch (cmd) {
+	case MPU_GET_EXT_SLAVE_PLATFORM_DATA:
+		retval = mpu_dev_ioctl_get_ext_slave_platform_data(
+			client,
+			(struct ext_slave_platform_data __user *)arg);
+		break;
+	case MPU_GET_MPU_PLATFORM_DATA:
+		retval = mpu_dev_ioctl_get_mpu_platform_data(
+			client,
+			(struct mpu_platform_data __user *)arg);
+		break;
+	case MPU_GET_EXT_SLAVE_DESCR:
+		retval = mpu_dev_ioctl_get_ext_slave_descr(
+			client,
+			(struct ext_slave_descr __user *)arg);
+		break;
+	case MPU_READ:
+	case MPU_WRITE:
+	case MPU_READ_MEM:
+	case MPU_WRITE_MEM:
+	case MPU_READ_FIFO:
+	case MPU_WRITE_FIFO:
+		retval = mpu_handle_mlsl(
+			slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
+			mldl_cfg->mpu_chip_info->addr, cmd,
+			(struct mpu_read_write __user *)arg);
+		break;
+	case MPU_CONFIG_GYRO:
+		retval = inv_mpu_config(
+			mldl_cfg,
+			slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
+			(struct ext_slave_config __user *)arg);
+		break;
+	case MPU_CONFIG_ACCEL:
+		retval = slave_config(
+			mldl_cfg,
+			slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
+			slave_adapter[EXT_SLAVE_TYPE_ACCEL],
+			slave[EXT_SLAVE_TYPE_ACCEL],
+			pdata_slave[EXT_SLAVE_TYPE_ACCEL],
+			(struct ext_slave_config __user *)arg);
+		break;
+	case MPU_CONFIG_COMPASS:
+		retval = slave_config(
+			mldl_cfg,
+			slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
+			slave_adapter[EXT_SLAVE_TYPE_COMPASS],
+			slave[EXT_SLAVE_TYPE_COMPASS],
+			pdata_slave[EXT_SLAVE_TYPE_COMPASS],
+			(struct ext_slave_config __user *)arg);
+		break;
+	case MPU_CONFIG_PRESSURE:
+		retval = slave_config(
+			mldl_cfg,
+			slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
+			slave_adapter[EXT_SLAVE_TYPE_PRESSURE],
+			slave[EXT_SLAVE_TYPE_PRESSURE],
+			pdata_slave[EXT_SLAVE_TYPE_PRESSURE],
+			(struct ext_slave_config __user *)arg);
+		break;
+	case MPU_GET_CONFIG_GYRO:
+		retval = inv_mpu_get_config(
+			mldl_cfg,
+			slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
+			(struct ext_slave_config __user *)arg);
+		break;
+	case MPU_GET_CONFIG_ACCEL:
+		retval = slave_get_config(
+			mldl_cfg,
+			slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
+			slave_adapter[EXT_SLAVE_TYPE_ACCEL],
+			slave[EXT_SLAVE_TYPE_ACCEL],
+			pdata_slave[EXT_SLAVE_TYPE_ACCEL],
+			(struct ext_slave_config __user *)arg);
+		break;
+	case MPU_GET_CONFIG_COMPASS:
+		retval = slave_get_config(
+			mldl_cfg,
+			slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
+			slave_adapter[EXT_SLAVE_TYPE_COMPASS],
+			slave[EXT_SLAVE_TYPE_COMPASS],
+			pdata_slave[EXT_SLAVE_TYPE_COMPASS],
+			(struct ext_slave_config __user *)arg);
+		break;
+	case MPU_GET_CONFIG_PRESSURE:
+		retval = slave_get_config(
+			mldl_cfg,
+			slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
+			slave_adapter[EXT_SLAVE_TYPE_PRESSURE],
+			slave[EXT_SLAVE_TYPE_PRESSURE],
+			pdata_slave[EXT_SLAVE_TYPE_PRESSURE],
+			(struct ext_slave_config __user *)arg);
+		break;
+	case MPU_SUSPEND:
+		retval = inv_mpu_suspend(
+			mldl_cfg,
+			slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
+			slave_adapter[EXT_SLAVE_TYPE_ACCEL],
+			slave_adapter[EXT_SLAVE_TYPE_COMPASS],
+			slave_adapter[EXT_SLAVE_TYPE_PRESSURE],
+			arg);
+		break;
+	case MPU_RESUME:
+		retval = inv_mpu_resume(
+			mldl_cfg,
+			slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
+			slave_adapter[EXT_SLAVE_TYPE_ACCEL],
+			slave_adapter[EXT_SLAVE_TYPE_COMPASS],
+			slave_adapter[EXT_SLAVE_TYPE_PRESSURE],
+			arg);
+		break;
+	case MPU_PM_EVENT_HANDLED:
+		dev_dbg(&client->adapter->dev, "%s: %d\n", __func__, cmd);
+		complete(&mpu->completion);
+		break;
+	case MPU_READ_ACCEL:
+		retval = inv_slave_read(
+			mldl_cfg,
+			slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
+			slave_adapter[EXT_SLAVE_TYPE_ACCEL],
+			slave[EXT_SLAVE_TYPE_ACCEL],
+			pdata_slave[EXT_SLAVE_TYPE_ACCEL],
+			(unsigned char __user *)arg);
+		break;
+	case MPU_READ_COMPASS:
+		retval = inv_slave_read(
+			mldl_cfg,
+			slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
+			slave_adapter[EXT_SLAVE_TYPE_COMPASS],
+			slave[EXT_SLAVE_TYPE_COMPASS],
+			pdata_slave[EXT_SLAVE_TYPE_COMPASS],
+			(unsigned char __user *)arg);
+		break;
+	case MPU_READ_PRESSURE:
+		retval = inv_slave_read(
+			mldl_cfg,
+			slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
+			slave_adapter[EXT_SLAVE_TYPE_PRESSURE],
+			slave[EXT_SLAVE_TYPE_PRESSURE],
+			pdata_slave[EXT_SLAVE_TYPE_PRESSURE],
+			(unsigned char __user *)arg);
+		break;
+	case MPU_GET_REQUESTED_SENSORS:
+		if (copy_to_user(
+			   (__u32 __user *)arg,
+			   &mldl_cfg->inv_mpu_cfg->requested_sensors,
+			   sizeof(mldl_cfg->inv_mpu_cfg->requested_sensors)))
+			retval = -EFAULT;
+		break;
+	case MPU_SET_REQUESTED_SENSORS:
+		mldl_cfg->inv_mpu_cfg->requested_sensors = arg;
+		break;
+	case MPU_GET_IGNORE_SYSTEM_SUSPEND:
+		if (copy_to_user(
+			(unsigned char __user *)arg,
+			&mldl_cfg->inv_mpu_cfg->ignore_system_suspend,
+			sizeof(mldl_cfg->inv_mpu_cfg->ignore_system_suspend)))
+			retval = -EFAULT;
+		break;
+	case MPU_SET_IGNORE_SYSTEM_SUSPEND:
+		mldl_cfg->inv_mpu_cfg->ignore_system_suspend = arg;
+		break;
+	case MPU_GET_MLDL_STATUS:
+		if (copy_to_user(
+			(unsigned char __user *)arg,
+			&mldl_cfg->inv_mpu_state->status,
+			sizeof(mldl_cfg->inv_mpu_state->status)))
+			retval = -EFAULT;
+		break;
+	case MPU_GET_I2C_SLAVES_ENABLED:
+		if (copy_to_user(
+			(unsigned char __user *)arg,
+			&mldl_cfg->inv_mpu_state->i2c_slaves_enabled,
+			sizeof(mldl_cfg->inv_mpu_state->i2c_slaves_enabled)))
+			retval = -EFAULT;
+		break;
+	default:
+		dev_err(&client->adapter->dev,
+			"%s: Unknown cmd %x, arg %lu\n",
+			__func__, cmd, arg);
+		retval = -EINVAL;
+	};
+
+	mutex_unlock(&mpu->mutex);
+	dev_dbg(&client->adapter->dev, "%s: %08x, %08lx, %d\n",
+		__func__, cmd, arg, retval);
+
+	if (retval > 0)
+		retval = -retval;
+
+	return retval;
+}
+
+void mpu_shutdown(struct i2c_client *client)
+{
+	struct mpu_private_data *mpu =
+	    (struct mpu_private_data *)i2c_get_clientdata(client);
+	struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
+	struct i2c_adapter *slave_adapter[EXT_SLAVE_NUM_TYPES];
+	struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave;
+	int ii;
+
+	for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) {
+		if (!pdata_slave[ii])
+			slave_adapter[ii] = NULL;
+		else
+			slave_adapter[ii] =
+				i2c_get_adapter(pdata_slave[ii]->adapt_num);
+	}
+	slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE] = client->adapter;
+
+	mutex_lock(&mpu->mutex);
+	(void)inv_mpu_suspend(mldl_cfg,
+			slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
+			slave_adapter[EXT_SLAVE_TYPE_ACCEL],
+			slave_adapter[EXT_SLAVE_TYPE_COMPASS],
+			slave_adapter[EXT_SLAVE_TYPE_PRESSURE],
+			INV_ALL_SENSORS);
+	mutex_unlock(&mpu->mutex);
+	dev_dbg(&client->adapter->dev, "%s\n", __func__);
+}
+
+int mpu_dev_suspend(struct i2c_client *client, pm_message_t mesg)
+{
+	struct mpu_private_data *mpu =
+	    (struct mpu_private_data *)i2c_get_clientdata(client);
+	struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
+	struct i2c_adapter *slave_adapter[EXT_SLAVE_NUM_TYPES];
+	struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave;
+	int ii;
+
+	for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) {
+		if (!pdata_slave[ii])
+			slave_adapter[ii] = NULL;
+		else
+			slave_adapter[ii] =
+				i2c_get_adapter(pdata_slave[ii]->adapt_num);
+	}
+	slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE] = client->adapter;
+
+	mutex_lock(&mpu->mutex);
+	if (!mldl_cfg->inv_mpu_cfg->ignore_system_suspend) {
+		dev_dbg(&client->adapter->dev,
+			"%s: suspending on event %d\n", __func__, mesg.event);
+		(void)inv_mpu_suspend(mldl_cfg,
+				slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
+				slave_adapter[EXT_SLAVE_TYPE_ACCEL],
+				slave_adapter[EXT_SLAVE_TYPE_COMPASS],
+				slave_adapter[EXT_SLAVE_TYPE_PRESSURE],
+				INV_ALL_SENSORS);
+	} else {
+		dev_dbg(&client->adapter->dev,
+			"%s: Already suspended %d\n", __func__, mesg.event);
+	}
+	mutex_unlock(&mpu->mutex);
+	return 0;
+}
+
+int mpu_dev_resume(struct i2c_client *client)
+{
+	struct mpu_private_data *mpu =
+	    (struct mpu_private_data *)i2c_get_clientdata(client);
+	struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
+	struct i2c_adapter *slave_adapter[EXT_SLAVE_NUM_TYPES];
+	struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave;
+	int ii;
+
+	for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) {
+		if (!pdata_slave[ii])
+			slave_adapter[ii] = NULL;
+		else
+			slave_adapter[ii] =
+				i2c_get_adapter(pdata_slave[ii]->adapt_num);
+	}
+	slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE] = client->adapter;
+
+	mutex_lock(&mpu->mutex);
+	if (mpu->pid && !mldl_cfg->inv_mpu_cfg->ignore_system_suspend) {
+		(void)inv_mpu_resume(mldl_cfg,
+				slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
+				slave_adapter[EXT_SLAVE_TYPE_ACCEL],
+				slave_adapter[EXT_SLAVE_TYPE_COMPASS],
+				slave_adapter[EXT_SLAVE_TYPE_PRESSURE],
+				mldl_cfg->inv_mpu_cfg->requested_sensors);
+		dev_dbg(&client->adapter->dev,
+			"%s for pid %d\n", __func__, mpu->pid);
+	}
+	mutex_unlock(&mpu->mutex);
+	return 0;
+}
+
+/* define which file operations are supported */
+static const struct file_operations mpu_fops = {
+	.owner = THIS_MODULE,
+	.read = mpu_read,
+	.poll = mpu_poll,
+	.unlocked_ioctl = mpu_dev_ioctl,
+	.open = mpu_dev_open,
+	.release = mpu_release,
+};
+
+int inv_mpu_register_slave(struct module *slave_module,
+			struct i2c_client *slave_client,
+			struct ext_slave_platform_data *slave_pdata,
+			struct ext_slave_descr *(*get_slave_descr)(void))
+{
+	struct mpu_private_data *mpu = mpu_private_data;
+	struct mldl_cfg *mldl_cfg;
+	struct ext_slave_descr *slave_descr;
+	struct ext_slave_platform_data **pdata_slave;
+	char *irq_name;
+	int result = 0;
+
+	if (!slave_client || !slave_pdata || !get_slave_descr)
+		return -EINVAL;
+
+	if (!mpu) {
+		dev_err(&slave_client->adapter->dev,
+			"%s: Null mpu_private_data\n", __func__);
+		return -EINVAL;
+	}
+	mldl_cfg = &mpu->mldl_cfg;
+	pdata_slave = mldl_cfg->pdata_slave;
+
+	slave_descr = get_slave_descr();
+	if (!slave_descr) {
+		dev_err(&slave_client->adapter->dev,
+			"%s: Null ext_slave_descr\n", __func__);
+		return -EINVAL;
+	}
+
+	mutex_lock(&mpu->mutex);
+	if (mpu->pid) {
+		mutex_unlock(&mpu->mutex);
+		return -EBUSY;
+	}
+
+	mpu->slave_modules[slave_descr->type] = slave_module;
+
+	if (pdata_slave[slave_descr->type]) {
+		result = -EBUSY;
+		goto out_unlock_mutex;
+	}
+
+	pdata_slave[slave_descr->type] = slave_pdata;
+	pdata_slave[slave_descr->type]->address		= slave_client->addr;
+	pdata_slave[slave_descr->type]->irq		= slave_client->irq;
+	pdata_slave[slave_descr->type]->adapt_num	=
+		i2c_adapter_id(slave_client->adapter);
+
+	switch (slave_descr->type) {
+	case EXT_SLAVE_TYPE_ACCEL:
+		irq_name = "accelirq";
+		break;
+	case EXT_SLAVE_TYPE_COMPASS:
+		irq_name = "compassirq";
+		break;
+	case EXT_SLAVE_TYPE_PRESSURE:
+		irq_name = "pressureirq";
+		break;
+	default:
+		irq_name = "none";
+	};
+
+	if (pdata_slave[slave_descr->type]->irq > 0) {
+		dev_info(&slave_client->adapter->dev,
+			"Installing %s irq using %d\n",
+			irq_name,
+			pdata_slave[slave_descr->type]->irq);
+		result = slaveirq_init(slave_client->adapter,
+				pdata_slave[slave_descr->type], irq_name);
+		if (result)
+			goto out_unlock_mutex;
+	} else {
+		dev_WARN(&slave_client->adapter->dev,
+			"Accel irq not assigned\n");
+	}
+
+	if (slave_descr->init) {
+		result = slave_descr->init(slave_client->adapter,
+					slave_descr,
+					pdata_slave[slave_descr->type]);
+		if (result) {
+			dev_err(&slave_client->adapter->dev,
+				"Accel init failed %d\n", result);
+			goto out_slaveirq_exit;
+		}
+	}
+
+	mldl_cfg->slave[slave_descr->type] = slave_descr;
+
+	dev_info(&slave_client->adapter->dev,
+		"%s: +%s Type %d: Addr: %2x IRQ: %2d, Adapt: %2d\n",
+		__func__,
+		mldl_cfg->slave[slave_descr->type]->name,
+		slave_descr->type,
+		pdata_slave[slave_descr->type]->address,
+		pdata_slave[slave_descr->type]->irq,
+		pdata_slave[slave_descr->type]->adapt_num);
+	goto out_unlock_mutex;
+
+out_slaveirq_exit:
+	if (pdata_slave[slave_descr->type]->irq > 0)
+		slaveirq_exit(pdata_slave[slave_descr->type]);
+out_unlock_mutex:
+	mutex_unlock(&mpu->mutex);
+	return result;
+}
+EXPORT_SYMBOL(inv_mpu_register_slave);
+
+void inv_mpu_unregister_slave(struct i2c_client *slave_client,
+			struct ext_slave_platform_data *slave_pdata,
+			struct ext_slave_descr *(*get_slave_descr)(void))
+{
+	struct mpu_private_data *mpu = mpu_private_data;
+	struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
+	struct ext_slave_descr *slave_descr;
+	int result;
+
+	dev_info(&slave_client->adapter->dev, "%s\n", __func__);
+
+	if (!slave_client || !slave_pdata || !get_slave_descr)
+		return;
+
+	slave_descr = get_slave_descr();
+	if (!slave_descr)
+		return;
+
+	mutex_lock(&mpu->mutex);
+
+	if (slave_descr->exit) {
+		result = slave_descr->exit(slave_client->adapter,
+					slave_descr,
+					slave_pdata);
+		if (result)
+			dev_err(&slave_client->adapter->dev,
+				"Accel exit failed %d\n", result);
+	}
+
+	if (slave_pdata->irq)
+		slaveirq_exit(slave_pdata);
+
+	mldl_cfg->slave[slave_descr->type] = NULL;
+	mldl_cfg->pdata_slave[slave_descr->type] = NULL;
+	mpu->slave_modules[slave_descr->type] = NULL;
+
+	mutex_unlock(&mpu->mutex);
+}
+EXPORT_SYMBOL(inv_mpu_unregister_slave);
+
+static unsigned short normal_i2c[] = { I2C_CLIENT_END };
+
+static const struct i2c_device_id mpu_id[] = {
+	{"mpu3050", 0},
+	{"mpu6050", 0},
+	{"mpu6050_no_accel", 0},
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, mpu_id);
+
+int mpu_probe(struct i2c_client *client, const struct i2c_device_id *devid)
+{
+	struct mpu_platform_data *pdata;
+	struct mpu_private_data *mpu;
+	struct mldl_cfg *mldl_cfg;
+	int res = 0;
+	int ii = 0;
+
+	dev_info(&client->adapter->dev, "%s: %d\n", __func__, ii++);
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		res = -ENODEV;
+		goto out_check_functionality_failed;
+	}
+
+	mpu = kzalloc(sizeof(struct mpu_private_data), GFP_KERNEL);
+	if (!mpu) {
+		res = -ENOMEM;
+		goto out_alloc_data_failed;
+	}
+	mldl_cfg = &mpu->mldl_cfg;
+	mldl_cfg->mpu_ram	= &mpu->mpu_ram;
+	mldl_cfg->mpu_gyro_cfg	= &mpu->mpu_gyro_cfg;
+	mldl_cfg->mpu_offsets	= &mpu->mpu_offsets;
+	mldl_cfg->mpu_chip_info	= &mpu->mpu_chip_info;
+	mldl_cfg->inv_mpu_cfg	= &mpu->inv_mpu_cfg;
+	mldl_cfg->inv_mpu_state	= &mpu->inv_mpu_state;
+
+	mldl_cfg->mpu_ram->length = MPU_MEM_NUM_RAM_BANKS * MPU_MEM_BANK_SIZE;
+	mldl_cfg->mpu_ram->ram = kzalloc(mldl_cfg->mpu_ram->length, GFP_KERNEL);
+	if (!mldl_cfg->mpu_ram->ram) {
+		res = -ENOMEM;
+		goto out_alloc_ram_failed;
+	}
+	mpu_private_data = mpu;
+	i2c_set_clientdata(client, mpu);
+	mpu->client = client;
+
+	init_waitqueue_head(&mpu->mpu_event_wait);
+	mutex_init(&mpu->mutex);
+	init_completion(&mpu->completion);
+
+	mpu->response_timeout = 60;	/* Seconds */
+	mpu->timeout.function = mpu_pm_timeout;
+	mpu->timeout.data = (u_long) mpu;
+	init_timer(&mpu->timeout);
+
+	mpu->nb.notifier_call = mpu_pm_notifier_callback;
+	mpu->nb.priority = 0;
+	res = register_pm_notifier(&mpu->nb);
+	if (res) {
+		dev_err(&client->adapter->dev,
+			"Unable to register pm_notifier %d\n", res);
+		goto out_register_pm_notifier_failed;
+	}
+
+	pdata = (struct mpu_platform_data *)client->dev.platform_data;
+	if (!pdata) {
+		dev_WARN(&client->adapter->dev,
+			 "Missing platform data for mpu\n");
+	}
+	mldl_cfg->pdata = pdata;
+
+	mldl_cfg->mpu_chip_info->addr = client->addr;
+	res = inv_mpu_open(&mpu->mldl_cfg, client->adapter, NULL, NULL, NULL);
+
+	if (res) {
+		dev_err(&client->adapter->dev,
+			"Unable to open %s %d\n", MPU_NAME, res);
+		res = -ENODEV;
+		goto out_whoami_failed;
+	}
+
+	mpu->dev.minor = MISC_DYNAMIC_MINOR;
+	mpu->dev.name = "mpu";
+	mpu->dev.fops = &mpu_fops;
+	res = misc_register(&mpu->dev);
+	if (res < 0) {
+		dev_err(&client->adapter->dev,
+			"ERROR: misc_register returned %d\n", res);
+		goto out_misc_register_failed;
+	}
+
+	if (client->irq) {
+		dev_info(&client->adapter->dev,
+			 "Installing irq using %d\n", client->irq);
+		res = mpuirq_init(client, mldl_cfg);
+		if (res)
+			goto out_mpuirq_failed;
+	} else {
+		dev_WARN(&client->adapter->dev,
+			 "Missing %s IRQ\n", MPU_NAME);
+	}
+
+	return res;
+
+out_mpuirq_failed:
+	misc_deregister(&mpu->dev);
+out_misc_register_failed:
+	inv_mpu_close(&mpu->mldl_cfg, client->adapter, NULL, NULL, NULL);
+out_whoami_failed:
+	unregister_pm_notifier(&mpu->nb);
+out_register_pm_notifier_failed:
+	kfree(mldl_cfg->mpu_ram->ram);
+	mpu_private_data = NULL;
+out_alloc_ram_failed:
+	kfree(mpu);
+out_alloc_data_failed:
+out_check_functionality_failed:
+	dev_err(&client->adapter->dev, "%s failed %d\n", __func__, res);
+	return res;
+
+}
+
+static int mpu_remove(struct i2c_client *client)
+{
+	struct mpu_private_data *mpu = i2c_get_clientdata(client);
+	struct i2c_adapter *slave_adapter[EXT_SLAVE_NUM_TYPES];
+	struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
+	struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave;
+	int ii;
+
+	for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) {
+		if (!pdata_slave[ii])
+			slave_adapter[ii] = NULL;
+		else
+			slave_adapter[ii] =
+				i2c_get_adapter(pdata_slave[ii]->adapt_num);
+	}
+
+	slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE] = client->adapter;
+	dev_dbg(&client->adapter->dev, "%s\n", __func__);
+
+	inv_mpu_close(mldl_cfg,
+		slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
+		slave_adapter[EXT_SLAVE_TYPE_ACCEL],
+		slave_adapter[EXT_SLAVE_TYPE_COMPASS],
+		slave_adapter[EXT_SLAVE_TYPE_PRESSURE]);
+
+	if (client->irq)
+		mpuirq_exit();
+
+	for (ii = EXT_SLAVE_TYPE_ACCEL - 1; ii >= 0; ii--) {
+		if (pdata_slave[ii] &&
+		    pdata_slave[ii]->irq) {
+			slaveirq_exit(pdata_slave[ii]);
+		}
+	}
+
+	unregister_pm_notifier(&mpu->nb);
+
+	misc_deregister(&mpu->dev);
+	kfree(mpu);
+
+	return 0;
+}
+
+static struct i2c_driver mpu_driver = {
+	.class = I2C_CLASS_HWMON,
+	.probe = mpu_probe,
+	.remove = mpu_remove,
+	.id_table = mpu_id,
+	.driver = {
+		   .owner = THIS_MODULE,
+		   .name = MPU_NAME,
+		   },
+	.address_list = normal_i2c,
+	.shutdown = mpu_shutdown,	/* optional */
+	.suspend = mpu_dev_suspend,	/* optional */
+	.resume = mpu_dev_resume,	/* optional */
+
+};
+
+static int __init mpu_init(void)
+{
+	int res = i2c_add_driver(&mpu_driver);
+	pr_info("%s: Probe name %s\n", __func__, MPU_NAME);
+	if (res)
+		pr_err("%s failed\n", __func__);
+	return res;
+}
+
+static void __exit mpu_exit(void)
+{
+	pr_info("%s\n", __func__);
+	i2c_del_driver(&mpu_driver);
+}
+
+module_init(mpu_init);
+module_exit(mpu_exit);
+
+MODULE_AUTHOR("Invensense Corporation");
+MODULE_DESCRIPTION("User space character device interface for MPU");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS(MPU_NAME);
diff --git a/drivers/misc/inv_mpu/mpu-dev.h b/drivers/misc/inv_mpu/mpu-dev.h
new file mode 100644
index 0000000..b6a4fcf
--- /dev/null
+++ b/drivers/misc/inv_mpu/mpu-dev.h
@@ -0,0 +1,36 @@
+/*
+	$License:
+	Copyright (C) 2011 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/>.
+	$
+ */
+
+
+#ifndef __MPU_DEV_H__
+#define __MPU_DEV_H__
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/mpu.h>
+
+int inv_mpu_register_slave(struct module *slave_module,
+			struct i2c_client *client,
+			struct ext_slave_platform_data *pdata,
+			struct ext_slave_descr *(*slave_descr)(void));
+
+void inv_mpu_unregister_slave(struct i2c_client *client,
+			struct ext_slave_platform_data *pdata,
+			struct ext_slave_descr *(*slave_descr)(void));
+#endif
-- 
1.7.4.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ