diff options
Diffstat (limited to 'drivers/staging/greybus')
-rw-r--r-- | drivers/staging/greybus/greybus_protocols.h | 10 | ||||
-rw-r--r-- | drivers/staging/greybus/svc.c | 114 | ||||
-rw-r--r-- | drivers/staging/greybus/svc.h | 3 |
3 files changed, 121 insertions, 6 deletions
diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 84fb6ab6deca..2a48d95ea1b8 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -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 */ 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); } diff --git a/drivers/staging/greybus/svc.h b/drivers/staging/greybus/svc.h index 4abc5efad807..f079b4dcc6e6 100644 --- a/drivers/staging/greybus/svc.h +++ b/drivers/staging/greybus/svc.h @@ -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) |