greybus: svc: add key event handling
authorRui Miguel Silva <rui.silva@linaro.org>
Thu, 21 Jan 2016 01:42:17 +0000 (01:42 +0000)
committerGreg Kroah-Hartman <gregkh@google.com>
Thu, 21 Jan 2016 02:03:00 +0000 (18:03 -0800)
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>
drivers/staging/greybus/greybus_protocols.h
drivers/staging/greybus/svc.c
drivers/staging/greybus/svc.h

index 84fb6ab..2a48d95 100644 (file)
@@ -781,6 +781,7 @@ struct gb_spi_transfer_response {
 #define GB_SVC_TYPE_ROUTE_DESTROY      0x0c
 #define GB_SVC_TYPE_INTF_SET_PWRM      0x10
 #define GB_SVC_TYPE_INTF_EJECT         0x11
+#define GB_SVC_TYPE_KEY_EVENT          0x12
 
 /*
  * SVC version request/response has the same payload as
@@ -930,6 +931,15 @@ struct gb_svc_intf_set_pwrm_response {
        __le16  result_code;
 } __packed;
 
+struct gb_svc_key_event_request {
+       __le16  key_code;
+#define GB_KEYCODE_ARA         0x00
+
+       __u8    key_event;
+#define GB_SVC_KEY_RELEASED    0x00
+#define GB_SVC_KEY_PRESSED     0x01
+} __packed;
+
 /* RAW */
 
 /* Version of the Greybus raw protocol we support */
index bc64f48..ad04a95 100644 (file)
@@ -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);
 }
index 4abc5ef..f079b4d 100644 (file)
@@ -30,6 +30,9 @@ struct gb_svc {
 
        u8 protocol_major;
        u8 protocol_minor;
+
+       struct input_dev        *input;
+       char                    *input_phys;
 };
 #define to_gb_svc(d) container_of(d, struct gb_svc, d)