blob: 20f09bba5fac6b805a0104ea97e6c6c2b506c420 [file] [log] [blame]
/*
* 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 operation 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
struct gb_vibrator_on_request {
__le16 timeout_ms;
};
/* Define get_version() routine */
define_get_version(gb_vibrator_device, VIBRATOR);
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_IDA(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 = ida_simple_get(&minors, 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 err_ida_remove;
}
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 err_ida_remove;
}
#endif
return 0;
err_ida_remove:
ida_simple_remove(&minors, vib->minor);
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
ida_simple_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);
}
module_init(protocol_init);
static __exit void protocol_exit(void)
{
gb_protocol_deregister(&vibrator_protocol);
class_unregister(&vibrator_class);
}
module_exit(protocol_exit);
MODULE_LICENSE("GPL v2");