patch-2.3.34 linux/drivers/i2c/i2c-dev.c

Next file: linux/drivers/i2c/i2c-elektor.c
Previous file: linux/drivers/i2c/i2c-core.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.3.33/linux/drivers/i2c/i2c-dev.c linux/drivers/i2c/i2c-dev.c
@@ -0,0 +1,552 @@
+/*
+    i2c-dev.c - i2c-bus driver, char device interface  
+
+    Copyright (C) 1995-97 Simon G. Vogl
+    Copyright (C) 1998-99 Frodo Looijaard <frodol@dds.nl>
+
+    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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/* Note that this is a complete rewrite of Simon Vogl's i2c-dev module.
+   But I have used so much of his original code and ideas that it seems
+   only fair to recognize him as co-author -- Frodo */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/malloc.h>
+#include <linux/version.h>
+
+/* If you want debugging uncomment: */
+/* #define DEBUG */
+
+#ifndef KERNEL_VERSION
+#define KERNEL_VERSION(a,b,c) (((a) << 16) | ((b) << 8) | (c))
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,51)
+#include <linux/init.h>
+#else
+#define __init
+#endif
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,4))
+#define copy_from_user memcpy_fromfs
+#define copy_to_user memcpy_tofs
+#define get_user_data(to,from) ((to) = get_user(from),0)
+#else
+#include <asm/uaccess.h>
+#define get_user_data(to,from) get_user(to,from)
+#endif
+
+/* 2.0.0 kernel compatibility */
+#if LINUX_VERSION_CODE < 0x020100
+#define MODULE_AUTHOR(noone)
+#define MODULE_DESCRIPTION(none)
+#define MODULE_PARM(no,param)
+#define MODULE_PARM_DESC(no,description)
+#define EXPORT_SYMBOL(noexport)
+#define EXPORT_NO_SYMBOLS
+#endif
+
+#include <linux/i2c.h>
+#include <linux/i2c-dev.h>
+
+#ifdef MODULE
+extern int init_module(void);
+extern int cleanup_module(void);
+#endif /* def MODULE */
+
+/* struct file_operations changed too often in the 2.1 series for nice code */
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70))
+static loff_t i2cdev_lseek (struct file *file, loff_t offset, int origin);
+#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,56))
+static long long i2cdev_lseek (struct file *file, long long offset, int origin);
+#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0))
+static long long i2cdev_llseek (struct inode *inode, struct file *file, 
+                                long long offset, int origin);
+#else
+static int i2cdev_lseek (struct inode *inode, struct file *file, off_t offset, 
+                         int origin);
+#endif
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70))
+static ssize_t i2cdev_read (struct file *file, char *buf, size_t count, 
+                            loff_t *offset);
+static ssize_t i2cdev_write (struct file *file, const char *buf, size_t count, 
+                             loff_t *offset);
+#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0))
+static long i2cdev_read (struct inode *inode, struct file *file, char *buf, 
+                         unsigned long count);
+static long i2cdev_write (struct inode *inode, struct file *file, 
+                          const char *buf, unsigned long offset);
+#else
+static int i2cdev_read(struct inode *inode, struct file *file, char *buf, 
+                       int count);
+static int i2cdev_write(struct inode *inode, struct file *file, 
+                        const char *buf, int count);
+#endif
+
+static int i2cdev_ioctl (struct inode *inode, struct file *file, 
+                         unsigned int cmd, unsigned long arg);
+static int i2cdev_open (struct inode *inode, struct file *file);
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,31))
+static int i2cdev_release (struct inode *inode, struct file *file);
+#else
+static void i2cdev_release (struct inode *inode, struct file *file);
+#endif
+
+
+static int i2cdev_attach_adapter(struct i2c_adapter *adap);
+static int i2cdev_detach_client(struct i2c_client *client);
+static int i2cdev_command(struct i2c_client *client, unsigned int cmd,
+                           void *arg);
+
+#ifdef MODULE
+static
+#else
+extern
+#endif
+       int __init i2c_dev_init(void);
+static int i2cdev_cleanup(void);
+
+static struct file_operations i2cdev_fops = {
+    i2cdev_lseek,
+    i2cdev_read,
+    i2cdev_write,
+    NULL,                   /* i2cdev_readdir  */
+    NULL,                   /* i2cdev_select   */
+    i2cdev_ioctl,
+    NULL,                   /* i2cdev_mmap     */
+    i2cdev_open,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,118)
+    NULL,                   /* i2cdev_flush    */
+#endif
+    i2cdev_release,
+};
+
+#define I2CDEV_ADAPS_MAX I2C_ADAP_MAX
+static struct i2c_adapter *i2cdev_adaps[I2CDEV_ADAPS_MAX];
+
+static struct i2c_driver i2cdev_driver = {
+  /* name */            "i2c-dev dummy driver",
+  /* id */              I2C_DRIVERID_I2CDEV,
+  /* flags */           I2C_DF_DUMMY,
+  /* attach_adapter */  i2cdev_attach_adapter,
+  /* detach_client */   i2cdev_detach_client,
+  /* command */         i2cdev_command,
+  /* inc_use */         NULL,
+  /* dec_use */         NULL,
+};
+
+static struct i2c_client i2cdev_client_template = {
+  /* name */          "I2C /dev entry",
+  /* id */            1,
+  /* flags */         0,
+  /* addr */          -1,
+  /* adapter */       NULL,
+  /* driver */        &i2cdev_driver,
+  /* data */          NULL
+};
+
+static int i2cdev_initialized;
+
+/* Note that the lseek function is called llseek in 2.1 kernels. But things
+   are complicated enough as is. */
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70))
+loff_t i2cdev_lseek (struct file *file, loff_t offset, int origin)
+#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,56))
+long long i2cdev_lseek (struct file *file, long long offset, int origin)
+#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0))
+long long i2cdev_llseek (struct inode *inode, struct file *file, 
+                         long long offset, int origin)
+#else
+int i2cdev_lseek (struct inode *inode, struct file *file, off_t offset, 
+                  int origin)
+#endif
+{
+#ifdef DEBUG
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,56))
+   struct inode *inode = file->f_dentry->d_inode;
+#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70)) */
+  printk("i2c-dev,o: i2c-%d lseek to %ld bytes relative to %d.\n",
+         MINOR(inode->i_rdev),(long) offset,origin);
+#endif /* DEBUG */
+  return -ESPIPE;
+}
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70))
+static ssize_t i2cdev_read (struct file *file, char *buf, size_t count,
+                            loff_t *offset)
+#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0))
+static long i2cdev_read (struct inode *inode, struct file *file, char *buf,
+                         unsigned long count)
+#else
+static int i2cdev_read(struct inode *inode, struct file *file, char *buf,
+                       int count)
+#endif
+{
+  char *tmp;
+  int ret;
+
+#ifdef DEBUG
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70))
+   struct inode *inode = file->f_dentry->d_inode;
+#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70)) */
+#endif /* DEBUG */
+
+  struct i2c_client *client = (struct i2c_client *)file->private_data;
+
+  /* copy user space data to kernel space. */
+  tmp = kmalloc(count,GFP_KERNEL);
+  if (tmp==NULL)
+     return -ENOMEM;
+
+#ifdef DEBUG
+  printk("i2c-dev,o: i2c-%d reading %d bytes.\n",MINOR(inode->i_rdev),count);
+#endif
+
+  ret = i2c_master_recv(client,tmp,count);
+  copy_to_user(buf,tmp,count);
+  kfree(tmp);
+  return ret;
+}
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70))
+static ssize_t i2cdev_write (struct file *file, const char *buf, size_t count,
+                             loff_t *offset)
+#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0))
+static long i2cdev_write (struct inode *inode, struct file *file,
+                          const char *buf, unsigned long offset)
+#else
+static int i2cdev_write(struct inode *inode, struct file *file,
+                        const char *buf, int count)
+#endif
+{
+  int ret;
+  char *tmp;
+  struct i2c_client *client = (struct i2c_client *)file->private_data;
+
+#ifdef DEBUG
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70))
+   struct inode *inode = file->f_dentry->d_inode;
+#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70)) */
+#endif /* DEBUG */
+
+  /* copy user space data to kernel space. */
+  tmp = kmalloc(count,GFP_KERNEL);
+  if (tmp==NULL)
+    return -ENOMEM;
+  copy_from_user(tmp,buf,count);
+
+#ifdef DEBUG
+  printk("i2c-dev,o: i2c-%d writing %d bytes.\n",MINOR(inode->i_rdev),count);
+#endif
+  ret = i2c_master_send(client,tmp,count);
+  kfree(tmp);
+  return ret;
+}
+
+int i2cdev_ioctl (struct inode *inode, struct file *file, unsigned int cmd, 
+                  unsigned long arg)
+{
+  struct i2c_client *client = (struct i2c_client *)file->private_data;
+  struct i2c_smbus_ioctl_data data_arg;
+  union i2c_smbus_data temp;
+  int ver,datasize,res;
+  unsigned long funcs;
+
+#ifdef DEBUG
+  printk("i2c-dev.o: i2c-%d ioctl, cmd: 0x%x, arg: %lx.\n", 
+         MINOR(inode->i_rdev),cmd, arg);
+#endif /* DEBUG */
+
+  switch ( cmd ) {
+    case I2C_SLAVE:
+      if ((arg > 0x3ff) || (((client->flags & I2C_M_TEN) == 0) && arg > 0x7f))
+        return -EINVAL;
+      client->addr = arg;
+      return 0;
+    case I2C_TENBIT:
+      if (arg)
+        client->flags |= I2C_M_TEN;
+      else
+        client->flags &= ~I2C_M_TEN;
+      return 0;
+    case I2C_FUNCS:
+      if (! arg) {
+#ifdef DEBUG
+        printk("i2c-dev.o: NULL argument pointer in ioctl I2C_SMBUS.\n");
+#endif
+        return -EINVAL;
+      }
+      if (verify_area(VERIFY_WRITE,(unsigned long *) arg,
+          sizeof(unsigned long))) {
+#ifdef DEBUG
+        printk("i2c-dev.o: invalid argument pointer (%ld) "
+               "in IOCTL I2C_SMBUS.\n", arg);
+#endif
+        return -EINVAL;
+      }
+      
+      funcs = i2c_get_functionality(client->adapter);
+      copy_to_user((unsigned long *)arg,&funcs,sizeof(unsigned long));
+      return 0;
+    case I2C_SMBUS:
+      if (! arg) {
+#ifdef DEBUG
+        printk("i2c-dev.o: NULL argument pointer in ioctl I2C_SMBUS.\n");
+#endif
+        return -EINVAL;
+      }
+      if (verify_area(VERIFY_READ,(struct i2c_smbus_ioctl_data *) arg,
+          sizeof(struct i2c_smbus_ioctl_data))) {
+#ifdef DEBUG
+        printk("i2c-dev.o: invalid argument pointer (%ld) "
+               "in IOCTL I2C_SMBUS.\n", arg);
+#endif
+        return -EINVAL;
+      }
+      copy_from_user(&data_arg,(struct i2c_smbus_ioctl_data *) arg,
+                     sizeof(struct i2c_smbus_ioctl_data));
+      if ((data_arg.size != I2C_SMBUS_BYTE) && 
+          (data_arg.size != I2C_SMBUS_QUICK) &&
+          (data_arg.size != I2C_SMBUS_BYTE_DATA) && 
+          (data_arg.size != I2C_SMBUS_WORD_DATA) &&
+          (data_arg.size != I2C_SMBUS_PROC_CALL) &&
+          (data_arg.size != I2C_SMBUS_BLOCK_DATA)) {
+#ifdef DEBUG
+        printk("i2c-dev.o: size out of range (%x) in ioctl I2C_SMBUS.\n",
+               data_arg.size);
+#endif
+        return -EINVAL;
+      }
+      /* Note that I2C_SMBUS_READ and I2C_SMBUS_WRITE are 0 and 1, 
+         so the check is valid if size==I2C_SMBUS_QUICK too. */
+      if ((data_arg.read_write != I2C_SMBUS_READ) && 
+          (data_arg.read_write != I2C_SMBUS_WRITE)) {
+#ifdef DEBUG
+        printk("i2c-dev.o: read_write out of range (%x) in ioctl I2C_SMBUS.\n",
+               data_arg.read_write);
+#endif
+        return -EINVAL;
+      }
+
+      /* Note that command values are always valid! */
+
+      if ((data_arg.size == I2C_SMBUS_QUICK) ||
+          ((data_arg.size == I2C_SMBUS_BYTE) && 
+           (data_arg.read_write == I2C_SMBUS_WRITE)))
+        /* These are special: we do not use data */
+        return i2c_smbus_xfer(client->adapter, client->addr, client->flags,
+                              data_arg.read_write, data_arg.command,
+                              data_arg.size, NULL);
+
+      if (data_arg.data == NULL) {
+#ifdef DEBUG
+        printk("i2c-dev.o: data is NULL pointer in ioctl I2C_SMBUS.\n");
+#endif
+        return -EINVAL;
+      }
+
+      /* This seems unlogical but it is not: if the user wants to read a
+         value, we must write that value to user memory! */
+      ver = ((data_arg.read_write == I2C_SMBUS_WRITE) && 
+             (data_arg.size != I2C_SMBUS_PROC_CALL))?VERIFY_READ:VERIFY_WRITE;
+
+      if ((data_arg.size == I2C_SMBUS_BYTE_DATA) || (data_arg.size == I2C_SMBUS_BYTE))
+        datasize = sizeof(data_arg.data->byte);
+      else if ((data_arg.size == I2C_SMBUS_WORD_DATA) || 
+               (data_arg.size == I2C_SMBUS_PROC_CALL))
+        datasize = sizeof(data_arg.data->word);
+      else /* size == I2C_SMBUS_BLOCK_DATA */
+        datasize = sizeof(data_arg.data->block);
+
+      if (verify_area(ver,data_arg.data,datasize)) {
+#ifdef DEBUG
+        printk("i2c-dev.o: invalid pointer data (%p) in ioctl I2C_SMBUS.\n",
+               data_arg.data);
+#endif
+        return -EINVAL;
+      }
+
+      if ((data_arg.size == I2C_SMBUS_PROC_CALL) || 
+          (data_arg.read_write == I2C_SMBUS_WRITE))
+        copy_from_user(&temp,data_arg.data,datasize);
+      res = i2c_smbus_xfer(client->adapter,client->addr,client->flags,
+                           data_arg.read_write,
+                           data_arg.command,data_arg.size,&temp);
+      if (! res && ((data_arg.size == I2C_SMBUS_PROC_CALL) || 
+                    (data_arg.read_write == I2C_SMBUS_READ)))
+        copy_to_user(data_arg.data,&temp,datasize);
+      return res;
+
+    default:
+      return i2c_control(client,cmd,arg);
+   }
+  return 0;
+}
+
+int i2cdev_open (struct inode *inode, struct file *file)
+{
+  unsigned int minor = MINOR(inode->i_rdev);
+  struct i2c_client *client;
+
+  if ((minor >= I2CDEV_ADAPS_MAX) || ! (i2cdev_adaps[minor])) {
+#ifdef DEBUG
+    printk("i2c-dev.o: Trying to open unattached adapter i2c-%d\n",minor);
+#endif
+    return -ENODEV;
+  }
+
+  /* Note that we here allocate a client for later use, but we will *not*
+     register this client! Yes, this is safe. No, it is not very clean. */
+  if(! (client = kmalloc(sizeof(struct i2c_client),GFP_KERNEL)))
+    return -ENOMEM;
+  memcpy(client,&i2cdev_client_template,sizeof(struct i2c_client));
+  client->adapter = i2cdev_adaps[minor];
+  file->private_data = client;
+
+  i2cdev_adaps[minor]->inc_use(i2cdev_adaps[minor]);
+  MOD_INC_USE_COUNT;
+
+#ifdef DEBUG
+  printk("i2c-dev.o: opened i2c-%d\n",minor);
+#endif
+  return 0;
+}
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,31))
+static int i2cdev_release (struct inode *inode, struct file *file)
+#else
+static void i2cdev_release (struct inode *inode, struct file *file)
+#endif
+{
+  unsigned int minor = MINOR(inode->i_rdev);
+   kfree(file->private_data);
+   file->private_data=NULL;
+#ifdef DEBUG
+   printk("i2c-dev.o: Closed: i2c-%d\n", minor);
+#endif
+  MOD_DEC_USE_COUNT;
+  i2cdev_adaps[minor]->dec_use(i2cdev_adaps[minor]);
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,31))
+   return 0;
+#endif
+}
+
+int i2cdev_attach_adapter(struct i2c_adapter *adap)
+{
+  int i;
+
+  if ((i = i2c_adapter_id(adap)) < 0) {
+    printk("i2c-dev.o: Unknown adapter ?!?\n");
+    return -ENODEV;
+  }
+  if (i >= I2CDEV_ADAPS_MAX) {
+    printk("i2c-dev.o: Adapter number too large?!? (%d)\n",i);
+    return -ENODEV;
+  }
+  
+  if (! i2cdev_adaps[i]) {
+    i2cdev_adaps[i] = adap;
+    printk("i2c-dev.o: Registered '%s' as minor %d\n",adap->name,i);
+  } else {
+    i2cdev_adaps[i] = NULL;
+#ifdef DEBUG
+    printk("i2c-dev.o: Adapter unregistered: %s\n",adap->name);
+#endif
+  }
+
+  return 0;
+}
+
+int i2cdev_detach_client(struct i2c_client *client)
+{
+  return 0;
+}
+
+static int i2cdev_command(struct i2c_client *client, unsigned int cmd,
+                           void *arg)
+{
+  return -1;
+}
+
+int __init i2c_dev_init(void)
+{
+  int res;
+
+  printk("i2c-dev.o: i2c /dev entries driver module\n");
+
+  i2cdev_initialized = 0;
+  if (register_chrdev(I2C_MAJOR,"i2c",&i2cdev_fops)) {
+    printk("i2c-dev.o: unable to get major %d for i2c bus\n",I2C_MAJOR);
+    return -EIO;
+  }
+  i2cdev_initialized ++;
+
+  if ((res = i2c_add_driver(&i2cdev_driver))) {
+    printk("i2c-dev.o: Driver registration failed, module not inserted.\n");
+    i2cdev_cleanup();
+    return res;
+  }
+  i2cdev_initialized ++;
+  return 0;
+}
+
+int i2cdev_cleanup(void)
+{
+  int res;
+
+  if (i2cdev_initialized >= 2) {
+    if ((res = i2c_del_driver(&i2cdev_driver))) {
+      printk("i2c-dev.o: Driver deregistration failed, "
+             "module not removed.\n");
+      return res;
+    }
+    i2cdev_initialized ++;
+  }
+
+  if (i2cdev_initialized >= 1) {
+    if ((res = unregister_chrdev(I2C_MAJOR,"i2c"))) {
+      printk("i2c-dev.o: unable to release major %d for i2c bus\n",I2C_MAJOR);
+      return res;
+    }
+    i2cdev_initialized --;
+  }
+  return 0;
+}
+
+EXPORT_NO_SYMBOLS;
+
+#ifdef MODULE
+
+MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl> and Simon G. Vogl <simon@tk.uni-linz.ac.at>");
+MODULE_DESCRIPTION("I2C /dev entries driver");
+
+int init_module(void)
+{
+  return i2c_dev_init();
+}
+
+int cleanup_module(void)
+{
+  return i2cdev_cleanup();
+}
+
+#endif /* def MODULE */
+

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)