greybus: es1: handle cport data in and out
diff --git a/drivers/staging/greybus/ap.c b/drivers/staging/greybus/ap.c
index 53ad66c..e606a45 100644
--- a/drivers/staging/greybus/ap.c
+++ b/drivers/staging/greybus/ap.c
@@ -298,6 +298,12 @@
 }
 EXPORT_SYMBOL_GPL(gb_new_ap_msg);
 
+void greybus_cport_in_data(struct greybus_host_device *hd, int cport, u8 *data,
+			   size_t length)
+{
+	// FIXME - implement...
+}
+EXPORT_SYMBOL_GPL(greybus_cport_in_data);
 
 int gb_thread_init(void)
 {
diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c
index cf2987e..6635194 100644
--- a/drivers/staging/greybus/es1-ap-usb.c
+++ b/drivers/staging/greybus/es1-ap-usb.c
@@ -233,7 +233,11 @@
 static void cport_in_callback(struct urb *urb)
 {
 	struct device *dev = &urb->dev->dev;
+	struct es1_ap_dev *es1 = urb->context;
 	int status = urb->status;
+	int retval;
+	u8 cport;
+	u8 *data;
 
 	switch (status) {
 	case 0:
@@ -252,15 +256,40 @@
 		goto exit;
 	}
 
-	// FIXME - handle the CPort in data
+	/* The size has to be more then just an "empty" transfer */
+	if (urb->actual_length <= 2) {
+		dev_err(dev, "%s: \"short\" cport in transfer of %d bytes?\n",
+			__func__, urb->actual_length);
+		goto exit;
+	}
+
+	/*
+	 * The CPort number is the first byte of the data stream, the rest of
+	 * the stream is "real" data
+	 */
+	data = urb->transfer_buffer;
+	cport = data[0];
+	data = &data[1];
+
+	/* Pass this data to the greybus core */
+	greybus_cport_in_data(es1->hd, cport, data, urb->actual_length - 1);
+
 exit:
-	return;
+	/* put our urb back in the request pool */
+	retval = usb_submit_urb(urb, GFP_ATOMIC);
+	if (retval)
+		dev_err(dev, "%s: error %d in submitting urb.\n",
+			__func__, retval);
 }
 
 static void cport_out_callback(struct urb *urb)
 {
 	struct device *dev = &urb->dev->dev;
+	struct gbuf *gbuf = urb->context;
+	struct es1_ap_dev *es1 = gbuf->hdpriv;
+	unsigned long flags;
 	int status = urb->status;
+	int i;
 
 	switch (status) {
 	case 0:
@@ -273,15 +302,34 @@
 	case -ESHUTDOWN:
 	case -EILSEQ:
 		/* device is gone, stop sending */
-		return;
+		goto exit;
 	default:
 		dev_err(dev, "%s: unknown status %d\n", __func__, status);
 		goto exit;
 	}
 
-	// FIXME - handle the CPort out data callback
+	// FIXME - do we care about errors going back up?
+
+	/* Tell the core the gbuf is properly sent */
+	greybus_gbuf_finished(gbuf);
+
 exit:
-	return;
+	/*
+	 * See if this was an urb in our pool, if so mark it "free", otherwise we
+	 * need to free it ourselves.
+	 */
+	spin_lock_irqsave(&es1->cport_out_urb_lock, flags);
+	for (i = 0; i < NUM_CPORT_OUT_URB; ++i) {
+		if (urb == es1->cport_out_urb[i]) {
+			es1->cport_out_urb_busy[i] = false;
+			urb = NULL;
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&es1->cport_out_urb_lock, flags);
+	if (urb)
+		usb_free_urb(urb);
+
 }
 
 /*
diff --git a/drivers/staging/greybus/gbuf.c b/drivers/staging/greybus/gbuf.c
index 1bf9226..e12a625 100644
--- a/drivers/staging/greybus/gbuf.c
+++ b/drivers/staging/greybus/gbuf.c
@@ -98,10 +98,19 @@
 
 int greybus_submit_gbuf(struct gbuf *gbuf, gfp_t mem_flags)
 {
+	// FIXME - implement
 	return -ENOMEM;
 }
 
 int greybus_kill_gbuf(struct gbuf *gbuf)
 {
+	// FIXME - implement
 	return -ENOMEM;
 }
+
+/* Can be called in interrupt context, do the work and get out of here */
+void greybus_gbuf_finished(struct gbuf *gbuf)
+{
+	// FIXME - implement
+}
+EXPORT_SYMBOL_GPL(greybus_gbuf_finished);
diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h
index 573274b..1badfa8 100644
--- a/drivers/staging/greybus/greybus.h
+++ b/drivers/staging/greybus/greybus.h
@@ -126,6 +126,9 @@
 struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *host_driver,
 					      struct device *parent);
 void greybus_remove_hd(struct greybus_host_device *hd);
+void greybus_cport_in_data(struct greybus_host_device *hd, int cport, u8 *data,
+			   size_t length);
+void greybus_gbuf_finished(struct gbuf *gbuf);
 
 
 /* Increase these values if needed */