usb: rmnet: Add support for smd close event
smd driver can generate close event depending on modem status changes.
When close event is received, indicate data session end to usb host
and also free pending control messages.
Change-Id: If1af85c5c2f64cae6b82e5079a502c643e8a581a
Signed-off-by: Vamsi Krishna <vskrishn@codeaurora.org>
diff --git a/drivers/usb/gadget/f_rmnet.c b/drivers/usb/gadget/f_rmnet.c
index 2a61337..ebbd1d8 100644
--- a/drivers/usb/gadget/f_rmnet.c
+++ b/drivers/usb/gadget/f_rmnet.c
@@ -41,6 +41,7 @@
int ifc_id;
u8 port_num;
atomic_t online;
+ atomic_t ctrl_online;
struct usb_composite_dev *cdev;
spinlock_t lock;
@@ -297,6 +298,8 @@
static void frmnet_disable(struct usb_function *f)
{
struct f_rmnet *dev = func_to_rmnet(f);
+ unsigned long flags;
+ struct rmnet_ctrl_pkt *cpkt;
pr_debug("%s: port#%d\n", __func__, dev->port_num);
@@ -304,6 +307,17 @@
atomic_set(&dev->online, 0);
+ spin_lock_irqsave(&dev->lock, flags);
+ while (!list_empty(&dev->cpkt_resp_q)) {
+ cpkt = list_first_entry(&dev->cpkt_resp_q,
+ struct rmnet_ctrl_pkt, list);
+
+ list_del(&cpkt->list);
+ rmnet_free_ctrl_pkt(cpkt);
+ }
+ atomic_set(&dev->notify_count, 0);
+ spin_unlock_irqrestore(&dev->lock, flags);
+
gport_rmnet_disconnect(dev);
}
@@ -384,6 +398,73 @@
}
}
+static void frmnet_connect(struct grmnet *gr)
+{
+ struct f_rmnet *dev;
+
+ if (!gr) {
+ pr_err("%s: Invalid grmnet:%p\n", __func__, gr);
+ return;
+ }
+
+ dev = port_to_rmnet(gr);
+
+ atomic_set(&dev->ctrl_online, 1);
+}
+
+static void frmnet_disconnect(struct grmnet *gr)
+{
+ struct f_rmnet *dev;
+ unsigned long flags;
+ struct usb_cdc_notification *event;
+ int status;
+ struct rmnet_ctrl_pkt *cpkt;
+
+ if (!gr) {
+ pr_err("%s: Invalid grmnet:%p\n", __func__, gr);
+ return;
+ }
+
+ dev = port_to_rmnet(gr);
+
+ atomic_set(&dev->ctrl_online, 0);
+
+ if (!atomic_read(&dev->online)) {
+ pr_debug("%s: nothing to do\n", __func__);
+ return;
+ }
+
+ usb_ep_fifo_flush(dev->notify);
+
+ event = dev->notify_req->buf;
+ event->bmRequestType = USB_DIR_IN | USB_TYPE_CLASS
+ | USB_RECIP_INTERFACE;
+ event->bNotificationType = USB_CDC_NOTIFY_NETWORK_CONNECTION;
+ event->wValue = cpu_to_le16(0);
+ event->wIndex = cpu_to_le16(dev->ifc_id);
+ event->wLength = cpu_to_le16(0);
+
+ status = usb_ep_queue(dev->notify, dev->notify_req, GFP_KERNEL);
+ if (status < 0) {
+ if (!atomic_read(&dev->online))
+ return;
+ pr_err("%s: rmnet notify ep enqueue error %d\n",
+ __func__, status);
+ }
+
+ spin_lock_irqsave(&dev->lock, flags);
+ while (!list_empty(&dev->cpkt_resp_q)) {
+ cpkt = list_first_entry(&dev->cpkt_resp_q,
+ struct rmnet_ctrl_pkt, list);
+
+ list_del(&cpkt->list);
+ rmnet_free_ctrl_pkt(cpkt);
+ }
+ atomic_set(&dev->notify_count, 0);
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+}
+
static int
frmnet_send_cpkt_response(struct grmnet *gr, struct rmnet_ctrl_pkt *cpkt)
{
@@ -400,7 +481,7 @@
pr_debug("%s: dev:%p port#%d\n", __func__, dev, dev->port_num);
- if (!atomic_read(&dev->online)) {
+ if (!atomic_read(&dev->online) || !atomic_read(&dev->ctrl_online)) {
rmnet_free_ctrl_pkt(cpkt);
return 0;
}
@@ -459,6 +540,9 @@
pr_err("rmnet notify ep error %d\n", status);
/* FALLTHROUGH */
case 0:
+ if (!atomic_read(&dev->ctrl_online))
+ break;
+
if (atomic_dec_and_test(&dev->notify_count))
break;
@@ -706,6 +790,8 @@
f->set_alt = frmnet_set_alt;
f->setup = frmnet_setup;
dev->port.send_cpkt_response = frmnet_send_cpkt_response;
+ dev->port.disconnect = frmnet_disconnect;
+ dev->port.connect = frmnet_connect;
status = usb_add_function(c, f);
if (status) {
diff --git a/drivers/usb/gadget/u_rmnet.h b/drivers/usb/gadget/u_rmnet.h
index aeaddee..3c21316 100644
--- a/drivers/usb/gadget/u_rmnet.h
+++ b/drivers/usb/gadget/u_rmnet.h
@@ -48,6 +48,9 @@
void (*send_cbits_tomodem)(struct grmnet *g,
u8 port_num,
int cbits);
+
+ void (*disconnect)(struct grmnet *g);
+ void (*connect)(struct grmnet *g);
};
int gbam_setup(unsigned int count);
diff --git a/drivers/usb/gadget/u_rmnet_ctrl_smd.c b/drivers/usb/gadget/u_rmnet_ctrl_smd.c
index b4d2828..fc159cc 100644
--- a/drivers/usb/gadget/u_rmnet_ctrl_smd.c
+++ b/drivers/usb/gadget/u_rmnet_ctrl_smd.c
@@ -273,6 +273,8 @@
{
struct rmnet_ctrl_port *port = p;
struct smd_ch_info *c = &port->ctrl_ch;
+ struct rmnet_ctrl_pkt *cpkt;
+ unsigned long flags;
pr_debug("%s: EVENT_(%s)\n", __func__, get_smd_event(event));
@@ -285,10 +287,27 @@
break;
case SMD_EVENT_OPEN:
set_bit(CH_OPENED, &c->flags);
- wake_up(&c->wait);
+
+ if (port && port->port_usb && port->port_usb->connect)
+ port->port_usb->connect(port->port_usb);
+
break;
case SMD_EVENT_CLOSE:
clear_bit(CH_OPENED, &c->flags);
+
+ if (port && port->port_usb && port->port_usb->disconnect)
+ port->port_usb->disconnect(port->port_usb);
+
+ spin_lock_irqsave(&port->port_lock, flags);
+ while (!list_empty(&c->tx_q)) {
+ cpkt = list_first_entry(&c->tx_q,
+ struct rmnet_ctrl_pkt, list);
+
+ list_del(&cpkt->list);
+ free_rmnet_ctrl_pkt(cpkt);
+ }
+ spin_unlock_irqrestore(&port->port_lock, flags);
+
break;
}
}
@@ -357,6 +376,7 @@
struct rmnet_ctrl_port *port;
unsigned long flags;
struct smd_ch_info *c;
+ struct rmnet_ctrl_pkt *cpkt;
pr_debug("%s: grmnet:%p port#%d\n", __func__, gr, port_num);
@@ -378,6 +398,14 @@
gr->send_cpkt_request = 0;
gr->send_cbits_tomodem = 0;
c->cbits_tomodem = 0;
+
+ while (!list_empty(&c->tx_q)) {
+ cpkt = list_first_entry(&c->tx_q, struct rmnet_ctrl_pkt, list);
+
+ list_del(&cpkt->list);
+ free_rmnet_ctrl_pkt(cpkt);
+ }
+
spin_unlock_irqrestore(&port->port_lock, flags);
if (test_bit(CH_OPENED, &c->flags)) {