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

Commit 8fdea91c authored by Yuanyuan Liu's avatar Yuanyuan Liu Committed by Gerrit - the friendly Code Review server
Browse files

cnss2: Serialize driver recovery in workqueue



Serialize driver recovery in workqueue so that everything is handled
in sequence.

Change-Id: I2a1dc956906db6d761acc7e4b00a170a2eadc072
CRs-fixed: 2012394
Signed-off-by: default avatarYuanyuan Liu <yuanliu@codeaurora.org>
parent 4be30a3e
Loading
Loading
Loading
Loading
+62 −38
Original line number Diff line number Diff line
@@ -564,6 +564,8 @@ static char *cnss_driver_event_to_str(enum cnss_driver_event_type type)
		return "REGISTER_DRIVER";
	case CNSS_DRIVER_EVENT_UNREGISTER_DRIVER:
		return "UNREGISTER_DRIVER";
	case CNSS_DRIVER_EVENT_RECOVERY:
		return "RECOVERY";
	case CNSS_DRIVER_EVENT_MAX:
		return "EVENT_MAX";
	}
@@ -571,14 +573,6 @@ static char *cnss_driver_event_to_str(enum cnss_driver_event_type type)
	return "UNKNOWN";
};

static void cnss_recovery_work_func(struct work_struct *work)
{
	struct cnss_recovery_work_t *ctx =
		container_of(work, struct cnss_recovery_work_t, work);

	cnss_self_recovery(ctx->dev, ctx->reason);
}

int cnss_driver_event_post(struct cnss_plat_data *plat_priv,
			   enum cnss_driver_event_type type,
			   bool sync, void *data)
@@ -1192,46 +1186,64 @@ static void cnss_crash_shutdown(const struct subsys_desc *subsys_desc)
	}
}

int cnss_self_recovery(struct device *dev,
static int cnss_do_recovery(struct cnss_plat_data *plat_priv,
			    enum cnss_recovery_reason reason)
{
	struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(dev);
	struct cnss_subsys_info *subsys_info;
	struct cnss_subsys_info *subsys_info =
		&plat_priv->subsys_info;

	if (!plat_priv) {
		cnss_pr_err("plat_priv is NULL!\n");
		return -EINVAL;
	}
	plat_priv->recovery_count++;

	if (!plat_priv->plat_dev) {
		cnss_pr_err("plat_dev is NULL!\n");
		return -EINVAL;
	if (plat_priv->device_id == QCA6174_DEVICE_ID) {
		cnss_shutdown(&subsys_info->subsys_desc, false);
		udelay(WLAN_RECOVERY_DELAY);
		cnss_powerup(&subsys_info->subsys_desc);
		return 0;
	}

	if (!plat_priv->driver_ops) {
		cnss_pr_err("Driver is not registered yet!\n");
		return -EINVAL;
	if (!subsys_info->subsys_device)
		return 0;

	subsys_set_crash_status(subsys_info->subsys_device, true);
	subsystem_restart_dev(subsys_info->subsys_device);

	return 0;
}

static int cnss_driver_recovery_hdlr(struct cnss_plat_data *plat_priv,
				     void *data)
{
	struct cnss_recovery_data *recovery_data = data;
	int ret = 0;

	cnss_pr_dbg("Driver recovery is triggered: reason %d\n",
		    recovery_data->reason);

	if (test_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state)) {
		cnss_pr_err("Recovery is already in progress!\n");
		return -EINVAL;
		ret = -EINVAL;
		goto out;
	}

	if (test_bit(CNSS_DRIVER_LOAD_UNLOAD, &plat_priv->driver_state)) {
		cnss_pr_err("Driver load or unload is in progress!\n");
		return -EINVAL;
		ret = -EINVAL;
		goto out;
	}

	subsys_info = &plat_priv->subsys_info;
	plat_priv->recovery_count++;
	set_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state);
	pm_stay_awake(dev);
	cnss_shutdown(&subsys_info->subsys_desc, false);
	udelay(WLAN_RECOVERY_DELAY);
	cnss_powerup(&subsys_info->subsys_desc);
	pm_relax(dev);

	ret = cnss_do_recovery(plat_priv, recovery_data->reason);

out:
	kfree(data);
	return ret;
}

int cnss_self_recovery(struct device *dev,
		       enum cnss_recovery_reason reason)
{
	cnss_schedule_recovery(dev, reason);
	return 0;
}
EXPORT_SYMBOL(cnss_self_recovery);
@@ -1240,11 +1252,20 @@ void cnss_schedule_recovery(struct device *dev,
			    enum cnss_recovery_reason reason)
{
	struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(dev);
	struct cnss_recovery_work_t *work = &plat_priv->cnss_recovery_work;
	struct cnss_recovery_data *data;
	int gfp = GFP_KERNEL;

	if (in_interrupt() || irqs_disabled())
		gfp = GFP_ATOMIC;

	data = kzalloc(sizeof(*data), gfp);
	if (!data)
		return;

	work->dev = dev;
	work->reason = reason;
	queue_work(plat_priv->event_wq, &work->work);
	data->reason = reason;
	cnss_driver_event_post(plat_priv,
			       CNSS_DRIVER_EVENT_RECOVERY,
			       false, data);
}
EXPORT_SYMBOL(cnss_schedule_recovery);

@@ -1349,6 +1370,10 @@ static void cnss_driver_event_work(struct work_struct *work)
		case CNSS_DRIVER_EVENT_UNREGISTER_DRIVER:
			ret = cnss_unregister_driver_hdlr(plat_priv);
			break;
		case CNSS_DRIVER_EVENT_RECOVERY:
			ret = cnss_driver_recovery_hdlr(plat_priv,
							event->data);
			break;
		default:
			cnss_pr_err("Invalid driver event type: %d",
				    event->type);
@@ -1662,8 +1687,7 @@ static int cnss_event_work_init(struct cnss_plat_data *plat_priv)

	INIT_WORK(&plat_priv->event_work, cnss_driver_event_work);
	INIT_LIST_HEAD(&plat_priv->event_list);
	INIT_WORK(&plat_priv->cnss_recovery_work.work,
		  cnss_recovery_work_func);

	return 0;
}

+2 −4
Original line number Diff line number Diff line
@@ -99,6 +99,7 @@ enum cnss_driver_event_type {
	CNSS_DRIVER_EVENT_COLD_BOOT_CAL_DONE,
	CNSS_DRIVER_EVENT_REGISTER_DRIVER,
	CNSS_DRIVER_EVENT_UNREGISTER_DRIVER,
	CNSS_DRIVER_EVENT_RECOVERY,
	CNSS_DRIVER_EVENT_MAX,
};

@@ -112,9 +113,7 @@ enum cnss_driver_state {
	CNSS_DRIVER_RECOVERY,
};

struct cnss_recovery_work_t {
	struct work_struct work;
	struct device *dev;
struct cnss_recovery_data {
	enum cnss_recovery_reason reason;
};

@@ -159,7 +158,6 @@ struct cnss_plat_data {
	spinlock_t event_lock; /* spinlock for driver work event handling */
	struct work_struct event_work;
	struct workqueue_struct *event_wq;
	struct cnss_recovery_work_t cnss_recovery_work;
	struct qmi_handle *qmi_wlfw_clnt;
	struct work_struct qmi_recv_msg_work;
	struct notifier_block qmi_wlfw_clnt_nb;
+2 −0
Original line number Diff line number Diff line
@@ -133,6 +133,8 @@ enum cnss_driver_mode {
enum cnss_recovery_reason {
	CNSS_REASON_DEFAULT,
	CNSS_REASON_LINK_DOWN,
	CNSS_REASON_RDDM,
	CNSS_REASON_TIMEOUT,
};

extern int cnss_wlan_register_driver(struct cnss_wlan_driver *driver);