diff options
| author | Chandana Kishori Chiluveru <cchiluve@codeaurora.org> | 2016-09-09 15:11:32 +0530 |
|---|---|---|
| committer | Chandana Kishori Chiluveru <cchiluve@codeaurora.org> | 2016-10-03 12:17:32 +0530 |
| commit | 5f7a2a2a57726b9ed2744e816228ff48ded7271f (patch) | |
| tree | 9d047c63eef66ff1dbaeb69d24c92c5f71dcca14 | |
| parent | 46692be6dd06ce405cb09cf8338b1f0edbbfb295 (diff) | |
usb: gadget: f_qc_rndis: Add RNDIS support using IPA over BAM2BAM
On some targets RNDIS function uses BAM to BAM transport, and is
handled by the f_qc_rndis driver. This change adds RNDIS support
using IPA over BAM2BAM. Also removes all different supported control
and data transports and assume BAM2BAM_IPA as default mode. RNDIS UL
changes using software path also removed as IPA supports both UL
and DL on latest products.
DPL support will be added in subsequent patches.
Change-Id: I8f7bf069523a8f3ea37e0a32a2e32830944a5c97
Signed-off-by: Chandana Kishori Chiluveru <cchiluve@codeaurora.org>
| -rw-r--r-- | drivers/usb/gadget/function/f_qc_rndis.c | 374 | ||||
| -rw-r--r-- | drivers/usb/gadget/function/rndis.c | 28 | ||||
| -rw-r--r-- | drivers/usb/gadget/function/rndis.h | 6 | ||||
| -rw-r--r-- | drivers/usb/gadget/function/u_data_ipa.c | 642 | ||||
| -rw-r--r-- | drivers/usb/gadget/function/u_data_ipa.h | 63 |
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 |
