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

Commit 62ebe110 authored by Ajay Prathi's avatar Ajay Prathi Committed by Gerrit - the friendly Code Review server
Browse files

mhi: netdev: Open mhi channels based on state notifications from host



MHI channels are opened at mhi start up,
before host opens the channels which results
in race conditions, when mhi device is switching
on or off, and if mhi netdev uses the channel before
the channel is initialised from host results in crash.
Add code to register for mhi channel state change,
open mhi channels and create network interface
when the state change notification comes from host to
fix the race condition.

Change-Id: Id497d8fe6721709369aa57802c399bcd154b19b6
Signed-off-by: default avatarAjay Prathi <aprathi@codeaurora.org>
parent 81e1f886
Loading
Loading
Loading
Loading
+112 −10
Original line number Diff line number Diff line
@@ -516,7 +516,7 @@ static int mhi_dev_net_enable_iface(struct mhi_dev_net_client *mhi_dev_net_ptr)
	return -ENOMEM;
}

static int mhi_dev_net_open_channels(struct mhi_dev_net_client *client)
static int mhi_dev_net_open_chan_create_netif(struct mhi_dev_net_client *client)
{
	int rc = 0;
	int ret = 0;
@@ -626,16 +626,79 @@ static int mhi_dev_net_rgstr_client(struct mhi_dev_net_client *client, int idx)
	spin_lock_init(&client->rd_lock);
	mhi_dev_net_log(MHI_INFO, "Registering out %d, In %d channels\n",
			client->out_chan, client->in_chan);
	return 0;
}

static int mhi_dev_net_dergstr_client
				(struct mhi_dev_net_client *client)
{
	mutex_destroy(&client->in_chan_lock);
	mutex_destroy(&client->out_chan_lock);

	/* Open IN and OUT channels for Network client*/
	mhi_dev_net_open_channels(client);
	return 0;
}

static void mhi_dev_net_state_cb(struct mhi_dev_client_cb_data *cb_data)
{
	struct mhi_dev_net_client *mhi_client;
	uint32_t info_in_ch = 0, info_out_ch = 0;
	int ret;

	if (!cb_data || !cb_data->user_data) {
		mhi_dev_net_log(MHI_ERROR, "invalid input received\n");
		return;
	}
	mhi_client = cb_data->user_data;

	ret = mhi_ctrl_state_info(mhi_client->in_chan, &info_in_ch);
	if (ret) {
		mhi_dev_net_log(MHI_ERROR,
			"Failed to obtain in_channel %d state\n",
			mhi_client->in_chan);
		return;
	}
	ret = mhi_ctrl_state_info(mhi_client->out_chan, &info_out_ch);
	if (ret) {
		mhi_dev_net_log(MHI_ERROR,
			"Failed to obtain out_channel %d state\n",
			mhi_client->out_chan);
		return;
	}
	mhi_dev_net_log(MHI_MSG_VERBOSE, "in_channel :%d, state :%d\n",
			mhi_client->in_chan, info_in_ch);
	mhi_dev_net_log(MHI_MSG_VERBOSE, "out_channel :%d, state :%d\n",
			mhi_client->out_chan, info_out_ch);
	if (info_in_ch == MHI_STATE_CONNECTED &&
		info_out_ch == MHI_STATE_CONNECTED) {
		/**
		 * Open IN and OUT channels for Network client
		 * and create Network Interface.
		 */
		ret = mhi_dev_net_open_chan_create_netif(mhi_client);
		if (ret) {
			mhi_dev_net_log(MHI_ERROR,
				"Failed to open channels\n");
			return;
		}
	} else if (info_in_ch == MHI_STATE_DISCONNECTED ||
				info_out_ch == MHI_STATE_DISCONNECTED) {
		if (mhi_client->dev != NULL) {
			netif_stop_queue(mhi_client->dev);
			unregister_netdev(mhi_client->dev);
			mhi_dev_close_channel(mhi_client->out_handle);
			mhi_dev_close_channel(mhi_client->in_handle);
			atomic_set(&mhi_client->tx_enabled, 0);
			atomic_set(&mhi_client->rx_enabled, 0);
			free_netdev(mhi_client->dev);
			mhi_client->dev = NULL;
		}
	}
}

int mhi_dev_net_interface_init(void)
{
	int ret_val = 0;
	int index = 0;
	int ret_val = 0, index = 0;
	bool out_channel_started = false;
	struct mhi_dev_net_client *mhi_net_client = NULL;

	if (mhi_net_ctxt.client_handle) {
@@ -650,9 +713,12 @@ int mhi_dev_net_interface_init(void)

	mhi_net_ipc_log = ipc_log_context_create(MHI_NET_IPC_PAGES,
						"mhi-net", 0);
	if (mhi_net_ipc_log == NULL)
	if (!mhi_net_ipc_log) {
		mhi_dev_net_log(MHI_DBG,
				"Failed to create IPC logging for mhi_dev_net\n");
		kfree(mhi_net_client);
		return -ENOMEM;
	}
	mhi_net_ctxt.client_handle = mhi_net_client;

	if (mhi_net_ctxt.pdev)
@@ -685,13 +751,49 @@ int mhi_dev_net_interface_init(void)
				"Failed to reg client %d ret 0\n", ret_val);
		goto client_register_fail;
	}
	return ret_val;
	ret_val = mhi_register_state_cb(mhi_dev_net_state_cb,
				mhi_net_client, MHI_CLIENT_IP_SW_4_OUT);
	/* -EEXIST indicates success and channel is already open */
	if (ret_val == -EEXIST)
		out_channel_started = true;
	else if (ret_val < 0)
		goto register_state_cb_fail;

	ret_val = mhi_register_state_cb(mhi_dev_net_state_cb,
				mhi_net_client, MHI_CLIENT_IP_SW_4_IN);
	/* -EEXIST indicates success and channel is already open */
	if (ret_val == -EEXIST) {
		/**
		 * If both in and out channels were opened by host at the
		 * time of registration proceed with opening channels and
		 * create network interface from device side.
		 * if the channels are not opened at the time of registration
		 * we will get a call back notification mhi_dev_net_state_cb()
		 * and proceed to open channels and create network interface
		 * with mhi_dev_net_open_chan_create_netif().
		 */
		ret_val = 0;
		if (out_channel_started) {
			ret_val = mhi_dev_net_open_chan_create_netif
							(mhi_net_client);
			if (ret_val < 0) {
				mhi_dev_net_log(MHI_ERROR,
					"Failed to open channels\n");
				goto channel_open_fail;
			}
		}
	} else if (ret_val < 0) {
		goto register_state_cb_fail;
	}

channel_init_fail:
	kfree(mhi_net_client);
	kfree(mhi_net_ipc_log);
	return ret_val;

channel_open_fail:
register_state_cb_fail:
	mhi_dev_net_dergstr_client(mhi_net_client);
client_register_fail:
channel_init_fail:
	destroy_workqueue(mhi_net_client->pending_pckt_wq);
	kfree(mhi_net_client);
	kfree(mhi_net_ipc_log);
	return ret_val;