From 30c6d9d753724bc616ee1e2ca5cadc03747b8b07 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 22 May 2015 13:02:08 -0500 Subject: greybus: introduce SVC protocol This patch adds support for the Greybus SVC protocol. We may want to rearrange protocol numbers at some point, since this is a pretty fundamental protocol. Note: It has only been compile tested; no SVC CPorts have yet been defined, so this code is not yet exercised. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Makefile | 2 + drivers/staging/greybus/greybus.h | 1 + drivers/staging/greybus/greybus_manifest.h | 1 + drivers/staging/greybus/greybus_protocols.h | 57 +++++++ drivers/staging/greybus/svc.c | 256 ++++++++++++++++++++++++++++ drivers/staging/greybus/svc.h | 22 +++ 6 files changed, 339 insertions(+) create mode 100644 drivers/staging/greybus/svc.c create mode 100644 drivers/staging/greybus/svc.h (limited to 'drivers') diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index e5cae29..42804547 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -25,6 +25,7 @@ gb-phy-y := gpbridge.o \ audio-gb-cmds.o # Prefix all modules with gb- +gb-svc-y := svc.o gb-vibrator-y := vibrator.o gb-battery-y := battery.o gb-loopback-y := loopback.o @@ -33,6 +34,7 @@ gb-es1-y := es1.o gb-es2-y := es2.o obj-m += greybus.o +obj-m += gb-svc.o obj-m += gb-phy.o obj-m += gb-vibrator.o obj-m += gb-battery.o diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index b9653f6..86b2f9c 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -26,6 +26,7 @@ #include "greybus_protocols.h" #include "manifest.h" #include "endo.h" +#include "svc.h" #include "module.h" #include "interface.h" #include "bundle.h" diff --git a/drivers/staging/greybus/greybus_manifest.h b/drivers/staging/greybus/greybus_manifest.h index de1b1db..d6175ce 100644 --- a/drivers/staging/greybus/greybus_manifest.h +++ b/drivers/staging/greybus/greybus_manifest.h @@ -42,6 +42,7 @@ enum greybus_protocol { GREYBUS_PROTOCOL_LOOPBACK = 0x11, GREYBUS_PROTOCOL_I2S_RECEIVER = 0x12, GREYBUS_PROTOCOL_I2S_TRANSMITTER = 0x13, + GREYBUS_PROTOCOL_SVC = 0x14, /* ... */ GREYBUS_PROTOCOL_RAW = 0xfe, GREYBUS_PROTOCOL_VENDOR = 0xff, diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index c820805..28c40a0 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -489,4 +489,61 @@ struct gb_spi_transfer_response { __u8 data[0]; /* inbound data */ }; +/* Version of the Greybus SVC protocol we support */ +#define GB_SVC_VERSION_MAJOR 0x00 +#define GB_SVC_VERSION_MINOR 0x01 + +/* Greybus SVC request types */ +#define GB_SVC_TYPE_INVALID 0x00 +#define GB_SVC_TYPE_PROTOCOL_VERSION 0x01 +#define GB_SVC_TYPE_INTF_DEVICE_ID 0x02 +#define GB_SVC_TYPE_INTF_HOTPLUG 0x03 +#define GB_SVC_TYPE_INTF_HOT_UNPLUG 0x04 +#define GB_SVC_TYPE_INTF_RESET 0x05 +#define GB_SVC_TYPE_CONN_CREATE 0x06 +#define GB_SVC_TYPE_CONN_DESTROY 0x07 + +struct gb_svc_intf_device_id_request { + __u8 intf_id; + __u8 device_id; +}; +/* device id response has no payload */ + +struct gb_svc_intf_hotplug_request { + __u8 intf_id; + struct { + __le32 unipro_mfg_id; + __le32 unipro_prod_id; + __le32 ara_vend_id; + __le32 ara_prod_id; + } data; +}; +/* hotplug response has no payload */ + +struct gb_svc_intf_hot_unplug_request { + __u8 intf_id; +}; +/* hot unplug response has no payload */ + +struct gb_svc_intf_reset_request { + __u8 intf_id; +}; +/* interface reset response has no payload */ + +struct gb_svc_conn_create_request { + __u8 intf1_id; + __u16 cport1_id; + __u8 intf2_id; + __u16 cport2_id; +}; +/* connection create response has no payload */ + +struct gb_svc_conn_destroy_request { + __u8 intf1_id; + __u16 cport1_id; + __u8 intf2_id; + __u16 cport2_id; +}; +/* connection destroy response has no payload */ + #endif /* __GB_GPBRIDGE_H__ */ diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c new file mode 100644 index 0000000..e39eddb --- /dev/null +++ b/drivers/staging/greybus/svc.c @@ -0,0 +1,256 @@ +/* + * SVC Greybus driver. + * + * Copyright 2015 Google Inc. + * Copyright 2015 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + +#include +#include +#include + +#include "greybus.h" +#include "greybus_protocols.h" + +struct gb_svc { + struct gb_connection *connection; + u8 version_major; + u8 version_minor; +}; + +/* Define get_version() routine */ +define_get_version(gb_svc, SVC); + +static int intf_device_id_operation(struct gb_svc *svc, + u8 intf_id, u8 device_id) +{ + struct gb_svc_intf_device_id_request request; + + request.intf_id = intf_id; + request.device_id = device_id; + + return gb_operation_sync(svc->connection, GB_SVC_TYPE_INTF_DEVICE_ID, + &request, sizeof(request), NULL, 0); +} + +static int intf_reset_operation(struct gb_svc *svc, u8 intf_id) +{ + struct gb_svc_intf_reset_request request; + + request.intf_id = intf_id; + + return gb_operation_sync(svc->connection, GB_SVC_TYPE_INTF_RESET, + &request, sizeof(request), NULL, 0); +} + +static int connection_create_operation(struct gb_svc *svc, + u8 intf1_id, u16 cport1_id, + u8 intf2_id, u16 cport2_id) +{ + struct gb_svc_conn_create_request request; + + request.intf1_id = intf1_id; + request.cport1_id = cport1_id; + request.intf2_id = intf2_id; + request.cport2_id = cport2_id; + + return gb_operation_sync(svc->connection, GB_SVC_TYPE_CONN_CREATE, + &request, sizeof(request), NULL, 0); +} + +static int connection_destroy_operation(struct gb_svc *svc, + u8 intf1_id, u16 cport1_id, + u8 intf2_id, u16 cport2_id) +{ + struct gb_svc_conn_destroy_request request; + + request.intf1_id = intf1_id; + request.cport1_id = cport1_id; + request.intf2_id = intf2_id; + request.cport2_id = cport2_id; + + return gb_operation_sync(svc->connection, GB_SVC_TYPE_CONN_DESTROY, + &request, sizeof(request), NULL, 0); +} + +int gb_svc_intf_device_id(struct gb_svc *svc, u8 intf_id, u8 device_id) +{ + return intf_device_id_operation(svc, intf_id, device_id); +} +EXPORT_SYMBOL_GPL(gb_svc_intf_device_id); + +int gb_svc_intf_reset(struct gb_svc *svc, u8 intf_id) +{ + return intf_reset_operation(svc, intf_id); +} +EXPORT_SYMBOL_GPL(gb_svc_intf_reset); + +int gb_svc_connection_create(struct gb_svc *svc, + u8 intf1_id, u16 cport1_id, + u8 intf2_id, u16 cport2_id) +{ + return connection_create_operation(svc, intf1_id, cport1_id, + intf2_id, cport2_id); +} +EXPORT_SYMBOL_GPL(gb_svc_connection_create); + +int gb_svc_connection_destroy(struct gb_svc *svc, + u8 intf1_id, u16 cport1_id, + u8 intf2_id, u16 cport2_id) +{ + return connection_destroy_operation(svc, intf1_id, cport1_id, + intf2_id, cport2_id); +} +EXPORT_SYMBOL_GPL(gb_svc_connection_destroy); + +static int gb_svc_intf_hotplug_recv(struct gb_operation *op) +{ + struct gb_message *request = op->request; + struct gb_svc_intf_hotplug_request *hotplug; + u8 intf_id; + u32 unipro_mfg_id; + u32 unipro_prod_id; + u32 ara_vend_id; + u32 ara_prod_id; + + if (request->payload_size < sizeof(*hotplug)) { + dev_err(&op->connection->dev, + "short hotplug request received\n"); + return -EINVAL; + } + hotplug = request->payload; + + /* + * Grab the information we need. + * + * XXX I'd really like to acknowledge receipt, and then + * XXX continue processing the request. There's no need + * XXX for the SVC to wait. In fact, it might be best to + * XXX have the SVC get acknowledgement before we proceed. + * */ + intf_id = hotplug->intf_id; + unipro_mfg_id = hotplug->data.unipro_mfg_id; + unipro_prod_id = hotplug->data.unipro_prod_id; + ara_vend_id = hotplug->data.ara_vend_id; + ara_prod_id = hotplug->data.ara_prod_id; + + /* FIXME Set up the interface here; may required firmware download */ + + return 0; +} + +static int gb_svc_intf_hot_unplug_recv(struct gb_operation *op) +{ + struct gb_message *request = op->request; + struct gb_svc_intf_hot_unplug_request *hot_unplug; + u8 intf_id; + + if (request->payload_size < sizeof(*hot_unplug)) { + dev_err(&op->connection->dev, + "short hot unplug request received\n"); + return -EINVAL; + } + hot_unplug = request->payload; + + intf_id = hot_unplug->intf_id; + + /* FIXME Tear down the interface here */ + + return 0; + +} + +static int gb_svc_intf_reset_recv(struct gb_operation *op) +{ + struct gb_message *request = op->request; + struct gb_svc_intf_reset_request *reset; + u8 intf_id; + + if (request->payload_size < sizeof(*reset)) { + dev_err(&op->connection->dev, + "short reset request received\n"); + return -EINVAL; + } + reset = request->payload; + + intf_id = reset->intf_id; + + /* FIXME Reset the interface here */ + + return 0; +} + +static int gb_svc_request_recv(u8 type, struct gb_operation *op) +{ + switch (type) { + case GB_SVC_TYPE_INTF_HOTPLUG: + return gb_svc_intf_hotplug_recv(op); + case GB_SVC_TYPE_INTF_HOT_UNPLUG: + return gb_svc_intf_hot_unplug_recv(op); + case GB_SVC_TYPE_INTF_RESET: + return gb_svc_intf_reset_recv(op); + default: + dev_err(&op->connection->dev, + "unsupported request: %hhu\n", type); + return -EINVAL; + } +} + +/* + * Do initial setup of the SVC. + */ +static int gb_svc_device_setup(struct gb_svc *gb_svc) +{ + /* First thing we need to do is check the version */ + return get_version(gb_svc); +} + +static int gb_svc_connection_init(struct gb_connection *connection) +{ + struct gb_svc *svc; + int ret; + + svc = kzalloc(sizeof(*svc), GFP_KERNEL); + if (!svc) + return -ENOMEM; + + svc->connection = connection; + connection->private = svc; + ret = gb_svc_device_setup(svc); + if (ret) + kfree(svc); + + return ret; +} + +static void gb_svc_connection_exit(struct gb_connection *connection) +{ + struct gb_svc *svc = connection->private; + + if (!svc) + return; + + kfree(svc); +} + +static struct gb_protocol svc_protocol = { + .name = "svc", + .id = GREYBUS_PROTOCOL_SVC, + .major = 0, + .minor = 1, + .connection_init = gb_svc_connection_init, + .connection_exit = gb_svc_connection_exit, + .request_recv = gb_svc_request_recv, +}; + +int gb_svc_protocol_init(void) +{ + return gb_protocol_register(&svc_protocol); +} + +void gb_svc_protocol_exit(void) +{ + gb_protocol_deregister(&svc_protocol); +} diff --git a/drivers/staging/greybus/svc.h b/drivers/staging/greybus/svc.h new file mode 100644 index 0000000..e26e637 --- /dev/null +++ b/drivers/staging/greybus/svc.h @@ -0,0 +1,22 @@ +/* + * Greybus SVC code + * + * Copyright 2015 Google Inc. + * Copyright 2015 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + +#ifndef __SVC_H +#define __SVC_H + +struct gb_svc; + +int gb_svc_intf_device_id(struct gb_svc *svc, u8 intf_id, u8 device_id); +int gb_svc_intf_reset(struct gb_svc *svc, u8 intf_id); +int gb_svc_connection_create(struct gb_svc *svc, u8 intf1_id, u16 cport1_id, + u8 intf2_id, u16 cport2_id); +int gb_svc_connection_destroy(struct gb_svc *svc, u8 intf1_id, u16 cport1_id, + u8 intf2_id, u16 cport2_id); + +#endif /* __SVC_H */ -- cgit v1.1