diff options
author | Linux Build Service Account <lnxbuild@localhost> | 2016-10-06 01:07:13 -0700 |
---|---|---|
committer | Gerrit - the friendly Code Review server <code-review@localhost> | 2016-10-06 01:07:13 -0700 |
commit | 4310d71096ee774b0e645ec1aa7b008ed5f748bb (patch) | |
tree | 9a007fd5f269b9b2248e8eb6ddffd31d29fb0609 | |
parent | 7b6f74a97af7f9ee5e5d8e869fe9656516856b99 (diff) | |
parent | 5f7a2a2a57726b9ed2744e816228ff48ded7271f (diff) |
Merge "usb: gadget: f_qc_rndis: Add RNDIS support using IPA over BAM2BAM"
-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 |