diag: Add functionality to close SDIO channel.

DIAG driver pulls MDM9k traffic onto MSM using the SDIO bridge.
This patch adds capability to close this channel under special
circumstances.

Signed-off-by: Shalabh Jain <shalabhj@codeaurora.org>
diff --git a/drivers/char/diag/diagchar.h b/drivers/char/diag/diagchar.h
index 6041954..5c685c3 100644
--- a/drivers/char/diag/diagchar.h
+++ b/drivers/char/diag/diagchar.h
@@ -213,6 +213,7 @@
 	struct workqueue_struct *diag_sdio_wq;
 	struct work_struct diag_read_sdio_work;
 	struct work_struct diag_remove_sdio_work;
+	struct work_struct diag_close_sdio_work;
 	struct diag_request *usb_read_mdm_ptr;
 	struct diag_request *write_ptr_mdm;
 #endif
diff --git a/drivers/char/diag/diagfwd_sdio.c b/drivers/char/diag/diagfwd_sdio.c
index 8d43286..8be9f46 100644
--- a/drivers/char/diag/diagfwd_sdio.c
+++ b/drivers/char/diag/diagfwd_sdio.c
@@ -40,11 +40,11 @@
 		if (r > IN_BUF_SIZE) {
 			if (r < MAX_IN_BUF_SIZE) {
 				pr_err("diag: SDIO sending"
-					  " in packets more than %d bytes", r);
+					  " packets more than %d bytes\n", r);
 				buf = krealloc(buf, r, GFP_KERNEL);
 			} else {
 				pr_err("diag: SDIO sending"
-			  " in packets more than %d bytes", MAX_IN_BUF_SIZE);
+			  " in packets more than %d bytes\n", MAX_IN_BUF_SIZE);
 				return;
 			}
 		}
@@ -69,6 +69,31 @@
 	__diag_sdio_send_req();
 }
 
+static void diag_sdio_notify(void *ctxt, unsigned event)
+{
+	if (event == SDIO_EVENT_DATA_READ_AVAIL)
+		queue_work(driver->diag_sdio_wq,
+				 &(driver->diag_read_sdio_work));
+
+	if (event == SDIO_EVENT_DATA_WRITE_AVAIL)
+		wake_up_interruptible(&driver->wait_q);
+}
+
+static int diag_sdio_close(void)
+{
+	queue_work(driver->diag_sdio_wq, &(driver->diag_close_sdio_work));
+	return 0;
+}
+
+static void diag_close_sdio_work_fn(struct work_struct *work)
+{
+	pr_debug("diag: sdio close called\n");
+	if (sdio_close(driver->sdio_ch))
+		pr_err("diag: could not close SDIO channel\n");
+	else
+		driver->sdio_ch = NULL; /* channel successfully closed */
+}
+
 int diagfwd_connect_sdio(void)
 {
 	int err;
@@ -76,9 +101,19 @@
 	err = usb_diag_alloc_req(driver->mdm_ch, N_MDM_WRITE,
 							 N_MDM_READ);
 	if (err)
-		printk(KERN_ERR "diag: unable to alloc USB req on mdm ch");
+		pr_err("diag: unable to alloc USB req on mdm ch\n");
 
 	driver->in_busy_sdio = 0;
+	if (!driver->sdio_ch) {
+		err = sdio_open("SDIO_DIAG", &driver->sdio_ch, driver,
+							 diag_sdio_notify);
+		if (err)
+			pr_info("diag: could not open SDIO channel\n");
+		else
+			pr_info("diag: opened SDIO channel\n");
+	} else {
+		pr_info("diag: SDIO channel already open\n");
+	}
 
 	/* Poll USB channel to check for data*/
 	queue_work(driver->diag_sdio_wq, &(driver->diag_read_mdm_work));
@@ -91,6 +126,8 @@
 {
 	driver->in_busy_sdio = 1;
 	usb_diag_free_req(driver->mdm_ch);
+	if (driver->sdio_ch && (driver->logging_mode == USB_MODE))
+		diag_sdio_close();
 	return 0;
 }
 
@@ -125,16 +162,6 @@
 	}
 }
 
-static void diag_sdio_notify(void *ctxt, unsigned event)
-{
-	if (event == SDIO_EVENT_DATA_READ_AVAIL)
-		queue_work(driver->diag_sdio_wq,
-				 &(driver->diag_read_sdio_work));
-
-	if (event == SDIO_EVENT_DATA_WRITE_AVAIL)
-		wake_up_interruptible(&driver->wait_q);
-}
-
 static int diag_sdio_probe(struct platform_device *pdev)
 {
 	int err;
@@ -159,7 +186,7 @@
 
 static void diag_remove_sdio_work_fn(struct work_struct *work)
 {
-	pr_debug("\n diag: sdio remove called");
+	pr_debug("diag: sdio remove called\n");
 	/*Disable SDIO channel to prevent further read/write */
 	driver->sdio_ch = NULL;
 }
@@ -226,6 +253,7 @@
 #endif
 	INIT_WORK(&(driver->diag_read_sdio_work), diag_read_sdio_work_fn);
 	INIT_WORK(&(driver->diag_remove_sdio_work), diag_remove_sdio_work_fn);
+	INIT_WORK(&(driver->diag_close_sdio_work), diag_close_sdio_work_fn);
 	ret = platform_driver_register(&msm_sdio_ch_driver);
 	if (ret)
 		printk(KERN_INFO "DIAG could not register SDIO device");