summaryrefslogtreecommitdiff
path: root/drivers/staging/greybus/svc.c
diff options
context:
space:
mode:
authorRui Miguel Silva <rui.silva@linaro.org>2016-01-21 01:42:17 +0000
committerGreg Kroah-Hartman <gregkh@google.com>2016-01-20 18:03:00 -0800
commitebe99d61815fea0ecece5ddb530b28339f8d5ed3 (patch)
tree2025fee89d00c715baa42dd42fa3f6f51049dc8d /drivers/staging/greybus/svc.c
parent4b874134284b1dbb340f063fe0cf5141ffd416b1 (diff)
greybus: svc: add key event handling
Add a new input device associated with the SVC to handle key events. This new events are transfer over a new greybus svc operation which is unidirectional. It was selected the KEY_A for representing the KEY_ARA_BUTTON key code. Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org> Reviewed-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
Diffstat (limited to 'drivers/staging/greybus/svc.c')
-rw-r--r--drivers/staging/greybus/svc.c114
1 files changed, 108 insertions, 6 deletions
diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c
index bc64f4894b46..ad04a952cb36 100644
--- a/drivers/staging/greybus/svc.c
+++ b/drivers/staging/greybus/svc.c
@@ -7,6 +7,7 @@
* Released under the GPLv2 only.
*/
+#include <linux/input.h>
#include <linux/workqueue.h>
#include "greybus.h"
@@ -15,6 +16,7 @@
#define CPORT_FLAGS_CSD_N BIT(1)
#define CPORT_FLAGS_CSV_N BIT(2)
+#define SVC_KEY_ARA_BUTTON KEY_A
struct gb_svc_deferred_request {
struct work_struct work;
@@ -420,6 +422,13 @@ static int gb_svc_hello(struct gb_operation *op)
return ret;
}
+ ret = input_register_device(svc->input);
+ if (ret) {
+ dev_err(&svc->dev, "failed to register input: %d\n", ret);
+ device_del(&svc->dev);
+ return ret;
+ }
+
return 0;
}
@@ -690,6 +699,53 @@ static int gb_svc_intf_reset_recv(struct gb_operation *op)
return 0;
}
+static int gb_svc_key_code_map(struct gb_svc *svc, u16 key_code, u16 *code)
+{
+ switch (key_code) {
+ case GB_KEYCODE_ARA:
+ *code = SVC_KEY_ARA_BUTTON;
+ break;
+ default:
+ dev_warn(&svc->dev, "unknown keycode received: %u\n", key_code);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int gb_svc_key_event_recv(struct gb_operation *op)
+{
+ struct gb_svc *svc = op->connection->private;
+ struct gb_message *request = op->request;
+ struct gb_svc_key_event_request *key;
+ u16 code;
+ u8 event;
+ int ret;
+
+ if (request->payload_size < sizeof(*key)) {
+ dev_warn(&svc->dev, "short key request received (%zu < %zu)\n",
+ request->payload_size, sizeof(*key));
+ return -EINVAL;
+ }
+
+ key = request->payload;
+
+ ret = gb_svc_key_code_map(svc, le16_to_cpu(key->key_code), &code);
+ if (ret < 0)
+ return ret;
+
+ event = key->key_event;
+ if ((event != GB_SVC_KEY_PRESSED) && (event != GB_SVC_KEY_RELEASED)) {
+ dev_warn(&svc->dev, "unknown key event received: %u\n", event);
+ return -EINVAL;
+ }
+
+ input_report_key(svc->input, code, (event == GB_SVC_KEY_PRESSED));
+ input_sync(svc->input);
+
+ return 0;
+}
+
static int gb_svc_request_handler(struct gb_operation *op)
{
struct gb_connection *connection = op->connection;
@@ -745,12 +801,42 @@ static int gb_svc_request_handler(struct gb_operation *op)
return gb_svc_intf_hot_unplug_recv(op);
case GB_SVC_TYPE_INTF_RESET:
return gb_svc_intf_reset_recv(op);
+ case GB_SVC_TYPE_KEY_EVENT:
+ return gb_svc_key_event_recv(op);
default:
dev_warn(&svc->dev, "unsupported request 0x%02x\n", type);
return -EINVAL;
}
}
+static struct input_dev *gb_svc_input_create(struct gb_svc *svc)
+{
+ struct input_dev *input_dev;
+
+ input_dev = input_allocate_device();
+ if (!input_dev)
+ return ERR_PTR(-ENOMEM);
+
+ input_dev->name = dev_name(&svc->dev);
+ svc->input_phys = kasprintf(GFP_KERNEL, "greybus-%s/input0",
+ input_dev->name);
+ if (!svc->input_phys)
+ goto err_free_input;
+
+ input_dev->phys = svc->input_phys;
+ input_dev->dev.parent = &svc->dev;
+
+ input_set_drvdata(input_dev, svc);
+
+ input_set_capability(input_dev, EV_KEY, SVC_KEY_ARA_BUTTON);
+
+ return input_dev;
+
+err_free_input:
+ input_free_device(svc->input);
+ return ERR_PTR(-ENOMEM);
+}
+
static void gb_svc_release(struct device *dev)
{
struct gb_svc *svc = to_gb_svc(dev);
@@ -759,6 +845,7 @@ static void gb_svc_release(struct device *dev)
gb_connection_destroy(svc->connection);
ida_destroy(&svc->device_id_map);
destroy_workqueue(svc->wq);
+ kfree(svc->input_phys);
kfree(svc);
}
@@ -794,17 +881,29 @@ struct gb_svc *gb_svc_create(struct gb_host_device *hd)
svc->state = GB_SVC_STATE_RESET;
svc->hd = hd;
+ svc->input = gb_svc_input_create(svc);
+ if (IS_ERR(svc->input)) {
+ dev_err(&svc->dev, "failed to create input device: %ld\n",
+ PTR_ERR(svc->input));
+ goto err_put_device;
+ }
+
svc->connection = gb_connection_create_static(hd, GB_SVC_CPORT_ID,
GREYBUS_PROTOCOL_SVC);
if (!svc->connection) {
dev_err(&svc->dev, "failed to create connection\n");
- put_device(&svc->dev);
- return NULL;
+ goto err_free_input;
}
svc->connection->private = svc;
return svc;
+
+err_free_input:
+ input_free_device(svc->input);
+err_put_device:
+ put_device(&svc->dev);
+ return NULL;
}
int gb_svc_add(struct gb_svc *svc)
@@ -825,13 +924,16 @@ int gb_svc_add(struct gb_svc *svc)
void gb_svc_del(struct gb_svc *svc)
{
+ gb_connection_disable(svc->connection);
+
/*
- * The SVC device may have been registered from the request handler.
+ * The SVC device and input device may have been registered
+ * from the request handler.
*/
- if (device_is_registered(&svc->dev))
+ if (device_is_registered(&svc->dev)) {
+ input_unregister_device(svc->input);
device_del(&svc->dev);
-
- gb_connection_disable(svc->connection);
+ }
flush_workqueue(svc->wq);
}