Signed-off-by: Prashant P Shah <[email protected]>
---
 Makefile |   24 +++
 si700x.c |  504 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 si700x.h |  102 +++++++++++++
 3 files changed, 630 insertions(+), 0 deletions(-)
 create mode 100644 Makefile
 create mode 100644 si700x.c
 create mode 100644 si700x.h

diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..9b33a3b
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,24 @@
+obj-m := si700x.o
+
+KERNELDIR := /lib/modules/$(shell uname -r)/build
+PWD := $(shell pwd)
+
+default:
+       $(MAKE) -C $(KERNELDIR) M=$(PWD)
+
+clean:
+       rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions 
modules.order  Module.symvers
+
+re:
+       sudo rmmod si700x
+       make clean
+       $(MAKE) -C $(KERNELDIR) M=$(PWD)
+       sudo insmod ./si700x.ko
+       sudo ./test
+
+depend .depend dep:
+       $(CC) $(CFLAGS) -M *.c > .depend
+
+ifeq (.depend,$(wildcard .depend))
+       include .depend
+endif
diff --git a/si700x.c b/si700x.c
new file mode 100644
index 0000000..92cd47d
--- /dev/null
+++ b/si700x.c
@@ -0,0 +1,504 @@
+/*
+* Copyright (C) 2012 Prashant Shah, [email protected]
+* Copyright (C) 2012 Silicon Labs, Inc. (www.silabs.com)
+*
+* 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.
+*/
+
+/*
+ * Silicon Labs Si700x USB Evaluation Board driver.
+ * http://www.silabs.com
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/ioctl.h>
+#include <linux/mutex.h>
+
+#include "si700x.h"
+
+/* Usb transfer request */
+struct transfer_req {
+       u8 type;
+       u8 status;
+       u8 address;
+       u8 length;
+       u8 data[4];
+} __attribute__ ((__packed__));
+
+struct si700x_dev {
+       struct usb_device *udev;                /* the usb device */
+       struct usb_interface *interface;        /* the usb interface */
+       struct transfer_req buffer;
+       int buffer_size;
+       int buffer_status;
+       struct mutex lock;
+};
+#define to_dev(d) container_of(d, struct si700x_dev, kref)
+
+static struct usb_driver si700x_driver;
+
+static int si700x_open(struct inode *i, struct file *f)
+{
+       struct si700x_dev *dev;
+       struct usb_interface *interface;
+       int minor;
+
+       pr_debug("Si700x: %s\n", __func__);
+
+       minor = iminor(i);
+
+       interface = usb_find_interface(&si700x_driver, minor);
+       if (!interface) {
+               printk(KERN_ERR "Si700x: failed to find interface "
+                       "for minor %d", minor);
+               return -ENODEV;
+       }
+
+       dev = usb_get_intfdata(interface);
+       if (!dev) {
+               printk(KERN_ERR "Si700x: failed to find device "
+                       "for minor %d\n", minor);
+               return -ENODEV;
+       }
+
+       /* save our object in the file's private structure */
+       mutex_lock(&dev->lock);
+       f->private_data = dev;
+       mutex_unlock(&dev->lock);
+       return 0;
+}
+
+static int si700x_release(struct inode *i, struct file *f)
+{
+       struct si700x_dev *dev;
+
+       pr_debug("Si700x: %s\n", __func__);
+
+       dev = (struct si700x_dev *)f->private_data;
+       if (dev == NULL) {
+               printk(KERN_ERR "Si700x: failed to find device from 
interface\n");
+               return -ENODEV;
+       }
+       mutex_lock(&dev->lock);
+       f->private_data = NULL;
+       mutex_unlock(&dev->lock);
+       return 0;
+}
+
+/*
+ * USB read function reads from the device with the address of the
+ * si700x_dev.buffer which was filled by the USB write function previously.
+ */
+static ssize_t si700x_read(struct file *f, char __user *user_buffer,
+               size_t count, loff_t *ppos)
+{
+       struct si700x_dev *dev;
+       int retval = 0;
+       int actual_length = 0;
+
+       pr_debug("Si700x: %s\n", __func__);
+
+       dev = (struct si700x_dev *)f->private_data;
+
+       /* check the size of the data buffer */
+       if (count != dev->buffer_size) {
+               printk(KERN_ERR "Si700x: invalid buffer size, "
+                       "it should be %d bytes\n", dev->buffer_size);
+               return -EFAULT;
+       }
+
+       /* check buffer status of the previous write function */
+       if (dev->buffer_status != 1) {
+               printk(KERN_ERR "Si700x: previous URB write was not 
successfull\n");
+               return -EFAULT;
+       }
+
+       /* check access to user space buffer */
+       if (!access_ok(VERIFY_WRITE, user_buffer, dev->buffer_size)) {
+               printk(KERN_ERR "Si700x: invalid user space data\n");
+               return -EFAULT;
+       }
+
+       mutex_lock(&dev->lock);
+       dev->buffer_status = 0;
+
+       retval = usb_bulk_msg(dev->udev,
+               usb_rcvbulkpipe(dev->udev, PIPE_DATA_IN),
+               &dev->buffer, dev->buffer_size, /* buffer, buffer length */
+               &actual_length, 0);             /* bytes written, interval */
+       if (retval < 0) {
+               printk(KERN_ERR "Si700x: failed to read URB\n");
+               mutex_unlock(&dev->lock);
+               return retval;
+       }
+
+       /* check if the read status is ok */
+       if (dev->buffer.status != XFER_STATUS_SUCCESS) {
+               printk(KERN_ERR "Si700x: device returned error "
+                       "status number %d\n", dev->buffer.status);
+               mutex_unlock(&dev->lock);
+               return -EFAULT;
+       }
+
+       /* saving the original data in the buffer for the USB read function */
+       if (copy_to_user(user_buffer, &dev->buffer, dev->buffer_size)) {
+               printk(KERN_ERR "Si700x: failed to copy data to user space\n");
+               mutex_unlock(&dev->lock);
+               return -EFAULT;
+       }
+
+       dev->buffer_status = 1;
+       mutex_unlock(&dev->lock);
+
+       return actual_length;
+}
+
+/*
+ * USB write function writes to the device and store the written data in the
+ * si700x_dev.buffer which is used by the USB read function
+ */
+static ssize_t si700x_write(struct file *f, const char __user *user_buffer,
+               size_t count, loff_t *ppos)
+{
+       struct si700x_dev *dev;
+       int retval = 0;
+       int actual_length = 0;
+
+       pr_debug("Si700x: %s\n", __func__);
+
+       dev = (struct si700x_dev *)f->private_data;
+
+       /* check the size of the data buffer */
+       if (count != dev->buffer_size) {
+               printk(KERN_ERR "Si700x: invalid buffer size, "
+                       "it should be %d bytes\n", dev->buffer_size);
+               return -EFAULT;
+       }
+
+       /* check access to user space buffer */
+       if (!access_ok(VERIFY_READ, user_buffer, dev->buffer_size)) {
+               printk(KERN_ERR "Si700x: invalid user space data\n");
+               return -EFAULT;
+       }
+
+       mutex_lock(&dev->lock);
+       /* saving the original data in the buffer for the USB read function */
+       if (copy_from_user(&dev->buffer, user_buffer, dev->buffer_size)) {
+               printk(KERN_ERR "Si700x: failed to copy data from user 
space\n");
+               mutex_unlock(&dev->lock);
+               return -EFAULT;
+       }
+
+       dev->buffer_status = 0;
+
+       retval = usb_bulk_msg(dev->udev,
+               usb_sndbulkpipe(dev->udev, PIPE_DATA_OUT),
+               &dev->buffer, dev->buffer_size, /* buffer, buffer length */
+               &actual_length, 0);             /* bytes written, interval */
+       if (retval < 0) {
+               printk(KERN_ERR "Si700x: failed to write URB\n");
+               mutex_unlock(&dev->lock);
+               return retval;
+       }
+
+       dev->buffer_status = 1;
+       mutex_unlock(&dev->lock);
+
+       return actual_length;
+}
+
+static long si700x_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
+{
+       struct si700x_dev *dev;
+       int retval = 0;
+       u16 version = 0;
+       u8 port_count = 0;
+       u8 board_id = 0;
+       u16 port_id = 0;
+
+       pr_debug("Si700x: %s\n", __func__);
+
+       dev = (struct si700x_dev *)f->private_data;
+
+       /* check if the ioctl is for the right device and within range */
+       if (_IOC_NR(cmd) > SI700X_IOC_MAXNR)
+               return -ENOTTY;
+
+       /* check if the data read and write from user is allowed */
+       if (_IOC_DIR(cmd) & _IOC_READ)
+               retval = !access_ok(VERIFY_WRITE, (void __user *)arg,
+                               _IOC_SIZE(cmd));
+       else if (_IOC_DIR(cmd) & _IOC_WRITE)
+               retval =  !access_ok(VERIFY_READ, (void __user *)arg,
+                               _IOC_SIZE(cmd));
+       if (retval)
+               return -EFAULT;
+
+       mutex_lock(&dev->lock);
+       switch (cmd) {
+
+       case SI700X_LED_ON:
+               /* turn on the LED */
+               retval = usb_control_msg(dev->udev,
+                       usb_sndctrlpipe(dev->udev, 0),
+                       REQ_SET_LED, CMD_VEN_DEV_OUT,
+                       1, 0,                   /* value, index */
+                       NULL, 0, 0);            /* data, size, timeout */
+               if (retval < 0) {
+                       printk(KERN_ERR "Si700x: failed to turn ON the LED\n");
+                       goto error;
+               }
+               mutex_unlock(&dev->lock);
+               return 0;
+
+       case SI700X_LED_OFF:
+               /* turn off the LED */
+               retval = usb_control_msg(dev->udev,
+                       usb_sndctrlpipe(dev->udev, 0),
+                       REQ_SET_LED, CMD_VEN_DEV_OUT,
+                       0, 0,                   /* value, index */
+                       NULL, 0, 0);            /* data, size, timeout */
+               if (retval < 0) {
+                       printk(KERN_ERR "Si700x: failed to turn OFF the LED\n");
+                       goto error;
+               }
+               mutex_unlock(&dev->lock);
+               return 0;
+
+       case SI700X_VERSION:
+               /* read vesion number from the board */
+               retval = usb_control_msg(dev->udev,
+                       usb_rcvctrlpipe(dev->udev, 0),
+                       REQ_GET_VERSION, CMD_VEN_DEV_IN,
+                       0, 0,                   /* value, index */
+                       &version, 2, 0);        /* data, size, timeout */
+               if (retval < 0) {
+                       printk(KERN_ERR "Si700x: failed to read version 
number\n");
+                       goto error;
+               }
+               mutex_unlock(&dev->lock);
+               return __put_user(version, (u16 __user *)arg);
+
+       case SI700X_PORT_COUNT:
+               /* read port count from the board */
+               retval = usb_control_msg(dev->udev,
+                       usb_rcvctrlpipe(dev->udev, 0),
+                       REQ_GET_PORT_COUNT, CMD_VEN_DEV_IN,
+                       0, 0,                   /* value, index */
+                       &port_count, 1, 0);     /* data, size, timeout */
+               if (retval < 0) {
+                       printk(KERN_ERR "Si700x: failed to read port count\n");
+                       goto error;
+               }
+               mutex_unlock(&dev->lock);
+               return __put_user(port_count, (u8 __user *)arg);
+
+       case SI700X_BOARDID:
+               /* read board id from the board */
+               retval = usb_control_msg(dev->udev,
+                       usb_rcvctrlpipe(dev->udev, 0),
+                       REQ_GET_BOARD_ID, CMD_VEN_DEV_IN,
+                       0, 0,                   /* value, index */
+                       &board_id, 1, 0);       /* data, size, timeout */
+               if (retval < 0) {
+                       printk(KERN_ERR "Si700x: failed to read board id\n");
+                       goto error;
+               }
+               mutex_unlock(&dev->lock);
+               return __put_user(board_id, (u8 __user *)arg);
+
+       case SI700X_SETPROG_ON:
+               /* turn on programming */
+               retval = usb_control_msg(dev->udev,
+                       usb_sndctrlpipe(dev->udev, 0),
+                       REQ_SET_PROG, CMD_VEN_DEV_OUT,
+                       1, 0,                   /* value, index - port */
+                       NULL, 0, 0);            /* data, size, timeout */
+               if (retval < 0) {
+                       printk(KERN_ERR "Si700x: failed to turn ON the 
programming mode\n");
+                       goto error;
+               }
+               mutex_unlock(&dev->lock);
+               return 0;
+
+       case SI700X_SETPROG_OFF:
+               /* turn off programming */
+               retval = usb_control_msg(dev->udev,
+                       usb_sndctrlpipe(dev->udev, 0),
+                       REQ_SET_PROG, CMD_VEN_DEV_OUT,
+                       0, 0,                   /* value, index - port */
+                       NULL, 0, 0);            /* data, size, timeout */
+               if (retval < 0) {
+                       printk(KERN_ERR "Si700x: failed to turn OFF the 
programming mode\n");
+                       goto error;
+               }
+               mutex_unlock(&dev->lock);
+               return 0;
+
+       case SI700X_SETSLEEP_ON:
+               port_id = arg;
+               /* turn on sleeping */
+               retval = usb_control_msg(dev->udev,
+                       usb_sndctrlpipe(dev->udev, 0),
+                       REQ_SET_SLEEP, CMD_VEN_DEV_OUT,
+                       0, port_id,             /* value, index - port */
+                       NULL, 0, 0);            /* data, size, timeout */
+               if (retval < 0) {
+                       printk(KERN_ERR "Si700x: failed to turn ON the sleeping 
"
+                               "for port %d\n", port_id);
+                       goto error;
+               }
+               mutex_unlock(&dev->lock);
+               return 0;
+
+       case SI700X_SETSLEEP_OFF:
+               port_id = arg;
+               /* turn off sleeping */
+               retval = usb_control_msg(dev->udev,
+                       usb_sndctrlpipe(dev->udev, 0),
+                       REQ_SET_SLEEP, CMD_VEN_DEV_OUT,
+                       0, port_id,             /* value, index - port */
+                       NULL, 0, 0);            /* data, size, timeout */
+               if (retval < 0) {
+                       printk(KERN_ERR "Si700x: failed to turn OFF the 
sleeping "
+                                       "for port %d\n", port_id);
+                       goto error;
+               }
+               mutex_unlock(&dev->lock);
+               return 0;
+
+       }
+       mutex_unlock(&dev->lock);
+       return -EINVAL;
+error:
+       mutex_unlock(&dev->lock);
+       return retval;
+}
+
+static const struct file_operations si700x_fops = {
+       .open = si700x_open,
+       .release = si700x_release,
+       .read = si700x_read,
+       .write = si700x_write,
+       .unlocked_ioctl = si700x_ioctl,
+};
+
+static struct usb_class_driver si700x_class = {
+       .name = "si700x%d",
+       .fops = &si700x_fops,
+       .minor_base = 192,
+};
+
+static int si700x_probe(struct usb_interface *interface,
+               const struct usb_device_id *id)
+{
+       struct si700x_dev *dev = NULL;
+       struct usb_host_interface *iface_desc;
+       int retval = -ENOMEM;
+
+       pr_debug("Si700x: %s\n", __func__);
+
+       dev = kmalloc(sizeof(struct si700x_dev), GFP_KERNEL);
+       if (!dev) {
+               printk(KERN_ERR "Si700x: failed to allocate memory for 
device\n");
+               return -ENOMEM;
+       }
+       memset(dev, 0x00, sizeof(dev));
+
+       mutex_init(&dev->lock);
+       mutex_lock(&dev->lock);
+       dev->interface = interface;
+       dev->udev = interface_to_usbdev(interface);
+       dev->buffer_size = sizeof(dev->buffer);
+
+       iface_desc = interface->cur_altsetting;
+
+       usb_set_intfdata(interface, dev);
+
+       retval = usb_register_dev(interface, &si700x_class);
+       if (retval < 0) {
+               printk(KERN_ERR "Si700x: failed to get minor number\n");
+               usb_set_intfdata(interface, NULL);
+               mutex_unlock(&dev->lock);
+               kfree(dev);
+               return retval;
+       }
+       mutex_unlock(&dev->lock);
+       printk(KERN_INFO "Si700x: minor number %d\n", interface->minor);
+       return 0;
+}
+
+static void si700x_disconnect(struct usb_interface *interface)
+{
+       struct si700x_dev *dev;
+       int minor = interface->minor;
+
+       pr_debug("Si700x: %s\n", __func__);
+
+       dev = usb_get_intfdata(interface);
+       mutex_lock(&dev->lock);
+       usb_deregister_dev(interface, &si700x_class);
+       usb_set_intfdata(interface, NULL);
+       mutex_unlock(&dev->lock);
+       kfree(dev);
+       printk(KERN_INFO "Si700x: USB #%d now disconnted\n", minor);
+}
+
+static struct usb_device_id si700x_table[] = {
+       { USB_DEVICE(0x10c4, 0x8649) },
+       {}
+};
+MODULE_DEVICE_TABLE(usb, si700x_table);
+
+static struct usb_driver si700x_driver = {
+       .name = "si700x",
+       .probe = si700x_probe,
+       .disconnect = si700x_disconnect,
+       .id_table = si700x_table,
+};
+
+static int __init si700x_init(void)
+{
+       int retval;
+
+       pr_debug("Si700x: %s\n", __func__);
+
+       retval = usb_register(&si700x_driver);
+       if (retval) {
+               printk(KERN_ERR "Si700x: failed to register usb device\n");
+               return retval;
+       }
+       return 0;
+}
+
+static void __exit si700x_exit(void)
+{
+       pr_debug("Si700x: %s\n", __func__);
+
+       usb_deregister(&si700x_driver);
+}
+
+module_init(si700x_init);
+module_exit(si700x_exit);
+
+MODULE_AUTHOR("Prashant Shah <[email protected]>");
+MODULE_DESCRIPTION("Silicon Labs Si700x USB Evaluation Board");
+MODULE_LICENSE("GPL v2");
+
diff --git a/si700x.h b/si700x.h
new file mode 100644
index 0000000..6778325
--- /dev/null
+++ b/si700x.h
@@ -0,0 +1,102 @@
+#ifndef _SI700X_H
+#define _SI700X_H
+
+/* IOCTL definitions */
+
+#define SI700X_IOC_MAGIC 'k'
+#define SI700X_IOC_MAXNR 9
+
+#define SI700X_LED_ON          _IO(SI700X_IOC_MAGIC, 1)
+#define SI700X_LED_OFF         _IO(SI700X_IOC_MAGIC, 2)
+#define SI700X_VERSION         _IOR(SI700X_IOC_MAGIC, 3, int)
+#define SI700X_PORT_COUNT      _IOR(SI700X_IOC_MAGIC, 4, int)
+#define SI700X_BOARDID         _IOR(SI700X_IOC_MAGIC, 5, int)
+#define SI700X_SETPROG_ON      _IO(SI700X_IOC_MAGIC, 6)
+#define SI700X_SETPROG_OFF     _IO(SI700X_IOC_MAGIC, 7)
+#define SI700X_SETSLEEP_ON     _IOW(SI700X_IOC_MAGIC, 8, unsigned int)
+#define SI700X_SETSLEEP_OFF    _IOW(SI700X_IOC_MAGIC, 9, unsigned int)
+
+#define XFER_TYPE_WRITE          0x10
+#define XFER_TYPE_READ           0x20
+#define XFER_TYPE_WRITE_READ    (XFER_TYPE_WRITE|XFER_TYPE_READ)
+
+/* Slave Address */
+#define SLAVE_NEW          0x40
+#define SLAVE_LEGACY       0X20
+
+/* Registers */
+#define REG_STATUS         0x00
+#define REG_DATA           0x01
+#define REG_CFG1           0x03
+#define REG_CFG2           0x04
+#define REG_DEVICE_ID      0x11
+#define REG_RAW_DATA       0x29
+
+/* Status Register */
+#define STATUS_NOT_READY   0x01
+
+/* Config 1 Register */
+#define CFG1_START_CONV    0x01
+#define CFG1_HUMIDITY      0x04
+#define CFG1_TEMPERATURE   0x10
+#define CFG1_FAST_CONV     0x20
+
+/* Config 2 Register */
+#define CFG2_EN_TEST_REG   0x80
+
+/* Coefficients */
+#define TEMPERATURE_OFFSET 50
+#define HUMIDITY_OFFSET    16
+#define SLOPE              32
+
+/* Return codes */
+#define SUCCESS             0      /* Success                      */
+#define ERROR_LENGTH_BAD   -1      /* DataLength is not valid      */
+#define ERROR_INIT_FAIL    -2      /* Can not initialize slave     */
+#define ERROR_READ_FAIL    -3      /* Can not read from slave      */
+#define ERROR_WRITE_FAIL   -4      /* Can not write to slave       */
+#define ERROR_TIME_OUT     -5      /* Timed out waiting for ready  */
+
+/* Transfer status */
+#define XFER_STATUS_NONE         0x00
+#define XFER_STATUS_SUCCESS      0x01
+#define XFER_STATUS_ADDR_NAK     0x02
+#define XFER_STATUS_DATA_NAK     0x03
+#define XFER_STATUS_TIMEOUT      0x04
+#define XFER_STATUS_ARBLOST      0x05
+#define XFER_STATUS_BAD_LENGTH   0x06
+#define XFER_STATUS_BAD_MODE     0x07
+#define XFER_STATUS_BAD_STATE    0x09
+
+/* Pipes */
+#define PIPE_DATA_OUT      0x02
+#define PIPE_DATA_IN       0x82
+
+/* Request type */
+#define CMD_VEN_DEV_OUT    0x40
+#define CMD_VEN_DEV_IN     0xC0
+
+/* Request */
+#define REQ_GET_VERSION    0
+#define REQ_SET_LED        1
+#define REQ_SET_SLEEP      2
+#define REQ_SET_PROG       3
+#define REQ_GET_PORT_COUNT 4
+#define REQ_GET_BOARD_ID   5
+
+/* Maximum data bytes that can be transfered with ReadData() and WriteData() */
+#define MAX_DATA_LENGTH  3
+
+/* Maximum number of slaves on a Si7001 board */
+#define MAX_SLAVE_COUNT  8
+
+/* Maximum number of bytes in a packet */
+#define MAX_PACKET_SIZE  64
+
+/* Maximum number of I2C data bytes that can be read or written */
+#define MAX_XFER_LENGTH  4
+
+/* Maximum number of transfer requests in a packet */
+#define MAX_XFER_COUNT   (MAX_PACKET_SIZE/(4+MAX_XFER_LENGTH))
+
+#endif
-- 
1.7.5.4

_______________________________________________
devel mailing list
[email protected]
http://driverdev.linuxdriverproject.org/mailman/listinfo/devel

Reply via email to