summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinux Build Service Account <lnxbuild@localhost>2016-10-06 01:07:13 -0700
committerGerrit - the friendly Code Review server <code-review@localhost>2016-10-06 01:07:13 -0700
commit4310d71096ee774b0e645ec1aa7b008ed5f748bb (patch)
tree9a007fd5f269b9b2248e8eb6ddffd31d29fb0609
parent7b6f74a97af7f9ee5e5d8e869fe9656516856b99 (diff)
parent5f7a2a2a57726b9ed2744e816228ff48ded7271f (diff)
Merge "usb: gadget: f_qc_rndis: Add RNDIS support using IPA over BAM2BAM"
-rw-r--r--drivers/usb/gadget/function/f_qc_rndis.c374
-rw-r--r--drivers/usb/gadget/function/rndis.c28
-rw-r--r--drivers/usb/gadget/function/rndis.h6
-rw-r--r--drivers/usb/gadget/function/u_data_ipa.c642
-rw-r--r--drivers/usb/gadget/function/u_data_ipa.h63
5 files changed, 759 insertions, 354 deletions
diff --git a/drivers/usb/gadget/function/f_qc_rndis.c b/drivers/usb/gadget/function/f_qc_rndis.c
index bc319d4fe16c..fe7dc76a8157 100644
--- a/drivers/usb/gadget/function/f_qc_rndis.c
+++ b/drivers/usb/gadget/function/f_qc_rndis.c
@@ -31,9 +31,8 @@
#include <linux/atomic.h>
#include "u_ether.h"
-#include "u_qc_ether.h"
#include "rndis.h"
-#include "u_bam_data.h"
+#include "u_data_ipa.h"
#include <linux/rndis_ipa.h>
unsigned int rndis_dl_max_xfer_size = 9216;
@@ -86,7 +85,7 @@ MODULE_PARM_DESC(rndis_dl_max_xfer_size,
*/
struct f_rndis_qc {
- struct qc_gether port;
+ struct usb_function func;
u8 ctrl_id, data_id;
u8 ethaddr[ETH_ALEN];
u32 vendorID;
@@ -94,27 +93,27 @@ struct f_rndis_qc {
u8 pkt_alignment_factor;
u32 max_pkt_size;
const char *manufacturer;
- int config;
+ struct rndis_params *params;
atomic_t ioctl_excl;
atomic_t open_excl;
struct usb_ep *notify;
struct usb_request *notify_req;
atomic_t notify_count;
- struct data_port bam_port;
- enum transport_type xport;
+ struct gadget_ipa_port bam_port;
u8 port_num;
+ u16 cdc_filter;
bool net_ready_trigger;
};
static struct ipa_usb_init_params rndis_ipa_params;
static spinlock_t rndis_lock;
static bool rndis_ipa_supported;
-static void rndis_qc_open(struct qc_gether *geth);
+static void rndis_qc_open(struct f_rndis_qc *rndis);
static inline struct f_rndis_qc *func_to_rndis_qc(struct usb_function *f)
{
- return container_of(f, struct f_rndis_qc, port.func);
+ return container_of(f, struct f_rndis_qc, func);
}
/* peak (theoretical) bulk transfer rate in bits-per-second */
@@ -322,10 +321,20 @@ static struct usb_endpoint_descriptor rndis_qc_ss_notify_desc = {
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_INT,
- .wMaxPacketSize = cpu_to_le16(STATUS_BYTECOUNT),
+ .wMaxPacketSize = cpu_to_le16(RNDIS_QC_STATUS_BYTECOUNT),
.bInterval = RNDIS_QC_LOG2_STATUS_INTERVAL_MSEC + 4,
};
+static struct usb_ss_ep_comp_descriptor ss_intr_comp_desc = {
+ .bLength = sizeof(ss_intr_comp_desc),
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+
+ /* the following 3 values can be tweaked if necessary */
+ /* .bMaxBurst = 0, */
+ /* .bmAttributes = 0, */
+ .wBytesPerInterval = cpu_to_le16(RNDIS_QC_STATUS_BYTECOUNT),
+};
+
static struct usb_ss_ep_comp_descriptor rndis_qc_ss_intr_comp_desc = {
.bLength = sizeof(ss_intr_comp_desc),
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
@@ -333,7 +342,16 @@ static struct usb_ss_ep_comp_descriptor rndis_qc_ss_intr_comp_desc = {
/* the following 3 values can be tweaked if necessary */
/* .bMaxBurst = 0, */
/* .bmAttributes = 0, */
- .wBytesPerInterval = cpu_to_le16(STATUS_BYTECOUNT),
+ .wBytesPerInterval = cpu_to_le16(RNDIS_QC_STATUS_BYTECOUNT),
+};
+
+static struct usb_ss_ep_comp_descriptor ss_bulk_comp_desc = {
+ .bLength = sizeof(ss_bulk_comp_desc),
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+
+ /* the following 2 values can be tweaked if necessary */
+ /* .bMaxBurst = 0, */
+ /* .bmAttributes = 0, */
};
static struct usb_endpoint_descriptor rndis_qc_ss_in_desc = {
@@ -407,7 +425,7 @@ struct f_rndis_qc *_rndis_qc;
static inline int rndis_qc_lock(atomic_t *excl)
{
- if (atomic_inc_return(excl) == 1) {
+ if (atomic_inc_return(excl) == 1)
return 0;
atomic_dec(excl);
@@ -421,46 +439,6 @@ static inline void rndis_qc_unlock(atomic_t *excl)
/*-------------------------------------------------------------------------*/
-static struct sk_buff *rndis_qc_add_header(struct qc_gether *port,
- struct sk_buff *skb)
-{
- struct sk_buff *skb2;
-
- skb2 = skb_realloc_headroom(skb, sizeof(struct rndis_packet_msg_type));
- if (skb2)
- rndis_add_hdr(skb2);
-
- dev_kfree_skb_any(skb);
- return skb2;
-}
-
-int rndis_qc_rm_hdr(struct qc_gether *port,
- struct sk_buff *skb,
- struct sk_buff_head *list)
-{
- /* tmp points to a struct rndis_packet_msg_type */
- __le32 *tmp = (void *)skb->data;
-
- /* MessageType, MessageLength */
- if (cpu_to_le32(RNDIS_MSG_PACKET)
- != get_unaligned(tmp++)) {
- dev_kfree_skb_any(skb);
- return -EINVAL;
- }
- tmp++;
-
- /* DataOffset, DataLength */
- if (!skb_pull(skb, get_unaligned_le32(tmp++) + 8)) {
- dev_kfree_skb_any(skb);
- return -EOVERFLOW;
- }
- skb_trim(skb, get_unaligned_le32(tmp++));
-
- skb_queue_tail(list, skb);
- return 0;
-}
-
-
static void rndis_qc_response_available(void *_rndis)
{
struct f_rndis_qc *rndis = _rndis;
@@ -496,12 +474,12 @@ static void rndis_qc_response_complete(struct usb_ep *ep,
int status = req->status;
struct usb_composite_dev *cdev;
- if (!rndis->port.func.config || !rndis->port.func.config->cdev) {
+ if (!rndis->func.config || !rndis->func.config->cdev) {
pr_err("%s(): cdev or config is NULL.\n", __func__);
return;
}
- cdev = rndis->port.func.config->cdev;
+ cdev = rndis->func.config->cdev;
/* after TX:
* - USB_CDC_GET_ENCAPSULATED_RESPONSE (ep0/control)
* - RNDIS_RESPONSE_AVAILABLE (status/irq)
@@ -544,7 +522,7 @@ static void rndis_qc_command_complete(struct usb_ep *ep,
u32 ul_max_xfer_size, dl_max_xfer_size;
/* received RNDIS command from USB_CDC_SEND_ENCAPSULATED_COMMAND */
- status = rndis_msg_parser(rndis->config, (u8 *) req->buf);
+ status = rndis_msg_parser(rndis->params, (u8 *) req->buf);
if (status < 0)
pr_err("RNDIS command error %d, %d/%d\n",
status, req->actual, req->length);
@@ -552,8 +530,8 @@ static void rndis_qc_command_complete(struct usb_ep *ep,
buf = (rndis_init_msg_type *)req->buf;
if (buf->MessageType == RNDIS_MSG_INIT) {
- ul_max_xfer_size = rndis_get_ul_max_xfer_size(rndis->config);
- u_bam_data_set_ul_max_xfer_size(ul_max_xfer_size);
+ ul_max_xfer_size = rndis_get_ul_max_xfer_size(rndis->params);
+ ipa_data_set_ul_max_xfer_size(ul_max_xfer_size);
/*
* For consistent data throughput from IPA, it is required to
* fine tune aggregation byte limit as 7KB. RNDIS IPA driver
@@ -565,11 +543,11 @@ static void rndis_qc_command_complete(struct usb_ep *ep,
*/
if (rndis_dl_max_xfer_size)
dl_max_xfer_size = min_t(u32, rndis_dl_max_xfer_size,
- rndis_get_dl_max_xfer_size(rndis->config));
+ rndis_get_dl_max_xfer_size(rndis->params));
else
dl_max_xfer_size =
- rndis_get_dl_max_xfer_size(rndis->config);
- u_bam_data_set_dl_max_xfer_size(dl_max_xfer_size);
+ rndis_get_dl_max_xfer_size(rndis->params);
+ ipa_data_set_dl_max_xfer_size(dl_max_xfer_size);
}
}
@@ -612,11 +590,11 @@ rndis_qc_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
u32 n;
/* return the result */
- buf = rndis_get_next_response(rndis->config, &n);
+ buf = rndis_get_next_response(rndis->params, &n);
if (buf) {
memcpy(req->buf, buf, n);
req->complete = rndis_qc_response_complete;
- rndis_free_response(rndis->config, buf);
+ rndis_free_response(rndis->params, buf);
value = n;
}
/* else stalls ... spec says to avoid that */
@@ -647,11 +625,30 @@ invalid:
return value;
}
+struct net_device *rndis_qc_get_net(const char *netname)
+{
+ struct net_device *net_dev;
+
+ net_dev = dev_get_by_name(&init_net, netname);
+ if (!net_dev)
+ return ERR_PTR(-EINVAL);
+
+ /*
+ * Decrement net_dev refcount as it was incremented in
+ * dev_get_by_name().
+ */
+ dev_put(net_dev);
+ return net_dev;
+}
static int rndis_qc_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
{
struct f_rndis_qc *rndis = func_to_rndis_qc(f);
struct usb_composite_dev *cdev = f->config->cdev;
+ u8 src_connection_idx;
+ u8 dst_connection_idx;
+ enum usb_ctrl usb_bam_type;
+ int ret;
/* we know alt == 0 */
@@ -672,35 +669,28 @@ static int rndis_qc_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
struct net_device *net;
rndis->net_ready_trigger = false;
- if (rndis->port.in_ep->driver_data) {
+ if (rndis->bam_port.in->driver_data) {
DBG(cdev, "reset rndis\n");
- /* rndis->port is needed for disconnecting the BAM data
+ /* bam_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.
*/
- bam_data_disconnect(&rndis->bam_port, USB_FUNC_RNDIS,
- rndis->port_num);
-
- if (rndis->xport != USB_GADGET_XPORT_BAM2BAM_IPA)
- gether_qc_disconnect_name(&rndis->port,
- "rndis0");
+ ipa_data_disconnect(&rndis->bam_port,
+ USB_IPA_FUNC_RNDIS);
}
- if (!rndis->port.in_ep->desc || !rndis->port.out_ep->desc) {
+ if (!rndis->bam_port.in->desc || !rndis->bam_port.out->desc) {
DBG(cdev, "init rndis\n");
if (config_ep_by_speed(cdev->gadget, f,
- rndis->port.in_ep) ||
+ rndis->bam_port.in) ||
config_ep_by_speed(cdev->gadget, f,
- rndis->port.out_ep)) {
- rndis->port.in_ep->desc = NULL;
- rndis->port.out_ep->desc = NULL;
+ rndis->bam_port.out)) {
+ rndis->bam_port.in->desc = NULL;
+ rndis->bam_port.out->desc = NULL;
goto fail;
}
}
- /* Avoid ZLPs; they can be troublesome. */
- rndis->port.is_zlp_ok = false;
-
/* RNDIS should be in the "RNDIS uninitialized" state,
* either never activated or after rndis_uninit().
*
@@ -713,30 +703,36 @@ static int rndis_qc_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
* very long time. We need another call to the link layer
* code -- gether_updown(...bool) maybe -- to do it right.
*/
- rndis->port.cdc_filter = 0;
+ rndis->cdc_filter = 0;
rndis->bam_port.cdev = cdev;
- rndis->bam_port.func = &rndis->port.func;
- rndis->bam_port.in = rndis->port.in_ep;
- rndis->bam_port.out = rndis->port.out_ep;
-
- if (bam_data_connect(&rndis->bam_port, rndis->xport,
- rndis->port_num, USB_FUNC_RNDIS))
+ rndis->bam_port.func = &rndis->func;
+ ipa_data_port_select(USB_IPA_FUNC_RNDIS);
+ usb_bam_type = usb_bam_get_bam_type(cdev->gadget->name);
+
+ src_connection_idx = usb_bam_get_connection_idx(usb_bam_type,
+ IPA_P_BAM, USB_TO_PEER_PERIPHERAL, USB_BAM_DEVICE,
+ rndis->port_num);
+ dst_connection_idx = usb_bam_get_connection_idx(usb_bam_type,
+ IPA_P_BAM, PEER_PERIPHERAL_TO_USB, USB_BAM_DEVICE,
+ rndis->port_num);
+ if (src_connection_idx < 0 || dst_connection_idx < 0) {
+ pr_err("%s: usb_bam_get_connection_idx failed\n",
+ __func__);
+ return ret;
+ }
+ if (ipa_data_connect(&rndis->bam_port, USB_IPA_FUNC_RNDIS,
+ src_connection_idx, dst_connection_idx))
goto fail;
DBG(cdev, "RNDIS RX/TX early activation ...\n");
- if (rndis->xport != USB_GADGET_XPORT_BAM2BAM_IPA) {
- net = gether_qc_connect_name(&rndis->port, "rndis0",
- false);
- } else {
- rndis_qc_open(&rndis->port);
- net = gether_qc_get_net("rndis0");
- }
+ rndis_qc_open(rndis);
+ net = rndis_qc_get_net("rndis0");
if (IS_ERR(net))
return PTR_ERR(net);
- rndis_set_param_dev(rndis->config, net,
- &rndis->port.cdc_filter);
+ rndis_set_param_dev(rndis->params, net,
+ &rndis->cdc_filter);
} else
goto fail;
@@ -753,18 +749,13 @@ static void rndis_qc_disable(struct usb_function *f)
if (!rndis->notify->driver_data)
return;
- pr_info("rndis deactivated\n");
+ DBG(cdev, "rndis deactivated\n");
- rndis_uninit(rndis->config);
- bam_data_disconnect(&rndis->bam_port, USB_FUNC_RNDIS, rndis->port_num);
- if (rndis->xport != USB_GADGET_XPORT_BAM2BAM_IPA)
- gether_qc_disconnect_name(&rndis->port, "rndis0");
+ rndis_uninit(rndis->params);
+ ipa_data_disconnect(&rndis->bam_port, USB_IPA_FUNC_RNDIS);
- if (rndis->xport == USB_GADGET_XPORT_BAM2BAM_IPA &&
- gadget_is_dwc3(cdev->gadget)) {
- msm_ep_unconfig(rndis->port.out_ep);
- msm_ep_unconfig(rndis->port.in_ep);
- }
+ msm_ep_unconfig(rndis->bam_port.out);
+ msm_ep_unconfig(rndis->bam_port.in);
usb_ep_disable(rndis->notify);
rndis->notify->driver_data = NULL;
}
@@ -789,11 +780,11 @@ static void rndis_qc_suspend(struct usb_function *f)
* host case. In case of windows, this RNDIS state machine is
* already updated due to receiving of PACKET_FILTER.
*/
- rndis_flow_control(rndis->config, true);
+ rndis_flow_control(rndis->params, true);
pr_debug("%s(): Disconnecting\n", __func__);
}
- bam_data_suspend(&rndis->bam_port, rndis->port_num, USB_FUNC_RNDIS,
+ ipa_data_suspend(&rndis->bam_port, USB_IPA_FUNC_RNDIS,
remote_wakeup_allowed);
pr_debug("rndis suspended\n");
}
@@ -816,12 +807,11 @@ static void rndis_qc_resume(struct usb_function *f)
else
remote_wakeup_allowed = f->config->cdev->gadget->remote_wakeup;
- bam_data_resume(&rndis->bam_port, rndis->port_num, USB_FUNC_RNDIS,
- remote_wakeup_allowed);
+ ipa_data_resume(&rndis->bam_port, USB_IPA_FUNC_RNDIS,
+ remote_wakeup_allowed);
if (!remote_wakeup_allowed) {
- if (rndis->xport == USB_GADGET_XPORT_BAM2BAM_IPA)
- rndis_qc_open(&rndis->port);
+ rndis_qc_open(rndis);
/*
* Linux Host doesn't sends RNDIS_MSG_INIT or non-zero value
* set with RNDIS_MESSAGE_PACKET_FILTER after performing bus
@@ -829,7 +819,7 @@ static void rndis_qc_resume(struct usb_function *f)
* explicitly here. For Windows host case is also being
* handle with RNDIS state machine.
*/
- rndis_flow_control(rndis->config, false);
+ rndis_flow_control(rndis->params, false);
}
pr_debug("%s: RNDIS resume completed\n", __func__);
@@ -844,26 +834,23 @@ static void rndis_qc_resume(struct usb_function *f)
* not used to tell whether the link should send packets or not.
*/
-static void rndis_qc_open(struct qc_gether *geth)
+static void rndis_qc_open(struct f_rndis_qc *rndis)
{
- struct f_rndis_qc *rndis = func_to_rndis_qc(&geth->func);
- struct usb_composite_dev *cdev = geth->func.config->cdev;
+ struct usb_composite_dev *cdev = rndis->func.config->cdev;
DBG(cdev, "%s\n", __func__);
- rndis_set_param_medium(rndis->config, RNDIS_MEDIUM_802_3,
+ rndis_set_param_medium(rndis->params, RNDIS_MEDIUM_802_3,
rndis_qc_bitrate(cdev->gadget) / 100);
- rndis_signal_connect(rndis->config);
+ rndis_signal_connect(rndis->params);
}
-static void rndis_qc_close(struct qc_gether *geth)
+void ipa_data_flow_control_enable(bool enable, struct rndis_params *param)
{
- struct f_rndis_qc *rndis = func_to_rndis_qc(&geth->func);
-
- DBG(geth->func.config->cdev, "%s\n", __func__);
-
- rndis_set_param_medium(rndis->config, RNDIS_MEDIUM_802_3, 0);
- rndis_signal_disconnect(rndis->config);
+ if (enable)
+ ipa_data_stop_rndis_ipa(USB_IPA_FUNC_RNDIS);
+ else
+ ipa_data_start_rndis_ipa(USB_IPA_FUNC_RNDIS);
}
/*-------------------------------------------------------------------------*/
@@ -875,6 +862,7 @@ rndis_qc_bind(struct usb_configuration *c, struct usb_function *f)
{
struct usb_composite_dev *cdev = c->cdev;
struct f_rndis_qc *rndis = func_to_rndis_qc(f);
+ struct rndis_params *params;
int status;
struct usb_ep *ep;
@@ -902,13 +890,13 @@ rndis_qc_bind(struct usb_configuration *c, struct usb_function *f)
ep = usb_ep_autoconfig(cdev->gadget, &rndis_qc_fs_in_desc);
if (!ep)
goto fail;
- rndis->port.in_ep = ep;
+ rndis->bam_port.in = ep;
ep->driver_data = cdev; /* claim */
ep = usb_ep_autoconfig(cdev->gadget, &rndis_qc_fs_out_desc);
if (!ep)
goto fail;
- rndis->port.out_ep = ep;
+ rndis->bam_port.out = ep;
ep->driver_data = cdev; /* claim */
/* NOTE: a status/notification endpoint is, strictly speaking,
@@ -972,33 +960,30 @@ rndis_qc_bind(struct usb_configuration *c, struct usb_function *f)
goto fail;
}
- rndis->port.open = rndis_qc_open;
- rndis->port.close = rndis_qc_close;
-
- status = rndis_register(rndis_qc_response_available, rndis,
- bam_data_flow_control_enable);
- if (status < 0)
+ params = rndis_register(rndis_qc_response_available, rndis,
+ ipa_data_flow_control_enable);
+ if (params < 0)
goto fail;
- rndis->config = status;
+ rndis->params = params;
- rndis_set_param_medium(rndis->config, RNDIS_MEDIUM_802_3, 0);
- rndis_set_host_mac(rndis->config, rndis->ethaddr);
+ rndis_set_param_medium(rndis->params, RNDIS_MEDIUM_802_3, 0);
+ rndis_set_host_mac(rndis->params, rndis->ethaddr);
if (rndis->manufacturer && rndis->vendorID &&
- rndis_set_param_vendor(rndis->config, rndis->vendorID,
+ rndis_set_param_vendor(rndis->params, rndis->vendorID,
rndis->manufacturer))
goto fail;
pr_debug("%s(): max_pkt_per_xfer:%d\n", __func__,
rndis->ul_max_pkt_per_xfer);
- rndis_set_max_pkt_xfer(rndis->config, rndis->ul_max_pkt_per_xfer);
+ rndis_set_max_pkt_xfer(rndis->params, rndis->ul_max_pkt_per_xfer);
/* In case of aggregated packets QC device will request
* aliment to 4 (2^2).
*/
pr_debug("%s(): pkt_alignment_factor:%d\n", __func__,
rndis->pkt_alignment_factor);
- rndis_set_pkt_alignment_factor(rndis->config,
+ rndis_set_pkt_alignment_factor(rndis->params,
rndis->pkt_alignment_factor);
/* NOTE: all that is done without knowing or caring about
@@ -1009,7 +994,7 @@ rndis_qc_bind(struct usb_configuration *c, struct usb_function *f)
DBG(cdev, "RNDIS: %s speed IN/%s OUT/%s NOTIFY/%s\n",
gadget_is_superspeed(c->cdev->gadget) ? "super" :
gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
- rndis->port.in_ep->name, rndis->port.out_ep->name,
+ rndis->bam_port.in->name, rndis->bam_port.out->name,
rndis->notify->name);
return 0;
@@ -1029,10 +1014,10 @@ fail:
/* we might as well release our claims on endpoints */
if (rndis->notify)
rndis->notify->driver_data = NULL;
- if (rndis->port.out_ep->desc)
- rndis->port.out_ep->driver_data = NULL;
- if (rndis->port.in_ep->desc)
- rndis->port.in_ep->driver_data = NULL;
+ if (rndis->bam_port.out->desc)
+ rndis->bam_port.out->driver_data = NULL;
+ if (rndis->bam_port.in->desc)
+ rndis->bam_port.in->driver_data = NULL;
pr_err("%s: can't bind, err %d\n", f->name, status);
@@ -1046,7 +1031,7 @@ rndis_qc_unbind(struct usb_configuration *c, struct usb_function *f)
unsigned long flags;
pr_debug("rndis_qc_unbind: free\n");
- rndis_deregister(rndis->config);
+ rndis_deregister(rndis->params);
if (gadget_is_dualspeed(c->cdev->gadget))
usb_free_descriptors(f->hs_descriptors);
@@ -1055,18 +1040,16 @@ rndis_qc_unbind(struct usb_configuration *c, struct usb_function *f)
kfree(rndis->notify_req->buf);
usb_ep_free_request(rndis->notify, rndis->notify_req);
- if (rndis->xport == USB_GADGET_XPORT_BAM2BAM_IPA) {
- /*
- * call flush_workqueue to make sure that any pending
- * disconnect_work() from u_bam_data.c file is being
- * flushed before calling this rndis_ipa_cleanup API
- * as rndis ipa disconnect API is required to be
- * called before this.
- */
- bam_data_flush_workqueue();
- rndis_ipa_cleanup(rndis_ipa_params.private);
- rndis_ipa_supported = false;
- }
+ /*
+ * call flush_workqueue to make sure that any pending
+ * disconnect_work() from u_bam_data.c file is being
+ * flushed before calling this rndis_ipa_cleanup API
+ * as rndis ipa disconnect API is required to be
+ * called before this.
+ */
+ ipa_data_flush_workqueue();
+ rndis_ipa_cleanup(rndis_ipa_params.private);
+ rndis_ipa_supported = false;
spin_lock_irqsave(&rndis_lock, flags);
kfree(rndis);
@@ -1099,7 +1082,6 @@ void rndis_net_ready_notify(void)
{
struct f_rndis_qc *rndis;
unsigned long flags;
- int port_num;
spin_lock_irqsave(&rndis_lock, flags);
rndis = _rndis_qc;
@@ -1117,14 +1099,9 @@ void rndis_net_ready_notify(void)
pr_debug("%s: Set net_ready_trigger", __func__);
rndis->net_ready_trigger = true;
spin_unlock_irqrestore(&rndis_lock, flags);
- port_num = (u_bam_data_func_to_port(USB_FUNC_RNDIS,
- RNDIS_QC_ACTIVE_PORT));
- if (port_num < 0)
- return;
- bam_data_start_rx_tx(port_num);
+ ipa_data_start_rx_tx(USB_IPA_FUNC_RNDIS);
}
-
/* Some controllers can't support RNDIS ... */
static inline bool can_support_rndis_qc(struct usb_configuration *c)
{
@@ -1147,15 +1124,14 @@ static inline bool can_support_rndis_qc(struct usb_configuration *c)
int
rndis_qc_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN])
{
- return rndis_qc_bind_config_vendor(c, ethaddr, 0, NULL, 1, 0, NULL);
+ return rndis_qc_bind_config_vendor(c, ethaddr, 0, NULL, 1, 0);
}
int
rndis_qc_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN],
u32 vendorID, const char *manufacturer,
u8 max_pkt_per_xfer,
- u8 pkt_alignment_factor,
- char *xport_name)
+ u8 pkt_alignment_factor)
{
struct f_rndis_qc *rndis;
int status;
@@ -1186,7 +1162,7 @@ rndis_qc_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN],
status = usb_string_id(c->cdev);
if (status < 0)
return status;
- rndis_qc_string_defs[2].id = status;
+ rnis_qc_string_defs[2].id = status;
rndis_qc_iad_descriptor.iFunction = status;
}
@@ -1199,29 +1175,23 @@ rndis_qc_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN],
goto fail;
}
- rndis->xport = str_to_xport(xport_name);
-
- /* export host's Ethernet address in CDC format */
- if (rndis->xport == USB_GADGET_XPORT_BAM2BAM_IPA) {
- gether_qc_get_macs(rndis_ipa_params.device_ethaddr,
- rndis_ipa_params.host_ethaddr);
- pr_debug("setting host_ethaddr=%pM, device_ethaddr=%pM\n",
- rndis_ipa_params.host_ethaddr,
- rndis_ipa_params.device_ethaddr);
- rndis_ipa_supported = true;
- ether_addr_copy(rndis->ethaddr, &rndis_ipa_params.host_ethaddr);
- rndis_ipa_params.device_ready_notify = rndis_net_ready_notify;
- } else
- ether_addr_copy(rndis->ethaddr, ethaddr);
-
rndis->vendorID = vendorID;
rndis->manufacturer = manufacturer;
+ /* export host's Ethernet address in CDC format */
+ random_ether_addr(rndis_ipa_params.host_ethaddr);
+ random_ether_addr(rndis_ipa_params.device_ethaddr);
+ pr_debug("setting host_ethaddr=%pM, device_ethaddr=%pM\n",
+ rndis_ipa_params.host_ethaddr,
+ rndis_ipa_params.device_ethaddr);
+ rndis_ipa_supported = true;
+ ether_addr_copy(rndis->ethaddr, rndis_ipa_params.host_ethaddr);
+ rndis_ipa_params.device_ready_notify = rndis_net_ready_notify;
/* if max_pkt_per_xfer was not configured set to default value */
rndis->ul_max_pkt_per_xfer =
max_pkt_per_xfer ? max_pkt_per_xfer :
DEFAULT_MAX_PKT_PER_XFER;
- u_bam_data_set_ul_max_pkt_num(rndis->ul_max_pkt_per_xfer);
+ ipa_data_set_ul_max_pkt_num(rndis->ul_max_pkt_per_xfer);
/*
* Check no RNDIS aggregation, and alignment if not mentioned,
@@ -1241,38 +1211,30 @@ rndis_qc_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN],
DEFAULT_PKT_ALIGNMENT_FACTOR;
/* RNDIS activates when the host changes this filter */
- rndis->port.cdc_filter = 0;
+ rndis->cdc_filter = 0;
- /* RNDIS has special (and complex) framing */
- rndis->port.header_len = sizeof(struct rndis_packet_msg_type);
- rndis->port.wrap = rndis_qc_add_header;
- rndis->port.unwrap = rndis_qc_rm_hdr;
-
- rndis->port.func.name = "rndis";
- rndis->port.func.strings = rndis_qc_strings;
+ rndis->func.name = "rndis";
+ rndis->func.strings = rndis_qc_strings;
/* descriptors are per-instance copies */
- rndis->port.func.bind = rndis_qc_bind;
- rndis->port.func.unbind = rndis_qc_unbind;
- rndis->port.func.set_alt = rndis_qc_set_alt;
- rndis->port.func.setup = rndis_qc_setup;
- rndis->port.func.disable = rndis_qc_disable;
- rndis->port.func.suspend = rndis_qc_suspend;
- rndis->port.func.resume = rndis_qc_resume;
+ rndis->func.bind = rndis_qc_bind;
+ rndis->func.unbind = rndis_qc_unbind;
+ rndis->func.set_alt = rndis_qc_set_alt;
+ rndis->func.setup = rndis_qc_setup;
+ rndis->func.disable = rndis_qc_disable;
+ rndis->func.suspend = rndis_qc_suspend;
+ rndis->func.resume = rndis_qc_resume;
_rndis_qc = rndis;
- if (rndis->xport == USB_GADGET_XPORT_BAM2BAM_IPA) {
- status = rndis_ipa_init(&rndis_ipa_params);
- if (status) {
- pr_err("%s: failed to init rndis_ipa\n", __func__);
- goto fail;
- }
+ status = rndis_ipa_init(&rndis_ipa_params);
+ if (status) {
+ pr_err("%s: failed to init rndis_ipa\n", __func__);
+ goto fail;
}
- status = usb_add_function(c, &rndis->port.func);
+ status = usb_add_function(c, &rndis->func);
if (status) {
- if (rndis->xport == USB_GADGET_XPORT_BAM2BAM_IPA)
- rndis_ipa_cleanup(rndis_ipa_params.private);
+ ndis_ipa_cleanup(rndis_ipa_params.private);
goto fail;
}
@@ -1381,7 +1343,7 @@ static int rndis_qc_init(void)
pr_err("rndis QC driver failed to register\n");
spin_lock_init(&rndis_lock);
- ret = bam_data_setup(USB_FUNC_RNDIS, RNDIS_QC_NO_PORTS);
+ ret = ipa_data_setup(USB_IPA_FUNC_RNDIS);
if (ret) {
pr_err("bam_data_setup failed err: %d\n", ret);
return ret;
diff --git a/drivers/usb/gadget/function/rndis.c b/drivers/usb/gadget/function/rndis.c
index b0e7b65b84bd..98ac1ff58323 100644
--- a/drivers/usb/gadget/function/rndis.c
+++ b/drivers/usb/gadget/function/rndis.c
@@ -596,6 +596,7 @@ static int rndis_init_response(struct rndis_params *params,
resp->AFListOffset = cpu_to_le32(0);
resp->AFListSize = cpu_to_le32(0);
+ params->ul_max_xfer_size = le32_to_cpu(resp->MaxTransferSize);
params->resp_avail(params->v);
return 0;
}
@@ -799,7 +800,7 @@ EXPORT_SYMBOL_GPL(rndis_set_host_mac);
*/
int rndis_msg_parser(struct rndis_params *params, u8 *buf)
{
- u32 MsgType, MsgLength;
+ u32 MsgType, MsgLength, major, minor, max_transfer_size;
__le32 *tmp;
if (!buf)
@@ -822,6 +823,19 @@ int rndis_msg_parser(struct rndis_params *params, u8 *buf)
case RNDIS_MSG_INIT:
pr_debug("%s: RNDIS_MSG_INIT\n",
__func__);
+ major = get_unaligned_le32(tmp++);
+ minor = get_unaligned_le32(tmp++);
+ max_transfer_size = get_unaligned_le32(tmp++);
+
+ params->host_rndis_major_ver = major;
+ params->host_rndis_minor_ver = minor;
+ params->dl_max_xfer_size = max_transfer_size;
+
+ pr_debug("%s(): RNDIS Host Major:%d Minor:%d version\n",
+ __func__, major, minor);
+ pr_debug("%s(): UL Max Transfer size:%x\n", __func__,
+ max_transfer_size);
+
params->state = RNDIS_INITIALIZED;
return rndis_init_response(params, (rndis_init_msg_type *)buf);
@@ -1013,6 +1027,18 @@ int rndis_set_param_medium(struct rndis_params *params, u32 medium, u32 speed)
}
EXPORT_SYMBOL_GPL(rndis_set_param_medium);
+u32 rndis_get_dl_max_xfer_size(struct rndis_params *params)
+{
+ pr_debug("%s:\n", __func__);
+ return params->dl_max_xfer_size;
+}
+
+u32 rndis_get_ul_max_xfer_size(struct rndis_params *params)
+{
+ pr_debug("%s:\n", __func__);
+ return params->ul_max_xfer_size;
+}
+
void rndis_set_max_pkt_xfer(struct rndis_params *params, u8 max_pkt_per_xfer)
{
pr_debug("%s:\n", __func__);
diff --git a/drivers/usb/gadget/function/rndis.h b/drivers/usb/gadget/function/rndis.h
index 939c3bebe015..3d130b0576fc 100644
--- a/drivers/usb/gadget/function/rndis.h
+++ b/drivers/usb/gadget/function/rndis.h
@@ -199,6 +199,10 @@ typedef struct rndis_params
void *v;
struct list_head resp_queue;
+ u32 host_rndis_major_ver;
+ u32 host_rndis_minor_ver;
+ u32 ul_max_xfer_size;
+ u32 dl_max_xfer_size;
} rndis_params;
/* RNDIS Message parser and other useless functions */
@@ -213,6 +217,8 @@ int rndis_set_param_vendor(struct rndis_params *params, u32 vendorID,
int rndis_set_param_medium(struct rndis_params *params, u32 medium,
u32 speed);
void rndis_set_max_pkt_xfer(struct rndis_params *params, u8 max_pkt_per_xfer);
+u32 rndis_get_ul_max_xfer_size(struct rndis_params *params);
+u32 rndis_get_dl_max_xfer_size(struct rndis_params *params);
void rndis_add_hdr(struct sk_buff *skb);
int rndis_rm_hdr(struct gether *port, struct sk_buff *skb,
struct sk_buff_head *list);
diff --git a/drivers/usb/gadget/function/u_data_ipa.c b/drivers/usb/gadget/function/u_data_ipa.c
index 3a5b1e2da2e6..887773318ea6 100644
--- a/drivers/usb/gadget/function/u_data_ipa.c
+++ b/drivers/usb/gadget/function/u_data_ipa.c
@@ -22,37 +22,49 @@
#include <linux/termios.h>
#include <linux/usb_bam.h>
-#include "usb_gadget_xport.h"
+#include "u_data_ipa.h"
-#define IPA_N_PORTS 4
struct ipa_data_ch_info {
- struct usb_request *rx_req;
- struct usb_request *tx_req;
- unsigned long flags;
- unsigned id;
- enum transport_type trans;
- enum gadget_type gtype;
- bool is_connected;
- unsigned port_num;
- spinlock_t port_lock;
-
- struct work_struct connect_w;
- struct work_struct disconnect_w;
- struct work_struct suspend_w;
- struct work_struct resume_w;
-
- u32 src_pipe_idx;
- u32 dst_pipe_idx;
- u8 src_connection_idx;
- u8 dst_connection_idx;
- enum usb_ctrl usb_bam_type;
- struct gadget_ipa_port *port_usb;
+ struct usb_request *rx_req;
+ struct usb_request *tx_req;
+ unsigned long flags;
+ unsigned id;
+ enum ipa_func_type func_type;
+ bool is_connected;
+ unsigned port_num;
+ spinlock_t port_lock;
+
+ struct work_struct connect_w;
+ struct work_struct disconnect_w;
+ struct work_struct suspend_w;
+ struct work_struct resume_w;
+
+ u32 src_pipe_idx;
+ u32 dst_pipe_idx;
+ u8 src_connection_idx;
+ u8 dst_connection_idx;
+ enum usb_ctrl usb_bam_type;
+ struct gadget_ipa_port *port_usb;
+ struct usb_gadget *gadget;
+ atomic_t pipe_connect_notified;
struct usb_bam_connect_ipa_params ipa_params;
};
-static int n_ipa_ports;
+struct rndis_data_ch_info {
+ /* this provides downlink (device->host i.e host) side configuration*/
+ u32 dl_max_transfer_size;
+ /* this provides uplink (host->device i.e device) side configuration */
+ u32 ul_max_transfer_size;
+ u32 ul_max_packets_number;
+ bool ul_aggregation_enable;
+ u32 prod_clnt_hdl;
+ u32 cons_clnt_hdl;
+ void *priv;
+};
+
static struct workqueue_struct *ipa_data_wq;
struct ipa_data_ch_info *ipa_data_ports[IPA_N_PORTS];
+static struct rndis_data_ch_info *rndis_data;
/**
* ipa_data_endless_complete() - completion callback for endless TX/RX request
* @ep: USB endpoint for which this completion happen
@@ -132,6 +144,56 @@ static void ipa_data_stop_endless_xfer(struct ipa_data_ch_info *port, bool in)
}
}
+/*
+ * Called when IPA triggers us that the network interface is up.
+ * Starts the transfers on bulk endpoints.
+ * (optimization reasons, the pipes and bam with IPA are already connected)
+ */
+void ipa_data_start_rx_tx(enum ipa_func_type func)
+{
+ struct ipa_data_ch_info *port;
+ unsigned long flags;
+
+ pr_debug("%s: Triggered: starting tx, rx", __func__);
+ /* queue in & out requests */
+ port = ipa_data_ports[func];
+ if (!port) {
+ pr_err("%s: port is NULL, can't start tx, rx", __func__);
+ return;
+ }
+
+ spin_lock_irqsave(&port->port_lock, flags);
+
+ if (!port->port_usb || !port->port_usb->in ||
+ !port->port_usb->out) {
+ pr_err("%s: Can't start tx, rx, ep not enabled", __func__);
+ spin_unlock_irqrestore(&port->port_lock, flags);
+ return;
+ }
+
+ if (!port->rx_req || !port->tx_req) {
+ pr_err("%s: No request d->rx_req=%p, d->tx_req=%p", __func__,
+ port->rx_req, port->tx_req);
+ spin_unlock_irqrestore(&port->port_lock, flags);
+ return;
+ }
+ if (!port->is_connected) {
+ pr_debug("%s: pipes are disconnected", __func__);
+ spin_unlock_irqrestore(&port->port_lock, flags);
+ return;
+ }
+
+ spin_unlock_irqrestore(&port->port_lock, flags);
+
+ /* queue in & out requests */
+ pr_debug("%s: Starting rx", __func__);
+ if (port->port_usb->out)
+ ipa_data_start_endless_xfer(port, false);
+
+ pr_debug("%s: Starting tx", __func__);
+ if (port->port_usb->in)
+ ipa_data_start_endless_xfer(port, true);
+}
/**
* ipa_data_disconnect_work() - Perform USB IPA BAM disconnect
* @w: disconnect work
@@ -166,6 +228,23 @@ static void ipa_data_disconnect_work(struct work_struct *w)
if (ret)
pr_err("usb_bam_disconnect_ipa failed: err:%d\n", ret);
+ if (port->func_type == USB_IPA_FUNC_RNDIS) {
+ /*
+ * NOTE: it is required to disconnect USB and IPA BAM related
+ * pipes before calling IPA tethered function related disconnect
+ * API. IPA tethered function related disconnect API delete
+ * depedency graph with IPA RM which would results into IPA not
+ * pulling data although there is pending data on USB BAM
+ * producer pipe.
+ */
+ if (atomic_xchg(&port->pipe_connect_notified, 0) == 1) {
+ void *priv;
+
+ priv = rndis_qc_get_ipa_priv();
+ rndis_ipa_pipe_disconnect_notify(priv);
+ }
+ }
+
if (port->ipa_params.prod_clnt_hdl)
usb_bam_free_fifos(port->usb_bam_type,
port->dst_connection_idx);
@@ -173,6 +252,12 @@ static void ipa_data_disconnect_work(struct work_struct *w)
usb_bam_free_fifos(port->usb_bam_type,
port->src_connection_idx);
+ /*
+ * Decrement usage count which was incremented
+ * upon cable connect or cable disconnect in suspended state.
+ */
+ usb_gadget_autopm_put_async(port->gadget);
+
pr_debug("%s(): disconnect work completed.\n", __func__);
}
@@ -186,15 +271,15 @@ static void ipa_data_disconnect_work(struct work_struct *w)
* switch is being trigger. This API performs restoring USB endpoint operation
* and disable USB endpoint used for accelerated path.
*/
-void ipa_data_disconnect(struct gadget_ipa_port *gp, u8 port_num)
+void ipa_data_disconnect(struct gadget_ipa_port *gp, enum ipa_func_type func)
{
struct ipa_data_ch_info *port;
unsigned long flags;
struct usb_gadget *gadget = NULL;
- pr_debug("dev:%p port number:%d\n", gp, port_num);
- if (port_num >= n_ipa_ports) {
- pr_err("invalid ipa portno#%d\n", port_num);
+ pr_debug("dev:%p port number:%d\n", gp, func);
+ if (func >= USB_IPA_NUM_FUNCS) {
+ pr_err("invalid ipa portno#%d\n", func);
return;
}
@@ -203,9 +288,9 @@ void ipa_data_disconnect(struct gadget_ipa_port *gp, u8 port_num)
return;
}
- port = ipa_data_ports[port_num];
+ port = ipa_data_ports[func];
if (!port) {
- pr_err("port %u is NULL", port_num);
+ pr_err("port %u is NULL", func);
return;
}
@@ -223,8 +308,7 @@ void ipa_data_disconnect(struct gadget_ipa_port *gp, u8 port_num)
* complete function will be called, where we try
* to obtain the spinlock as well.
*/
- if (gadget_is_dwc3(gadget))
- msm_ep_unconfig(port->port_usb->in);
+ msm_ep_unconfig(port->port_usb->in);
spin_unlock_irqrestore(&port->port_lock, flags);
usb_ep_disable(port->port_usb->in);
spin_lock_irqsave(&port->port_lock, flags);
@@ -232,8 +316,7 @@ void ipa_data_disconnect(struct gadget_ipa_port *gp, u8 port_num)
}
if (port->port_usb->out) {
- if (gadget_is_dwc3(gadget))
- msm_ep_unconfig(port->port_usb->out);
+ msm_ep_unconfig(port->port_usb->out);
spin_unlock_irqrestore(&port->port_lock, flags);
usb_ep_disable(port->port_usb->out);
spin_lock_irqsave(&port->port_lock, flags);
@@ -257,14 +340,14 @@ void ipa_data_disconnect(struct gadget_ipa_port *gp, u8 port_num)
*/
static void configure_fifo(enum usb_ctrl bam_type, u8 idx, struct usb_ep *ep)
{
- struct u_bam_data_connect_info bam_info;
struct sps_mem_buffer data_fifo = {0};
+ u32 usb_bam_pipe_idx;
get_bam2bam_connection_info(bam_type, idx,
- &bam_info.usb_bam_pipe_idx,
+ &usb_bam_pipe_idx,
NULL, &data_fifo, NULL);
msm_data_fifo_config(ep, data_fifo.phys_base, data_fifo.size,
- bam_info.usb_bam_pipe_idx);
+ usb_bam_pipe_idx);
}
/**
@@ -308,8 +391,21 @@ static void ipa_data_connect_work(struct work_struct *w)
return;
}
+ /*
+ * check if connect_w got called two times during RNDIS resume as
+ * explicit flow control is called to start data transfers after
+ * ipa_data_connect()
+ */
+ if (port->is_connected) {
+ pr_debug("IPA connect is already done & Transfers started\n");
+ spin_unlock_irqrestore(&port->port_lock, flags);
+ usb_gadget_autopm_put_async(port->gadget);
+ return;
+ }
+
gport->ipa_consumer_ep = -1;
gport->ipa_producer_ep = -1;
+
if (gport->out) {
port->rx_req = usb_ep_alloc_request(gport->out, GFP_ATOMIC);
if (!port->rx_req) {
@@ -341,8 +437,7 @@ static void ipa_data_connect_work(struct work_struct *w)
/* update IPA Parameteres here. */
port->ipa_params.usb_connection_speed = gadget->speed;
- if (gadget_is_dwc3(gadget))
- port->ipa_params.reset_pipe_after_lpm =
+ port->ipa_params.reset_pipe_after_lpm =
msm_dwc3_reset_ep_after_lpm(gadget);
port->ipa_params.skip_ep_cfg = true;
port->ipa_params.keep_ipa_awake = true;
@@ -354,49 +449,35 @@ static void ipa_data_connect_work(struct work_struct *w)
usb_bam_alloc_fifos(port->usb_bam_type,
port->src_connection_idx);
- if (gadget_is_dwc3(gadget)) {
- sps_params = MSM_SPS_MODE | MSM_DISABLE_WB
- | MSM_PRODUCER | port->src_pipe_idx;
- port->rx_req->length = 32*1024;
- port->rx_req->udc_priv = sps_params;
- configure_fifo(port->usb_bam_type,
- port->src_connection_idx,
- port->port_usb->out);
- ret = msm_ep_config(gport->out, port->rx_req,
- GFP_ATOMIC);
- if (ret) {
- pr_err("msm_ep_config() failed for OUT EP\n");
- usb_bam_free_fifos(port->usb_bam_type,
- port->src_connection_idx);
- goto free_rx_tx_req;
- }
- } else {
- sps_params = (MSM_SPS_MODE | port->src_pipe_idx |
- MSM_VENDOR_ID) & ~MSM_IS_FINITE_TRANSFER;
- port->rx_req->udc_priv = sps_params;
+ sps_params = MSM_SPS_MODE | MSM_DISABLE_WB
+ | MSM_PRODUCER | port->src_pipe_idx;
+ port->rx_req->length = 32*1024;
+ port->rx_req->udc_priv = sps_params;
+ configure_fifo(port->usb_bam_type,
+ port->src_connection_idx,
+ port->port_usb->out);
+ ret = msm_ep_config(gport->out);
+ if (ret) {
+ pr_err("msm_ep_config() failed for OUT EP\n");
+ usb_bam_free_fifos(port->usb_bam_type,
+ port->src_connection_idx);
+ goto free_rx_tx_req;
}
}
if (gport->in) {
usb_bam_alloc_fifos(port->usb_bam_type,
port->dst_connection_idx);
- if (gadget_is_dwc3(gadget)) {
- sps_params = MSM_SPS_MODE | MSM_DISABLE_WB |
- port->dst_pipe_idx;
- port->tx_req->length = 32*1024;
- port->tx_req->udc_priv = sps_params;
- configure_fifo(port->usb_bam_type,
- port->dst_connection_idx, gport->in);
- ret = msm_ep_config(gport->in, port->tx_req,
- GFP_ATOMIC);
- if (ret) {
- pr_err("msm_ep_config() failed for IN EP\n");
- goto unconfig_msm_ep_out;
- }
- } else {
- sps_params = (MSM_SPS_MODE | port->dst_pipe_idx |
- MSM_VENDOR_ID) & ~MSM_IS_FINITE_TRANSFER;
- port->tx_req->udc_priv = sps_params;
+ sps_params = MSM_SPS_MODE | MSM_DISABLE_WB |
+ port->dst_pipe_idx;
+ port->tx_req->length = 32*1024;
+ port->tx_req->udc_priv = sps_params;
+ configure_fifo(port->usb_bam_type,
+ port->dst_connection_idx, gport->in);
+ ret = msm_ep_config(gport->in);
+ if (ret) {
+ pr_err("msm_ep_config() failed for IN EP\n");
+ goto unconfig_msm_ep_out;
}
}
@@ -410,13 +491,20 @@ static void ipa_data_connect_work(struct work_struct *w)
if (gport->out) {
pr_debug("configure bam ipa connect for USB OUT\n");
port->ipa_params.dir = USB_TO_PEER_PERIPHERAL;
+
+ if (port->func_type == USB_IPA_FUNC_RNDIS) {
+ port->ipa_params.notify = rndis_qc_get_ipa_rx_cb();
+ port->ipa_params.priv = rndis_qc_get_ipa_priv();
+ port->ipa_params.skip_ep_cfg =
+ rndis_qc_get_skip_ep_config();
+ }
+
ret = usb_bam_connect_ipa(port->usb_bam_type,
&port->ipa_params);
if (ret) {
pr_err("usb_bam_connect_ipa out failed err:%d\n", ret);
goto unconfig_msm_ep_in;
}
- gadget->bam2bam_func_enabled = true;
gport->ipa_consumer_ep = port->ipa_params.ipa_cons_ep_idx;
is_ipa_disconnected = false;
@@ -425,30 +513,71 @@ static void ipa_data_connect_work(struct work_struct *w)
if (gport->in) {
pr_debug("configure bam ipa connect for USB IN\n");
port->ipa_params.dir = PEER_PERIPHERAL_TO_USB;
- port->ipa_params.dst_client = IPA_CLIENT_USB_DPL_CONS;
+
+ if (port->func_type == USB_IPA_FUNC_RNDIS) {
+ port->ipa_params.notify = rndis_qc_get_ipa_tx_cb();
+ port->ipa_params.priv = rndis_qc_get_ipa_priv();
+ port->ipa_params.skip_ep_cfg =
+ rndis_qc_get_skip_ep_config();
+ }
+
+ if (port->func_type == USB_IPA_FUNC_DPL)
+ port->ipa_params.dst_client = IPA_CLIENT_USB_DPL_CONS;
ret = usb_bam_connect_ipa(port->usb_bam_type,
&port->ipa_params);
if (ret) {
pr_err("usb_bam_connect_ipa IN failed err:%d\n", ret);
goto disconnect_usb_bam_ipa_out;
}
- gadget->bam2bam_func_enabled = true;
gport->ipa_producer_ep = port->ipa_params.ipa_prod_ep_idx;
is_ipa_disconnected = false;
}
- pr_debug("ipa_producer_ep:%d ipa_consumer_ep:%d\n",
- gport->ipa_producer_ep,
- gport->ipa_consumer_ep);
+ /* For DPL need to update_ipa_pipes to qti */
+ if (port->func_type == USB_IPA_FUNC_RNDIS) {
+ rndis_data->prod_clnt_hdl =
+ port->ipa_params.prod_clnt_hdl;
+ rndis_data->cons_clnt_hdl =
+ port->ipa_params.cons_clnt_hdl;
+ rndis_data->priv = port->ipa_params.priv;
+
+ pr_debug("ul_max_transfer_size:%d\n",
+ rndis_data->ul_max_transfer_size);
+ pr_debug("ul_max_packets_number:%d\n",
+ rndis_data->ul_max_packets_number);
+ pr_debug("dl_max_transfer_size:%d\n",
+ rndis_data->dl_max_transfer_size);
+
+ ret = rndis_ipa_pipe_connect_notify(
+ rndis_data->cons_clnt_hdl,
+ rndis_data->prod_clnt_hdl,
+ rndis_data->ul_max_transfer_size,
+ rndis_data->ul_max_packets_number,
+ rndis_data->dl_max_transfer_size,
+ rndis_data->priv);
+ if (ret) {
+ pr_err("%s: failed to connect IPA: err:%d\n",
+ __func__, ret);
+ return;
+ }
+ atomic_set(&port->pipe_connect_notified, 1);
+ }
- gqti_ctrl_update_ipa_pipes(NULL, DPL_QTI_CTRL_PORT_NO,
+ pr_debug("ipa_producer_ep:%d ipa_consumer_ep:%d\n",
gport->ipa_producer_ep,
gport->ipa_consumer_ep);
pr_debug("src_bam_idx:%d dst_bam_idx:%d\n",
port->src_connection_idx, port->dst_connection_idx);
+ /* Don't queue the transfers yet, only after network stack is up */
+ if (port->func_type == USB_IPA_FUNC_RNDIS) {
+ pr_debug("%s: Not starting now, waiting for network notify",
+ __func__);
+ return;
+ }
+
if (gport->out)
ipa_data_start_endless_xfer(port, false);
if (gport->in)
@@ -496,7 +625,7 @@ free_rx_req:
* initiate USB BAM IPA connection. This API is enabling accelerated endpoints
* and schedule connect_work() which establishes USB IPA BAM communication.
*/
-int ipa_data_connect(struct gadget_ipa_port *gp, u8 port_num,
+int ipa_data_connect(struct gadget_ipa_port *gp, enum ipa_func_type func,
u8 src_connection_idx, u8 dst_connection_idx)
{
struct ipa_data_ch_info *port;
@@ -504,10 +633,10 @@ int ipa_data_connect(struct gadget_ipa_port *gp, u8 port_num,
int ret;
pr_debug("dev:%p port#%d src_connection_idx:%d dst_connection_idx:%d\n",
- gp, port_num, src_connection_idx, dst_connection_idx);
+ gp, func, src_connection_idx, dst_connection_idx);
- if (port_num >= n_ipa_ports) {
- pr_err("invalid portno#%d\n", port_num);
+ if (func >= USB_IPA_NUM_FUNCS) {
+ pr_err("invalid portno#%d\n", func);
ret = -ENODEV;
goto err;
}
@@ -518,10 +647,11 @@ int ipa_data_connect(struct gadget_ipa_port *gp, u8 port_num,
goto err;
}
- port = ipa_data_ports[port_num];
+ port = ipa_data_ports[func];
spin_lock_irqsave(&port->port_lock, flags);
port->port_usb = gp;
+ port->gadget = gp->cdev->gadget;
port->src_connection_idx = src_connection_idx;
port->dst_connection_idx = dst_connection_idx;
port->usb_bam_type = usb_bam_get_bam_type(gp->cdev->gadget->name);
@@ -565,6 +695,19 @@ int ipa_data_connect(struct gadget_ipa_port *gp, u8 port_num,
goto err_usb_in;
}
+ /* Wait for host to enable flow_control */
+ if (port->func_type == USB_IPA_FUNC_RNDIS) {
+ ret = 0;
+ goto err_usb_in;
+ }
+
+ /*
+ * Increment usage count upon cable connect. Decrement after IPA
+ * handshake is done in disconnect work (due to cable disconnect)
+ * or in suspend work.
+ */
+ usb_gadget_autopm_get_noresume(port->gadget);
+
queue_work(ipa_data_wq, &port->connect_w);
spin_unlock_irqrestore(&port->port_lock, flags);
@@ -642,6 +785,12 @@ static void ipa_data_stop(void *param, enum usb_bam_pipe_dir dir)
}
}
+void ipa_data_flush_workqueue(void)
+{
+ pr_debug("%s(): Flushing workqueue\n", __func__);
+ flush_workqueue(ipa_data_wq);
+}
+
/**
* ipa_data_suspend() - Initiate USB BAM IPA suspend functionality
* @gp: Gadget IPA port
@@ -650,15 +799,14 @@ static void ipa_data_stop(void *param, enum usb_bam_pipe_dir dir)
* It is being used to initiate USB BAM IPA suspend functionality
* for USB bus suspend functionality.
*/
-void ipa_data_suspend(struct gadget_ipa_port *gp, u8 port_num)
+void ipa_data_suspend(struct gadget_ipa_port *gp, enum ipa_func_type func,
+ bool remote_wakeup_enabled)
{
struct ipa_data_ch_info *port;
- int ret;
-
- pr_debug("dev:%p port number:%d\n", gp, port_num);
+ unsigned long flags;
- if (port_num >= n_ipa_ports) {
- pr_err("invalid ipa portno#%d\n", port_num);
+ if (func >= USB_IPA_NUM_FUNCS) {
+ pr_err("invalid ipa portno#%d\n", func);
return;
}
@@ -666,14 +814,61 @@ void ipa_data_suspend(struct gadget_ipa_port *gp, u8 port_num)
pr_err("data port is null\n");
return;
}
+ pr_debug("%s: suspended port %d\n", __func__, func);
- port = ipa_data_ports[port_num];
+ port = ipa_data_ports[func];
if (!port) {
- pr_err("port %u is NULL", port_num);
+ pr_err("%s(): Port is NULL.\n", __func__);
return;
}
+ /* suspend with remote wakeup disabled */
+ if (!remote_wakeup_enabled) {
+ /*
+ * When remote wakeup is disabled, IPA BAM is disconnected
+ * because it cannot send new data until the USB bus is resumed.
+ * Endpoint descriptors info is saved before it gets reset by
+ * the BAM disconnect API. This lets us restore this info when
+ * the USB bus is resumed.
+ */
+ gp->in_ep_desc_backup = gp->in->desc;
+ gp->out_ep_desc_backup = gp->out->desc;
+
+ pr_debug("in_ep_desc_backup = %p, out_ep_desc_backup = %p",
+ gp->in_ep_desc_backup,
+ gp->out_ep_desc_backup);
+
+ ipa_data_disconnect(gp, func);
+ return;
+ }
+
+ spin_lock_irqsave(&port->port_lock, flags);
+ queue_work(ipa_data_wq, &port->suspend_w);
+ spin_unlock_irqrestore(&port->port_lock, flags);
+}
+static void bam2bam_data_suspend_work(struct work_struct *w)
+{
+ struct ipa_data_ch_info *port = container_of(w, struct ipa_data_ch_info,
+ connect_w);
+ unsigned long flags;
+ int ret;
+
pr_debug("%s: suspend started\n", __func__);
+ spin_lock_irqsave(&port->port_lock, flags);
+
+ /* In case of RNDIS, host enables flow_control invoking connect_w. If it
+ * is delayed then we may end up having suspend_w run before connect_w.
+ * In this scenario, connect_w may or may not at all start if cable gets
+ * disconnected or if host changes configuration e.g. RNDIS --> MBIM
+ * For these cases don't do runtime_put as there was no _get yet, and
+ * detect this condition on disconnect to not do extra pm_runtme_get
+ * for SUSPEND --> DISCONNECT scenario.
+ */
+ if (!port->is_connected) {
+ pr_err("%s: Not yet connected. SUSPEND pending.\n", __func__);
+ spin_unlock_irqrestore(&port->port_lock, flags);
+ return;
+ }
ret = usb_bam_register_wake_cb(port->usb_bam_type,
port->dst_connection_idx, NULL, port);
if (ret) {
@@ -685,7 +880,23 @@ void ipa_data_suspend(struct gadget_ipa_port *gp, u8 port_num)
usb_bam_register_start_stop_cbs(port->usb_bam_type,
port->dst_connection_idx, ipa_data_start,
ipa_data_stop, port);
+ /*
+ * release lock here because bam_data_start() or
+ * bam_data_stop() called from usb_bam_suspend()
+ * re-acquires port lock.
+ */
+ spin_unlock_irqrestore(&port->port_lock, flags);
usb_bam_suspend(port->usb_bam_type, &port->ipa_params);
+ spin_lock_irqsave(&port->port_lock, flags);
+
+ /*
+ * Decrement usage count after IPA handshake is done
+ * to allow gadget parent to go to lpm. This counter was
+ * incremented upon cable connect.
+ */
+ usb_gadget_autopm_put_async(port->gadget);
+
+ spin_unlock_irqrestore(&port->port_lock, flags);
}
/**
@@ -696,17 +907,20 @@ void ipa_data_suspend(struct gadget_ipa_port *gp, u8 port_num)
* It is being used to initiate USB resume functionality
* for USB bus resume case.
*/
-void ipa_data_resume(struct gadget_ipa_port *gp, u8 port_num)
+void ipa_data_resume(struct gadget_ipa_port *gp, enum ipa_func_type func,
+ bool remote_wakeup_enabled)
{
struct ipa_data_ch_info *port;
unsigned long flags;
struct usb_gadget *gadget = NULL;
- int ret;
+ u8 src_connection_idx;
+ u8 dst_connection_idx;
+ enum usb_ctrl usb_bam_type;
- pr_debug("dev:%p port number:%d\n", gp, port_num);
+ pr_debug("dev:%p port number:%d\n", gp, func);
- if (port_num >= n_ipa_ports) {
- pr_err("invalid ipa portno#%d\n", port_num);
+ if (func >= USB_IPA_NUM_FUNCS) {
+ pr_err("invalid ipa portno#%d\n", func);
return;
}
@@ -715,12 +929,66 @@ void ipa_data_resume(struct gadget_ipa_port *gp, u8 port_num)
return;
}
- port = ipa_data_ports[port_num];
+ port = ipa_data_ports[func];
if (!port) {
- pr_err("port %u is NULL", port_num);
+ pr_err("port %u is NULL", func);
return;
}
+ gadget = gp->cdev->gadget;
+ /* resume with remote wakeup disabled */
+ if (!remote_wakeup_enabled) {
+ /* Restore endpoint descriptors info. */
+ gp->in->desc = gp->in_ep_desc_backup;
+ gp->out->desc = gp->out_ep_desc_backup;
+
+ pr_debug("in_ep_desc_backup = %p, out_ep_desc_backup = %p",
+ gp->in_ep_desc_backup,
+ gp->out_ep_desc_backup);
+ usb_bam_type = usb_bam_get_bam_type(gadget->name);
+ src_connection_idx = usb_bam_get_connection_idx(usb_bam_type,
+ IPA_P_BAM, USB_TO_PEER_PERIPHERAL, USB_BAM_DEVICE,
+ 0);
+ dst_connection_idx = usb_bam_get_connection_idx(usb_bam_type,
+ IPA_P_BAM, PEER_PERIPHERAL_TO_USB, USB_BAM_DEVICE,
+ 0);
+ ipa_data_connect(gp, func,
+ src_connection_idx, dst_connection_idx);
+ return;
+ }
+
+ spin_lock_irqsave(&port->port_lock, flags);
+
+ /*
+ * Increment usage count here to disallow gadget
+ * parent suspend. This counter will decrement
+ * after IPA handshake is done in disconnect work
+ * (due to cable disconnect) or in bam_data_disconnect
+ * in suspended state.
+ */
+ usb_gadget_autopm_get_noresume(port->gadget);
+ queue_work(ipa_data_wq, &port->resume_w);
+ spin_unlock_irqrestore(&port->port_lock, flags);
+}
+
+static void bam2bam_data_resume_work(struct work_struct *w)
+{
+ struct ipa_data_ch_info *port = container_of(w, struct ipa_data_ch_info,
+ connect_w);
+ struct usb_gadget *gadget;
+ unsigned long flags;
+ int ret;
+
+ if (!port->port_usb->cdev) {
+ pr_err("!port->port_usb->cdev is NULL");
+ goto exit;
+ }
+
+ if (!port->port_usb->cdev->gadget) {
+ pr_err("!port->port_usb->cdev->gadget is NULL");
+ goto exit;
+ }
+
pr_debug("%s: resume started\n", __func__);
spin_lock_irqsave(&port->port_lock, flags);
gadget = port->port_usb->cdev->gadget;
@@ -750,6 +1018,7 @@ void ipa_data_resume(struct gadget_ipa_port *gp, u8 port_num)
usb_bam_resume(port->usb_bam_type, &port->ipa_params);
}
+exit:
spin_unlock_irqrestore(&port->port_lock, flags);
}
@@ -762,12 +1031,12 @@ void ipa_data_resume(struct gadget_ipa_port *gp, u8 port_num)
*
* Retrun: 0 in case of success, otherwise errno.
*/
-static int ipa_data_port_alloc(int portno)
+static int ipa_data_port_alloc(enum ipa_func_type func)
{
struct ipa_data_ch_info *port = NULL;
- if (ipa_data_ports[portno] != NULL) {
- pr_debug("port %d already allocated.\n", portno);
+ if (ipa_data_ports[func] != NULL) {
+ pr_debug("port %d already allocated.\n", func);
return 0;
}
@@ -775,29 +1044,29 @@ static int ipa_data_port_alloc(int portno)
if (!port)
return -ENOMEM;
- ipa_data_ports[portno] = port;
+ ipa_data_ports[func] = port;
- pr_debug("port:%p with portno:%d allocated\n", port, portno);
+ pr_debug("port:%p with portno:%d allocated\n", port, func);
return 0;
}
/**
* ipa_data_port_select() - Select particular port for BAM2BAM IPA mode
* @portno: port number to be used by particular USB function
- * @gtype: USB gadget function type
+ * @func_type: USB gadget function type
*
* It is being used by USB function driver to select which BAM2BAM IPA
* port particular USB function wants to use.
*
*/
-void ipa_data_port_select(int portno, enum gadget_type gtype)
+void ipa_data_port_select(enum ipa_func_type func)
{
struct ipa_data_ch_info *port = NULL;
- pr_debug("portno:%d\n", portno);
+ pr_debug("portno:%d\n", func);
- port = ipa_data_ports[portno];
- port->port_num = portno;
+ port = ipa_data_ports[func];
+ port->port_num = func;
port->is_connected = false;
spin_lock_init(&port->port_lock);
@@ -808,14 +1077,16 @@ void ipa_data_port_select(int portno, enum gadget_type gtype)
if (!work_pending(&port->disconnect_w))
INIT_WORK(&port->disconnect_w, ipa_data_disconnect_work);
+ INIT_WORK(&port->suspend_w, bam2bam_data_suspend_work);
+ INIT_WORK(&port->resume_w, bam2bam_data_resume_work);
+
port->ipa_params.src_client = IPA_CLIENT_USB_PROD;
port->ipa_params.dst_client = IPA_CLIENT_USB_CONS;
- port->gtype = gtype;
+ port->func_type = func;
};
/**
* ipa_data_setup() - setup BAM2BAM IPA port
- * @no_ipa_port: total number of BAM2BAM IPA port to support
*
* Each USB function who wants to use BAM2BAM IPA port would
* be counting number of IPA port to use and initialize those
@@ -823,32 +1094,34 @@ void ipa_data_port_select(int portno, enum gadget_type gtype)
*
* Retrun: 0 in case of success, otherwise errno.
*/
-int ipa_data_setup(unsigned int no_ipa_port)
+int ipa_data_setup(enum ipa_func_type func)
{
- int i, ret;
+ int ret;
- pr_debug("requested %d IPA BAM ports", no_ipa_port);
+ pr_debug("requested %d IPA BAM port", func);
- if (!no_ipa_port || no_ipa_port > IPA_N_PORTS) {
- pr_err("Invalid num of ports count:%d\n", no_ipa_port);
+ if (func >= USB_IPA_NUM_FUNCS) {
+ pr_err("Invalid num of ports count:%d\n", func);
return -EINVAL;
}
- for (i = 0; i < no_ipa_port; i++) {
- n_ipa_ports++;
- ret = ipa_data_port_alloc(i);
- if (ret) {
- n_ipa_ports--;
- pr_err("Failed to alloc port:%d\n", i);
+ ret = ipa_data_port_alloc(func);
+ if (ret) {
+ pr_err("Failed to alloc port:%d\n", func);
+ return ret;
+ }
+
+ if (func == USB_IPA_FUNC_RNDIS) {
+ rndis_data = kzalloc(sizeof(*rndis_data), GFP_KERNEL);
+ if (!rndis_data) {
+ pr_err("%s: fail allocate and initialize new instance\n",
+ __func__);
goto free_ipa_ports;
}
}
-
- pr_debug("n_ipa_ports:%d\n", n_ipa_ports);
-
if (ipa_data_wq) {
pr_debug("ipa_data_wq is already setup.");
- return 0;
+ goto free_rndis_data;
}
ipa_data_wq = alloc_workqueue("k_usb_ipa_data",
@@ -856,20 +1129,111 @@ int ipa_data_setup(unsigned int no_ipa_port)
if (!ipa_data_wq) {
pr_err("Failed to create workqueue\n");
ret = -ENOMEM;
- goto free_ipa_ports;
+ goto free_rndis_data;
}
return 0;
+free_rndis_data:
+ if (func == USB_IPA_FUNC_RNDIS)
+ kfree(rndis_data);
free_ipa_ports:
- for (i = 0; i < n_ipa_ports; i++) {
- kfree(ipa_data_ports[i]);
- ipa_data_ports[i] = NULL;
- if (ipa_data_wq) {
- destroy_workqueue(ipa_data_wq);
- ipa_data_wq = NULL;
- }
- }
+ kfree(ipa_data_ports[func]);
+ ipa_data_ports[func] = NULL;
return ret;
}
+
+void ipa_data_set_ul_max_xfer_size(u32 max_transfer_size)
+{
+ if (!max_transfer_size) {
+ pr_err("%s: invalid parameters\n", __func__);
+ return;
+ }
+ rndis_data->ul_max_transfer_size = max_transfer_size;
+ pr_debug("%s(): ul_max_xfer_size:%d\n", __func__, max_transfer_size);
+}
+
+void ipa_data_set_dl_max_xfer_size(u32 max_transfer_size)
+{
+
+ if (!max_transfer_size) {
+ pr_err("%s: invalid parameters\n", __func__);
+ return;
+ }
+ rndis_data->dl_max_transfer_size = max_transfer_size;
+ pr_debug("%s(): dl_max_xfer_size:%d\n", __func__, max_transfer_size);
+}
+
+void ipa_data_set_ul_max_pkt_num(u8 max_packets_number)
+{
+ if (!max_packets_number) {
+ pr_err("%s: invalid parameters\n", __func__);
+ return;
+ }
+
+ rndis_data->ul_max_packets_number = max_packets_number;
+
+ if (max_packets_number > 1)
+ rndis_data->ul_aggregation_enable = true;
+ else
+ rndis_data->ul_aggregation_enable = false;
+
+ pr_debug("%s(): ul_aggregation enable:%d ul_max_packets_number:%d\n",
+ __func__, rndis_data->ul_aggregation_enable,
+ max_packets_number);
+}
+
+void ipa_data_start_rndis_ipa(enum ipa_func_type func)
+{
+ struct ipa_data_ch_info *port;
+
+ pr_debug("%s\n", __func__);
+
+ port = ipa_data_ports[func];
+ if (!port) {
+ pr_err("%s: port is NULL", __func__);
+ return;
+ }
+
+ if (atomic_read(&port->pipe_connect_notified)) {
+ pr_debug("%s: Transfers already started?\n", __func__);
+ return;
+ }
+ /*
+ * Increment usage count upon cable connect. Decrement after IPA
+ * handshake is done in disconnect work due to cable disconnect
+ * or in suspend work.
+ */
+ usb_gadget_autopm_get_noresume(port->gadget);
+ queue_work(ipa_data_wq, &port->connect_w);
+}
+
+void ipa_data_stop_rndis_ipa(enum ipa_func_type func)
+{
+ struct ipa_data_ch_info *port;
+ unsigned long flags;
+
+ pr_debug("%s\n", __func__);
+
+ port = ipa_data_ports[func];
+ if (!port) {
+ pr_err("%s: port is NULL", __func__);
+ return;
+ }
+
+ if (!atomic_read(&port->pipe_connect_notified))
+ return;
+
+ rndis_ipa_reset_trigger();
+ ipa_data_stop_endless_xfer(port, true);
+ ipa_data_stop_endless_xfer(port, false);
+ spin_lock_irqsave(&port->port_lock, flags);
+ /* check if USB cable is disconnected or not */
+ if (port->port_usb) {
+ msm_ep_unconfig(port->port_usb->in);
+ msm_ep_unconfig(port->port_usb->out);
+ }
+ spin_unlock_irqrestore(&port->port_lock, flags);
+ queue_work(ipa_data_wq, &port->disconnect_w);
+}
diff --git a/drivers/usb/gadget/function/u_data_ipa.h b/drivers/usb/gadget/function/u_data_ipa.h
index b7d47ab1bb04..6f51a3de9618 100644
--- a/drivers/usb/gadget/function/u_data_ipa.h
+++ b/drivers/usb/gadget/function/u_data_ipa.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014,2016 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -13,23 +13,70 @@
#ifndef __U_DATA_IPA_H
#define __U_DATA_IPA_H
-#include "usb_gadget_xport.h"
+#include <linux/usb/composite.h>
+#include <linux/rndis_ipa.h>
+#include <linux/usb/msm_hsusb.h>
+#include <linux/miscdevice.h>
+#include <linux/ipa_usb.h>
+#include <linux/usb_bam.h>
+
+enum ipa_func_type {
+ USB_IPA_FUNC_ECM,
+ USB_IPA_FUNC_MBIM,
+ USB_IPA_FUNC_RMNET,
+ USB_IPA_FUNC_RNDIS,
+ USB_IPA_FUNC_DPL,
+ USB_IPA_NUM_FUNCS,
+};
+
+/* Max Number of IPA data ports supported */
+#define IPA_N_PORTS USB_IPA_NUM_FUNCS
struct gadget_ipa_port {
struct usb_composite_dev *cdev;
struct usb_function *func;
+ int rx_buffer_size;
struct usb_ep *in;
struct usb_ep *out;
int ipa_consumer_ep;
int ipa_producer_ep;
+ const struct usb_endpoint_descriptor *in_ep_desc_backup;
+ const struct usb_endpoint_descriptor *out_ep_desc_backup;
+
};
-void ipa_data_port_select(int portno, enum gadget_type gtype);
-void ipa_data_disconnect(struct gadget_ipa_port *gp, u8 port_num);
-int ipa_data_connect(struct gadget_ipa_port *gp, u8 port_num,
+void ipa_data_port_select(enum ipa_func_type func);
+void ipa_data_disconnect(struct gadget_ipa_port *gp, enum ipa_func_type func);
+int ipa_data_connect(struct gadget_ipa_port *gp, enum ipa_func_type func,
u8 src_connection_idx, u8 dst_connection_idx);
-int ipa_data_setup(unsigned int no_ipa_port);
-void ipa_data_resume(struct gadget_ipa_port *gp, u8 port_num);
-void ipa_data_suspend(struct gadget_ipa_port *gp, u8 port_num);
+int ipa_data_setup(enum ipa_func_type func);
+
+void ipa_data_flush_workqueue(void);
+void ipa_data_resume(struct gadget_ipa_port *gp, enum ipa_func_type func,
+ bool remote_wakeup_enabled);
+void ipa_data_suspend(struct gadget_ipa_port *gp, enum ipa_func_type func,
+ bool remote_wakeup_enabled);
+
+void ipa_data_set_ul_max_xfer_size(u32 ul_max_xfer_size);
+
+void ipa_data_set_dl_max_xfer_size(u32 dl_max_transfer_size);
+
+void ipa_data_set_ul_max_pkt_num(u8 ul_max_packets_number);
+
+void ipa_data_start_rx_tx(enum ipa_func_type func);
+
+void ipa_data_start_rndis_ipa(enum ipa_func_type func);
+
+void ipa_data_stop_rndis_ipa(enum ipa_func_type func);
+
+int
+rndis_qc_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN],
+ u32 vendorID, const char *manufacturer,
+ u8 maxPktPerXfer, u8 pkt_alignment_factor);
+void *rndis_qc_get_ipa_priv(void);
+void *rndis_qc_get_ipa_rx_cb(void);
+bool rndis_qc_get_skip_ep_config(void);
+void *rndis_qc_get_ipa_tx_cb(void);
+void rndis_ipa_reset_trigger(void);
#endif