usb: gadget: u_bam_data: Add support for BAM reset
This change adds the capability for f_qc_rndis, f_qc_ecm and
f_mbim running in BAM-to-BAM mode, to perform USB BAM reset,
upon a peer BAM reset.
The u_bam_data registers a callback from USB BAM driver, to be
notified about peer BAM reset. When the callback is invoked,
USB BAM reset is executed.
Change-Id: Ia7d8f4a5bbef78c83e0ca0891511547001df29a9
CRs-Fixed: 424867
Signed-off-by: Amit Blay <ablay@codeaurora.org>
diff --git a/drivers/usb/gadget/f_mbim.c b/drivers/usb/gadget/f_mbim.c
index 98c6dbc..16a40dd 100644
--- a/drivers/usb/gadget/f_mbim.c
+++ b/drivers/usb/gadget/f_mbim.c
@@ -659,7 +659,7 @@
pr_info("dev:%p port:%d. Do nothing.\n",
dev, dev->port_num);
- /* bam_data_disconnect(&dev->bam_port, dev->port_num); */
+ bam_data_disconnect(&dev->bam_port, dev->port_num);
return 0;
}
@@ -1273,7 +1273,6 @@
if (mbim->bam_port.in->driver_data) {
pr_info("reset mbim\n");
mbim_reset_values(mbim);
- mbim_bam_disconnect(mbim);
}
/*
diff --git a/drivers/usb/gadget/f_qc_ecm.c b/drivers/usb/gadget/f_qc_ecm.c
index 0b41197..b1c5985 100644
--- a/drivers/usb/gadget/f_qc_ecm.c
+++ b/drivers/usb/gadget/f_qc_ecm.c
@@ -324,8 +324,9 @@
static int ecm_qc_bam_disconnect(struct f_ecm_qc *dev)
{
- pr_debug("dev:%p. %s Do nothing.\n",
- dev, __func__);
+ pr_debug("dev:%p. %s Disconnect BAM.\n", dev, __func__);
+
+ bam_data_disconnect(&ecm_qc_bam_port, 0);
return 0;
}
@@ -518,8 +519,12 @@
if (ecm->port.in_ep->driver_data) {
DBG(cdev, "reset ecm\n");
- gether_qc_disconnect_name(&ecm->port, "ecm0");
+ /* ecm->port is needed for disconnecting the BAM data
+ * path. Only after the BAM data path is disconnected,
+ * we can disconnect the port from the network layer.
+ */
ecm_qc_bam_disconnect(ecm);
+ gether_qc_disconnect_name(&ecm->port, "ecm0");
}
if (!ecm->port.in_ep->desc ||
@@ -591,8 +596,8 @@
DBG(cdev, "ecm deactivated\n");
if (ecm->port.in_ep->driver_data) {
- gether_qc_disconnect_name(&ecm->port, "ecm0");
ecm_qc_bam_disconnect(ecm);
+ gether_qc_disconnect_name(&ecm->port, "ecm0");
}
if (ecm->notify->driver_data) {
diff --git a/drivers/usb/gadget/f_qc_rndis.c b/drivers/usb/gadget/f_qc_rndis.c
index b632b54..0d229dc 100644
--- a/drivers/usb/gadget/f_qc_rndis.c
+++ b/drivers/usb/gadget/f_qc_rndis.c
@@ -441,8 +441,9 @@
static int rndis_qc_bam_disconnect(struct f_rndis_qc *dev)
{
- pr_info("dev:%p. %s Do nothing.\n",
- dev, __func__);
+ pr_debug("dev:%p. %s Disconnect BAM.\n", dev, __func__);
+
+ bam_data_disconnect(&dev->bam_port, 0);
return 0;
}
@@ -674,8 +675,12 @@
if (rndis->port.in_ep->driver_data) {
DBG(cdev, "reset rndis\n");
- gether_qc_disconnect_name(&rndis->port, "rndis0");
+ /* rndis->port is needed for disconnecting the BAM data
+ * path. Only after the BAM data path is disconnected,
+ * we can disconnect the port from the network layer.
+ */
rndis_qc_bam_disconnect(rndis);
+ gether_qc_disconnect_name(&rndis->port, "rndis0");
}
if (!rndis->port.in_ep->desc || !rndis->port.out_ep->desc) {
@@ -735,8 +740,8 @@
pr_info("rndis deactivated\n");
rndis_uninit(rndis->config);
- gether_qc_disconnect_name(&rndis->port, "rndis0");
rndis_qc_bam_disconnect(rndis);
+ gether_qc_disconnect_name(&rndis->port, "rndis0");
usb_ep_disable(rndis->notify);
rndis->notify->driver_data = NULL;
diff --git a/drivers/usb/gadget/u_bam_data.c b/drivers/usb/gadget/u_bam_data.c
index 32f683e..3cc0419 100644
--- a/drivers/usb/gadget/u_bam_data.c
+++ b/drivers/usb/gadget/u_bam_data.c
@@ -73,7 +73,7 @@
{
int status = req->status;
- pr_info("status: %d\n", status);
+ pr_debug("%s: status: %d\n", __func__, status);
}
static void bam_data_endless_tx_complete(struct usb_ep *ep,
@@ -81,7 +81,7 @@
{
int status = req->status;
- pr_info("status: %d\n", status);
+ pr_debug("%s: status: %d\n", __func__, status);
}
static void bam_data_start_endless_rx(struct bam_data_port *port)
@@ -89,6 +89,9 @@
struct bam_data_ch_info *d = &port->data_ch;
int status;
+ if (!port->port_usb)
+ return;
+
status = usb_ep_queue(port->port_usb->out, d->rx_req, GFP_ATOMIC);
if (status)
pr_err("error enqueuing transfer, %d\n", status);
@@ -99,32 +102,77 @@
struct bam_data_ch_info *d = &port->data_ch;
int status;
+ if (!port->port_usb)
+ return;
+
status = usb_ep_queue(port->port_usb->in, d->tx_req, GFP_ATOMIC);
if (status)
pr_err("error enqueuing transfer, %d\n", status);
}
-static void bam2bam_data_disconnect_work(struct work_struct *w)
+static int bam_data_peer_reset_cb(void *param)
{
- struct bam_data_port *port =
- container_of(w, struct bam_data_port, disconnect_w);
+ struct bam_data_port *port = (struct bam_data_port *)param;
+ struct bam_data_ch_info *d;
+ int ret;
+ bool reenable_eps = false;
- pr_info("Enter");
+ d = &port->data_ch;
- /* disable endpoints */
- if (!port->port_usb || !port->port_usb->out || !port->port_usb->in) {
- pr_err("port_usb->out/in == NULL. Exit");
- return;
+ pr_debug("%s: reset by peer\n", __func__);
+
+ /* Disable the relevant EPs if currently EPs are enabled */
+ if (port->port_usb && port->port_usb->in &&
+ port->port_usb->in->driver_data) {
+ usb_ep_disable(port->port_usb->out);
+ usb_ep_disable(port->port_usb->in);
+
+ port->port_usb->in->driver_data = NULL;
+ port->port_usb->out->driver_data = NULL;
+ reenable_eps = true;
}
- usb_ep_disable(port->port_usb->out);
- usb_ep_disable(port->port_usb->in);
- port->port_usb->in->driver_data = NULL;
- port->port_usb->out->driver_data = NULL;
+ /* Disable BAM */
+ msm_hw_bam_disable(1);
- port->port_usb = 0;
+ /* Reset BAM */
+ ret = usb_bam_reset();
+ if (ret) {
+ pr_err("%s: BAM reset failed %d\n", __func__, ret);
+ goto reenable_eps;
+ }
- pr_info("Exit");
+ /* Enable BAM */
+ msm_hw_bam_disable(0);
+
+reenable_eps:
+ /* Re-Enable the relevant EPs, if EPs were originally enabled */
+ if (reenable_eps) {
+ ret = usb_ep_enable(port->port_usb->in);
+ if (ret) {
+ pr_err("%s: usb_ep_enable failed eptype:IN ep:%p",
+ __func__, port->port_usb->in);
+ return ret;
+ }
+ port->port_usb->in->driver_data = port;
+
+ ret = usb_ep_enable(port->port_usb->out);
+ if (ret) {
+ pr_err("%s: usb_ep_enable failed eptype:OUT ep:%p",
+ __func__, port->port_usb->out);
+ port->port_usb->in->driver_data = 0;
+ return ret;
+ }
+ port->port_usb->out->driver_data = port;
+
+ bam_data_start_endless_rx(port);
+ bam_data_start_endless_tx(port);
+ }
+
+ /* Unregister the peer reset callback */
+ usb_bam_register_peer_reset_cb(d->connection_idx, NULL, NULL);
+
+ return 0;
}
static void bam2bam_data_connect_work(struct work_struct *w)
@@ -135,7 +183,7 @@
u32 sps_params;
int ret;
- pr_info("Enter");
+ pr_debug("%s: Connect workqueue started", __func__);
ret = usb_bam_connect(d->connection_idx, &d->src_pipe_idx,
&d->dst_pipe_idx);
@@ -182,7 +230,18 @@
bam_data_start_endless_rx(port);
bam_data_start_endless_tx(port);
- pr_info("Done\n");
+ /* Register for peer reset callback */
+ usb_bam_register_peer_reset_cb(d->connection_idx,
+ bam_data_peer_reset_cb, port);
+
+ ret = usb_bam_client_ready(true);
+ if (ret) {
+ pr_err("%s: usb_bam_client_ready failed: err:%d\n",
+ __func__, ret);
+ return;
+ }
+
+ pr_debug("%s: Connect workqueue done", __func__);
}
static void bam2bam_data_port_free(int portno)
@@ -203,14 +262,13 @@
port->port_num = portno;
INIT_WORK(&port->connect_w, bam2bam_data_connect_work);
- INIT_WORK(&port->disconnect_w, bam2bam_data_disconnect_work);
/* data ch */
d = &port->data_ch;
d->port = port;
bam2bam_data_ports[portno] = port;
- pr_info("port:%p portno:%d\n", port, portno);
+ pr_debug("port:%p portno:%d\n", port, portno);
return 0;
}
@@ -218,9 +276,8 @@
void bam_data_disconnect(struct data_port *gr, u8 port_num)
{
struct bam_data_port *port;
- struct bam_data_ch_info *d;
- pr_info("dev:%p port#%d\n", gr, port_num);
+ pr_debug("dev:%p port#%d\n", gr, port_num);
if (port_num >= n_bam2bam_data_ports) {
pr_err("invalid bam2bam portno#%d\n", port_num);
@@ -234,10 +291,20 @@
port = bam2bam_data_ports[port_num];
- d = &port->data_ch;
- port->port_usb = gr;
+ if (port->port_usb && port->port_usb->in &&
+ port->port_usb->in->driver_data) {
+ /* disable endpoints */
+ usb_ep_disable(port->port_usb->out);
+ usb_ep_disable(port->port_usb->in);
- queue_work(bam_data_wq, &port->disconnect_w);
+ port->port_usb->in->driver_data = NULL;
+ port->port_usb->out->driver_data = NULL;
+
+ port->port_usb = 0;
+ }
+
+ if (usb_bam_client_ready(false))
+ pr_err("%s: usb_bam_client_ready failed\n", __func__);
}
int bam_data_connect(struct data_port *gr, u8 port_num,
@@ -247,7 +314,7 @@
struct bam_data_ch_info *d;
int ret;
- pr_info("dev:%p port#%d\n", gr, port_num);
+ pr_debug("dev:%p port#%d\n", gr, port_num);
if (port_num >= n_bam2bam_data_ports) {
pr_err("invalid portno#%d\n", port_num);
@@ -292,7 +359,7 @@
int i;
int ret;
- pr_info("requested %d BAM2BAM ports", no_bam2bam_port);
+ pr_debug("requested %d BAM2BAM ports", no_bam2bam_port);
if (!no_bam2bam_port || no_bam2bam_port > BAM2BAM_DATA_N_PORTS) {
pr_err("Invalid num of ports count:%d\n", no_bam2bam_port);
@@ -331,7 +398,7 @@
struct bam_data_port *port = (struct bam_data_port *)param;
struct data_port *d_port = port->port_usb;
- pr_info("%s: woken up by peer\n", __func__);
+ pr_debug("%s: woken up by peer\n", __func__);
if (!d_port) {
pr_err("FAILED: d_port == NULL");
@@ -360,7 +427,7 @@
port = bam2bam_data_ports[port_num];
d = &port->data_ch;
- pr_info("%s: suspended port %d\n", __func__, port_num);
+ pr_debug("%s: suspended port %d\n", __func__, port_num);
usb_bam_register_wake_cb(d->connection_idx, bam_data_wake_cb, port);
}
@@ -373,7 +440,7 @@
port = bam2bam_data_ports[port_num];
d = &port->data_ch;
- pr_info("%s: resumed port %d\n", __func__, port_num);
+ pr_debug("%s: resumed port %d\n", __func__, port_num);
usb_bam_register_wake_cb(d->connection_idx, NULL, NULL);
}