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>
This commit is contained in:
Rui Miguel Silva 2016-01-21 01:42:17 +00:00 committed by Greg Kroah-Hartman
parent 4b87413428
commit ebe99d6181
3 changed files with 123 additions and 8 deletions

View File

@ -781,6 +781,7 @@ struct gb_spi_transfer_response {
#define GB_SVC_TYPE_ROUTE_DESTROY 0x0c #define GB_SVC_TYPE_ROUTE_DESTROY 0x0c
#define GB_SVC_TYPE_INTF_SET_PWRM 0x10 #define GB_SVC_TYPE_INTF_SET_PWRM 0x10
#define GB_SVC_TYPE_INTF_EJECT 0x11 #define GB_SVC_TYPE_INTF_EJECT 0x11
#define GB_SVC_TYPE_KEY_EVENT 0x12
/* /*
* SVC version request/response has the same payload as * SVC version request/response has the same payload as
@ -930,6 +931,15 @@ struct gb_svc_intf_set_pwrm_response {
__le16 result_code; __le16 result_code;
} __packed; } __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 */ /* RAW */
/* Version of the Greybus raw protocol we support */ /* Version of the Greybus raw protocol we support */

View File

@ -7,6 +7,7 @@
* Released under the GPLv2 only. * Released under the GPLv2 only.
*/ */
#include <linux/input.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include "greybus.h" #include "greybus.h"
@ -15,6 +16,7 @@
#define CPORT_FLAGS_CSD_N BIT(1) #define CPORT_FLAGS_CSD_N BIT(1)
#define CPORT_FLAGS_CSV_N BIT(2) #define CPORT_FLAGS_CSV_N BIT(2)
#define SVC_KEY_ARA_BUTTON KEY_A
struct gb_svc_deferred_request { struct gb_svc_deferred_request {
struct work_struct work; struct work_struct work;
@ -420,6 +422,13 @@ static int gb_svc_hello(struct gb_operation *op)
return ret; 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; return 0;
} }
@ -690,6 +699,53 @@ static int gb_svc_intf_reset_recv(struct gb_operation *op)
return 0; 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) static int gb_svc_request_handler(struct gb_operation *op)
{ {
struct gb_connection *connection = op->connection; 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); return gb_svc_intf_hot_unplug_recv(op);
case GB_SVC_TYPE_INTF_RESET: case GB_SVC_TYPE_INTF_RESET:
return gb_svc_intf_reset_recv(op); return gb_svc_intf_reset_recv(op);
case GB_SVC_TYPE_KEY_EVENT:
return gb_svc_key_event_recv(op);
default: default:
dev_warn(&svc->dev, "unsupported request 0x%02x\n", type); dev_warn(&svc->dev, "unsupported request 0x%02x\n", type);
return -EINVAL; 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) static void gb_svc_release(struct device *dev)
{ {
struct gb_svc *svc = to_gb_svc(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); gb_connection_destroy(svc->connection);
ida_destroy(&svc->device_id_map); ida_destroy(&svc->device_id_map);
destroy_workqueue(svc->wq); destroy_workqueue(svc->wq);
kfree(svc->input_phys);
kfree(svc); kfree(svc);
} }
@ -794,17 +881,29 @@ struct gb_svc *gb_svc_create(struct gb_host_device *hd)
svc->state = GB_SVC_STATE_RESET; svc->state = GB_SVC_STATE_RESET;
svc->hd = hd; 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, svc->connection = gb_connection_create_static(hd, GB_SVC_CPORT_ID,
GREYBUS_PROTOCOL_SVC); GREYBUS_PROTOCOL_SVC);
if (!svc->connection) { if (!svc->connection) {
dev_err(&svc->dev, "failed to create connection\n"); dev_err(&svc->dev, "failed to create connection\n");
put_device(&svc->dev); goto err_free_input;
return NULL;
} }
svc->connection->private = svc; svc->connection->private = svc;
return 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) int gb_svc_add(struct gb_svc *svc)
@ -825,14 +924,17 @@ int gb_svc_add(struct gb_svc *svc)
void gb_svc_del(struct gb_svc *svc) void gb_svc_del(struct gb_svc *svc)
{ {
/*
* The SVC device may have been registered from the request handler.
*/
if (device_is_registered(&svc->dev))
device_del(&svc->dev);
gb_connection_disable(svc->connection); gb_connection_disable(svc->connection);
/*
* The SVC device and input device may have been registered
* from the request handler.
*/
if (device_is_registered(&svc->dev)) {
input_unregister_device(svc->input);
device_del(&svc->dev);
}
flush_workqueue(svc->wq); flush_workqueue(svc->wq);
} }

View File

@ -30,6 +30,9 @@ struct gb_svc {
u8 protocol_major; u8 protocol_major;
u8 protocol_minor; u8 protocol_minor;
struct input_dev *input;
char *input_phys;
}; };
#define to_gb_svc(d) container_of(d, struct gb_svc, d) #define to_gb_svc(d) container_of(d, struct gb_svc, d)