usb: gsmd: Add support for SMD close event
SMD can generate close event when modem sub-system restart. Clean
USB buffers and drop the carrier detect during smd close.
Signed-off-by: Vamsi Krishna <vskrishn@codeaurora.org>
diff --git a/drivers/usb/gadget/u_smd.c b/drivers/usb/gadget/u_smd.c
index 3c91a47..bdcbdd7 100644
--- a/drivers/usb/gadget/u_smd.c
+++ b/drivers/usb/gadget/u_smd.c
@@ -45,7 +45,6 @@
struct smd_channel *ch;
char *name;
unsigned long flags;
- wait_queue_head_t wait;
};
struct smd_port_info smd_pi[SMD_N_PORTS] = {
@@ -184,7 +183,7 @@
pool = &port->read_pool;
out = port->port_usb->out;
- while (!list_empty(pool)) {
+ while (test_bit(CH_OPENED, &port->pi->flags) && !list_empty(pool)) {
struct usb_request *req;
req = list_entry(pool->next, struct usb_request, list);
@@ -355,7 +354,6 @@
static void gsmd_read_complete(struct usb_ep *ep, struct usb_request *req)
{
struct gsmd_port *port = ep->driver_data;
- unsigned long flags;
pr_debug("%s: ep:%p port:%p\n", __func__, ep, port);
@@ -364,10 +362,16 @@
return;
}
- spin_lock_irqsave(&port->port_lock, flags);
+ spin_lock(&port->port_lock);
+ if (!test_bit(CH_OPENED, &port->pi->flags)) {
+ gsmd_free_req(ep, req);
+ spin_unlock(&port->port_lock);
+ return;
+ }
+
list_add_tail(&req->list, &port->read_queue);
queue_work(gsmd_wq, &port->push);
- spin_unlock_irqrestore(&port->port_lock, flags);
+ spin_unlock(&port->port_lock);
return;
}
@@ -375,7 +379,6 @@
static void gsmd_write_complete(struct usb_ep *ep, struct usb_request *req)
{
struct gsmd_port *port = ep->driver_data;
- unsigned long flags;
pr_debug("%s: ep:%p port:%p\n", __func__, ep, port);
@@ -384,7 +387,13 @@
return;
}
- spin_lock_irqsave(&port->port_lock, flags);
+ spin_lock(&port->port_lock);
+ if (!test_bit(CH_OPENED, &port->pi->flags)) {
+ gsmd_free_req(ep, req);
+ spin_unlock(&port->port_lock);
+ return;
+ }
+
list_add(&req->list, &port->write_pool);
switch (req->status) {
@@ -403,8 +412,7 @@
gsmd_free_req(ep, req);
break;
}
-
- spin_unlock_irqrestore(&port->port_lock, flags);
+ spin_unlock(&port->port_lock);
return;
}
@@ -412,15 +420,18 @@
static void gsmd_start_io(struct gsmd_port *port)
{
int ret = -ENODEV;
- unsigned long flags;
pr_debug("%s: port: %p\n", __func__, port);
- spin_lock_irqsave(&port->port_lock, flags);
+ spin_lock(&port->port_lock);
if (!port->port_usb)
goto start_io_out;
+ smd_tiocmset_from_cb(port->pi->ch,
+ port->cbits_to_modem,
+ ~port->cbits_to_modem);
+
ret = gsmd_alloc_requests(port->port_usb->out,
&port->read_pool,
SMD_RX_QUEUE_SIZE, SMD_RX_BUF_SIZE,
@@ -443,7 +454,7 @@
}
start_io_out:
- spin_unlock_irqrestore(&port->port_lock, flags);
+ spin_unlock(&port->port_lock);
if (ret)
return;
@@ -483,6 +494,42 @@
return uart_sig;
}
+
+static void gsmd_stop_io(struct gsmd_port *port)
+{
+ struct usb_ep *in;
+ struct usb_ep *out;
+ unsigned long flags;
+
+ spin_lock_irqsave(&port->port_lock, flags);
+ if (!port->port_usb) {
+ spin_unlock_irqrestore(&port->port_lock, flags);
+ return;
+ }
+ in = port->port_usb->in;
+ out = port->port_usb->out;
+ spin_unlock_irqrestore(&port->port_lock, flags);
+
+ usb_ep_fifo_flush(in);
+ usb_ep_fifo_flush(out);
+
+ spin_lock(&port->port_lock);
+ if (port->port_usb) {
+ gsmd_free_requests(out, &port->read_pool);
+ gsmd_free_requests(out, &port->read_queue);
+ gsmd_free_requests(in, &port->write_pool);
+ port->n_read = 0;
+ port->cbits_to_laptop = 0;
+ }
+
+ if (port->port_usb->send_modem_ctrl_bits)
+ port->port_usb->send_modem_ctrl_bits(
+ port->port_usb,
+ port->cbits_to_laptop);
+ spin_unlock(&port->port_lock);
+
+}
+
static void gsmd_notify(void *priv, unsigned event)
{
struct gsmd_port *port = priv;
@@ -500,11 +547,12 @@
case SMD_EVENT_OPEN:
pr_debug("%s: Event Open\n", __func__);
set_bit(CH_OPENED, &pi->flags);
- wake_up(&pi->wait);
+ gsmd_start_io(port);
break;
case SMD_EVENT_CLOSE:
pr_debug("%s: Event Close\n", __func__);
clear_bit(CH_OPENED, &pi->flags);
+ gsmd_stop_io(port);
break;
case SMD_EVENT_STATUS:
i = smd_tiocmget(port->pi->ch);
@@ -521,7 +569,6 @@
struct gsmd_port *port;
struct smd_port_info *pi;
int ret;
- unsigned long flags;
port = container_of(w, struct gsmd_port, connect_work);
pi = port->pi;
@@ -533,24 +580,11 @@
ret = smd_named_open_on_edge(pi->name, SMD_APPS_MODEM,
&pi->ch, port, gsmd_notify);
-
if (ret) {
pr_err("%s: unable to open smd port:%s err:%d\n",
__func__, pi->name, ret);
return;
}
-
- wait_event(pi->wait, test_bit(CH_OPENED, &pi->flags));
-
- spin_lock_irqsave(&port->port_lock, flags);
- /* update usb control signals to modem */
- if (port->port_usb && port->cbits_to_modem)
- smd_tiocmset(port->pi->ch,
- port->cbits_to_modem,
- ~port->cbits_to_modem);
- spin_unlock_irqrestore(&port->port_lock, flags);
-
- gsmd_start_io(port);
}
static void gsmd_notify_modem(struct gserial *gser, u8 portno, int ctrl_bits)
@@ -775,7 +809,6 @@
INIT_WORK(&port->pull, gsmd_tx_pull);
INIT_WORK(&port->connect_work, gsmd_connect_work);
- init_waitqueue_head(&port->pi->wait);
smd_ports[portno].port = port;
pdrv = &smd_ports[portno].pdrv;