summaryrefslogtreecommitdiffstats
path: root/drivers/staging/greybus/control.c
diff options
context:
space:
mode:
authorViresh Kumar <viresh.kumar@linaro.org>2015-06-22 16:42:26 +0530
committerGreg Kroah-Hartman <gregkh@google.com>2015-06-23 23:16:15 -0700
commitcdee4f7505dbb06671a41f63b295d3d7680d760c (patch)
tree31dda554c994f8443a94d89dd29dd2b9377992df /drivers/staging/greybus/control.c
parentb08488bea5929f14b1e08d701652427bf63a9b47 (diff)
downloadop-kernel-dev-cdee4f7505dbb06671a41f63b295d3d7680d760c.zip
op-kernel-dev-cdee4f7505dbb06671a41f63b295d3d7680d760c.tar.gz
greybus: Add control protocol support
Add control protocol driver that is responsible for handling operations on control CPort. The AP also needs to support incoming requests on its control port. Features not implemented yet are marked as TODO for now. NOTE: This also fixes cport-bundle-id to 0 and cport-id to 2 for control protocol. Reviewed-by: Alex Elder <elder@linaro.org> Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
Diffstat (limited to 'drivers/staging/greybus/control.c')
-rw-r--r--drivers/staging/greybus/control.c150
1 files changed, 150 insertions, 0 deletions
diff --git a/drivers/staging/greybus/control.c b/drivers/staging/greybus/control.c
new file mode 100644
index 0000000..c19814d
--- /dev/null
+++ b/drivers/staging/greybus/control.c
@@ -0,0 +1,150 @@
+/*
+ * Greybus CPort control protocol.
+ *
+ * Copyright 2015 Google Inc.
+ * Copyright 2015 Linaro Ltd.
+ *
+ * Released under the GPLv2 only.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include "greybus.h"
+
+/* Define get_version() routine */
+define_get_version(gb_control, CONTROL);
+
+/* Get Manifest's size from the interface */
+int gb_control_get_manifest_size_operation(struct gb_interface *intf)
+{
+ struct gb_control_get_manifest_size_response response;
+ struct gb_connection *connection = intf->control->connection;
+ int ret;
+
+ ret = gb_operation_sync(connection, GB_CONTROL_TYPE_GET_MANIFEST_SIZE,
+ NULL, 0, &response, sizeof(response));
+ if (ret) {
+ dev_err(&connection->dev,
+ "%s: Manifest size get operation failed (%d)\n",
+ __func__, ret);
+ return ret;
+ }
+
+ return le16_to_cpu(response.size);
+}
+
+/* Reads Manifest from the interface */
+int gb_control_get_manifest_operation(struct gb_interface *intf, void *manifest,
+ size_t size)
+{
+ struct gb_connection *connection = intf->control->connection;
+
+ return gb_operation_sync(connection, GB_CONTROL_TYPE_GET_MANIFEST,
+ NULL, 0, manifest, size);
+}
+
+int gb_control_connected_operation(struct gb_control *control, u16 cport_id)
+{
+ struct gb_control_connected_request request;
+
+ request.cport_id = cpu_to_le16(cport_id);
+ return gb_operation_sync(control->connection, GB_CONTROL_TYPE_CONNECTED,
+ &request, sizeof(request), NULL, 0);
+}
+
+int gb_control_disconnected_operation(struct gb_control *control, u16 cport_id)
+{
+ struct gb_control_disconnected_request request;
+
+ request.cport_id = cpu_to_le16(cport_id);
+ return gb_operation_sync(control->connection,
+ GB_CONTROL_TYPE_DISCONNECTED, &request,
+ sizeof(request), NULL, 0);
+}
+
+static int gb_control_request_recv(u8 type, struct gb_operation *op)
+{
+ struct gb_connection *connection = op->connection;
+ struct gb_protocol_version_response *version;
+
+ switch (type) {
+ case GB_CONTROL_TYPE_PROBE_AP:
+ // TODO
+ // Send authenticated block of data, confirming this module is
+ // an AP.
+ break;
+ case GB_CONTROL_TYPE_PROTOCOL_VERSION:
+ if (!gb_operation_response_alloc(op, sizeof(*version))) {
+ dev_err(&connection->dev,
+ "%s: error allocating response\n", __func__);
+ return -ENOMEM;
+ }
+
+ version = op->response->payload;
+ version->major = GB_CONTROL_VERSION_MAJOR;
+ version->minor = GB_CONTROL_VERSION_MINOR;
+ break;
+ case GB_CONTROL_TYPE_CONNECTED:
+ case GB_CONTROL_TYPE_DISCONNECTED:
+ break;
+ default:
+ WARN_ON(1);
+ break;
+ }
+
+ return 0;
+}
+
+static int gb_control_connection_init(struct gb_connection *connection)
+{
+ struct gb_control *control;
+ int ret;
+
+ control = kzalloc(sizeof(*control), GFP_KERNEL);
+ if (!control)
+ return -ENOMEM;
+
+ control->connection = connection;
+ connection->private = control;
+
+ ret = get_version(control);
+ if (ret)
+ kfree(control);
+
+ /* Set interface's control connection */
+ connection->bundle->intf->control = control;
+
+ return ret;
+}
+
+static void gb_control_connection_exit(struct gb_connection *connection)
+{
+ struct gb_control *control = connection->private;
+
+ if (WARN_ON(connection->bundle->intf->control != control))
+ return;
+
+ connection->bundle->intf->control = NULL;
+ kfree(control);
+}
+
+static struct gb_protocol control_protocol = {
+ .name = "control",
+ .id = GREYBUS_PROTOCOL_CONTROL,
+ .major = 0,
+ .minor = 1,
+ .connection_init = gb_control_connection_init,
+ .connection_exit = gb_control_connection_exit,
+ .request_recv = gb_control_request_recv,
+};
+
+int gb_control_protocol_init(void)
+{
+ return gb_protocol_register(&control_protocol);
+}
+
+void gb_control_protocol_exit(void)
+{
+ gb_protocol_deregister(&control_protocol);
+}
OpenPOWER on IntegriCloud