Merge "usb: gadget: f_qc_rndis: Add RNDIS support using IPA over BAM2BAM"
This commit is contained in:
commit
4310d71096
5 changed files with 756 additions and 351 deletions
|
@ -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;
|
||||
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);
|
||||
|
||||
if (bam_data_connect(&rndis->bam_port, rndis->xport,
|
||||
rndis->port_num, USB_FUNC_RNDIS))
|
||||
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;
|
||||
|
|
|
@ -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__);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 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;
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
pr_debug("ipa_producer_ep:%d ipa_consumer_ep:%d\n",
|
||||
gport->ipa_producer_ep,
|
||||
gport->ipa_consumer_ep);
|
||||
|
||||
gqti_ctrl_update_ipa_pipes(NULL, DPL_QTI_CTRL_PORT_NO,
|
||||
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;
|
||||
unsigned long flags;
|
||||
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue