mirror of
https://github.com/torvalds/linux.git
synced 2024-11-10 22:21:40 +00:00
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:
parent
4b87413428
commit
ebe99d6181
@ -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 */
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user