Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d3210c71 authored by Dov Levenglick's avatar Dov Levenglick
Browse files

usb: bam: mechanism for sys2bam for UL



Provide a mechanism to sidestep the default bam2bam connection
between the USB and IPA cores for UL. This mechanism uses a
sys2bam approach instead. The UL packets are put back in the
IPA BAM using dedicated API.

Change-Id: I92051c2d23fb5f025ad1ce6d0ab200675097f657
Signed-off-by: default avatarDov Levenglick <dovl@codeaurora.org>
parent a260c9b2
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -278,6 +278,9 @@ Optional Properties for Subnode:
- qcom,src-bam-pipe-index: source BAM pipe index
- qcom,data-fifo-offset: data fifo offset address
- qcom,descriptor-fifo-offset: descriptor fifo offset address
- qcom,pipe-connection-type: type of pipe connection. Can be one of
	0 - BAM2BAM (default if not specified)
	1 - SYS2BAM (only supported on UL)

Optional properties :
- qcom,bam-mode: BAM mode can be one of.
+25 −1
Original line number Diff line number Diff line
/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
/* Copyright (c) 2011-2014, 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
@@ -53,6 +53,14 @@ enum usb_bam_event_type {
	USB_BAM_EVENT_INACTIVITY,	/* Inactivity on all pipes */
};

enum usb_bam_pipe_type {
	USB_BAM_PIPE_BAM2BAM = 0,	/* Connection is BAM2BAM (default) */
	USB_BAM_PIPE_SYS2BAM,		/* Connection is SYS2BAM or BAM2SYS
					 * depending on usb_bam_pipe_dir
					 */
	USB_BAM_MAX_PIPE_TYPES,
};

struct usb_bam_connect_ipa_params {
	u8 src_idx;
	u8 dst_idx;
@@ -130,6 +138,7 @@ struct usb_bam_pipe_connect {
	enum usb_bam bam_type;
	enum usb_bam_mode bam_mode;
	enum peer_bam peer_bam;
	enum usb_bam_pipe_type pipe_type;
	u32 src_phy_addr;
	u32 src_pipe_index;
	u32 dst_phy_addr;
@@ -368,6 +377,16 @@ void usb_bam_set_qdss_core(const char *qdss_core);
int usb_bam_get_connection_idx(const char *name, enum peer_bam client,
	enum usb_bam_pipe_dir dir, enum usb_bam_mode bam_mode, u32 num);

/**
* Indicates the type of connection the USB side of the connection is.
*
* @idx - Pipe number.
*
* @type - Type of connection
*
* @return 0 on success, negative value on error
*/
int usb_bam_get_pipe_type(u8 idx, enum usb_bam_pipe_type *type);
#else
static inline int usb_bam_connect(u8 idx, u32 *bam_pipe_idx)
{
@@ -462,5 +481,10 @@ static inline int usb_bam_get_connection_idx(const char *name,
{
	return -ENODEV;
}

static inline int usb_bam_get_pipe_type(u8 idx, enum usb_bam_pipe_type *type)
{
	return -ENODEV;
}
#endif
#endif				/* _USB_BAM_H_ */
+221 −92
Original line number Diff line number Diff line
/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
/* Copyright (c) 2011-2014, 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
@@ -481,7 +481,59 @@ free_sps_endpoint:
	return ret;
}

static int connect_pipe_ipa(u8 idx,
static int connect_pipe_sys2bam_ipa(u8 idx,
			struct usb_bam_connect_ipa_params *ipa_params)
{
	int ret;
	enum usb_bam_pipe_dir dir = ipa_params->dir;
	struct usb_bam_pipe_connect *pipe_connect = &usb_bam_connections[idx];
	struct ipa_sys_connect_params sys_in_params;
	u32 usb_handle, usb_phy_addr;
	u32 clnt_hdl = 0;

	memset(&sys_in_params, 0, sizeof(sys_in_params));

	if (dir == USB_TO_PEER_PERIPHERAL) {
		usb_phy_addr = pipe_connect->src_phy_addr;
		sys_in_params.client = ipa_params->src_client;
	} else {
		usb_phy_addr = pipe_connect->dst_phy_addr;
		sys_in_params.client = ipa_params->dst_client;
	}
	/* Get HSUSB / HSIC bam handle */
	ret = sps_phy2h(usb_phy_addr, &usb_handle);
	if (ret) {
		pr_err("%s: sps_phy2h failed (HSUSB/HSIC BAM) %d\n",
			__func__, ret);
		return ret;
	}

	pipe_connect->activity_notify = ipa_params->activity_notify;
	pipe_connect->inactivity_notify = ipa_params->inactivity_notify;
	pipe_connect->priv = ipa_params->priv;

	/* IPA sys connection params */
	sys_in_params.desc_fifo_sz = pipe_connect->desc_fifo_size;
	sys_in_params.priv = ipa_params->priv;
	sys_in_params.notify = ipa_params->notify;
	memcpy(&sys_in_params.ipa_ep_cfg, &ipa_params->ipa_ep_cfg,
		   sizeof(struct ipa_ep_cfg));

	ret = ipa_setup_sys_pipe(&sys_in_params, &clnt_hdl);
	if (ret) {
		pr_err("%s: ipa_connect failed\n", __func__);
		return ret;
	}
	pipe_connect->ipa_clnt_hdl = clnt_hdl;
	if (dir == USB_TO_PEER_PERIPHERAL)
		ipa_params->cons_clnt_hdl = clnt_hdl;
	else
		ipa_params->prod_clnt_hdl = clnt_hdl;

	return 0;
}

static int connect_pipe_bam2bam_ipa(u8 idx,
			struct usb_bam_connect_ipa_params *ipa_params)
{
	int ret;
@@ -809,6 +861,135 @@ static bool usb_bam_resume_core(enum usb_bam bam_type,
	}
}

static int usb_bam_disconnect_ipa_prod(
		struct usb_bam_connect_ipa_params *ipa_params,
		enum usb_bam cur_bam, enum usb_bam_mode bam_mode)
{
	int ret;
	u8 idx = 0;
	struct usb_bam_pipe_connect *pipe_connect;
	struct sps_connect *sps_connection;

	idx = ipa_params->dst_idx;
	pipe_connect = &usb_bam_connections[idx];

	pipe_connect->activity_notify = NULL;
	pipe_connect->inactivity_notify = NULL;
	pipe_connect->priv = NULL;

	/* Do the release handshake with the IPA via RM */
	spin_lock(&usb_bam_ipa_handshake_info_lock);
	if (bam_mode == USB_BAM_DEVICE) {
		info[cur_bam].connect_complete = 0;
		info[cur_bam].lpm_wait_pipes = 1;
		info[cur_bam].disconnected = 1;
	}
	spin_unlock(&usb_bam_ipa_handshake_info_lock);

	/* Start release handshake on the last producer pipe */
	if (info[cur_bam].prod_pipes_enabled_per_bam == 1)
		wait_for_prod_release(cur_bam);
	if (pipe_connect->bam_mode == USB_BAM_DEVICE)
		usb_bam_resume_core(cur_bam, pipe_connect->bam_mode);

	/* close USB -> IPA pipe */
	if (pipe_connect->pipe_type == USB_BAM_PIPE_BAM2BAM) {
		ret = ipa_disconnect(ipa_params->prod_clnt_hdl);
		if (ret) {
			pr_err("%s: dst pipe disconnection failure\n",
					__func__);
			return ret;
		}

		sps_connection = &ctx.usb_bam_sps.sps_connections[idx];
		sps_connection->data.phys_base = 0;
		sps_connection->desc.phys_base = 0;

		ret = usb_bam_disconnect_pipe(idx);
		if (ret) {
			pr_err("%s: failure to disconnect pipe %d\n",
				   __func__, idx);
			return ret;
		}
	} else {
		ret = ipa_teardown_sys_pipe(ipa_params->prod_clnt_hdl);
		if (ret) {
			pr_err("%s: dst pipe disconnection failure\n",
				  __func__);
			return ret;
		}
	}
	info[cur_bam].prod_pipes_enabled_per_bam -= 1;

	return 0;
}

static int usb_bam_disconnect_ipa_cons(
		struct usb_bam_connect_ipa_params *ipa_params,
		enum usb_bam cur_bam)
{
	int ret;
	u8 idx = 0;
	struct usb_bam_pipe_connect *pipe_connect;
	struct sps_connect *sps_connection;

	idx = ipa_params->src_idx;
	pipe_connect = &usb_bam_connections[idx];

	pipe_connect->activity_notify = NULL;
	pipe_connect->inactivity_notify = NULL;
	pipe_connect->priv = NULL;

	/* close IPA -> USB pipe */
	if (pipe_connect->pipe_type == USB_BAM_PIPE_BAM2BAM) {
		ret = ipa_disconnect(ipa_params->cons_clnt_hdl);
		if (ret) {
			pr_err("%s: src pipe disconnection failure\n",
					__func__);
			return ret;
		}

		sps_connection = &ctx.usb_bam_sps.sps_connections[idx];
		sps_connection->data.phys_base = 0;
		sps_connection->desc.phys_base = 0;

		ret = usb_bam_disconnect_pipe(idx);
		if (ret) {
			pr_err("%s: failure to disconnect pipe %d\n",
				__func__, idx);
			return ret;
		}
	} else {
		ret = ipa_teardown_sys_pipe(ipa_params->prod_clnt_hdl);
		if (ret) {
			pr_err("%s: src pipe disconnection failure\n",
					__func__);
			return ret;
		}
	}

	pipe_connect->ipa_clnt_hdl = -1;

	/* Notify CONS release on the last cons pipe released */
	if (ctx.pipes_enabled_per_bam[cur_bam] == 0) {
		if (info[cur_bam].cur_cons_state ==
				IPA_RM_RESOURCE_RELEASED) {
			pr_debug("%s: Notify CONS_RELEASED\n",
				 __func__);
			ipa_rm_notify_completion(
				IPA_RM_RESOURCE_RELEASED,
				ipa_rm_resource_cons[cur_bam]);
		}
		if (pipe_connect->bam_mode == USB_BAM_DEVICE) {
			pr_debug("%s Ended disconnect sequence\n", __func__);
			usb_bam_suspend_core(cur_bam,
				USB_BAM_DEVICE, 1);
		}
	}

	return 0;
}

int usb_bam_connect(int idx, u32 *bam_pipe_idx)
{
	int ret;
@@ -1819,6 +2000,7 @@ int usb_bam_connect_ipa(struct usb_bam_connect_ipa_params *ipa_params)
	int ret;
	struct msm_usb_bam_platform_data *pdata =
					ctx.usb_bam_pdev->dev.platform_data;
	bool bam2bam;

	pr_debug("%s: start\n", __func__);

@@ -1841,6 +2023,8 @@ int usb_bam_connect_ipa(struct usb_bam_connect_ipa_params *ipa_params)
	pipe_connect = &usb_bam_connections[idx];
	cur_bam = pipe_connect->bam_type;
	cur_mode = pipe_connect->bam_mode;
	bam2bam = (pdata->connections[idx].pipe_type ==
			USB_BAM_PIPE_BAM2BAM);

	/* Set the BAM mode (host/device) according to connected pipe */
	info[cur_bam].cur_bam_mode = pipe_connect->bam_mode;
@@ -1880,7 +2064,7 @@ int usb_bam_connect_ipa(struct usb_bam_connect_ipa_params *ipa_params)
	 /* Check if BAM requires RESET before connect and reset first pipe */
	 spin_lock(&usb_bam_lock);
	 if ((pdata->reset_on_connect[cur_bam] == true) &&
	     (ctx.pipes_enabled_per_bam[cur_bam] == 0)) {
	     (ctx.pipes_enabled_per_bam[cur_bam] == 0) && bam2bam) {
		spin_unlock(&usb_bam_lock);

		if (cur_bam != HSIC_BAM)
@@ -1906,7 +2090,10 @@ int usb_bam_connect_ipa(struct usb_bam_connect_ipa_params *ipa_params)
		info[cur_bam].prod_pipes_enabled_per_bam += 1;
	}

	ret = connect_pipe_ipa(idx, ipa_params);
	if (bam2bam)
		ret = connect_pipe_bam2bam_ipa(idx, ipa_params);
	else
		ret = connect_pipe_sys2bam_ipa(idx, ipa_params);
	if (ret) {
		pr_err("%s: pipe connection failure\n", __func__);
		if (cur_mode == USB_BAM_DEVICE)
@@ -1921,7 +2108,7 @@ int usb_bam_connect_ipa(struct usb_bam_connect_ipa_params *ipa_params)
	/* Set global inactivity timer upon first pipe connection */
	if (ctx.pipes_enabled_per_bam[pipe_connect->bam_type] == 0 &&
		ctx.inactivity_timer_ms[pipe_connect->bam_type] &&
		pipe_connect->inactivity_notify)
		pipe_connect->inactivity_notify && bam2bam)
		usb_bam_set_inactivity_timer(pipe_connect->bam_type);

	ctx.pipes_enabled_per_bam[cur_bam] += 1;
@@ -1941,6 +2128,26 @@ int usb_bam_connect_ipa(struct usb_bam_connect_ipa_params *ipa_params)
}
EXPORT_SYMBOL(usb_bam_connect_ipa);

int usb_bam_get_pipe_type(u8 idx, enum usb_bam_pipe_type *type)
{
	struct msm_usb_bam_platform_data *pdata =
			ctx.usb_bam_pdev->dev.platform_data;

	if (idx >= ctx.max_connections) {
		pr_err("%s: Invalid connection index\n",
			__func__);
		return -EINVAL;
	}
	if (!type) {
		pr_err("%s: null pointer provided for type\n", __func__);
		return -EINVAL;
	} else
		*type = pdata->connections[idx].pipe_type;

	return 0;
}
EXPORT_SYMBOL(usb_bam_get_pipe_type);

int usb_bam_client_ready(bool ready)
{
	spin_lock(&usb_bam_peer_handshake_info_lock);
@@ -2357,7 +2564,6 @@ int usb_bam_disconnect_ipa(struct usb_bam_connect_ipa_params *ipa_params)
	int ret;
	u8 idx = 0;
	struct usb_bam_pipe_connect *pipe_connect;
	struct sps_connect *sps_connection;
	enum usb_bam cur_bam;
	enum usb_bam_mode bam_mode;

@@ -2379,103 +2585,20 @@ int usb_bam_disconnect_ipa(struct usb_bam_connect_ipa_params *ipa_params)
	mutex_lock(&info[cur_bam].suspend_resume_mutex);
	/* Delay USB core to go into lpm before we finish our handshake */
	if (ipa_params->prod_clnt_hdl) {
		idx = ipa_params->dst_idx;
		pipe_connect = &usb_bam_connections[idx];

		pipe_connect->activity_notify = NULL;
		pipe_connect->inactivity_notify = NULL;
		pipe_connect->priv = NULL;

		/* Do the release handshake with the A2 via RM */
		spin_lock(&usb_bam_ipa_handshake_info_lock);
		if (bam_mode == USB_BAM_DEVICE) {
			info[cur_bam].connect_complete = 0;
			info[cur_bam].lpm_wait_pipes = 1;
			info[cur_bam].disconnected = 1;
		}
		spin_unlock(&usb_bam_ipa_handshake_info_lock);

		/* Start release handshake on the last producer pipe */
		if (info[cur_bam].prod_pipes_enabled_per_bam == 1)
			wait_for_prod_release(cur_bam);
		if (pipe_connect->bam_mode == USB_BAM_DEVICE)
			usb_bam_resume_core(cur_bam, pipe_connect->bam_mode);
		/* close USB -> IPA pipe */
		ret = ipa_disconnect(ipa_params->prod_clnt_hdl);
		if (ret) {
			pr_err("%s: dst pipe disconnection failure\n",
			      __func__);
			mutex_unlock(&info[cur_bam].suspend_resume_mutex);
			return ret;
		}
		sps_connection = &ctx.usb_bam_sps.sps_connections[idx];
		sps_connection->data.phys_base = 0;
		sps_connection->desc.phys_base = 0;

		ret = usb_bam_disconnect_pipe(idx);
		ret = usb_bam_disconnect_ipa_prod(ipa_params,
				cur_bam, bam_mode);
		if (ret) {
			pr_err("%s: failure to disconnect pipe %d\n",
			       __func__, idx);
			mutex_unlock(&info[cur_bam].suspend_resume_mutex);
			return ret;
		}
		info[cur_bam].prod_pipes_enabled_per_bam -= 1;

	}

	if (ipa_params->cons_clnt_hdl) {
		idx = ipa_params->src_idx;
		pipe_connect = &usb_bam_connections[idx];

		pipe_connect->activity_notify = NULL;
		pipe_connect->inactivity_notify = NULL;
		pipe_connect->priv = NULL;

		cur_bam = pipe_connect->bam_type;
		/* close IPA -> USB pipe */
		ret = ipa_disconnect(ipa_params->cons_clnt_hdl);
		if (ret) {
			pr_err("%s: src pipe disconnection failure\n",
				__func__);
			mutex_unlock(&info[cur_bam].suspend_resume_mutex);
			return ret;
		}

		sps_connection = &ctx.usb_bam_sps.sps_connections[idx];
		sps_connection->data.phys_base = 0;
		sps_connection->desc.phys_base = 0;

		ret = usb_bam_disconnect_pipe(idx);
		ret = usb_bam_disconnect_ipa_cons(ipa_params, cur_bam);
		if (ret) {
			pr_err("%s: failure to disconnect pipe %d\n",
				__func__, idx);
			mutex_unlock(&info[cur_bam].suspend_resume_mutex);
			return ret;
		}

		pipe_connect->ipa_clnt_hdl = -1;

		/* Notify CONS release on the last cons pipe released */
		if (ctx.pipes_enabled_per_bam[cur_bam] == 0) {
			if (info[cur_bam].cur_cons_state ==
					IPA_RM_RESOURCE_RELEASED) {
				pr_debug("%s: Notify CONS_RELEASED\n",
					 __func__);
				ipa_rm_notify_completion(
					IPA_RM_RESOURCE_RELEASED,
					ipa_rm_resource_cons[cur_bam]);
			}
			/* TODO: Make also for SSUSB_BAM, when correct suspend
			* in place
			*/
			if (cur_bam == HSUSB_BAM &&
				pipe_connect->bam_mode == USB_BAM_DEVICE) {
				pr_debug("%s Ended disconnect sequence\n",
					 __func__);
				usb_bam_suspend_core(cur_bam,
					USB_BAM_DEVICE, 1);
			}
		}
	}

	mutex_unlock(&info[cur_bam].suspend_resume_mutex);
@@ -2743,6 +2866,12 @@ static struct msm_usb_bam_platform_data *usb_bam_dt_to_pdata(
			goto err;
		}

		rc = of_property_read_u32(node, "qcom,pipe-connection-type",
			&usb_bam_connections[i].pipe_type);
		if (rc)
			pr_debug("%s: pipe type is defaulting to bam2bam\n",
					__func__);

		reset_bam = of_property_read_bool(node,
			"qcom,reset-bam-on-connect");
		if (reset_bam)
+162 −45
Original line number Diff line number Diff line
@@ -91,6 +91,11 @@ module_param(dl_intr_threshold, uint, S_IRUGO | S_IWUSR);
#define BAM_CH_OPENED	BIT(0)
#define BAM_CH_READY	BIT(1)

struct sys2ipa_sw {
	void		*teth_priv;
	ipa_notify_cb	teth_cb;
};

struct bam_ch_info {
	unsigned long		flags;
	unsigned		id;
@@ -115,6 +120,11 @@ struct bam_ch_info {
	enum transport_type trans;
	struct usb_bam_connect_ipa_params ipa_params;

	/* added to support sys to ipa sw UL path */
	struct sys2ipa_sw	ul_params;
	enum usb_bam_pipe_type	src_pipe_type;
	enum usb_bam_pipe_type	dst_pipe_type;

	/* stats */
	unsigned int		pending_with_bam;
	unsigned int		tohost_drp_cnt;
@@ -157,6 +167,7 @@ static void gbam_start_rx(struct gbam_port *port);
static void gbam_start_endless_rx(struct gbam_port *port);
static void gbam_start_endless_tx(struct gbam_port *port);
static int gbam_peer_reset_cb(void *param);
static void gbam_notify(void *p, int event, unsigned long data);

/*---------------misc functions---------------- */
static void gbam_free_requests(struct usb_ep *ep, struct list_head *head)
@@ -193,6 +204,32 @@ static int gbam_alloc_requests(struct usb_ep *ep, struct list_head *head,

	return 0;
}

/*----- sys2bam towards the IPA --------------- */
static void gbam_ipa_sys2bam_notify_cb(void *priv, enum ipa_dp_evt_type event,
		unsigned long data)
{
	struct sys2ipa_sw *ul = (struct sys2ipa_sw *)priv;

	switch (event) {
	case IPA_WRITE_DONE:
		/* call into bam_demux functionality that'll recycle the data */
		gbam_notify(ul->teth_priv, BAM_DMUX_WRITE_DONE, data);
		break;
	case IPA_RECEIVE:
		/* call the callback given by tethering driver init function
		 * (and was given to ipa_connect)
		 */
		ul->teth_cb(ul->teth_priv, event, data);
		break;
	default:
		/* unexpected event */
		pr_err("%s: unexpected event %d\n", __func__, event);
		break;
	}
}


/*--------------------------------------------- */

/*------------data_path----------------------------*/
@@ -353,6 +390,9 @@ static void gbam_data_write_tobam(struct work_struct *w)
				port->port_num);

		spin_unlock_irqrestore(&port->port_lock_ul, flags);
		if (d->src_pipe_type == USB_BAM_PIPE_SYS2BAM)
			ret = ipa_tx_dp(usb_prod[port->port_num], skb, NULL);
		else
			ret = msm_bam_dmux_write(d->id, skb);
		spin_lock_irqsave(&port->port_lock_ul, flags);
		if (ret) {
@@ -636,65 +676,90 @@ static void gbam_start(void *param, enum usb_bam_pipe_dir dir)
{
	struct gbam_port *port = param;

	if (dir == USB_TO_PEER_PERIPHERAL)
	if (dir == USB_TO_PEER_PERIPHERAL) {
		if (port->data_ch.src_pipe_type == USB_BAM_PIPE_BAM2BAM)
			gbam_start_endless_rx(port);
		else
			gbam_start_rx(port);
	} else {
		gbam_start_endless_tx(port);
	}
}

static void gbam_stop(void *param, enum usb_bam_pipe_dir dir)
{
	struct gbam_port *port = param;

	if (dir == USB_TO_PEER_PERIPHERAL)
	if (dir == USB_TO_PEER_PERIPHERAL) {
		if (port->data_ch.src_pipe_type == USB_BAM_PIPE_BAM2BAM)
			gbam_stop_endless_rx(port);
		else
			pr_warn("%s: no function equivalent to gbam_stop_endless_rx for sys2bam pipe\n",
					__func__);
	} else {
		gbam_stop_endless_tx(port);
	}
}

static void gbam_start_io(struct gbam_port *port)
static int _gbam_start_io(struct gbam_port *port, bool in)
{
	unsigned long		flags;
	struct usb_ep		*ep;
	int			ret;
	struct bam_ch_info	*d;
	struct usb_ep		*ep;
	struct list_head	*idle;
	unsigned		queue_size;
	spinlock_t		*spinlock;
	void		(*ep_complete)(struct usb_ep *, struct usb_request *);

	pr_debug("%s: port:%p\n", __func__, port);
	if (in)
		spinlock = &port->port_lock_ul;
	else
		spinlock = &port->port_lock_dl;

	spin_lock_irqsave(&port->port_lock_ul, flags);
	spin_lock_irqsave(spinlock, flags);
	if (!port->port_usb) {
		spin_unlock_irqrestore(&port->port_lock_ul, flags);
		return;
		spin_unlock_irqrestore(spinlock, flags);
		return -EBUSY;
	}

	d = &port->data_ch;
	if (in) {
		ep = port->port_usb->in;
		idle = &port->data_ch.tx_idle;
		queue_size = bam_mux_tx_q_size;
		ep_complete = gbam_epin_complete;
	} else {
		ep = port->port_usb->out;
	ret = gbam_alloc_requests(ep, &d->rx_idle, bam_mux_rx_q_size,
			gbam_epout_complete, GFP_ATOMIC);
		idle = &port->data_ch.rx_idle;
		queue_size = bam_mux_rx_q_size;
		ep_complete = gbam_epout_complete;
	}
	ret = gbam_alloc_requests(ep, idle, queue_size, ep_complete,
			GFP_ATOMIC);
	if (ret) {
		pr_err("%s: rx req allocation failed\n", __func__);
		spin_unlock_irqrestore(&port->port_lock_ul, flags);
		return;
		pr_err("%s: allocation failed\n", __func__);
		spin_unlock_irqrestore(spinlock, flags);
		return ret;
	}
	spin_unlock_irqrestore(spinlock, flags);

	spin_unlock_irqrestore(&port->port_lock_ul, flags);
	spin_lock_irqsave(&port->port_lock_dl, flags);
	if (!port->port_usb) {
		gbam_free_requests(ep, &d->rx_idle);
		spin_unlock_irqrestore(&port->port_lock_dl, flags);
		return;
	return 0;
}
	ep = port->port_usb->in;
	ret = gbam_alloc_requests(ep, &d->tx_idle, bam_mux_tx_q_size,
			gbam_epin_complete, GFP_ATOMIC);
	if (ret) {
		pr_err("%s: tx req allocation failed\n", __func__);
		gbam_free_requests(ep, &d->rx_idle);
		spin_unlock_irqrestore(&port->port_lock_dl, flags);

static void gbam_start_io(struct gbam_port *port)
{
	unsigned long		flags;

	pr_debug("%s: port:%p\n", __func__, port);

	if (_gbam_start_io(port, true))
		return;
	}

	if (_gbam_start_io(port, false)) {
		spin_lock_irqsave(&port->port_lock_dl, flags);
		gbam_free_requests(port->port_usb->in, &port->data_ch.rx_idle);
		spin_unlock_irqrestore(&port->port_lock_dl, flags);
		return;
	}

	/* queue out requests */
	gbam_start_rx(port);
@@ -810,11 +875,13 @@ static void gbam_connect_work(struct work_struct *w)
 * This function configured data fifo based on index passed to get bam2bam
 * configuration.
 */
static void configure_data_fifo(u8 idx, struct usb_ep *ep)
static void configure_data_fifo(u8 idx, struct usb_ep *ep,
		enum usb_bam_pipe_type pipe_type)
{
	struct u_bam_data_connect_info bam_info;
	struct sps_mem_buffer data_fifo = {0};

	if (pipe_type == USB_BAM_PIPE_BAM2BAM) {
		get_bam2bam_connection_info(idx,
				&bam_info.usb_bam_handle,
				&bam_info.usb_bam_pipe_idx,
@@ -825,7 +892,7 @@ static void configure_data_fifo(u8 idx, struct usb_ep *ep)
				data_fifo.phys_base,
				data_fifo.size,
				bam_info.usb_bam_pipe_idx);

	}
}

static void gbam2bam_connect_work(struct work_struct *w)
@@ -862,14 +929,37 @@ static void gbam2bam_connect_work(struct work_struct *w)
			return;
		}
	} else if (d->trans == USB_GADGET_XPORT_BAM2BAM_IPA) {
		if (usb_bam_get_pipe_type(d->ipa_params.src_idx,
				&d->src_pipe_type) ||
			usb_bam_get_pipe_type(d->ipa_params.dst_idx,
					&d->dst_pipe_type)) {
			pr_err("%s:usb_bam_get_pipe_type() failed\n", __func__);
			return;
		}
		if (d->dst_pipe_type != USB_BAM_PIPE_BAM2BAM) {
			pr_err("%s: no software preparation for DL not using bam2bam\n",
					__func__);
			return;
		}

		ret = teth_bridge_init(&usb_notify_cb, &priv,
				d->ipa_params.src_client);
		if (ret) {
			pr_err("%s:teth_bridge_init() failed\n", __func__);
			return;
		}

		/* Support for UL using system-to-IPA */
		if (d->src_pipe_type == USB_BAM_PIPE_SYS2BAM) {
			d->ul_params.teth_priv = priv;
			d->ul_params.teth_cb = usb_notify_cb;
			d->ipa_params.notify = gbam_ipa_sys2bam_notify_cb;
			d->ipa_params.priv = &d->ul_params;

		} else {
			d->ipa_params.notify = usb_notify_cb;
			d->ipa_params.priv = priv;
		}
		d->ipa_params.ipa_ep_cfg.mode.mode = IPA_BASIC;
		d->ipa_params.dir = USB_TO_PEER_PERIPHERAL;
		ret = usb_bam_connect_ipa(&d->ipa_params);
@@ -881,6 +971,7 @@ static void gbam2bam_connect_work(struct work_struct *w)

		if (gadget && gadget_is_dwc3(gadget)) {
			u8 idx;

			idx = usb_bam_get_connection_idx(gadget->name,
				IPA_P_BAM, USB_TO_PEER_PERIPHERAL,
				USB_BAM_DEVICE, 0);
@@ -890,10 +981,16 @@ static void gbam2bam_connect_work(struct work_struct *w)
				return;
			}

			configure_data_fifo(idx, port->port_usb->out);
			configure_data_fifo(idx, port->port_usb->out,
						d->src_pipe_type);
		}


		/* Remove support for UL using system-to-IPA towards DL */
		if (d->src_pipe_type == USB_BAM_PIPE_SYS2BAM) {
			d->ipa_params.notify = d->ul_params.teth_cb;
			d->ipa_params.priv = d->ul_params.teth_priv;
		}
		d->ipa_params.notify = usb_notify_cb;
		d->ipa_params.dir = PEER_PERIPHERAL_TO_USB;
		ret = usb_bam_connect_ipa(&d->ipa_params);
		if (ret) {
@@ -914,7 +1011,8 @@ static void gbam2bam_connect_work(struct work_struct *w)
				return;
			}

			configure_data_fifo(idx, port->port_usb->in);
			configure_data_fifo(idx, port->port_usb->in,
						d->dst_pipe_type);
		}

		gqti_ctrl_update_ipa_pipes(port->port_usb, port->port_num,
@@ -986,7 +1084,21 @@ static void gbam2bam_connect_work(struct work_struct *w)
	d->tx_req->udc_priv = sps_params;

	/* queue in & out requests */
	if (d->trans == USB_GADGET_XPORT_BAM2BAM ||
			d->src_pipe_type == USB_BAM_PIPE_BAM2BAM) {
		gbam_start_endless_rx(port);
	} else {
		/* The use-case of UL (OUT) ports using sys2bam is based on
		 * partial reuse of the system-to-bam_demux code. The following
		 * lines perform the branching out of the standard bam2bam flow
		 * on the USB side of the UL channel
		 */
		if (_gbam_start_io(port, false)) {
			pr_err("%s: _gbam_start_io failed\n", __func__);
			return;
		}
		gbam_start_rx(port);
	}
	gbam_start_endless_tx(port);

	if (d->trans == USB_GADGET_XPORT_BAM2BAM && port->port_num == 0) {
@@ -1247,6 +1359,11 @@ static int gbam2bam_port_alloc(int portno)
	d->ipa_params.dst_client = usb_cons[portno];
	bam2bam_ports[portno] = port;

	/* UL workaround requirements */
	skb_queue_head_init(&d->rx_skb_q);
	INIT_LIST_HEAD(&d->rx_idle);
	INIT_WORK(&d->write_tobam_w, gbam_data_write_tobam);

	pr_debug("%s: port:%p portno:%d\n", __func__, port, portno);

	return 0;
+334 −21

File changed.

Preview size limit exceeded, changes collapsed.