|
@@ -131,6 +131,8 @@ struct iwl_drv {
|
|
|
#define DVM_OP_MODE 0
|
|
|
#define MVM_OP_MODE 1
|
|
|
|
|
|
+/* Protects the table contents, i.e. the ops pointer & drv list */
|
|
|
+static struct mutex iwlwifi_opmode_table_mtx;
|
|
|
static struct iwlwifi_opmode_table {
|
|
|
const char *name; /* name: iwldvm, iwlmvm, etc */
|
|
|
const struct iwl_op_mode_ops *ops; /* pointer to op_mode ops */
|
|
@@ -899,6 +901,7 @@ static void iwl_ucode_callback(const struct firmware *ucode_raw, void *context)
|
|
|
release_firmware(ucode_raw);
|
|
|
complete(&drv->request_firmware_complete);
|
|
|
|
|
|
+ mutex_lock(&iwlwifi_opmode_table_mtx);
|
|
|
op = &iwlwifi_opmode_table[DVM_OP_MODE];
|
|
|
|
|
|
/* add this device to the list of devices using this op_mode */
|
|
@@ -910,6 +913,7 @@ static void iwl_ucode_callback(const struct firmware *ucode_raw, void *context)
|
|
|
} else {
|
|
|
request_module_nowait("%s", op->name);
|
|
|
}
|
|
|
+ mutex_unlock(&iwlwifi_opmode_table_mtx);
|
|
|
|
|
|
return;
|
|
|
|
|
@@ -944,6 +948,7 @@ struct iwl_drv *iwl_drv_start(struct iwl_trans *trans,
|
|
|
drv->cfg = cfg;
|
|
|
|
|
|
init_completion(&drv->request_firmware_complete);
|
|
|
+ INIT_LIST_HEAD(&drv->list);
|
|
|
|
|
|
ret = iwl_request_firmware(drv, true);
|
|
|
|
|
@@ -966,6 +971,16 @@ void iwl_drv_stop(struct iwl_drv *drv)
|
|
|
|
|
|
iwl_dealloc_ucode(drv);
|
|
|
|
|
|
+ mutex_lock(&iwlwifi_opmode_table_mtx);
|
|
|
+ /*
|
|
|
+ * List is empty (this item wasn't added)
|
|
|
+ * when firmware loading failed -- in that
|
|
|
+ * case we can't remove it from any list.
|
|
|
+ */
|
|
|
+ if (!list_empty(&drv->list))
|
|
|
+ list_del(&drv->list);
|
|
|
+ mutex_unlock(&iwlwifi_opmode_table_mtx);
|
|
|
+
|
|
|
kfree(drv);
|
|
|
}
|
|
|
|
|
@@ -988,6 +1003,7 @@ int iwl_opmode_register(const char *name, const struct iwl_op_mode_ops *ops)
|
|
|
int i;
|
|
|
struct iwl_drv *drv;
|
|
|
|
|
|
+ mutex_lock(&iwlwifi_opmode_table_mtx);
|
|
|
for (i = 0; i < ARRAY_SIZE(iwlwifi_opmode_table); i++) {
|
|
|
if (strcmp(iwlwifi_opmode_table[i].name, name))
|
|
|
continue;
|
|
@@ -995,8 +1011,10 @@ int iwl_opmode_register(const char *name, const struct iwl_op_mode_ops *ops)
|
|
|
list_for_each_entry(drv, &iwlwifi_opmode_table[i].drv, list)
|
|
|
drv->op_mode = ops->start(drv->trans, drv->cfg,
|
|
|
&drv->fw);
|
|
|
+ mutex_unlock(&iwlwifi_opmode_table_mtx);
|
|
|
return 0;
|
|
|
}
|
|
|
+ mutex_unlock(&iwlwifi_opmode_table_mtx);
|
|
|
return -EIO;
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(iwl_opmode_register);
|
|
@@ -1006,6 +1024,7 @@ void iwl_opmode_deregister(const char *name)
|
|
|
int i;
|
|
|
struct iwl_drv *drv;
|
|
|
|
|
|
+ mutex_lock(&iwlwifi_opmode_table_mtx);
|
|
|
for (i = 0; i < ARRAY_SIZE(iwlwifi_opmode_table); i++) {
|
|
|
if (strcmp(iwlwifi_opmode_table[i].name, name))
|
|
|
continue;
|
|
@@ -1018,8 +1037,10 @@ void iwl_opmode_deregister(const char *name)
|
|
|
drv->op_mode = NULL;
|
|
|
}
|
|
|
}
|
|
|
+ mutex_unlock(&iwlwifi_opmode_table_mtx);
|
|
|
return;
|
|
|
}
|
|
|
+ mutex_unlock(&iwlwifi_opmode_table_mtx);
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(iwl_opmode_deregister);
|
|
|
|
|
@@ -1027,6 +1048,8 @@ static int __init iwl_drv_init(void)
|
|
|
{
|
|
|
int i;
|
|
|
|
|
|
+ mutex_init(&iwlwifi_opmode_table_mtx);
|
|
|
+
|
|
|
for (i = 0; i < ARRAY_SIZE(iwlwifi_opmode_table); i++)
|
|
|
INIT_LIST_HEAD(&iwlwifi_opmode_table[i].drv);
|
|
|
|