greybus: Merge branch 'master' into branch 'svc'.

This drags in the firmware driver, and the start of some loopback
changes.

Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile
index 3c32d14..1162703 100644
--- a/drivers/staging/greybus/Makefile
+++ b/drivers/staging/greybus/Makefile
@@ -1,6 +1,5 @@
 greybus-y :=	core.o		\
 		debugfs.o	\
-		ap.o		\
 		manifest.o	\
 		endo.o		\
 		module.o	\
diff --git a/drivers/staging/greybus/ap.c b/drivers/staging/greybus/ap.c
deleted file mode 100644
index fc23817..0000000
--- a/drivers/staging/greybus/ap.c
+++ /dev/null
@@ -1,351 +0,0 @@
-/*
- * Greybus "AP" message loop handling
- *
- * Copyright 2014 Google Inc.
- * Copyright 2014 Linaro Ltd.
- *
- * Released under the GPLv2 only.
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/types.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/uaccess.h>
-#include <linux/workqueue.h>
-#include <linux/device.h>
-#include "svc_msg.h"
-#include "greybus_manifest.h"
-#include "greybus.h"
-
-struct ap_msg {
-	u8 *data;
-	size_t size;
-	struct greybus_host_device *hd;
-	struct work_struct event;
-};
-
-static struct workqueue_struct *ap_workqueue;
-
-static struct svc_msg *svc_msg_alloc(enum svc_function_id id)
-{
-	struct svc_msg *svc_msg;
-
-	svc_msg = kzalloc((sizeof *svc_msg), GFP_KERNEL);
-	if (!svc_msg)
-		return NULL;
-
-	// FIXME - verify we are only sending function IDs we should be
-	svc_msg->header.function_id = id;
-	return svc_msg;
-}
-
-static void svc_msg_free(struct svc_msg *svc_msg)
-{
-	kfree(svc_msg);
-}
-
-static int svc_msg_send(struct svc_msg *svc_msg, struct greybus_host_device *hd)
-{
-	int retval;
-
-	// FIXME - Do we need to do more than just pass it to the hd and then
-	// free it?
-	retval = hd->driver->submit_svc(svc_msg, hd);
-
-	svc_msg_free(svc_msg);
-	return retval;
-}
-
-static void svc_handshake(struct svc_function_handshake *handshake,
-			  int payload_length, struct greybus_host_device *hd)
-{
-	struct svc_msg *svc_msg;
-
-	if (payload_length != sizeof(*handshake)) {
-		dev_err(hd->parent,
-			"Illegal size of svc handshake message %d\n",
-			payload_length);
-		return;
-	}
-
-	/* A new SVC communication channel, let's verify a supported version */
-	if ((handshake->version_major != GREYBUS_VERSION_MAJOR) ||
-	    (handshake->version_minor != GREYBUS_VERSION_MINOR)) {
-		dev_warn(hd->parent,
-			"received invalid greybus version %u.%u\n",
-			handshake->version_major, handshake->version_minor);
-		return;
-	}
-
-	/* Validate that the handshake came from the SVC */
-	if (handshake->handshake_type != SVC_HANDSHAKE_SVC_HELLO) {
-		/* we don't know what to do with this, log it and return */
-		dev_dbg(hd->parent, "received invalid handshake type %d\n",
-			handshake->handshake_type);
-		return;
-	}
-
-	/* Send back a AP_HELLO message */
-	svc_msg = svc_msg_alloc(SVC_FUNCTION_HANDSHAKE);
-	if (!svc_msg)
-		return;
-
-	svc_msg->header.message_type = SVC_MSG_DATA;
-	svc_msg->header.payload_length =
-		cpu_to_le16(sizeof(*handshake));
-	svc_msg->handshake.version_major = GREYBUS_VERSION_MAJOR;
-	svc_msg->handshake.version_minor = GREYBUS_VERSION_MINOR;
-	svc_msg->handshake.handshake_type = SVC_HANDSHAKE_AP_HELLO;
-
-	(void)svc_msg_send(svc_msg, hd);
-}
-
-static void svc_management(struct svc_function_unipro_management *management,
-			   int payload_length, struct greybus_host_device *hd)
-{
-	struct gb_interface *intf;
-	int ret;
-
-	if (payload_length != sizeof(*management)) {
-		dev_err(hd->parent,
-			"Illegal size of svc management message %d\n",
-			payload_length);
-		return;
-	}
-
-	switch (management->management_packet_type) {
-	case SVC_MANAGEMENT_AP_ID:
-		hd->device_id = management->ap_id.device_id;
-		break;
-	case SVC_MANAGEMENT_LINK_UP:
-		intf = gb_interface_find(hd, management->link_up.interface_id);
-		if (!intf) {
-			dev_err(hd->parent, "Interface ID %d not found\n",
-				management->link_up.interface_id);
-			return;
-		}
-		ret = gb_interface_init(intf, management->link_up.device_id);
-		if (ret) {
-			dev_err(hd->parent,
-				"error %d initializing interface %hhu\n",
-				ret, management->link_up.interface_id);
-			return;
-		}
-		break;
-	default:
-		dev_err(hd->parent, "Unhandled UniPro management message\n");
-	}
-}
-
-static void svc_hotplug(struct svc_function_hotplug *hotplug,
-			int payload_length, struct greybus_host_device *hd)
-{
-	u8 interface_id = hotplug->interface_id;
-
-	switch (hotplug->hotplug_event) {
-	case SVC_HOTPLUG_EVENT:
-		/* Add a new interface to the system */
-		if (payload_length != sizeof(*hotplug)) {
-			dev_err(hd->parent,
-				"Illegal size of svc hotplug message %d\n",
-				payload_length);
-			return;
-		}
-		dev_dbg(hd->parent, "interface id %d added\n", interface_id);
-		gb_interface_create(hd, interface_id);
-		break;
-
-	case SVC_HOTUNPLUG_EVENT:
-		/* Remove a interface from the system */
-		if (payload_length != sizeof(*hotplug)) {
-			dev_err(hd->parent,
-				"Illegal size of svc hotunplug message %d\n",
-				payload_length);
-			return;
-		}
-		dev_dbg(hd->parent, "interface id %d removed\n", interface_id);
-		gb_interface_remove(hd, interface_id);
-		break;
-
-	default:
-		dev_err(hd->parent,
-			"Received invalid hotplug message type %d\n",
-			hotplug->hotplug_event);
-		break;
-	}
-}
-
-static void svc_power(struct svc_function_power *power,
-		      int payload_length, struct greybus_host_device *hd)
-{
-	u8 interface_id = power->interface_id;
-
-	/*
-	 * The AP is only allowed to get a Battery Status message, not a Battery
-	 * Status Request
-	 */
-	if (power->power_type != SVC_POWER_BATTERY_STATUS) {
-		dev_err(hd->parent, "Received invalid power type %d\n",
-			power->power_type);
-		return;
-	}
-
-	/*
-	 * As struct struct svc_function_power_battery_status_request is 0 bytes
-	 * big, we can just check the union of the whole structure to validate
-	 * the size of this message.
-	 */
-	if (payload_length != sizeof(*power)) {
-		dev_err(hd->parent,
-			"Illegal size of svc power message %d\n",
-			payload_length);
-		return;
-	}
-
-	dev_dbg(hd->parent, "power status for interface id %d is %d\n",
-		interface_id, power->status.status);
-
-	// FIXME - do something with the power information, like update our
-	// battery information...
-}
-
-static void svc_epm(struct svc_function_epm *epm,
-		    int payload_length, struct greybus_host_device *hd)
-{
-	/* What?  An AP should not get this message */
-	dev_err(hd->parent, "Got an EPM message???\n");
-}
-
-static void svc_suspend(struct svc_function_suspend *suspend,
-			int payload_length, struct greybus_host_device *hd)
-{
-	/* What?  An AP should not get this message */
-	dev_err(hd->parent, "Got an suspend message???\n");
-}
-
-static struct svc_msg *convert_ap_message(struct ap_msg *ap_msg)
-{
-	struct svc_msg *svc_msg;
-	struct svc_msg_header *header;
-	struct greybus_host_device *hd = ap_msg->hd;
-
-	svc_msg = (struct svc_msg *)ap_msg->data;
-	header = &svc_msg->header;
-
-	/* Validate the message type */
-	if (header->message_type != SVC_MSG_DATA) {
-		dev_err(hd->parent, "message type %d received?\n",
-			header->message_type);
-		return NULL;
-	}
-
-	/*
-	 * The validation of the size of the message buffer happens in each
-	 * svc_* function, due to the different types of messages, keeping the
-	 * logic for each message only in one place.
-	 */
-
-	return svc_msg;
-}
-
-static void ap_process_event(struct work_struct *work)
-{
-	struct svc_msg *svc_msg;
-	struct greybus_host_device *hd;
-	struct ap_msg *ap_msg;
-	int payload_length;
-
-	ap_msg = container_of(work, struct ap_msg, event);
-	hd = ap_msg->hd;
-
-	/* Turn the "raw" data into a real message */
-	svc_msg = convert_ap_message(ap_msg);
-	if (!svc_msg)
-		return;
-
-	payload_length = le16_to_cpu(svc_msg->header.payload_length);
-
-	/* Look at the message to figure out what to do with it */
-	switch (svc_msg->header.function_id) {
-	case SVC_FUNCTION_HANDSHAKE:
-		svc_handshake(&svc_msg->handshake, payload_length, hd);
-		break;
-	case SVC_FUNCTION_UNIPRO_NETWORK_MANAGEMENT:
-		svc_management(&svc_msg->management, payload_length, hd);
-		break;
-	case SVC_FUNCTION_HOTPLUG:
-		svc_hotplug(&svc_msg->hotplug, payload_length, hd);
-		break;
-	case SVC_FUNCTION_POWER:
-		svc_power(&svc_msg->power, payload_length, hd);
-		break;
-	case SVC_FUNCTION_EPM:
-		svc_epm(&svc_msg->epm, payload_length, hd);
-		break;
-	case SVC_FUNCTION_SUSPEND:
-		svc_suspend(&svc_msg->suspend, payload_length, hd);
-		break;
-	default:
-		dev_err(hd->parent, "received invalid SVC function ID %d\n",
-			svc_msg->header.function_id);
-	}
-
-	/* clean the message up */
-	kfree(ap_msg->data);
-	kfree(ap_msg);
-}
-
-int greybus_svc_in(struct greybus_host_device *hd, u8 *data, int size)
-{
-	struct ap_msg *ap_msg;
-
-	/*
-	 * Totally naive copy the message into a new structure that we slowly
-	 * create and add it to the list.  Let's get this working, the odds of
-	 * this being any "slow path" for AP messages is really low at this
-	 * point in time, but you never know, so this comment is here to point
-	 * out that maybe we should use a slab allocator, or even just not copy
-	 * the data, but use it directly and force the urbs to be "new" each
-	 * time.
-	 */
-
-	/* Note - this can, and will, be called in interrupt context. */
-	ap_msg = kmalloc(sizeof(*ap_msg), GFP_ATOMIC);
-	if (!ap_msg)
-		return -ENOMEM;
-	ap_msg->data = kmalloc(size, GFP_ATOMIC);
-	if (!ap_msg->data) {
-		kfree(ap_msg);
-		return -ENOMEM;
-	}
-	memcpy(ap_msg->data, data, size);
-	ap_msg->size = size;
-	ap_msg->hd = hd;
-
-	INIT_WORK(&ap_msg->event, ap_process_event);
-	queue_work(ap_workqueue, &ap_msg->event);
-
-	return 0;
-}
-EXPORT_SYMBOL_GPL(greybus_svc_in);
-
-int __init gb_ap_init(void)
-{
-	ap_workqueue = alloc_ordered_workqueue("greybus_ap", 0);
-	if (!ap_workqueue)
-		return -ENOMEM;
-
-	return 0;
-}
-
-void gb_ap_exit(void)
-{
-	destroy_workqueue(ap_workqueue);
-	ap_workqueue = NULL;
-}
-
-
diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c
index 6078443a..eae6ad1 100644
--- a/drivers/staging/greybus/connection.c
+++ b/drivers/staging/greybus/connection.c
@@ -283,7 +283,12 @@
 
 	spin_unlock_irq(&gb_connections_lock);
 
-	/* XXX Will have to establish connections to get version */
+	if (hd_cport_id != GB_SVC_CPORT_ID) {
+		gb_svc_connection_create(hd->svc,
+					 hd->endo->ap_intf_id, hd_cport_id,
+					 bundle->intf->interface_id, cport_id);
+	}
+
 	gb_connection_bind_protocol(connection);
 	if (!connection->protocol)
 		dev_warn(&connection->dev,
@@ -374,7 +379,8 @@
 	 * Inform Interface about In-active CPorts. We don't need to do this
 	 * operation for control cport.
 	 */
-	if (cport_id == GB_CONTROL_CPORT_ID)
+	if ((cport_id == GB_CONTROL_CPORT_ID) ||
+	    (connection->hd_cport_id == GB_SVC_CPORT_ID))
 		return;
 
 	control = connection->bundle->intf->control;
@@ -399,7 +405,8 @@
 	 * Inform Interface about Active CPorts. We don't need to do this
 	 * operation for control cport.
 	 */
-	if (cport_id != GB_CONTROL_CPORT_ID) {
+	if (cport_id != GB_CONTROL_CPORT_ID &&
+	    connection->hd_cport_id != GB_SVC_CPORT_ID) {
 		struct gb_control *control = connection->bundle->intf->control;
 
 		ret = gb_control_connected_operation(control, cport_id);
diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c
index 6edeec9..af71d02 100644
--- a/drivers/staging/greybus/core.c
+++ b/drivers/staging/greybus/core.c
@@ -176,8 +176,7 @@
 	 * Validate that the driver implements all of the callbacks
 	 * so that we don't have to every time we make them.
 	 */
-	if ((!driver->message_send) || (!driver->message_cancel) ||
-	    (!driver->submit_svc)) {
+	if ((!driver->message_send) || (!driver->message_cancel)) {
 		pr_err("Must implement all greybus_host_driver callbacks!\n");
 		return ERR_PTR(-EINVAL);
 	}
@@ -209,6 +208,21 @@
 	ida_init(&hd->cport_id_map);
 	hd->buffer_size_max = buffer_size_max;
 
+	/*
+	 * Initialize AP's SVC protocol connection:
+	 *
+	 * This is required as part of early initialization of the host device
+	 * as we need this connection in order to start any kind of message
+	 * exchange between the AP and the SVC. SVC will start with a
+	 * 'get-version' request followed by a 'svc-hello' message and at that
+	 * time we will create a fully initialized svc-connection, as we need
+	 * endo-id and AP's interface id for that.
+	 */
+	if (!gb_ap_svc_connection_create(hd)) {
+		kref_put_mutex(&hd->kref, free_hd, &hd_mutex);
+		return ERR_PTR(-ENOMEM);
+	}
+
 	return hd;
 }
 EXPORT_SYMBOL_GPL(greybus_create_hd);
@@ -270,12 +284,6 @@
 		goto error_bus;
 	}
 
-	retval = gb_ap_init();
-	if (retval) {
-		pr_err("gb_ap_init failed (%d)\n", retval);
-		goto error_ap;
-	}
-
 	retval = gb_operation_init();
 	if (retval) {
 		pr_err("gb_operation_init failed (%d)\n", retval);
@@ -317,8 +325,6 @@
 error_endo:
 	gb_operation_exit();
 error_operation:
-	gb_ap_exit();
-error_ap:
 	bus_unregister(&greybus_bus_type);
 error_bus:
 	gb_debugfs_cleanup();
@@ -334,7 +340,6 @@
 	gb_control_protocol_exit();
 	gb_endo_exit();
 	gb_operation_exit();
-	gb_ap_exit();
 	bus_unregister(&greybus_bus_type);
 	gb_debugfs_cleanup();
 }
diff --git a/drivers/staging/greybus/es1.c b/drivers/staging/greybus/es1.c
index 7fe1eaa..2aa2717 100644
--- a/drivers/staging/greybus/es1.c
+++ b/drivers/staging/greybus/es1.c
@@ -14,12 +14,10 @@
 #include <asm/unaligned.h>
 
 #include "greybus.h"
-#include "svc_msg.h"
 #include "kernel_ver.h"
 #include "connection.h"
 
 /* Memory sizes for the buffers sent to/from the ES1 controller */
-#define ES1_SVC_MSG_SIZE	(sizeof(struct svc_msg) + SZ_64K)
 #define ES1_GBUF_MSG_SIZE_MAX	2048
 
 static const struct usb_device_id id_table[] = {
@@ -59,11 +57,8 @@
  * @usb_intf: pointer to the USB interface we are bound to.
  * @hd: pointer to our greybus_host_device structure
  * @control_endpoint: endpoint to send data to SVC
- * @svc_endpoint: endpoint for SVC data in
  * @cport_in_endpoint: bulk in endpoint for CPort data
  * @cport-out_endpoint: bulk out endpoint for CPort data
- * @svc_buffer: buffer for SVC messages coming in on @svc_endpoint
- * @svc_urb: urb for SVC messages coming in on @svc_endpoint
  * @cport_in_urb: array of urbs for the CPort in messages
  * @cport_in_buffer: array of buffers for the @cport_in_urb urbs
  * @cport_out_urb: array of urbs for the CPort out messages
@@ -79,13 +74,9 @@
 	struct greybus_host_device *hd;
 
 	__u8 control_endpoint;
-	__u8 svc_endpoint;
 	__u8 cport_in_endpoint;
 	__u8 cport_out_endpoint;
 
-	u8 *svc_buffer;
-	struct urb *svc_urb;
-
 	struct urb *cport_in_urb[NUM_CPORT_IN_URB];
 	u8 *cport_in_buffer[NUM_CPORT_IN_URB];
 	struct urb *cport_out_urb[NUM_CPORT_OUT_URB];
@@ -104,26 +95,6 @@
 static void usb_log_disable(struct es1_ap_dev *es1);
 
 #define ES1_TIMEOUT	500	/* 500 ms for the SVC to do something */
-static int submit_svc(struct svc_msg *svc_msg, struct greybus_host_device *hd)
-{
-	struct es1_ap_dev *es1 = hd_to_es1(hd);
-	int retval;
-
-	/* SVC messages go down our control pipe */
-	retval = usb_control_msg(es1->usb_dev,
-				 usb_sndctrlpipe(es1->usb_dev,
-						 es1->control_endpoint),
-				 REQUEST_SVC,
-				 USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
-				 0x00, 0x00,
-				 (char *)svc_msg,
-				 sizeof(*svc_msg),
-				 ES1_TIMEOUT);
-	if (retval != sizeof(*svc_msg))
-		return retval;
-
-	return 0;
-}
 
 static struct urb *next_free_urb(struct es1_ap_dev *es1, gfp_t gfp_mask)
 {
@@ -305,7 +276,6 @@
 	.hd_priv_size		= sizeof(struct es1_ap_dev),
 	.message_send		= message_send,
 	.message_cancel		= message_cancel,
-	.submit_svc		= submit_svc,
 };
 
 /* Common function to report consistent warnings based on URB status */
@@ -369,12 +339,6 @@
 		es1->cport_in_buffer[i] = NULL;
 	}
 
-	usb_kill_urb(es1->svc_urb);
-	usb_free_urb(es1->svc_urb);
-	es1->svc_urb = NULL;
-	kfree(es1->svc_buffer);
-	es1->svc_buffer = NULL;
-
 	usb_set_intfdata(interface, NULL);
 	udev = es1->usb_dev;
 	greybus_remove_hd(es1->hd);
@@ -382,33 +346,6 @@
 	usb_put_dev(udev);
 }
 
-/* Callback for when we get a SVC message */
-static void svc_in_callback(struct urb *urb)
-{
-	struct greybus_host_device *hd = urb->context;
-	struct device *dev = &urb->dev->dev;
-	int status = check_urb_status(urb);
-	int retval;
-
-	if (status) {
-		if ((status == -EAGAIN) || (status == -EPROTO))
-			goto exit;
-		dev_err(dev, "urb svc in error %d (dropped)\n", status);
-		return;
-	}
-
-	/* We have a message, create a new message structure, add it to the
-	 * list, and wake up our thread that will process the messages.
-	 */
-	greybus_svc_in(hd, urb->transfer_buffer, urb->actual_length);
-
-exit:
-	/* resubmit the urb to get more messages */
-	retval = usb_submit_urb(urb, GFP_ATOMIC);
-	if (retval)
-		dev_err(dev, "Can not submit urb for AP data: %d\n", retval);
-}
-
 static void cport_in_callback(struct urb *urb)
 {
 	struct greybus_host_device *hd = urb->context;
@@ -612,14 +549,10 @@
 	struct usb_device *udev;
 	struct usb_host_interface *iface_desc;
 	struct usb_endpoint_descriptor *endpoint;
-	bool int_in_found = false;
 	bool bulk_in_found = false;
 	bool bulk_out_found = false;
 	int retval = -ENOMEM;
 	int i;
-	u16 endo_id = 0x4755;	// FIXME - get endo "ID" from the SVC
-	u8 ap_intf_id = 0x01;	// FIXME - get endo "ID" from the SVC
-	u8 svc_interval = 0;
 
 	/* We need to fit a CPort ID in one byte of a message header */
 	BUILD_BUG_ON(CPORT_ID_MAX > U8_MAX);
@@ -648,11 +581,7 @@
 	for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
 		endpoint = &iface_desc->endpoint[i].desc;
 
-		if (usb_endpoint_is_int_in(endpoint)) {
-			es1->svc_endpoint = endpoint->bEndpointAddress;
-			svc_interval = endpoint->bInterval;
-			int_in_found = true;
-		} else if (usb_endpoint_is_bulk_in(endpoint)) {
+		if (usb_endpoint_is_bulk_in(endpoint)) {
 			es1->cport_in_endpoint = endpoint->bEndpointAddress;
 			bulk_in_found = true;
 		} else if (usb_endpoint_is_bulk_out(endpoint)) {
@@ -664,27 +593,12 @@
 				endpoint->bEndpointAddress);
 		}
 	}
-	if ((int_in_found == false) ||
-	    (bulk_in_found == false) ||
+	if ((bulk_in_found == false) ||
 	    (bulk_out_found == false)) {
 		dev_err(&udev->dev, "Not enough endpoints found in device, aborting!\n");
 		goto error;
 	}
 
-	/* Create our buffer and URB to get SVC messages, and start it up */
-	es1->svc_buffer = kmalloc(ES1_SVC_MSG_SIZE, GFP_KERNEL);
-	if (!es1->svc_buffer)
-		goto error;
-
-	es1->svc_urb = usb_alloc_urb(0, GFP_KERNEL);
-	if (!es1->svc_urb)
-		goto error;
-
-	usb_fill_int_urb(es1->svc_urb, udev,
-			 usb_rcvintpipe(udev, es1->svc_endpoint),
-			 es1->svc_buffer, ES1_SVC_MSG_SIZE, svc_in_callback,
-			 hd, svc_interval);
-
 	/* Allocate buffers for our cport in messages and start them up */
 	for (i = 0; i < NUM_CPORT_IN_URB; ++i) {
 		struct urb *urb;
@@ -720,22 +634,6 @@
 		es1->cport_out_urb_busy[i] = false;	/* just to be anal */
 	}
 
-	/*
-	 * XXX Soon this will be initiated later, with a combination
-	 * XXX of a Control protocol probe operation and a
-	 * XXX subsequent Control protocol connected operation for
-	 * XXX the SVC connection.  At that point we know we're
-	 * XXX properly connected to an Endo.
-	 */
-	retval = greybus_endo_setup(hd, endo_id, ap_intf_id);
-	if (retval)
-		goto error;
-
-	/* Start up our svc urb, which allows events to start flowing */
-	retval = usb_submit_urb(es1->svc_urb, GFP_KERNEL);
-	if (retval)
-		goto error;
-
 	apb1_log_enable_dentry = debugfs_create_file("apb1_log_enable",
 							(S_IWUSR | S_IRUGO),
 							gb_debugfs_get(), es1,
diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c
index 43cbc4d..008685b 100644
--- a/drivers/staging/greybus/es2.c
+++ b/drivers/staging/greybus/es2.c
@@ -14,12 +14,10 @@
 #include <asm/unaligned.h>
 
 #include "greybus.h"
-#include "svc_msg.h"
 #include "kernel_ver.h"
 #include "connection.h"
 
 /* Memory sizes for the buffers sent to/from the ES1 controller */
-#define ES1_SVC_MSG_SIZE	(sizeof(struct svc_msg) + SZ_64K)
 #define ES1_GBUF_MSG_SIZE_MAX	2048
 
 static const struct usb_device_id id_table[] = {
@@ -86,10 +84,7 @@
  * @usb_intf: pointer to the USB interface we are bound to.
  * @hd: pointer to our greybus_host_device structure
  * @control_endpoint: endpoint to send data to SVC
- * @svc_endpoint: endpoint for SVC data in
 
- * @svc_buffer: buffer for SVC messages coming in on @svc_endpoint
- * @svc_urb: urb for SVC messages coming in on @svc_endpoint
  * @cport_in: endpoint, urbs and buffer for cport in messages
  * @cport_out: endpoint for for cport out messages
  * @cport_out_urb: array of urbs for the CPort out messages
@@ -105,10 +100,6 @@
 	struct greybus_host_device *hd;
 
 	__u8 control_endpoint;
-	__u8 svc_endpoint;
-
-	u8 *svc_buffer;
-	struct urb *svc_urb;
 
 	struct es1_cport_in cport_in[NUM_BULKS];
 	struct es1_cport_out cport_out[NUM_BULKS];
@@ -143,26 +134,6 @@
 }
 
 #define ES1_TIMEOUT	500	/* 500 ms for the SVC to do something */
-static int submit_svc(struct svc_msg *svc_msg, struct greybus_host_device *hd)
-{
-	struct es1_ap_dev *es1 = hd_to_es1(hd);
-	int retval;
-
-	/* SVC messages go down our control pipe */
-	retval = usb_control_msg(es1->usb_dev,
-				 usb_sndctrlpipe(es1->usb_dev,
-						 es1->control_endpoint),
-				 REQUEST_SVC,
-				 USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
-				 0x00, 0x00,
-				 (char *)svc_msg,
-				 sizeof(*svc_msg),
-				 ES1_TIMEOUT);
-	if (retval != sizeof(*svc_msg))
-		return retval;
-
-	return 0;
-}
 
 static int ep_in_use(struct es1_ap_dev *es1, int bulk_ep_set)
 {
@@ -401,7 +372,6 @@
 	.hd_priv_size		= sizeof(struct es1_ap_dev),
 	.message_send		= message_send,
 	.message_cancel		= message_cancel,
-	.submit_svc		= submit_svc,
 };
 
 /* Common function to report consistent warnings based on URB status */
@@ -469,12 +439,6 @@
 		}
 	}
 
-	usb_kill_urb(es1->svc_urb);
-	usb_free_urb(es1->svc_urb);
-	es1->svc_urb = NULL;
-	kfree(es1->svc_buffer);
-	es1->svc_buffer = NULL;
-
 	usb_set_intfdata(interface, NULL);
 	udev = es1->usb_dev;
 	greybus_remove_hd(es1->hd);
@@ -482,33 +446,6 @@
 	usb_put_dev(udev);
 }
 
-/* Callback for when we get a SVC message */
-static void svc_in_callback(struct urb *urb)
-{
-	struct greybus_host_device *hd = urb->context;
-	struct device *dev = &urb->dev->dev;
-	int status = check_urb_status(urb);
-	int retval;
-
-	if (status) {
-		if ((status == -EAGAIN) || (status == -EPROTO))
-			goto exit;
-		dev_err(dev, "urb svc in error %d (dropped)\n", status);
-		return;
-	}
-
-	/* We have a message, create a new message structure, add it to the
-	 * list, and wake up our thread that will process the messages.
-	 */
-	greybus_svc_in(hd, urb->transfer_buffer, urb->actual_length);
-
-exit:
-	/* resubmit the urb to get more messages */
-	retval = usb_submit_urb(urb, GFP_ATOMIC);
-	if (retval)
-		dev_err(dev, "Can not submit urb for AP data: %d\n", retval);
-}
-
 static void cport_in_callback(struct urb *urb)
 {
 	struct greybus_host_device *hd = urb->context;
@@ -712,14 +649,10 @@
 	struct usb_device *udev;
 	struct usb_host_interface *iface_desc;
 	struct usb_endpoint_descriptor *endpoint;
-	bool int_in_found = false;
 	int bulk_in = 0;
 	int bulk_out = 0;
 	int retval = -ENOMEM;
 	int i;
-	u16 endo_id = 0x4755;	// FIXME - get endo "ID" from the SVC
-	u8 ap_intf_id = 0x01;	// FIXME - get endo "ID" from the SVC
-	u8 svc_interval = 0;
 
 	/* We need to fit a CPort ID in one byte of a message header */
 	BUILD_BUG_ON(CPORT_ID_MAX > U8_MAX);
@@ -748,11 +681,7 @@
 	for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
 		endpoint = &iface_desc->endpoint[i].desc;
 
-		if (usb_endpoint_is_int_in(endpoint)) {
-			es1->svc_endpoint = endpoint->bEndpointAddress;
-			svc_interval = endpoint->bInterval;
-			int_in_found = true;
-		} else if (usb_endpoint_is_bulk_in(endpoint)) {
+		if (usb_endpoint_is_bulk_in(endpoint)) {
 			es1->cport_in[bulk_in++].endpoint =
 				endpoint->bEndpointAddress;
 		} else if (usb_endpoint_is_bulk_out(endpoint)) {
@@ -764,27 +693,12 @@
 				endpoint->bEndpointAddress);
 		}
 	}
-	if ((int_in_found == false) ||
-	    (bulk_in == 0) ||
+	if ((bulk_in == 0) ||
 	    (bulk_out == 0)) {
 		dev_err(&udev->dev, "Not enough endpoints found in device, aborting!\n");
 		goto error;
 	}
 
-	/* Create our buffer and URB to get SVC messages, and start it up */
-	es1->svc_buffer = kmalloc(ES1_SVC_MSG_SIZE, GFP_KERNEL);
-	if (!es1->svc_buffer)
-		goto error;
-
-	es1->svc_urb = usb_alloc_urb(0, GFP_KERNEL);
-	if (!es1->svc_urb)
-		goto error;
-
-	usb_fill_int_urb(es1->svc_urb, udev,
-			 usb_rcvintpipe(udev, es1->svc_endpoint),
-			 es1->svc_buffer, ES1_SVC_MSG_SIZE, svc_in_callback,
-			 hd, svc_interval);
-
 	/* Allocate buffers for our cport in messages and start them up */
 	for (bulk_in = 0; bulk_in < NUM_BULKS; bulk_in++) {
 		struct es1_cport_in *cport_in = &es1->cport_in[bulk_in];
@@ -824,22 +738,6 @@
 		es1->cport_out_urb_busy[i] = false;	/* just to be anal */
 	}
 
-	/*
-	 * XXX Soon this will be initiated later, with a combination
-	 * XXX of a Control protocol probe operation and a
-	 * XXX subsequent Control protocol connected operation for
-	 * XXX the SVC connection.  At that point we know we're
-	 * XXX properly connected to an Endo.
-	 */
-	retval = greybus_endo_setup(hd, endo_id, ap_intf_id);
-	if (retval)
-		goto error;
-
-	/* Start up our svc urb, which allows events to start flowing */
-	retval = usb_submit_urb(es1->svc_urb, GFP_KERNEL);
-	if (retval)
-		goto error;
-
 	apb1_log_enable_dentry = debugfs_create_file("apb1_log_enable",
 							(S_IWUSR | S_IRUGO),
 							gb_debugfs_get(), es1,
diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h
index 0d4ca70..5726555 100644
--- a/drivers/staging/greybus/greybus.h
+++ b/drivers/staging/greybus/greybus.h
@@ -74,7 +74,6 @@
  */
 
 struct greybus_host_device;
-struct svc_msg;
 
 /* Greybus "Host driver" structure, needed by a host controller driver to be
  * able to handle both SVC control as well as "real" greybus messages
@@ -85,8 +84,6 @@
 	int (*message_send)(struct greybus_host_device *hd, u16 dest_cport_id,
 			struct gb_message *message, gfp_t gfp_mask);
 	void (*message_cancel)(struct gb_message *message);
-	int (*submit_svc)(struct svc_msg *svc_msg,
-			    struct greybus_host_device *hd);
 };
 
 struct greybus_host_device {
@@ -104,6 +101,7 @@
 
 	struct gb_endo *endo;
 	struct gb_connection *initial_svc_connection;
+	struct gb_svc *svc;
 
 	/* Private data for the host driver */
 	unsigned long hd_priv[0] __aligned(sizeof(s64));
@@ -156,9 +154,6 @@
 
 int greybus_disabled(void);
 
-int greybus_svc_in(struct greybus_host_device *hd, u8 *data, int length);
-int gb_ap_init(void);
-void gb_ap_exit(void);
 void gb_debugfs_init(void);
 void gb_debugfs_cleanup(void);
 struct dentry *gb_debugfs_get(void);
diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h
index 357ecd3..43a0728 100644
--- a/drivers/staging/greybus/greybus_protocols.h
+++ b/drivers/staging/greybus/greybus_protocols.h
@@ -64,9 +64,9 @@
  * CONTROL and SVC protocols for communication between AP and SVC.
  */
 #define GB_SVC_BUNDLE_ID			0
-#define GB_SVC_CPORT_ID				2
+#define GB_SVC_CPORT_ID				0
 #define GB_CONTROL_BUNDLE_ID			0
-#define GB_CONTROL_CPORT_ID			2
+#define GB_CONTROL_CPORT_ID			0
 
 
 /*
@@ -650,6 +650,7 @@
 #define GB_SVC_TYPE_INTF_RESET		0x06
 #define GB_SVC_TYPE_CONN_CREATE		0x07
 #define GB_SVC_TYPE_CONN_DESTROY	0x08
+#define GB_SVC_TYPE_ROUTE_CREATE	0x0b
 
 /* SVC version request/response have same payload as gb_protocol_version_response */
 
@@ -692,6 +693,8 @@
 	__u16	cport1_id;
 	__u8	intf2_id;
 	__u16	cport2_id;
+	__u8	tc;
+	__u8	flags;
 } __packed;
 /* connection create response has no payload */
 
@@ -703,6 +706,13 @@
 } __packed;
 /* connection destroy response has no payload */
 
+struct gb_svc_route_create_request {
+	__u8	intf1_id;
+	__u8	dev1_id;
+	__u8	intf2_id;
+	__u8	dev2_id;
+};
+
 /* UART */
 
 /* Version of the Greybus UART protocol we support */
diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c
index f1e2956..4a26bf6 100644
--- a/drivers/staging/greybus/interface.c
+++ b/drivers/staging/greybus/interface.c
@@ -183,7 +183,7 @@
 /*
  * Tear down a previously set up module.
  */
-void gb_interface_destroy(struct gb_interface *intf)
+static void interface_destroy(struct gb_interface *intf)
 {
 	struct gb_module *module;
 	struct gb_bundle *bundle;
@@ -279,7 +279,7 @@
 	struct gb_interface *intf = gb_interface_find(hd, interface_id);
 
 	if (intf)
-		gb_interface_destroy(intf);
+		interface_destroy(intf);
 	else
 		dev_err(hd->parent, "interface id %d not found\n",
 			interface_id);
@@ -290,5 +290,5 @@
 	struct gb_interface *intf, *temp;
 
 	list_for_each_entry_safe(intf, temp, &hd->interfaces, links)
-		gb_interface_destroy(intf);
+		interface_destroy(intf);
 }
diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c
index 73e7947..5ff7cf3 100644
--- a/drivers/staging/greybus/svc.c
+++ b/drivers/staging/greybus/svc.c
@@ -8,11 +8,22 @@
  */
 
 #include "greybus.h"
+#include <linux/workqueue.h>
+
+#define CPORT_FLAGS_E2EFC       (1)
+#define CPORT_FLAGS_CSD_N       (2)
+#define CPORT_FLAGS_CSV_N       (4)
 
 struct gb_svc {
 	struct gb_connection	*connection;
 };
 
+struct svc_hotplug {
+	struct work_struct work;
+	struct gb_connection *connection;
+	struct gb_svc_intf_hotplug_request data;
+};
+
 static struct ida greybus_svc_device_id_map;
 
 /*
@@ -34,7 +45,6 @@
 
 	return connection;
 }
-EXPORT_SYMBOL_GPL(gb_ap_svc_connection_create);
 
 /*
  * We know endo-type and AP's interface id now, lets create a proper svc
@@ -96,6 +106,12 @@
 	request.cport1_id = cport1_id;
 	request.intf2_id = intf2_id;
 	request.cport2_id = cport2_id;
+	/*
+	 * XXX: fix connections paramaters to TC0 and all CPort flags
+	 * for now.
+	 */
+	request.tc = 0;
+	request.flags = CPORT_FLAGS_CSV_N | CPORT_FLAGS_E2EFC;
 
 	return gb_operation_sync(svc->connection, GB_SVC_TYPE_CONN_CREATE,
 				 &request, sizeof(request), NULL, 0);
@@ -116,6 +132,20 @@
 				 &request, sizeof(request), NULL, 0);
 }
 
+static int route_create_operation(struct gb_svc *svc, u8 intf1_id, u8 dev1_id,
+				  u8 intf2_id, u8 dev2_id)
+{
+	struct gb_svc_route_create_request request;
+
+	request.intf1_id = intf1_id;
+	request.dev1_id = dev1_id;
+	request.intf2_id = intf2_id;
+	request.dev2_id = dev2_id;
+
+	return gb_operation_sync(svc->connection, GB_SVC_TYPE_ROUTE_CREATE,
+				 &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);
@@ -146,6 +176,14 @@
 }
 EXPORT_SYMBOL_GPL(gb_svc_connection_destroy);
 
+int gb_svc_route_create(struct gb_svc *svc, u8 intf1_id, u8 dev1_id,
+			u8 intf2_id, u8 dev2_id)
+{
+	return route_create_operation(svc, intf1_id, dev1_id,
+				      intf2_id, dev2_id);
+}
+EXPORT_SYMBOL_GPL(gb_svc_route_create);
+
 static int gb_svc_version_request(struct gb_operation *op)
 {
 	struct gb_connection *connection = op->connection;
@@ -223,29 +261,25 @@
 	return 0;
 }
 
-static int gb_svc_intf_hotplug_recv(struct gb_operation *op)
+/*
+ * 'struct svc_hotplug' should be freed by svc_process_hotplug() before it
+ * returns, irrespective of success or Failure in bringing up the module.
+ */
+static void svc_process_hotplug(struct work_struct *work)
 {
-	struct gb_message *request = op->request;
-	struct gb_svc_intf_hotplug_request *hotplug = request->payload;
-	struct gb_svc *svc = op->connection->private;
-	struct greybus_host_device *hd = op->connection->bundle->intf->hd;
-	struct device *dev = &op->connection->dev;
+	struct svc_hotplug *svc_hotplug = container_of(work, struct svc_hotplug,
+						       work);
+	struct gb_svc_intf_hotplug_request *hotplug = &svc_hotplug->data;
+	struct gb_connection *connection = svc_hotplug->connection;
+	struct gb_svc *svc = connection->private;
+	struct greybus_host_device *hd = connection->bundle->intf->hd;
+	struct device *dev = &connection->dev;
 	struct gb_interface *intf;
 	u8 intf_id, device_id;
 	int ret;
 
-	if (request->payload_size < sizeof(*hotplug)) {
-		dev_err(dev, "%s: short hotplug request received\n", __func__);
-		return -EINVAL;
-	}
-
 	/*
 	 * 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;
 
@@ -253,7 +287,7 @@
 	if (!intf) {
 		dev_err(dev, "%s: Failed to create interface with id %hhu\n",
 			__func__, intf_id);
-		return -EINVAL;
+		goto free_svc_hotplug;
 	}
 
 	intf->unipro_mfg_id = le32_to_cpu(hotplug->data.unipro_mfg_id);
@@ -285,6 +319,25 @@
 		goto ida_put;
 	}
 
+	/*
+	 * Create a two-way route between the AP and the new interface
+	 */
+	ret = route_create_operation(svc, hd->endo->ap_intf_id,
+				     GB_DEVICE_ID_AP, intf_id, device_id);
+	if (ret) {
+		dev_err(dev, "%s: Route create operation failed, interface %hhu device_id %hhu (%d)\n",
+			__func__, intf_id, device_id, ret);
+		goto ida_put;
+	}
+
+	ret = route_create_operation(svc, intf_id, device_id,
+				     hd->endo->ap_intf_id, GB_DEVICE_ID_AP);
+	if (ret) {
+		dev_err(dev, "%s: Route create operation failed, interface %hhu device_id %hhu (%d)\n",
+			__func__, intf_id, device_id, ret);
+		goto ida_put;
+	}
+
 	ret = gb_interface_init(intf, device_id);
 	if (ret) {
 		dev_err(dev, "%s: Failed to initialize interface, interface %hhu device_id %hhu (%d)\n",
@@ -292,7 +345,7 @@
 		goto svc_id_free;
 	}
 
-	return 0;
+	goto free_svc_hotplug;
 
 svc_id_free:
 	/*
@@ -303,8 +356,43 @@
 	ida_simple_remove(&greybus_svc_device_id_map, device_id);
 destroy_interface:
 	gb_interface_remove(hd, intf_id);
+free_svc_hotplug:
+	kfree(svc_hotplug);
+}
 
-	return ret;
+/*
+ * Bringing up a module can be time consuming, as that may require lots of
+ * initialization on the module side. Over that, we may also need to download
+ * the firmware first and flash that on the module.
+ *
+ * In order to make other hotplug events to not wait for all this to finish,
+ * handle most of module hotplug stuff outside of the hotplug callback, with
+ * help of a workqueue.
+ */
+static int gb_svc_intf_hotplug_recv(struct gb_operation *op)
+{
+	struct gb_message *request = op->request;
+	struct svc_hotplug *svc_hotplug;
+
+	if (request->payload_size < sizeof(svc_hotplug->data)) {
+		dev_err(&op->connection->dev,
+			"%s: short hotplug request received (%zu < %zu)\n",
+			__func__, request->payload_size,
+			sizeof(svc_hotplug->data));
+		return -EINVAL;
+	}
+
+	svc_hotplug = kmalloc(sizeof(*svc_hotplug), GFP_ATOMIC);
+	if (!svc_hotplug)
+		return -ENOMEM;
+
+	svc_hotplug->connection = op->connection;
+	memcpy(&svc_hotplug->data, op->request->payload, sizeof(svc_hotplug->data));
+
+	INIT_WORK(&svc_hotplug->work, svc_process_hotplug);
+	queue_work(system_unbound_wq, &svc_hotplug->work);
+
+	return 0;
 }
 
 static int gb_svc_intf_hot_unplug_recv(struct gb_operation *op)
@@ -319,7 +407,8 @@
 
 	if (request->payload_size < sizeof(*hot_unplug)) {
 		dev_err(&op->connection->dev,
-			"short hot unplug request received\n");
+			"short hot unplug request received (%zu < %zu)\n",
+			request->payload_size, sizeof(*hot_unplug));
 		return -EINVAL;
 	}
 
@@ -347,7 +436,8 @@
 
 	if (request->payload_size < sizeof(*reset)) {
 		dev_err(&op->connection->dev,
-			"short reset request received\n");
+			"short reset request received (%zu < %zu)\n",
+			request->payload_size, sizeof(*reset));
 		return -EINVAL;
 	}
 	reset = request->payload;
@@ -387,6 +477,7 @@
 	if (!svc)
 		return -ENOMEM;
 
+	connection->hd->svc = svc;
 	svc->connection = connection;
 	connection->private = svc;
 
@@ -402,6 +493,7 @@
 {
 	struct gb_svc *svc = connection->private;
 
+	connection->hd->svc = NULL;
 	connection->private = NULL;
 	kfree(svc);
 }
diff --git a/drivers/staging/greybus/svc_msg.h b/drivers/staging/greybus/svc_msg.h
deleted file mode 100644
index 3c628c5..0000000
--- a/drivers/staging/greybus/svc_msg.h
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- * Greybus AP <-> SVC message structure format.
- *
- * See "Greybus Application Protocol" document (version 0.1) for
- * details on these values and structures.
- *
- * Copyright 2014 Google Inc.
- * Copyright 2014 Linaro Ltd.
- *
- * Released under the GPLv2 and BSD license.
- */
-
-#ifndef __SVC_MSG_H
-#define __SVC_MSG_H
-
-enum svc_function_id {
-	SVC_FUNCTION_HANDSHAKE			= 0x00,
-	SVC_FUNCTION_UNIPRO_NETWORK_MANAGEMENT	= 0x01,
-	SVC_FUNCTION_HOTPLUG			= 0x02,
-	SVC_FUNCTION_POWER			= 0x03,
-	SVC_FUNCTION_EPM			= 0x04,
-	SVC_FUNCTION_SUSPEND			= 0x05,
-};
-
-enum svc_msg_type {
-	SVC_MSG_DATA				= 0x00,
-	SVC_MSG_ERROR				= 0xff,
-};
-
-struct svc_msg_header {
-	__u8	function_id;	/* enum svc_function_id */
-	__u8	message_type;
-	__le16	payload_length;
-} __packed;
-
-enum svc_function_handshake_type {
-	SVC_HANDSHAKE_SVC_HELLO		= 0x00,
-	SVC_HANDSHAKE_AP_HELLO		= 0x01,
-	SVC_HANDSHAKE_MODULE_HELLO	= 0x02,
-};
-
-struct svc_function_handshake {
-	__u8	version_major;
-	__u8	version_minor;
-	__u8	handshake_type;	/* enum svc_function_handshake_type */
-} __packed;
-
-struct svc_function_unipro_set_route {
-	__u8	device_id;
-} __packed;
-
-struct svc_function_unipro_link_up {
-	__u8	interface_id;	/* Interface id within the Endo */
-	__u8	device_id;
-} __packed;
-
-struct svc_function_ap_id {
-	__u8	interface_id;
-	__u8	device_id;
-} __packed;
-
-enum svc_function_management_event {
-	SVC_MANAGEMENT_AP_ID		= 0x00,
-	SVC_MANAGEMENT_LINK_UP		= 0x01,
-	SVC_MANAGEMENT_SET_ROUTE	= 0x02,
-};
-
-struct svc_function_unipro_management {
-	__u8	management_packet_type;	/* enum svc_function_management_event */
-	union {
-		struct svc_function_ap_id		ap_id;
-		struct svc_function_unipro_link_up	link_up;
-		struct svc_function_unipro_set_route	set_route;
-	};
-} __packed;
-
-enum svc_function_hotplug_event {
-	SVC_HOTPLUG_EVENT	= 0x00,
-	SVC_HOTUNPLUG_EVENT	= 0x01,
-};
-
-struct svc_function_hotplug {
-	__u8	hotplug_event;	/* enum svc_function_hotplug_event */
-	__u8	interface_id;	/* Interface id within the Endo */
-} __packed;
-
-enum svc_function_power_type {
-	SVC_POWER_BATTERY_STATUS		= 0x00,
-	SVC_POWER_BATTERY_STATUS_REQUEST	= 0x01,
-};
-
-enum svc_function_battery_status {
-	SVC_BATTERY_UNKNOWN		= 0x00,
-	SVC_BATTERY_CHARGING		= 0x01,
-	SVC_BATTERY_DISCHARGING		= 0x02,
-	SVC_BATTERY_NOT_CHARGING	= 0x03,
-	SVC_BATTERY_FULL		= 0x04,
-};
-
-struct svc_function_power_battery_status {
-	__le16	charge_full;
-	__le16	charge_now;
-	__u8	status;	/* enum svc_function_battery_status */
-} __packed;
-
-struct svc_function_power_battery_status_request {
-} __packed;
-
-/* XXX
- * Each interface carries power, so it's possible these things
- * are associated with each UniPro device and not just the module.
- * For now it's safe to assume it's per-module.
- */
-struct svc_function_power {
-	__u8	power_type;	/* enum svc_function_power_type */
-	__u8	interface_id;
-	union {
-		struct svc_function_power_battery_status		status;
-		struct svc_function_power_battery_status_request	request;
-	};
-} __packed;
-
-enum svc_function_epm_command_type {
-	SVC_EPM_ENABLE	= 0x00,
-	SVC_EPM_DISABLE	= 0x01,
-};
-
-/* EPM's are associated with the module */
-struct svc_function_epm {
-	__u8	epm_command_type;	/* enum svc_function_epm_command_type */
-	__u8	module_id;
-} __packed;
-
-enum svc_function_suspend_command_type {
-	SVC_SUSPEND_FIXME_1	= 0x00,	// FIXME
-	SVC_SUSPEND_FIXME_2	= 0x01,
-};
-
-/* We'll want independent control for multi-interface modules */
-struct svc_function_suspend {
-	__u8	suspend_command_type;	/* enum function_suspend_command_type */
-	__u8	device_id;
-} __packed;
-
-struct svc_msg {
-	struct svc_msg_header	header;
-	union {
-		struct svc_function_handshake		handshake;
-		struct svc_function_unipro_management	management;
-		struct svc_function_hotplug		hotplug;
-		struct svc_function_power		power;
-		struct svc_function_epm			epm;
-		struct svc_function_suspend		suspend;
-	};
-} __packed;
-
-#endif /* __SVC_MSG_H */