[PATCH REVIEW] Drivers for Si700x Evaluation Module

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Signed-off-by: Prashant P Shah <pshah.mumbai@xxxxxxxxx>
---
 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, pshah.mumbai@xxxxxxxxx
+* 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 <pshah.mumbai@xxxxxxxxx>");
+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
devel@xxxxxxxxxxxxxxxxxxxxxx
http://driverdev.linuxdriverproject.org/mailman/listinfo/devel


[Index of Archives]     [Linux Driver Backports]     [DMA Engine]     [Linux GPIO]     [Linux SPI]     [Video for Linux]     [Linux USB Devel]     [Linux Coverity]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux