summaryrefslogtreecommitdiff
path: root/drivers/staging/greybus/vibrator.c
diff options
context:
space:
mode:
authorViresh Kumar <viresh.kumar@linaro.org>2015-01-21 16:10:41 +0530
committerGreg Kroah-Hartman <greg@kroah.com>2015-01-22 11:27:20 +0800
commit98abb4146ed31f1ec97145ef808d864096d31c4b (patch)
treea37a74a175b28e69fa0ed4541a9afb31fb455007 /drivers/staging/greybus/vibrator.c
parent5357cf323110ee4a3f4a12870618eca28672c7b9 (diff)
greybus: Remove "gb-" prefix from .c files
Some files are still prefixed with "gb-" with the reasoning that the modules would be named so, i.e. gb-*.ko. But this can be done by playing a bit in Makefile instead and keep uniform naming of .c files. Lets try it. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Reviewed-by: Alex Elder <elder@linaro.org> Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
Diffstat (limited to 'drivers/staging/greybus/vibrator.c')
-rw-r--r--drivers/staging/greybus/vibrator.c227
1 files changed, 227 insertions, 0 deletions
diff --git a/drivers/staging/greybus/vibrator.c b/drivers/staging/greybus/vibrator.c
new file mode 100644
index 000000000000..b5332df7039c
--- /dev/null
+++ b/drivers/staging/greybus/vibrator.c
@@ -0,0 +1,227 @@
+/*
+ * Greybus Vibrator protocol driver.
+ *
+ * Copyright 2014 Google Inc.
+ * Copyright 2014 Linaro Ltd.
+ *
+ * Released under the GPLv2 only.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/kdev_t.h>
+#include <linux/idr.h>
+#include "greybus.h"
+
+struct gb_vibrator_device {
+ struct gb_connection *connection;
+ struct device *dev;
+ int minor; /* vibrator minor number */
+ u8 version_major;
+ u8 version_minor;
+};
+
+/* Version of the Greybus vibrator protocol we support */
+#define GB_VIBRATOR_VERSION_MAJOR 0x00
+#define GB_VIBRATOR_VERSION_MINOR 0x01
+
+/* Greybus Vibrator request types */
+#define GB_VIBRATOR_TYPE_INVALID 0x00
+#define GB_VIBRATOR_TYPE_PROTOCOL_VERSION 0x01
+#define GB_VIBRATOR_TYPE_ON 0x02
+#define GB_VIBRATOR_TYPE_OFF 0x03
+#define GB_VIBRATOR_TYPE_RESPONSE 0x80 /* OR'd with rest */
+
+struct gb_vibrator_proto_version_response {
+ __u8 major;
+ __u8 minor;
+};
+
+struct gb_vibrator_on_request {
+ __le16 timeout_ms;
+};
+
+/*
+ * This request only uses the connection field, and if successful,
+ * fills in the major and minor protocol version of the target.
+ */
+static int get_version(struct gb_vibrator_device *vib)
+{
+ struct gb_connection *connection = vib->connection;
+ struct gb_vibrator_proto_version_response version_response;
+ int retval;
+
+ retval = gb_operation_sync(connection,
+ GB_VIBRATOR_TYPE_PROTOCOL_VERSION,
+ NULL, 0,
+ &version_response, sizeof(version_response));
+ if (retval)
+ return retval;
+
+ if (version_response.major > GB_VIBRATOR_VERSION_MAJOR) {
+ dev_err(&connection->dev,
+ "unsupported major version (%hhu > %hhu)\n",
+ version_response.major, GB_VIBRATOR_VERSION_MAJOR);
+ return -ENOTSUPP;
+ }
+
+ vib->version_major = version_response.major;
+ vib->version_minor = version_response.minor;
+ return 0;
+}
+
+static int turn_on(struct gb_vibrator_device *vib, u16 timeout_ms)
+{
+ struct gb_vibrator_on_request request;
+
+ request.timeout_ms = cpu_to_le16(timeout_ms);
+ return gb_operation_sync(vib->connection, GB_VIBRATOR_TYPE_ON,
+ &request, sizeof(request), NULL, 0);
+}
+
+static int turn_off(struct gb_vibrator_device *vib)
+{
+ return gb_operation_sync(vib->connection, GB_VIBRATOR_TYPE_OFF,
+ NULL, 0, NULL, 0);
+}
+
+static ssize_t timeout_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct gb_vibrator_device *vib = dev_get_drvdata(dev);
+ unsigned long val;
+ int retval;
+
+ retval = kstrtoul(buf, 10, &val);
+ if (retval < 0) {
+ dev_err(dev, "could not parse timeout value %d\n", retval);
+ return retval;
+ }
+
+ if (val)
+ retval = turn_on(vib, (u16)val);
+ else
+ retval = turn_off(vib);
+ if (retval)
+ return retval;
+
+ return count;
+}
+static DEVICE_ATTR_WO(timeout);
+
+static struct attribute *vibrator_attrs[] = {
+ &dev_attr_timeout.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(vibrator);
+
+static struct class vibrator_class = {
+ .name = "vibrator",
+ .owner = THIS_MODULE,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0)
+ .dev_groups = vibrator_groups,
+#endif
+};
+
+static DEFINE_IDR(minors);
+
+static int gb_vibrator_connection_init(struct gb_connection *connection)
+{
+ struct gb_vibrator_device *vib;
+ struct device *dev;
+ int retval;
+
+ vib = kzalloc(sizeof(*vib), GFP_KERNEL);
+ if (!vib)
+ return -ENOMEM;
+
+ vib->connection = connection;
+ connection->private = vib;
+
+ retval = get_version(vib);
+ if (retval)
+ goto error;
+
+ /*
+ * For now we create a device in sysfs for the vibrator, but odds are
+ * there is a "real" device somewhere in the kernel for this, but I
+ * can't find it at the moment...
+ */
+ vib->minor = idr_alloc(&minors, vib, 0, 0, GFP_KERNEL);
+ if (vib->minor < 0) {
+ retval = vib->minor;
+ goto error;
+ }
+ dev = device_create(&vibrator_class, &connection->dev, MKDEV(0, 0), vib,
+ "vibrator%d", vib->minor);
+ if (IS_ERR(dev)) {
+ retval = -EINVAL;
+ goto error;
+ }
+ vib->dev = dev;
+
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(3,11,0)
+ /*
+ * Newer kernels handle this in a race-free manner, by the dev_groups
+ * field in the struct class up above. But for older kernels, we need
+ * to "open code this :(
+ */
+ retval = sysfs_create_group(&dev->kobj, vibrator_groups[0]);
+ if (retval) {
+ device_unregister(dev);
+ goto error;
+ }
+#endif
+
+ return 0;
+
+error:
+ kfree(vib);
+ return retval;
+}
+
+static void gb_vibrator_connection_exit(struct gb_connection *connection)
+{
+ struct gb_vibrator_device *vib = connection->private;
+
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(3,11,0)
+ sysfs_remove_group(&vib->dev->kobj, vibrator_groups[0]);
+#endif
+ idr_remove(&minors, vib->minor);
+ device_unregister(vib->dev);
+ kfree(vib);
+}
+
+static struct gb_protocol vibrator_protocol = {
+ .name = "vibrator",
+ .id = GREYBUS_PROTOCOL_VIBRATOR,
+ .major = 0,
+ .minor = 1,
+ .connection_init = gb_vibrator_connection_init,
+ .connection_exit = gb_vibrator_connection_exit,
+ .request_recv = NULL, /* no incoming requests */
+};
+
+static __init int protocol_init(void)
+{
+ int retval;
+
+ retval = class_register(&vibrator_class);
+ if (retval)
+ return retval;
+
+ return gb_protocol_register(&vibrator_protocol);
+}
+
+static __exit void protocol_exit(void)
+{
+ gb_protocol_deregister(&vibrator_protocol);
+ class_unregister(&vibrator_class);
+}
+
+module_init(protocol_init);
+module_exit(protocol_exit);
+
+MODULE_LICENSE("GPL v2");