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

Commit 898b585a authored by Mayank Rana's avatar Mayank Rana
Browse files

USB: gadget: Fix port allocation issue on next USB cable connect



commit "457ac5b0 USB: gadget: Fix race condition to over come memory
corruption" fixed memory corruption issue with dynamic composition
switch for port structure. Although it misses handling of USB cable
disconnect/connect part and due to which USB composition having
RNDIS/ECM/MBIM composition is failing on USB cable disconnect/connect
case. This change accomodates both dynamic composition switch and
USB cable disconnect/connect usecases.

CRs-Fixed: 653163
Change-Id: I2880e6f534bbc7bdcd92692acf5c56397446401e
Signed-off-by: default avatarMayank Rana <mrana@codeaurora.org>
parent 7ab1b9a7
Loading
Loading
Loading
Loading
+9 −1
Original line number Diff line number Diff line
@@ -1057,6 +1057,14 @@ static int ecm_function_init(struct android_usb_function *f,
	return 0;
}

static int ecm_qc_function_init(struct android_usb_function *f,
				struct usb_composite_dev *cdev)
{
	f->config = kzalloc(sizeof(struct ecm_function_config), GFP_KERNEL);
	if (!f->config)
		return -ENOMEM;
	return ecm_qc_init();
}
static void ecm_function_cleanup(struct android_usb_function *f)
{
	kfree(f->config);
@@ -1154,7 +1162,7 @@ static struct device_attribute *ecm_function_attributes[] = {

static struct android_usb_function ecm_qc_function = {
	.name		= "ecm_qc",
	.init		= ecm_function_init,
	.init		= ecm_qc_function_init,
	.cleanup	= ecm_function_cleanup,
	.bind_config	= ecm_qc_function_bind_config,
	.unbind_config	= ecm_qc_function_unbind_config,
+13 −7
Original line number Diff line number Diff line
@@ -67,6 +67,7 @@ struct mbim_ipa_ep_info {


#define NR_MBIM_PORTS			1
#define MBIM_DEFAULT_PORT		0

/* ID for Microsoft OS String */
#define MBIM_OS_STRING_ID   0xEE
@@ -780,6 +781,12 @@ static int mbim_bam_connect(struct f_mbim *dev)

	pr_info("dev:%p portno:%d\n", dev, dev->port_num);

	ret = bam2bam_data_port_select(MBIM_DEFAULT_PORT);
	if (ret) {
		pr_err("mbim port select failed err: %d\n", ret);
		return ret;
	}

	src_connection_idx = usb_bam_get_connection_idx(gadget->name, bam_name,
		USB_TO_PEER_PERIPHERAL, USB_BAM_DEVICE, dev->port_num);
	dst_connection_idx = usb_bam_get_connection_idx(gadget->name, bam_name,
@@ -1721,7 +1728,6 @@ static void mbim_unbind(struct usb_configuration *c, struct usb_function *f)
	usb_ep_free_request(mbim->not_port.notify, mbim->not_port.notify_req);

	mbim_ext_config_desc.function.subCompatibleID[0] = 0;
	bam_work_destroy();
}

/**
@@ -1745,12 +1751,6 @@ int mbim_bind_config(struct usb_configuration *c, unsigned portno,
		return -ENODEV;
	}

	status = mbim_bam_setup(nr_mbim_ports);
	if (status) {
		pr_err("bam setup failed\n");
		return status;
	}

	/* maybe allocate device-global string IDs */
	if (mbim_string_defs[0].id == 0) {

@@ -2148,6 +2148,12 @@ static int mbim_init(int instances)

	pr_info("Initialized %d ports\n", nr_mbim_ports);

	ret = mbim_bam_setup(nr_mbim_ports);
	if (ret) {
		pr_err("bam_data_setup failed err: %d\n", ret);
		return ret;
	}

	return ret;

fail_probe:
+22 −20
Original line number Diff line number Diff line
@@ -117,6 +117,7 @@ static inline unsigned ecm_qc_bitrate(struct usb_gadget *g)

/* Currently only one std ecm instance is supported - port index 0. */
#define ECM_QC_NO_PORTS						1
#define ECM_QC_DEFAULT_PORT					0
#define ECM_QC_ACTIVE_PORT					0

/* interface descriptor: */
@@ -441,19 +442,6 @@ static void ecm_qc_notify(struct f_ecm_qc *ecm)
	ecm_qc_do_notify(ecm);
}

static int ecm_qc_bam_setup(void)
{
	int ret;

	ret = bam_data_setup(ECM_QC_NO_PORTS);
	if (ret) {
		pr_err("bam_data_setup failed err: %d\n", ret);
		return ret;
	}

	return 0;
}

static int ecm_qc_bam_connect(struct f_ecm_qc *dev)
{
	int ret;
@@ -463,6 +451,12 @@ static int ecm_qc_bam_connect(struct f_ecm_qc *dev)
	enum peer_bam peer_bam = (dev->xport == USB_GADGET_XPORT_BAM2BAM_IPA) ?
		IPA_P_BAM : A2_P_BAM;

	ret = bam2bam_data_port_select(ECM_QC_DEFAULT_PORT);
	if (ret) {
		pr_err("ecm_qc port select failed with err:%d\n", ret);
		return ret;
	}

	dev->bam_port.cdev = cdev;
	dev->bam_port.func = &dev->port.func;
	dev->bam_port.in = dev->port.in_ep;
@@ -1031,7 +1025,6 @@ ecm_qc_unbind(struct usb_configuration *c, struct usb_function *f)
		ecm_ipa_cleanup(ipa_params.private);

	kfree(ecm);
	bam_work_destroy();
}

/**
@@ -1057,12 +1050,6 @@ ecm_qc_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN],
	if (!can_support_ecm(c->cdev->gadget) || !ethaddr)
		return -EINVAL;

	status = ecm_qc_bam_setup();
	if (status) {
		pr_err("bam setup failed\n");
		return status;
	}

	pr_debug("data transport type is %s\n", xport_name);

	/* maybe allocate device-global string IDs */
@@ -1153,3 +1140,18 @@ ecm_qc_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN],

	return status;
}

static int ecm_qc_init(void)
{
	int ret;

	pr_debug("initialize ecm qc port instance\n");

	ret = bam_data_setup(ECM_QC_NO_PORTS);
	if (ret) {
		pr_err("bam_data_setup failed err: %d\n", ret);
		return ret;
	}

	return ret;
}
+12 −21
Original line number Diff line number Diff line
@@ -413,20 +413,6 @@ static inline void rndis_qc_unlock(atomic_t *excl)
}

/* MSM bam support */

static int rndis_qc_bam_setup(void)
{
	int ret;

	ret = bam_data_setup(RNDIS_QC_NO_PORTS);
	if (ret) {
		pr_err("bam_data_setup failed err: %d\n", ret);
		return ret;
	}

	return 0;
}

static int rndis_qc_bam_connect(struct f_rndis_qc *dev)
{
	int ret;
@@ -441,6 +427,12 @@ static int rndis_qc_bam_connect(struct f_rndis_qc *dev)
	dev->bam_port.in = dev->port.in_ep;
	dev->bam_port.out = dev->port.out_ep;

	ret = bam2bam_data_port_select(RNDIS_QC_ACTIVE_PORT);
	if (ret) {
		pr_err("qc rndis bam port setup failed err:%d\n", ret);
		return ret;
	}

	/* currently we use the first connection */
	src_connection_idx = usb_bam_get_connection_idx(gadget->name, peer_bam,
		USB_TO_PEER_PERIPHERAL, USB_BAM_DEVICE, 0);
@@ -1044,7 +1036,6 @@ rndis_qc_unbind(struct usb_configuration *c, struct usb_function *f)
	}

	kfree(rndis);
	bam_work_destroy();
}

bool is_rndis_ipa_supported(void)
@@ -1097,12 +1088,6 @@ rndis_qc_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN],
	if (status < 0)
		return status;

	status = rndis_qc_bam_setup();
	if (status) {
		pr_err("%s: bam setup failed\n", __func__);
		return status;
	}

	/* maybe allocate device-global string IDs */
	if (rndis_qc_string_defs[0].id == 0) {

@@ -1318,6 +1303,12 @@ static int rndis_qc_init(void)
	if (ret)
		pr_err("rndis QC driver failed to register\n");

	ret = bam_data_setup(RNDIS_QC_NO_PORTS);
	if (ret) {
		pr_err("bam_data_setup failed err: %d\n", ret);
		return ret;
	}

	return ret;
}

+48 −83
Original line number Diff line number Diff line
@@ -114,7 +114,6 @@ struct bam_data_port {
	bool                            is_connected;
	unsigned			port_num;
	spinlock_t			port_lock_ul;
	unsigned int                    ref_count;
	struct data_port		*port_usb;
	struct bam_data_ch_info		data_ch;

@@ -134,7 +133,6 @@ static struct rndis_data_ch_info rndis_data;

static void bam2bam_data_suspend_work(struct work_struct *w);
static void bam2bam_data_resume_work(struct work_struct *w);
static void bam2bam_data_port_free(int portno);

/*----- sys2bam towards the IPA (UL workaround) --------------- */

@@ -676,12 +674,8 @@ static void bam2bam_data_disconnect_work(struct work_struct *w)
		}

		ret = usb_bam_disconnect_ipa(&d->ipa_params);
		if (!ret) {
			pr_debug("%s(): freeing bam2bam_data_port\n", __func__);
			bam2bam_data_port_free(0);
		} else {
		if (ret)
			pr_err("usb_bam_disconnect_ipa failed: err:%d\n", ret);
		}

		if (d->func_type == USB_FUNC_MBIM)
			teth_bridge_disconnect(d->ipa_params.src_client);
@@ -1030,33 +1024,13 @@ static void bam2bam_data_connect_work(struct work_struct *w)
	pr_debug("Connect workqueue done (port %p)", port);
}

static void bam2bam_data_port_free(int portno)
{
	struct bam_data_port *port = bam2bam_data_ports[portno];

	if (port == NULL) {
		pr_debug("port %d already free\n", portno);
		return;
	}

	if (--port->ref_count == 0) {
		kfree(port);
		bam2bam_data_ports[portno] = NULL;
		n_bam2bam_data_ports--;
		pr_debug("freed port %d\n", portno);
	}
}

static int bam2bam_data_port_alloc(int portno)
{
	struct bam_data_port    *port = NULL;
	struct bam_data_ch_info	*d = NULL;

	if (bam2bam_data_ports[portno] != NULL) {
		pr_debug("port %d already allocated. incremeting ref_count\n",
				portno);
		bam2bam_data_ports[portno]->ref_count++;
		goto done;
		pr_debug("port %d already allocated.\n", portno);
		return 0;
	}

	port = kzalloc(sizeof(struct bam_data_port), GFP_KERNEL);
@@ -1065,8 +1039,18 @@ static int bam2bam_data_port_alloc(int portno)
		return -ENOMEM;
	}

	bam2bam_data_ports[portno] = port;
	return 0;
}
int bam2bam_data_port_select(int portno)
{
	struct bam_data_port	*port = NULL;
	struct bam_data_ch_info	*d = NULL;

	pr_debug("Inside: portno:%d\n", portno);

	port = bam2bam_data_ports[portno];
	port->port_num  = portno;
	port->ref_count = 1;
	port->is_connected = false;

	spin_lock_init(&port->port_lock_ul);
@@ -1090,7 +1074,6 @@ static int bam2bam_data_port_alloc(int portno)

	rndis_disconn_w = &port->disconnect_w;

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

	return 0;
@@ -1188,7 +1171,6 @@ int bam_data_connect(struct data_port *gr, u8 port_num,
	unsigned long		flags;

	pr_debug("dev:%p port#%d\n", gr, port_num);

	if (port_num >= n_bam2bam_data_ports) {
		pr_err("invalid portno#%d\n", port_num);
		return -ENODEV;
@@ -1288,30 +1270,6 @@ exit:
	return ret;
}

int bam_data_destroy(unsigned int no_bam2bam_port)
{
	struct bam_data_ch_info	*d;
	struct bam_data_port	*port;

	port = bam2bam_data_ports[no_bam2bam_port];
	d = &port->data_ch;

	pr_debug("bam_data_destroy: Freeing ports\n");
	bam2bam_data_port_free(no_bam2bam_port);
	if (bam_data_wq)
		destroy_workqueue(bam_data_wq);
	bam_data_wq = NULL;

	return 0;
}

void bam_work_destroy(void)
{
	if (bam_data_wq)
		destroy_workqueue(bam_data_wq);
	bam_data_wq = NULL;
}

int bam_data_setup(unsigned int no_bam2bam_port)
{
	int	i;
@@ -1324,8 +1282,20 @@ int bam_data_setup(unsigned int no_bam2bam_port)
		return -EINVAL;
	}

	for (i = 0; i < no_bam2bam_port; i++) {
		n_bam2bam_data_ports++;
		ret = bam2bam_data_port_alloc(i);
		if (ret) {
			n_bam2bam_data_ports--;
			pr_err("Failed to alloc port:%d\n", i);
			goto free_bam_ports;
		}
	}

	pr_debug("n_bam2bam_data_ports:%d\n", n_bam2bam_data_ports);

	if (bam_data_wq) {
		pr_debug("bam_data is already setup");
		pr_debug("bam_data is already setup.");
		return 0;
	}

@@ -1333,25 +1303,21 @@ int bam_data_setup(unsigned int no_bam2bam_port)
				WQ_UNBOUND | WQ_MEM_RECLAIM, 1);
	if (!bam_data_wq) {
		pr_err("Failed to create workqueue\n");
		return -ENOMEM;
	}

	for (i = 0; i < no_bam2bam_port; i++) {
		n_bam2bam_data_ports++;
		ret = bam2bam_data_port_alloc(i);
		if (ret) {
			n_bam2bam_data_ports--;
			pr_err("Failed to alloc port:%d\n", i);
		ret = -ENOMEM;
		goto free_bam_ports;
	}
	}

	return 0;

free_bam_ports:
	for (i = 0; i < n_bam2bam_data_ports; i++)
		bam2bam_data_port_free(i);
	for (i = 0; i < n_bam2bam_data_ports; i++) {
		kfree(bam2bam_data_ports[i]);
		bam2bam_data_ports[i] = NULL;
		if (bam_data_wq) {
			destroy_workqueue(bam_data_wq);
			bam_data_wq = NULL;
		}
	}

	return ret;
}
@@ -1442,28 +1408,27 @@ static void bam_data_stop(void *param, enum usb_bam_pipe_dir dir)
void bam_data_suspend(u8 port_num)
{
	struct bam_data_port	*port;
	struct bam_data_ch_info *d;

	port = bam2bam_data_ports[port_num];
	d = &port->data_ch;

	pr_debug("%s: suspended port %d\n", __func__, port_num);

	port = bam2bam_data_ports[port_num];
	if (port)
		queue_work(bam_data_wq, &port->suspend_w);
	else
		pr_err("%s(): Port is NULL.\n", __func__);
}

void bam_data_resume(u8 port_num)
{

	struct bam_data_port	*port;
	struct bam_data_ch_info *d;

	port = bam2bam_data_ports[port_num];
	d = &port->data_ch;

	pr_debug("%s: resumed port %d\n", __func__, port_num);

	port = bam2bam_data_ports[port_num];
	if (port)
		queue_work(bam_data_wq, &port->resume_w);
	else
		pr_err("%s(): Port is NULL.\n", __func__);
}

static void bam2bam_data_suspend_work(struct work_struct *w)
Loading