usb: smd: Always call smd_close upon cable disconnect
During subsystem restart, SMD_EVENT_CLOSE is sent via callback,
which the DUN and RMNET control modules mistakenly interpret as the
channel being closed automatically. This causes a problem when the
modem comes back online and notifies via SMD_EVENT_OPEN: the gadget
driver now has a handle to an open SMD channel even without a USB
connection. Then when the USB cable is reconnected, due to disconnect
being called first, it accidentally closes the re-opened channel,
causing the following smd_open() to fail because it is busy being
closed.
Instead, simply always call smd_close() on actual disconnect,
so that the subsequent connect will successfully be able to open the
channel.
CRs-fixed: 327710
Change-Id: Ia7b3e52638f9bdc96c083cb16e0231e7af7ff884
Signed-off-by: Jack Pham <jackp@codeaurora.org>
diff --git a/drivers/usb/gadget/u_rmnet_ctrl_smd.c b/drivers/usb/gadget/u_rmnet_ctrl_smd.c
index 90cbe54..edba510 100644
--- a/drivers/usb/gadget/u_rmnet_ctrl_smd.c
+++ b/drivers/usb/gadget/u_rmnet_ctrl_smd.c
@@ -412,10 +412,13 @@
spin_unlock_irqrestore(&port->port_lock, flags);
- if (test_bit(CH_OPENED, &c->flags)) {
- /* this should send the dtr zero */
+ if (test_and_clear_bit(CH_OPENED, &c->flags))
+ /* send dtr zero */
+ smd_tiocmset(c->ch, c->cbits_tomodem, ~c->cbits_tomodem);
+
+ if (c->ch) {
smd_close(c->ch);
- clear_bit(CH_OPENED, &c->flags);
+ c->ch = NULL;
}
}
@@ -464,7 +467,10 @@
if (!strncmp(c->name, pdev->name, SMD_CH_MAX_LEN)) {
clear_bit(CH_READY, &c->flags);
clear_bit(CH_OPENED, &c->flags);
- smd_close(c->ch);
+ if (c->ch) {
+ smd_close(c->ch);
+ c->ch = NULL;
+ }
break;
}
}
@@ -603,8 +609,8 @@
c->cbits_tomodem ? "HIGH" : "LOW",
test_bit(CH_OPENED, &c->flags),
test_bit(CH_READY, &c->flags),
- smd_read_avail(c->ch),
- smd_write_avail(c->ch));
+ c->ch ? smd_read_avail(c->ch) : 0,
+ c->ch ? smd_write_avail(c->ch) : 0);
spin_unlock_irqrestore(&port->port_lock, flags);
}
diff --git a/drivers/usb/gadget/u_smd.c b/drivers/usb/gadget/u_smd.c
index caccade..93a96be 100644
--- a/drivers/usb/gadget/u_smd.c
+++ b/drivers/usb/gadget/u_smd.c
@@ -711,17 +711,18 @@
port->n_read = 0;
spin_unlock_irqrestore(&port->port_lock, flags);
- if (!test_bit(CH_OPENED, &port->pi->flags))
- return;
+ if (test_and_clear_bit(CH_OPENED, &port->pi->flags)) {
+ /* lower the dtr */
+ port->cbits_to_modem = 0;
+ smd_tiocmset(port->pi->ch,
+ port->cbits_to_modem,
+ ~port->cbits_to_modem);
+ }
- /* lower the dtr */
- port->cbits_to_modem = 0;
- smd_tiocmset(port->pi->ch,
- port->cbits_to_modem,
- ~port->cbits_to_modem);
-
- smd_close(port->pi->ch);
- clear_bit(CH_OPENED, &port->pi->flags);
+ if (port->pi->ch) {
+ smd_close(port->pi->ch);
+ port->pi->ch = NULL;
+ }
}
#define SMD_CH_MAX_LEN 20
@@ -765,7 +766,10 @@
if (!strncmp(pi->name, pdev->name, SMD_CH_MAX_LEN)) {
clear_bit(CH_READY, &pi->flags);
clear_bit(CH_OPENED, &pi->flags);
- smd_close(pi->ch);
+ if (pi->ch) {
+ smd_close(pi->ch);
+ pi->ch = NULL;
+ }
break;
}
}
@@ -821,6 +825,7 @@
size_t count, loff_t *ppos)
{
struct gsmd_port *port;
+ struct smd_port_info *pi;
char *buf;
unsigned long flags;
int temp = 0;
@@ -833,6 +838,7 @@
for (i = 0; i < n_smd_ports; i++) {
port = smd_ports[i].port;
+ pi = port->pi;
spin_lock_irqsave(&port->port_lock, flags);
temp += scnprintf(buf + temp, 512 - temp,
"###PORT:%d###\n"
@@ -848,10 +854,10 @@
i, port->nbytes_tolaptop, port->nbytes_tomodem,
port->cbits_to_modem, port->cbits_to_laptop,
port->n_read,
- smd_read_avail(port->pi->ch),
- smd_write_avail(port->pi->ch),
- test_bit(CH_OPENED, &port->pi->flags),
- test_bit(CH_READY, &port->pi->flags));
+ pi->ch ? smd_read_avail(pi->ch) : 0,
+ pi->ch ? smd_write_avail(pi->ch) : 0,
+ test_bit(CH_OPENED, &pi->flags),
+ test_bit(CH_READY, &pi->flags));
spin_unlock_irqrestore(&port->port_lock, flags);
}