OSDN Git Service

greybus: introduce SVC protocol
authorAlex Elder <elder@linaro.org>
Fri, 22 May 2015 18:02:08 +0000 (13:02 -0500)
committerGreg Kroah-Hartman <gregkh@google.com>
Sat, 23 May 2015 23:54:39 +0000 (16:54 -0700)
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 <elder@linaro.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
drivers/staging/greybus/Makefile
drivers/staging/greybus/greybus.h
drivers/staging/greybus/greybus_manifest.h
drivers/staging/greybus/greybus_protocols.h
drivers/staging/greybus/svc.c [new file with mode: 0644]
drivers/staging/greybus/svc.h [new file with mode: 0644]

index e5cae29..4280454 100644 (file)
@@ -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
index b9653f6..86b2f9c 100644 (file)
@@ -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"
index de1b1db..d6175ce 100644 (file)
@@ -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,
index c820805..28c40a0 100644 (file)
@@ -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 (file)
index 0000000..e39eddb
--- /dev/null
@@ -0,0 +1,256 @@
+/*
+ * SVC Greybus driver.
+ *
+ * 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"
+#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 (file)
index 0000000..e26e637
--- /dev/null
@@ -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 */