usb: gsdio: Drop the carrier detect when sdio device is removed

USB DUN(dial-up networking) driver communicates to modem over
sdio. When sdio platform device is removed, drop the carrier
detect(CD) signal to indicate call drop to USB host(laptop).
One instance where sdio platform device can be removed is when
modem sub-system restart is initiated.

CRs-Fixed: 294871
Signed-off-by: Vamsi Krishna <vskrishn@codeaurora.org>
diff --git a/drivers/usb/gadget/u_sdio.c b/drivers/usb/gadget/u_sdio.c
index 3441839..fd01dbf 100644
--- a/drivers/usb/gadget/u_sdio.c
+++ b/drivers/usb/gadget/u_sdio.c
@@ -88,6 +88,7 @@
 	struct usb_cdc_line_coding	line_coding;
 
 	int				sdio_open;
+	int				sdio_probe;
 	int				ctrl_ch_err;
 	struct sdio_port_info		*sport_info;
 	struct delayed_work		sdio_open_work;
@@ -188,6 +189,11 @@
 		goto start_rx_end;
 	}
 
+	if (!port->sdio_open) {
+		pr_debug("%s: sdio is not open\n", __func__);
+		goto start_rx_end;
+	}
+
 	pool = &port->read_pool;
 	out = port->port_usb->out;
 
@@ -731,6 +737,7 @@
 		return;
 	}
 
+	port->ctrl_ch_err = 0;
 	ret = sdio_cmux_open(pi->ctrl_ch_id, 0, 0,
 			gsdio_ctrl_modem_status, port);
 	if (ret) {
@@ -767,6 +774,57 @@
 
 #define SDIO_CH_NAME_MAX_LEN	9
 #define SDIO_OPEN_DELAY		msecs_to_jiffies(10000)
+static int gsdio_ch_remove(struct platform_device *dev)
+{
+	struct gsdio_port	*port;
+	struct sdio_port_info	*pi;
+	int i;
+	unsigned long		flags;
+
+	pr_debug("%s: name:%s\n", __func__, dev->name);
+
+	for (i = 0; i < n_sdio_ports; i++) {
+		port = sdio_ports[i].port;
+		pi = port->sport_info;
+
+		if (!strncmp(pi->data_ch_name, dev->name,
+					SDIO_CH_NAME_MAX_LEN)) {
+			struct gserial *gser = port->port_usb;
+
+			port->sdio_open = 0;
+			port->sdio_probe = 0;
+			port->ctrl_ch_err = 1;
+
+			/* check if usb cable is connected */
+			if (!gser)
+				continue;
+
+			/* indicated call status to usb host */
+			gsdio_ctrl_modem_status(0, port);
+
+			usb_ep_fifo_flush(gser->in);
+			usb_ep_fifo_flush(gser->out);
+
+			cancel_work_sync(&port->push);
+			cancel_work_sync(&port->pull);
+
+			spin_lock_irqsave(&port->port_lock, flags);
+			gsdio_free_requests(gser->out, &port->read_pool);
+			gsdio_free_requests(gser->out, &port->read_queue);
+			gsdio_free_requests(gser->in, &port->write_pool);
+
+			port->rp_len = 0;
+			port->rq_len = 0;
+			port->wp_len = 0;
+			port->n_read = 0;
+			spin_unlock_irqrestore(&port->port_lock, flags);
+
+		}
+	}
+
+	return 0;
+}
+
 static int gsdio_ch_probe(struct platform_device *dev)
 {
 	struct gsdio_port	*port;
@@ -788,6 +846,7 @@
 		 */
 		if (!strncmp(pi->data_ch_name, dev->name,
 					SDIO_CH_NAME_MAX_LEN)) {
+			port->sdio_probe = 1;
 			queue_delayed_work(gsdio_wq,
 				&port->sdio_open_work, SDIO_OPEN_DELAY);
 			return 0;
@@ -834,6 +893,7 @@
 	pdriver = &sdio_ports[portno].gsdio_ch;
 
 	pdriver->probe = gsdio_ch_probe;
+	pdriver->remove = gsdio_ch_remove;
 	pdriver->driver.name = pi->data_ch_name;
 	pdriver->driver.owner = THIS_MODULE;
 
@@ -951,10 +1011,15 @@
 		size_t count, loff_t *ppos)
 {
 	struct gsdio_port *port;
-	char *buf = debug_buffer;
+	char *buf;
 	unsigned long flags;
 	int i = 0;
 	int temp = 0;
+	int ret;
+
+	buf = kzalloc(sizeof(char) * 1024, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
 
 	while (i < n_sdio_ports) {
 		port = sdio_ports[i].port;
@@ -968,17 +1033,24 @@
 				"read_pool_len:   %lu\n"
 				"read_queue_len:  %lu\n"
 				"write_pool_len:  %lu\n"
-				"n_read:          %u\n",
+				"n_read:          %u\n"
+				"sdio_open:       %d\n"
+				"sdio_probe:      %d\n",
 				i, port,
 				port->nbytes_tolaptop, port->nbytes_tomodem,
 				port->cbits_to_modem, port->cbits_to_laptop,
 				port->rp_len, port->rq_len, port->wp_len,
-				port->n_read);
+				port->n_read,
+				port->sdio_open, port->sdio_probe);
 		spin_unlock_irqrestore(&port->port_lock, flags);
 		i++;
 	}
 
-	return simple_read_from_buffer(ubuf, count, ppos, buf, temp);
+	ret = simple_read_from_buffer(ubuf, count, ppos, buf, temp);
+
+	kfree(buf);
+
+	return ret;
 }
 
 static ssize_t debug_sdio_reset_stats(struct file *file, const char __user *buf,