summaryrefslogtreecommitdiff
path: root/drivers/i2c-slave/i2c_slave_core.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/i2c-slave/i2c_slave_core.c')
-rw-r--r--drivers/i2c-slave/i2c_slave_core.c359
1 files changed, 359 insertions, 0 deletions
diff --git a/drivers/i2c-slave/i2c_slave_core.c b/drivers/i2c-slave/i2c_slave_core.c
new file mode 100644
index 000000000000..4d3a21a5f45f
--- /dev/null
+++ b/drivers/i2c-slave/i2c_slave_core.c
@@ -0,0 +1,359 @@
+/*
+ * Copyright 2007-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <asm/uaccess.h>
+#include "i2c_slave_device.h"
+
+int i2c_slave_major;
+
+static ssize_t i2c_slave_read(struct file *fd, char __user *buf, size_t len,
+ loff_t *ptr)
+{
+ i2c_slave_device_t *dev;
+ void *kbuf;
+ int ret;
+
+ if (len == 0)
+ return 0;
+
+ kbuf = kmalloc(len, GFP_KERNEL);
+ if (!kbuf) {
+ ret = -ENOMEM;
+ goto error0;
+ }
+
+ dev = (i2c_slave_device_t *) fd->private_data;
+ if (!dev) {
+ ret = -ENODEV;
+ goto error1;
+ }
+
+ ret = i2c_slave_device_read(dev, len, kbuf);
+ if (ret <= 0 || copy_to_user(buf, kbuf, len)) {
+ ret = -EFAULT;
+ }
+
+ error1:
+ kfree(kbuf);
+ error0:
+ return ret;
+}
+
+static ssize_t i2c_slave_write(struct file *fd, const char __user *buf,
+ size_t len, loff_t *ptr)
+{
+ i2c_slave_device_t *dev;
+ void *kbuf;
+ int ret;
+
+ if (len == 0)
+ return 0;
+
+ kbuf = kmalloc(len, GFP_KERNEL);
+ if (!kbuf) {
+ ret = -ENOMEM;
+ goto error0;
+ }
+ if (copy_from_user(kbuf, buf, len)) {
+ ret = -EFAULT;
+ goto error1;
+ }
+
+ dev = (i2c_slave_device_t *) fd->private_data;
+ if (!dev) {
+ ret = -ENODEV;
+ goto error1;
+ }
+
+ ret = i2c_slave_device_write(dev, len, (u8 *) kbuf);
+
+ error1:
+ kfree(kbuf);
+ error0:
+ return ret;
+}
+
+static int i2c_slave_ioctl(struct inode *inode, struct file *fd,
+ unsigned code, unsigned long value)
+{
+ /*todo */
+ return 0;
+}
+
+static int i2c_slave_open(struct inode *inode, struct file *fd)
+{
+ int ret;
+ unsigned int id;
+ i2c_slave_device_t *dev;
+ id = iminor(inode);
+
+ if (id >= I2C_SLAVE_DEVICE_MAX) {
+ ret = -ENODEV;
+ goto error;
+ }
+
+ dev = i2c_slave_device_find(id);
+ if (!dev) {
+ ret = -ENODEV;
+ goto error;
+ }
+
+ i2c_slave_rb_clear(dev->receive_buffer);
+ i2c_slave_rb_clear(dev->send_buffer);
+
+ if (i2c_slave_device_start(dev)) {
+ ret = -EBUSY;
+ goto error;
+ }
+
+ fd->private_data = (void *)dev;
+ ret = 0;
+
+ error:
+ return ret;
+}
+
+static int i2c_slave_release(struct inode *inode, struct file *fd)
+{
+ int ret;
+ unsigned int id;
+ i2c_slave_device_t *dev;
+ id = iminor(inode);
+
+ if (id >= I2C_SLAVE_DEVICE_MAX) {
+ ret = -ENODEV;
+ goto error;
+ }
+
+ dev = i2c_slave_device_find(id);
+ if (!dev) {
+ ret = -ENODEV;
+ goto error;
+ }
+
+ if (i2c_slave_device_stop(dev)) {
+ ret = -EBUSY;
+ goto error;
+ }
+
+ ret = 0;
+
+ error:
+ return ret;
+}
+
+static const struct file_operations i2c_slave_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .read = i2c_slave_read,
+ .write = i2c_slave_write,
+ .ioctl = i2c_slave_ioctl,
+ .open = i2c_slave_open,
+ .release = i2c_slave_release,
+};
+
+static int i2c_slave_bus_match(struct device *dev, struct device_driver *drv)
+{
+ return 0;
+}
+
+/*
+static int i2c_slave_bus_probe(struct device *dev)
+{
+ struct device_driver *driver = dev->driver;
+
+ if (driver) {
+ return driver->probe(dev);
+ } else {
+ dev_err(dev, "%s: no driver\n", __func__);
+ return -ENODEV;
+ }
+}
+*/
+
+static int i2c_slave_bus_remove(struct device *dev)
+{
+ struct device_driver *driver = dev->driver;
+
+ if (driver) {
+ if (!driver->remove) {
+ return 0;
+ } else {
+ return driver->remove(dev);
+ }
+ } else {
+
+ dev_err(dev, "%s: no driver\n", __func__);
+ return -ENODEV;
+ }
+}
+
+static void i2c_slave_bus_shutdown(struct device *dev)
+{
+ struct device_driver *driver = dev->driver;
+
+ if (driver) {
+
+ driver->shutdown(dev);
+ } else {
+
+ dev_err(dev, "%s: no driver\n", __func__);
+ return;
+ }
+}
+static int i2c_slave_bus_suspend(struct device *dev, pm_message_t state)
+{
+ struct device_driver *driver = dev->driver;
+
+ if (driver) {
+
+ if (!driver->suspend) {
+ return 0;
+ } else {
+ return driver->suspend(dev, state);
+ }
+ } else {
+
+ dev_err(dev, "%s: no driver\n", __func__);
+ return -ENODEV;
+ }
+}
+
+static int i2c_slave_bus_resume(struct device *dev)
+{
+ struct device_driver *driver = dev->driver;
+
+ if (driver) {
+
+ if (!driver->resume) {
+ return 0;
+ } else {
+ return driver->resume(dev);
+ }
+ } else {
+
+ dev_err(dev, "%s: no driver\n", __func__);
+ return -ENODEV;
+ }
+}
+
+struct bus_type i2c_slave_bus_type = {
+ .name = "i2c-slave",
+ .match = i2c_slave_bus_match,
+ .remove = i2c_slave_bus_remove,
+ .shutdown = i2c_slave_bus_shutdown,
+ .suspend = i2c_slave_bus_suspend,
+ .resume = i2c_slave_bus_resume,
+};
+
+EXPORT_SYMBOL_GPL(i2c_slave_bus_type);
+
+static int i2c_slave_driver_probe(struct device *dev)
+{
+ return 0;
+}
+
+static int i2c_slave_driver_remove(struct device *dev)
+{
+ return 0;
+}
+
+static int i2c_slave_driver_shutdown(struct device *dev)
+{
+ return 0;
+}
+
+static int i2c_slave_driver_suspend(struct device *dev, pm_message_t state)
+{
+ return 0;
+}
+
+static int i2c_slave_driver_resume(struct device *dev)
+{
+ return 0;
+}
+
+extern struct class *i2c_slave_class;
+
+static struct device_driver i2c_slave_driver = {
+ .name = "i2c-slave",
+ .owner = THIS_MODULE,
+ .bus = &i2c_slave_bus_type,
+ .probe = i2c_slave_driver_probe,
+ .remove = i2c_slave_driver_remove,
+ .shutdown = i2c_slave_driver_shutdown,
+ .suspend = i2c_slave_driver_suspend,
+ .resume = i2c_slave_driver_resume,
+};
+
+static int __init i2c_slave_dev_init(void)
+{
+ int ret;
+
+ printk(KERN_INFO "i2c slave /dev entries driver\n");
+
+ ret = bus_register(&i2c_slave_bus_type);
+ if (ret) {
+ printk(KERN_ERR "%s: bus_register error\n", __func__);
+ goto out;
+ }
+
+ i2c_slave_class = class_create(THIS_MODULE, "i2c-slave");
+ if (IS_ERR(i2c_slave_class)) {
+ pr_err("%s: class_create error\n", __func__);
+ goto out_unreg_bus;
+ }
+
+ i2c_slave_major = register_chrdev(0, "i2c-slave", &i2c_slave_fops);
+ if (i2c_slave_major <= 0) {
+ pr_err("%s: register_chrdev error\n", __func__);
+ goto out_unreg_class;
+ }
+
+ ret = driver_register(&i2c_slave_driver);
+ if (ret) {
+ pr_err("%s: driver_register error\n", __func__);
+ goto out_unreg_chrdev;
+ }
+
+ return 0;
+
+ out_unreg_chrdev:
+ unregister_chrdev(i2c_slave_major, "i2c-slave");
+ out_unreg_class:
+ class_destroy(i2c_slave_class);
+ out_unreg_bus:
+ bus_unregister(&i2c_slave_bus_type);
+ out:
+ pr_err("%s: init error\n", __func__);
+ return ret;
+}
+
+static void __exit i2c_dev_exit(void)
+{
+ driver_unregister(&i2c_slave_driver);
+ class_destroy(i2c_slave_class);
+ unregister_chrdev(i2c_slave_major, "i2c-slave");
+ bus_unregister(&i2c_slave_bus_type);
+}
+
+module_init(i2c_slave_dev_init);
+module_exit(i2c_dev_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("I2C Slave Driver Core");
+MODULE_LICENSE("GPL");