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

Commit 2d603b98 authored by Arun Kumar Neelakantam's avatar Arun Kumar Neelakantam
Browse files

msm: smd_tty: Support multiple unique smd channels with the same name



When the Linux SMD driver detects a new channel, it creates a platform
device using the name of the channel as the name of the device, and uses
the channel type as the instance id in the platform device. Platform
drivers can only list the name of the device that the driver supports,
and only one platform driver is allowed to be registered for a given
device name.

The smd_tty port activation is failing for the smd channels which has same
name and tries to register same platform drivers again.

Add platfrom drivers registration check to avoid the multiple
registration of same platform drivers.

Change-Id: Iedf78e6931ac1982a203512e2a78c5939c487296
Signed-off-by: default avatarArun Kumar Neelakantam <aneela@codeaurora.org>
parent 84dce0d6
Loading
Loading
Loading
Loading
+127 −10
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@
#include <linux/pm.h>
#include <linux/platform_device.h>
#include <linux/sched.h>
#include <linux/slab.h>

#include <linux/tty.h>
#include <linux/tty_driver.h>
@@ -73,7 +74,6 @@ static int smd_tty_probe_done;
 * @tty_tsklt:  read tasklet
 * @buf_req_timer:  RX buffer retry timer
 * @ch_allocated:  completion set when SMD channel is allocated
 * @driver:  SMD channel platform driver context structure
 * @pil:  Peripheral Image Loader handle
 * @edge:  SMD edge associated with port
 * @ch_name:  SMD channel name associated with port
@@ -100,7 +100,6 @@ struct smd_tty_info {
	struct tasklet_struct tty_tsklt;
	struct timer_list buf_req_timer;
	struct completion ch_allocated;
	struct platform_driver driver;
	void *pil;
	uint32_t edge;
	char ch_name[SMD_MAX_CH_NAME_LEN];
@@ -120,6 +119,19 @@ struct smd_tty_info {
	struct wakeup_source ra_wakeup_source;
};

/**
 * struct smd_tty_pfdriver - SMD tty channel platform driver structure
 *
 * @list:  Adds this structure into smd_tty_platform_driver_list::list.
 * @ref_cnt:  reference count for this structure.
 * @driver:  SMD channel platform driver context structure
 */
struct smd_tty_pfdriver {
	struct list_head list;
	int ref_cnt;
	struct platform_driver driver;
};

/**
 * SMD port configuration.
 *
@@ -162,6 +174,9 @@ static struct smd_config smd_configs[] = {
static struct delayed_work loopback_work;
static struct smd_tty_info smd_tty[MAX_SMD_TTYS];

static DEFINE_MUTEX(smd_tty_pfdriver_lock_lha1);
static LIST_HEAD(smd_tty_pfdriver_list);

static int is_in_reset(struct smd_tty_info *info)
{
	return info->in_reset;
@@ -403,6 +418,113 @@ static int smd_tty_dummy_probe(struct platform_device *pdev)
	return -ENODEV;
}

/**
 * smd_tty_add_driver() - Add platform drivers for smd tty device
 *
 * @info: context for an individual SMD TTY device
 *
 * @returns: 0 for success, standard Linux error code otherwise
 *
 * This function is used to register platform driver once for all
 * smd tty devices which have same names and increment the reference
 * count for 2nd to nth devices.
 */
static int smd_tty_add_driver(struct smd_tty_info *info)
{
	int r = 0;
	struct smd_tty_pfdriver *smd_tty_pfdriverp;
	struct smd_tty_pfdriver *item;

	if (!info) {
		pr_err("%s on a NULL device structure\n", __func__);
		return -EINVAL;
	}

	SMD_TTY_INFO("Begin %s on smd_tty[%s]\n", __func__,
					info->ch_name);

	mutex_lock(&smd_tty_pfdriver_lock_lha1);
	list_for_each_entry(item, &smd_tty_pfdriver_list, list) {
		if (!strcmp(item->driver.driver.name, info->ch_name)) {
			SMD_TTY_INFO("%s:%s Driver Already reg. cnt:%d\n",
				__func__, info->ch_name, item->ref_cnt);
			++item->ref_cnt;
			goto exit;
		}
	}

	smd_tty_pfdriverp = kzalloc(sizeof(*smd_tty_pfdriverp), GFP_KERNEL);
	if (IS_ERR_OR_NULL(smd_tty_pfdriverp)) {
		pr_err("%s: kzalloc() failed for smd_tty_pfdriver[%s]\n",
			__func__, info->ch_name);
		r = -ENOMEM;
		goto exit;
	}

	smd_tty_pfdriverp->driver.probe = smd_tty_dummy_probe;
	smd_tty_pfdriverp->driver.driver.name = info->dev_name;
	smd_tty_pfdriverp->driver.driver.owner = THIS_MODULE;
	r = platform_driver_register(&smd_tty_pfdriverp->driver);
	if (r) {
		pr_err("%s: %s Platform driver reg. failed\n",
			__func__, info->ch_name);
		kfree(smd_tty_pfdriverp);
		goto exit;
	}
	++smd_tty_pfdriverp->ref_cnt;
	list_add(&smd_tty_pfdriverp->list, &smd_tty_pfdriver_list);

exit:
	SMD_TTY_INFO("End %s on smd_tty_ch[%s]\n", __func__, info->ch_name);
	mutex_unlock(&smd_tty_pfdriver_lock_lha1);
	return r;
}

/**
 * smd_tty_remove_driver() - Remove the platform drivers for smd tty device
 *
 * @info: context for an individual SMD TTY device
 *
 * This function is used to decrement the reference count on
 * platform drivers for smd pkt devices and removes the drivers
 * when the reference count becomes zero.
 */
static void smd_tty_remove_driver(struct smd_tty_info *info)
{
	struct smd_tty_pfdriver *smd_tty_pfdriverp;

	if (!info) {
		pr_err("%s on a NULL device\n", __func__);
		return;
	}

	SMD_TTY_INFO("Begin %s on smd_tty_ch[%s]\n", __func__,
					info->ch_name);
	mutex_lock(&smd_tty_pfdriver_lock_lha1);
	list_for_each_entry(smd_tty_pfdriverp, &smd_tty_pfdriver_list, list) {
		if (!strcmp(smd_tty_pfdriverp->driver.driver.name,
					info->ch_name)) {
			SMD_TTY_INFO("%s:%s Platform driver cnt:%d\n",
				__func__, info->ch_name,
				smd_tty_pfdriverp->ref_cnt);
			if (smd_tty_pfdriverp->ref_cnt > 0)
				--smd_tty_pfdriverp->ref_cnt;
			else
				pr_warn("%s reference count <= 0\n", __func__);
			break;
		}
	}

	if (smd_tty_pfdriverp->ref_cnt == 0) {
		platform_driver_unregister(&smd_tty_pfdriverp->driver);
		smd_tty_pfdriverp->driver.probe = NULL;
		list_del(&smd_tty_pfdriverp->list);
		kfree(smd_tty_pfdriverp);
	}
	mutex_unlock(&smd_tty_pfdriver_lock_lha1);
	SMD_TTY_INFO("End %s on smd_tty_ch[%s]\n", __func__, info->ch_name);
}

static int smd_tty_port_activate(struct tty_port *tport,
				 struct tty_struct *tty)
{
@@ -411,7 +533,6 @@ static int smd_tty_port_activate(struct tty_port *tport,
	struct smd_tty_info *info;
	const char *peripheral = NULL;


	if (n >= MAX_SMD_TTYS || !smd_tty[n].ch_name)
		return -ENODEV;

@@ -420,14 +541,10 @@ static int smd_tty_port_activate(struct tty_port *tport,
	mutex_lock(&info->open_lock_lha1);
	tty->driver_data = info;

	info->driver.probe = smd_tty_dummy_probe;
	info->driver.driver.name = smd_tty[n].dev_name;
	info->driver.driver.owner = THIS_MODULE;
	res = platform_driver_register(&info->driver);
	res = smd_tty_add_driver(info);
	if (res) {
		SMD_TTY_ERR("%s:%d Idx smd_tty_driver register failed %d\n",
							__func__, n, res);
		info->driver.probe = NULL;
		goto out;
	}

@@ -532,7 +649,7 @@ release_pil:
	subsystem_put(info->pil);

platform_unregister:
	platform_driver_unregister(&info->driver);
	smd_tty_remove_driver(info);

out:
	mutex_unlock(&info->open_lock_lha1);
@@ -571,7 +688,7 @@ static void smd_tty_port_shutdown(struct tty_port *tport)
	smd_close(info->ch);
	info->ch = NULL;
	subsystem_put(info->pil);
	platform_driver_unregister(&info->driver);
	smd_tty_remove_driver(info);

	mutex_unlock(&info->open_lock_lha1);
	tty_kref_put(tty);