|
|
|
@@ -0,0 +1,298 @@
|
|
|
|
|
/*
|
|
|
|
|
* I2C bridge driver for the Greybus "generic" I2C module.
|
|
|
|
|
*
|
|
|
|
|
* Copyright 2014 Google Inc.
|
|
|
|
|
*
|
|
|
|
|
* 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 "greybus.h"
|
|
|
|
|
|
|
|
|
|
struct gb_vibrator_device {
|
|
|
|
|
struct gb_connection *connection;
|
|
|
|
|
struct device *dev;
|
|
|
|
|
u8 version_major;
|
|
|
|
|
u8 version_minor;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* Version of the Greybus i2c 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 status;
|
|
|
|
|
__u8 major;
|
|
|
|
|
__u8 minor;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct gb_vibrator_on_request {
|
|
|
|
|
__le16 timeout_ms;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct gb_vibrator_simple_response {
|
|
|
|
|
__u8 status;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static int request_operation(struct gb_connection *connection, int type,
|
|
|
|
|
void *response, int response_size)
|
|
|
|
|
{
|
|
|
|
|
struct gb_operation *operation;
|
|
|
|
|
struct gb_vibrator_simple_response *fake_request;
|
|
|
|
|
u8 *local_response;
|
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
local_response = kmalloc(response_size, GFP_KERNEL);
|
|
|
|
|
if (!local_response)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
|
|
operation = gb_operation_create(connection, type, 0, response_size);
|
|
|
|
|
if (!operation) {
|
|
|
|
|
kfree(local_response);
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Synchronous operation--no callback */
|
|
|
|
|
ret = gb_operation_request_send(operation, NULL);
|
|
|
|
|
if (ret) {
|
|
|
|
|
pr_err("version operation failed (%d)\n", ret);
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* We only want to look at the status, and all requests have the same
|
|
|
|
|
* layout for where the status is, so cast this to a random request so
|
|
|
|
|
* we can see the status easier.
|
|
|
|
|
*/
|
|
|
|
|
fake_request = (struct gb_vibrator_simple_response *)local_response;
|
|
|
|
|
if (fake_request->status) {
|
|
|
|
|
gb_connection_err(connection, "response %hhu",
|
|
|
|
|
fake_request->status);
|
|
|
|
|
ret = -EIO;
|
|
|
|
|
} else {
|
|
|
|
|
/* Good request, so copy to the caller's buffer */
|
|
|
|
|
if (response_size && response)
|
|
|
|
|
memcpy(response, local_response, response_size);
|
|
|
|
|
}
|
|
|
|
|
out:
|
|
|
|
|
gb_operation_destroy(operation);
|
|
|
|
|
kfree(local_response);
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* 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_request;
|
|
|
|
|
int retval;
|
|
|
|
|
|
|
|
|
|
retval = request_operation(connection,
|
|
|
|
|
GB_VIBRATOR_TYPE_PROTOCOL_VERSION,
|
|
|
|
|
&version_request, sizeof(version_request));
|
|
|
|
|
if (retval)
|
|
|
|
|
return retval;
|
|
|
|
|
|
|
|
|
|
if (version_request.major > GB_VIBRATOR_VERSION_MAJOR) {
|
|
|
|
|
dev_err(&connection->dev,
|
|
|
|
|
"unsupported major version (%hhu > %hhu)\n",
|
|
|
|
|
version_request.major, GB_VIBRATOR_VERSION_MAJOR);
|
|
|
|
|
return -ENOTSUPP;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vib->version_major = version_request.major;
|
|
|
|
|
vib->version_minor = version_request.minor;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int turn_on(struct gb_vibrator_device *vib, u16 timeout_ms)
|
|
|
|
|
{
|
|
|
|
|
struct gb_connection *connection = vib->connection;
|
|
|
|
|
struct gb_operation *operation;
|
|
|
|
|
struct gb_vibrator_on_request *request;
|
|
|
|
|
struct gb_vibrator_simple_response *response;
|
|
|
|
|
int retval;
|
|
|
|
|
|
|
|
|
|
operation = gb_operation_create(connection, GB_VIBRATOR_TYPE_ON,
|
|
|
|
|
sizeof(*request), sizeof(*response));
|
|
|
|
|
if (!operation)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
request = operation->request_payload;
|
|
|
|
|
request->timeout_ms = cpu_to_le16(timeout_ms);
|
|
|
|
|
|
|
|
|
|
/* Synchronous operation--no callback */
|
|
|
|
|
retval = gb_operation_request_send(operation, NULL);
|
|
|
|
|
if (retval) {
|
|
|
|
|
dev_err(&connection->dev,
|
|
|
|
|
"send data operation failed (%d)\n", retval);
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
response = operation->response_payload;
|
|
|
|
|
if (response->status) {
|
|
|
|
|
gb_connection_err(connection, "send data response %hhu",
|
|
|
|
|
response->status);
|
|
|
|
|
retval = -EIO;
|
|
|
|
|
}
|
|
|
|
|
out:
|
|
|
|
|
gb_operation_destroy(operation);
|
|
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int turn_off(struct gb_vibrator_device *vib)
|
|
|
|
|
{
|
|
|
|
|
struct gb_connection *connection = vib->connection;
|
|
|
|
|
int retval;
|
|
|
|
|
|
|
|
|
|
retval = request_operation(connection, GB_VIBRATOR_TYPE_OFF, NULL, 0);
|
|
|
|
|
if (retval)
|
|
|
|
|
return retval;
|
|
|
|
|
|
|
|
|
|
return 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 < 0)
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
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 int minor;
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
retval = get_version(vib);
|
|
|
|
|
if (retval)
|
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* FIXME: 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...
|
|
|
|
|
*/
|
|
|
|
|
dev = device_create(&vibrator_class, NULL, MKDEV(0, 0), vib,
|
|
|
|
|
"vibrator%d", minor);
|
|
|
|
|
if (IS_ERR(dev)) {
|
|
|
|
|
retval = -EINVAL;
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
minor++;
|
|
|
|
|
vib->dev = dev;
|
|
|
|
|
|
|
|
|
|
#if LINUX_VERSION_CODE <= KERNEL_VERSION(3,11,0)
|
|
|
|
|
/*
|
|
|
|
|
* Newer kernels handle this in a race-free manner, for us, 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
|
|
|
|
|
device_unregister(vib->dev);
|
|
|
|
|
kfree(vib);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct gb_protocol vibrator_protocol = {
|
|
|
|
|
.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 */
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
bool gb_vibrator_protocol_init(void)
|
|
|
|
|
{
|
|
|
|
|
int retval;
|
|
|
|
|
|
|
|
|
|
retval = class_register(&vibrator_class);
|
|
|
|
|
if (retval)
|
|
|
|
|
return retval;
|
|
|
|
|
|
|
|
|
|
return gb_protocol_register(&vibrator_protocol);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void gb_vibrator_protocol_exit(void)
|
|
|
|
|
{
|
|
|
|
|
gb_protocol_deregister(&vibrator_protocol);
|
|
|
|
|
class_unregister(&vibrator_class);
|
|
|
|
|
}
|