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

Commit 4a695f22 authored by Siddartha Mohanadoss's avatar Siddartha Mohanadoss Committed by Rama Krishna Phani A
Browse files

msm: mhi_dev: Add uevent support



Clients need notification for connect, disconnect state
for the individual channels once they are updated by the
host. Send the events for clients listening to uevents
such as MBIMD for channel state updates. Client can also
poll for the current channel state through MHI ctrl uevent.

Change-Id: Ie764b4866e5e41df072a7a76105a740c89f91f17
Signed-off-by: default avatarSiddartha Mohanadoss <smohanad@codeaurora.org>
parent dab1ac3b
Loading
Loading
Loading
Loading
+42 −14
Original line number Diff line number Diff line
/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
/* Copyright (c) 2015-2018, 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
@@ -72,11 +72,12 @@ static struct mhi_dev *mhi_ctx;
static void mhi_hwc_cb(void *priv, enum ipa_mhi_event_type event,
	unsigned long data);
static void mhi_ring_init_cb(void *user_data);
static void mhi_update_state_info(uint32_t info);
static void mhi_update_state_info(uint32_t uevent_idx, enum mhi_ctrl_info info);
static int mhi_deinit(struct mhi_dev *mhi);
static void mhi_dev_resume_init_with_link_up(struct ep_pcie_notify *notify);
static int mhi_dev_pcie_notify_event;
static void mhi_dev_transfer_completion_cb(void *mreq);
static struct mhi_dev_uevent_info channel_state_info[MHI_MAX_CHANNELS];

/*
 * mhi_dev_ring_cache_completion_cb () - Call back function called
@@ -466,7 +467,7 @@ static void mhi_hwc_cb(void *priv, enum ipa_mhi_event_type event,
			return;
		}

		mhi_update_state_info(MHI_STATE_CONNECTED);
		mhi_update_state_info(MHI_DEV_UEVENT_CTRL, MHI_STATE_CONNECTED);

		ep_pcie_mask_irq_event(mhi_ctx->phandle,
				EP_PCIE_INT_EVT_MHI_A7, true);
@@ -927,6 +928,8 @@ static void mhi_dev_process_cmd_ring(struct mhi_dev *mhi,
	struct mhi_addr host_addr;
	struct mhi_dev_channel *ch;
	struct mhi_dev_ring *ring;
	char *connected[2] = { "MHI_CHANNEL_STATE_12=CONNECTED", NULL};
	char *disconnected[2] = { "MHI_CHANNEL_STATE_12=DISCONNECTED", NULL};

	ch_id = el->generic.chid;
	mhi_log(MHI_MSG_VERBOSE, "for channel:%d and cmd:%d\n",
@@ -992,6 +995,10 @@ send_start_completion_event:
		if (rc)
			pr_err("Error sending command completion event\n");

		mhi_update_state_info(ch_id, MHI_STATE_CONNECTED);
		if (ch_id == MHI_CLIENT_MBIM_OUT)
			kobject_uevent_env(&mhi_ctx->dev->kobj,
						KOBJ_CHANGE, connected);
		break;
	case MHI_DEV_RING_EL_STOP:
		if (ch_id >= HW_CHANNEL_BASE) {
@@ -1046,6 +1053,10 @@ send_start_completion_event:
				pr_err("stop event send failed\n");

			mutex_unlock(&ch->ch_lock);
			mhi_update_state_info(ch_id, MHI_STATE_DISCONNECTED);
			if (ch_id == MHI_CLIENT_MBIM_OUT)
				kobject_uevent_env(&mhi_ctx->dev->kobj,
						KOBJ_CHANGE, disconnected);
		}
		break;
	case MHI_DEV_RING_EL_RESET:
@@ -1119,6 +1130,10 @@ send_start_completion_event:
			if (rc)
				pr_err("Error sending command completion event\n");
			mutex_unlock(&ch->ch_lock);
			mhi_update_state_info(ch_id, MHI_STATE_DISCONNECTED);
			if (ch_id == MHI_CLIENT_MBIM_OUT)
				kobject_uevent_env(&mhi_ctx->dev->kobj,
						KOBJ_CHANGE, disconnected);
		}
		break;
	default:
@@ -1300,7 +1315,7 @@ static int mhi_dev_abort(struct mhi_dev *mhi)
	}

	/* Update ctrl node */
	mhi_update_state_info(MHI_STATE_DISCONNECTED);
	mhi_update_state_info(MHI_DEV_UEVENT_CTRL, MHI_STATE_DISCONNECTED);

	flush_workqueue(mhi->ring_init_wq);
	flush_workqueue(mhi->pending_ring_wq);
@@ -1781,7 +1796,7 @@ int mhi_dev_resume(struct mhi_dev *mhi)
		mhi_dev_write_to_host(mhi, &data_transfer, NULL,
				MHI_DEV_DMA_SYNC);
	}
	mhi_update_state_info(MHI_STATE_CONNECTED);
	mhi_update_state_info(MHI_DEV_UEVENT_CTRL, MHI_STATE_CONNECTED);

	atomic_set(&mhi->is_suspended, 0);

@@ -2338,7 +2353,8 @@ static void mhi_dev_enable(struct work_struct *work)
	if (mhi_ctx->config_iatu || mhi_ctx->mhi_int)
		enable_irq(mhi_ctx->mhi_irq);

	mhi_update_state_info(MHI_STATE_CONNECTED);
	mhi_update_state_info(MHI_DEV_UEVENT_CTRL,
						MHI_STATE_CONFIGURED);
}

static void mhi_ring_init_cb(void *data)
@@ -2353,25 +2369,36 @@ static void mhi_ring_init_cb(void *data)
	queue_work(mhi->ring_init_wq, &mhi->ring_init_cb_work);
}

static void mhi_update_state_info(uint32_t info)
static void mhi_update_state_info(uint32_t uevent_idx, enum mhi_ctrl_info info)
{
	struct mhi_dev_client_cb_reason reason;

	if (uevent_idx == MHI_DEV_UEVENT_CTRL)
		mhi_ctx->ctrl_info = info;

	channel_state_info[uevent_idx].ctrl_info = info;

	if (uevent_idx == MHI_CLIENT_QMI_OUT ||
			uevent_idx == MHI_CLIENT_QMI_IN) {
		/* For legacy reasons for QTI client */
		reason.reason = MHI_DEV_CTRL_UPDATE;
		uci_ctrl_update(&reason);
	}
}

int mhi_ctrl_state_info(uint32_t *info)
int mhi_ctrl_state_info(uint32_t idx, uint32_t *info)
{
	if (!info) {
		pr_err("Invalid info\n");
		return -EINVAL;
	}

	if (idx == MHI_DEV_UEVENT_CTRL)
		*info = mhi_ctx->ctrl_info;
	mhi_log(MHI_MSG_VERBOSE, "ctrl:%d", mhi_ctx->ctrl_info);
	else
		*info = channel_state_info[idx].ctrl_info;

	mhi_log(MHI_MSG_VERBOSE, "idx:%d, ctrl:%d", idx, *info);

	return 0;
}
@@ -2866,7 +2893,8 @@ static int mhi_dev_probe(struct platform_device *pdev)
				"Failed to create IPC logging context\n");
		}
		mhi_uci_init();
		mhi_update_state_info(MHI_STATE_CONFIGURED);
		mhi_update_state_info(MHI_DEV_UEVENT_CTRL,
						MHI_STATE_CONFIGURED);
	}

	INIT_WORK(&mhi_ctx->pcie_event, mhi_dev_pcie_handle_event);
+25 −3
Original line number Diff line number Diff line
/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
/* Copyright (c) 2015-2018, 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
@@ -273,7 +273,7 @@ struct mhi_config {
#define MHI_ENV_VALUE			2
#define MHI_MASK_ROWS_CH_EV_DB		4
#define TRB_MAX_DATA_SIZE		8192
#define MHI_CTRL_STATE			25
#define MHI_CTRL_STATE			100
#define IPA_DMA_SYNC                    1
#define IPA_DMA_ASYNC                   0

@@ -603,6 +603,8 @@ struct mhi_dev {

	/*Register for interrupt*/
	bool				mhi_int;

	struct kobj_uevent_env		kobj_env;
};

struct mhi_req {
@@ -708,6 +710,14 @@ enum mhi_client_channel {
	MHI_MAX_CHANNELS = 102,
};

/* Use ID 0 for legacy /dev/mhi_ctrl. Channel 0 used for internal only */
#define MHI_DEV_UEVENT_CTRL	0

struct mhi_dev_uevent_info {
	enum mhi_client_channel	channel;
	enum mhi_ctrl_info	ctrl_info;
};

struct mhi_dev_iov {
	void		*addr;
	uint32_t	buf_size;
@@ -1221,7 +1231,19 @@ int mhi_dev_net_interface_init(void);

void mhi_dev_notify_a7_event(struct mhi_dev *mhi);

int mhi_ctrl_state_info(uint32_t *info);
/**
 * mhi_ctrl_state_info() - Provide MHI state info
 *		@idx: Channel number idx. Look at channel_state_info and
 *		pass the index for the corresponding channel.
 *		@info: Return the control info.
 *		MHI_STATE=CONFIGURED - MHI device is present but not ready
 *					for data traffic.
 *		MHI_STATE=CONNECTED - MHI device is ready for data transfer.
 *		MHI_STATE=DISCONNECTED - MHI device has its pipes suspended.
 *		exposes device nodes for the supported MHI software
 *		channels.
 */
int mhi_ctrl_state_info(uint32_t idx, uint32_t *info);

void uci_ctrl_update(struct mhi_dev_client_cb_reason *reason);

+59 −2
Original line number Diff line number Diff line
/* Copyright (c) 2015,2017, The Linux Foundation. All rights reserved.
/* Copyright (c) 2015,2017-2018, 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
@@ -441,6 +441,61 @@ static int mhi_uci_client_release(struct inode *mhi_inode,
	return rc;
}

static void  mhi_parse_state(char *buf, int *nbytes, uint32_t info)
{
	switch (info) {
	case MHI_STATE_CONNECTED:
		*nbytes = scnprintf(buf, MHI_CTRL_STATE,
			"CONNECTED");
		break;
	case MHI_STATE_DISCONNECTED:
		*nbytes = scnprintf(buf, MHI_CTRL_STATE,
			"DISCONNECTED");
		break;
	case MHI_STATE_CONFIGURED:
	default:
		*nbytes = scnprintf(buf, MHI_CTRL_STATE,
			"CONFIGURED");
		break;
	}
}

static int mhi_state_uevent(struct device *dev, struct kobj_uevent_env *env)
{
	int rc, nbytes = 0;
	uint32_t info = 0;
	char buf[MHI_CTRL_STATE];

	rc = mhi_ctrl_state_info(MHI_DEV_UEVENT_CTRL, &info);
	if (rc) {
		pr_err("Failed to obtain MHI_STATE\n");
		return -EINVAL;
	}

	mhi_parse_state(buf, &nbytes, info);
	add_uevent_var(env, "MHI_STATE=%s", buf);

	rc = mhi_ctrl_state_info(MHI_CLIENT_QMI_OUT, &info);
	if (rc) {
		pr_err("Failed to obtain channel 14 state\n");
		return -EINVAL;
	}
	nbytes = 0;
	mhi_parse_state(buf, &nbytes, info);
	add_uevent_var(env, "MHI_CHANNEL_STATE_14=%s", buf);

	rc = mhi_ctrl_state_info(MHI_CLIENT_MBIM_OUT, &info);
	if (rc) {
		pr_err("Failed to obtain channel 12 state\n");
		return -EINVAL;
	}
	nbytes = 0;
	mhi_parse_state(buf, &nbytes, info);
	add_uevent_var(env, "MHI_CHANNEL_STATE_12=%s", buf);

	return 0;
}

static ssize_t mhi_uci_ctrl_client_read(struct file *file,
		char __user *user_buf,
		size_t count, loff_t *offp)
@@ -455,7 +510,7 @@ static ssize_t mhi_uci_ctrl_client_read(struct file *file,
		return -EINVAL;

	uci_ctrl_handle = file->private_data;
	rc = mhi_ctrl_state_info(&info);
	rc = mhi_ctrl_state_info(MHI_CLIENT_QMI_OUT, &info);
	if (rc)
		return -EINVAL;

@@ -983,6 +1038,8 @@ int mhi_uci_init(void)
		cdev_del(&uci_ctxt.cdev_ctrl);
	}

	uci_ctxt.mhi_uci_class->dev_uevent = mhi_state_uevent;

	return 0;

failed_char_add: