cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

fw-management.c (19080B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Greybus Firmware Management Protocol Driver.
      4 *
      5 * Copyright 2016 Google Inc.
      6 * Copyright 2016 Linaro Ltd.
      7 */
      8
      9#include <linux/cdev.h>
     10#include <linux/completion.h>
     11#include <linux/firmware.h>
     12#include <linux/fs.h>
     13#include <linux/idr.h>
     14#include <linux/ioctl.h>
     15#include <linux/uaccess.h>
     16#include <linux/greybus.h>
     17
     18#include "firmware.h"
     19#include "greybus_firmware.h"
     20
     21#define FW_MGMT_TIMEOUT_MS		1000
     22
     23struct fw_mgmt {
     24	struct device		*parent;
     25	struct gb_connection	*connection;
     26	struct kref		kref;
     27	struct list_head	node;
     28
     29	/* Common id-map for interface and backend firmware requests */
     30	struct ida		id_map;
     31	struct mutex		mutex;
     32	struct completion	completion;
     33	struct cdev		cdev;
     34	struct device		*class_device;
     35	dev_t			dev_num;
     36	unsigned int		timeout_jiffies;
     37	bool			disabled; /* connection getting disabled */
     38
     39	/* Interface Firmware specific fields */
     40	bool			mode_switch_started;
     41	bool			intf_fw_loaded;
     42	u8			intf_fw_request_id;
     43	u8			intf_fw_status;
     44	u16			intf_fw_major;
     45	u16			intf_fw_minor;
     46
     47	/* Backend Firmware specific fields */
     48	u8			backend_fw_request_id;
     49	u8			backend_fw_status;
     50};
     51
     52/*
     53 * Number of minor devices this driver supports.
     54 * There will be exactly one required per Interface.
     55 */
     56#define NUM_MINORS		U8_MAX
     57
     58static struct class *fw_mgmt_class;
     59static dev_t fw_mgmt_dev_num;
     60static DEFINE_IDA(fw_mgmt_minors_map);
     61static LIST_HEAD(fw_mgmt_list);
     62static DEFINE_MUTEX(list_mutex);
     63
     64static void fw_mgmt_kref_release(struct kref *kref)
     65{
     66	struct fw_mgmt *fw_mgmt = container_of(kref, struct fw_mgmt, kref);
     67
     68	ida_destroy(&fw_mgmt->id_map);
     69	kfree(fw_mgmt);
     70}
     71
     72/*
     73 * All users of fw_mgmt take a reference (from within list_mutex lock), before
     74 * they get a pointer to play with. And the structure will be freed only after
     75 * the last user has put the reference to it.
     76 */
     77static void put_fw_mgmt(struct fw_mgmt *fw_mgmt)
     78{
     79	kref_put(&fw_mgmt->kref, fw_mgmt_kref_release);
     80}
     81
     82/* Caller must call put_fw_mgmt() after using struct fw_mgmt */
     83static struct fw_mgmt *get_fw_mgmt(struct cdev *cdev)
     84{
     85	struct fw_mgmt *fw_mgmt;
     86
     87	mutex_lock(&list_mutex);
     88
     89	list_for_each_entry(fw_mgmt, &fw_mgmt_list, node) {
     90		if (&fw_mgmt->cdev == cdev) {
     91			kref_get(&fw_mgmt->kref);
     92			goto unlock;
     93		}
     94	}
     95
     96	fw_mgmt = NULL;
     97
     98unlock:
     99	mutex_unlock(&list_mutex);
    100
    101	return fw_mgmt;
    102}
    103
    104static int fw_mgmt_interface_fw_version_operation(struct fw_mgmt *fw_mgmt,
    105		struct fw_mgmt_ioc_get_intf_version *fw_info)
    106{
    107	struct gb_connection *connection = fw_mgmt->connection;
    108	struct gb_fw_mgmt_interface_fw_version_response response;
    109	int ret;
    110
    111	ret = gb_operation_sync(connection,
    112				GB_FW_MGMT_TYPE_INTERFACE_FW_VERSION, NULL, 0,
    113				&response, sizeof(response));
    114	if (ret) {
    115		dev_err(fw_mgmt->parent,
    116			"failed to get interface firmware version (%d)\n", ret);
    117		return ret;
    118	}
    119
    120	fw_info->major = le16_to_cpu(response.major);
    121	fw_info->minor = le16_to_cpu(response.minor);
    122
    123	strncpy(fw_info->firmware_tag, response.firmware_tag,
    124		GB_FIRMWARE_TAG_MAX_SIZE);
    125
    126	/*
    127	 * The firmware-tag should be NULL terminated, otherwise throw error but
    128	 * don't fail.
    129	 */
    130	if (fw_info->firmware_tag[GB_FIRMWARE_TAG_MAX_SIZE - 1] != '\0') {
    131		dev_err(fw_mgmt->parent,
    132			"fw-version: firmware-tag is not NULL terminated\n");
    133		fw_info->firmware_tag[GB_FIRMWARE_TAG_MAX_SIZE - 1] = '\0';
    134	}
    135
    136	return 0;
    137}
    138
    139static int fw_mgmt_load_and_validate_operation(struct fw_mgmt *fw_mgmt,
    140					       u8 load_method, const char *tag)
    141{
    142	struct gb_fw_mgmt_load_and_validate_fw_request request;
    143	int ret;
    144
    145	if (load_method != GB_FW_LOAD_METHOD_UNIPRO &&
    146	    load_method != GB_FW_LOAD_METHOD_INTERNAL) {
    147		dev_err(fw_mgmt->parent,
    148			"invalid load-method (%d)\n", load_method);
    149		return -EINVAL;
    150	}
    151
    152	request.load_method = load_method;
    153	strncpy(request.firmware_tag, tag, GB_FIRMWARE_TAG_MAX_SIZE);
    154
    155	/*
    156	 * The firmware-tag should be NULL terminated, otherwise throw error and
    157	 * fail.
    158	 */
    159	if (request.firmware_tag[GB_FIRMWARE_TAG_MAX_SIZE - 1] != '\0') {
    160		dev_err(fw_mgmt->parent, "load-and-validate: firmware-tag is not NULL terminated\n");
    161		return -EINVAL;
    162	}
    163
    164	/* Allocate ids from 1 to 255 (u8-max), 0 is an invalid id */
    165	ret = ida_simple_get(&fw_mgmt->id_map, 1, 256, GFP_KERNEL);
    166	if (ret < 0) {
    167		dev_err(fw_mgmt->parent, "failed to allocate request id (%d)\n",
    168			ret);
    169		return ret;
    170	}
    171
    172	fw_mgmt->intf_fw_request_id = ret;
    173	fw_mgmt->intf_fw_loaded = false;
    174	request.request_id = ret;
    175
    176	ret = gb_operation_sync(fw_mgmt->connection,
    177				GB_FW_MGMT_TYPE_LOAD_AND_VALIDATE_FW, &request,
    178				sizeof(request), NULL, 0);
    179	if (ret) {
    180		ida_simple_remove(&fw_mgmt->id_map,
    181				  fw_mgmt->intf_fw_request_id);
    182		fw_mgmt->intf_fw_request_id = 0;
    183		dev_err(fw_mgmt->parent,
    184			"load and validate firmware request failed (%d)\n",
    185			ret);
    186		return ret;
    187	}
    188
    189	return 0;
    190}
    191
    192static int fw_mgmt_interface_fw_loaded_operation(struct gb_operation *op)
    193{
    194	struct gb_connection *connection = op->connection;
    195	struct fw_mgmt *fw_mgmt = gb_connection_get_data(connection);
    196	struct gb_fw_mgmt_loaded_fw_request *request;
    197
    198	/* No pending load and validate request ? */
    199	if (!fw_mgmt->intf_fw_request_id) {
    200		dev_err(fw_mgmt->parent,
    201			"unexpected firmware loaded request received\n");
    202		return -ENODEV;
    203	}
    204
    205	if (op->request->payload_size != sizeof(*request)) {
    206		dev_err(fw_mgmt->parent, "illegal size of firmware loaded request (%zu != %zu)\n",
    207			op->request->payload_size, sizeof(*request));
    208		return -EINVAL;
    209	}
    210
    211	request = op->request->payload;
    212
    213	/* Invalid request-id ? */
    214	if (request->request_id != fw_mgmt->intf_fw_request_id) {
    215		dev_err(fw_mgmt->parent, "invalid request id for firmware loaded request (%02u != %02u)\n",
    216			fw_mgmt->intf_fw_request_id, request->request_id);
    217		return -ENODEV;
    218	}
    219
    220	ida_simple_remove(&fw_mgmt->id_map, fw_mgmt->intf_fw_request_id);
    221	fw_mgmt->intf_fw_request_id = 0;
    222	fw_mgmt->intf_fw_status = request->status;
    223	fw_mgmt->intf_fw_major = le16_to_cpu(request->major);
    224	fw_mgmt->intf_fw_minor = le16_to_cpu(request->minor);
    225
    226	if (fw_mgmt->intf_fw_status == GB_FW_LOAD_STATUS_FAILED)
    227		dev_err(fw_mgmt->parent,
    228			"failed to load interface firmware, status:%02x\n",
    229			fw_mgmt->intf_fw_status);
    230	else if (fw_mgmt->intf_fw_status == GB_FW_LOAD_STATUS_VALIDATION_FAILED)
    231		dev_err(fw_mgmt->parent,
    232			"failed to validate interface firmware, status:%02x\n",
    233			fw_mgmt->intf_fw_status);
    234	else
    235		fw_mgmt->intf_fw_loaded = true;
    236
    237	complete(&fw_mgmt->completion);
    238
    239	return 0;
    240}
    241
    242static int fw_mgmt_backend_fw_version_operation(struct fw_mgmt *fw_mgmt,
    243		struct fw_mgmt_ioc_get_backend_version *fw_info)
    244{
    245	struct gb_connection *connection = fw_mgmt->connection;
    246	struct gb_fw_mgmt_backend_fw_version_request request;
    247	struct gb_fw_mgmt_backend_fw_version_response response;
    248	int ret;
    249
    250	strncpy(request.firmware_tag, fw_info->firmware_tag,
    251		GB_FIRMWARE_TAG_MAX_SIZE);
    252
    253	/*
    254	 * The firmware-tag should be NULL terminated, otherwise throw error and
    255	 * fail.
    256	 */
    257	if (request.firmware_tag[GB_FIRMWARE_TAG_MAX_SIZE - 1] != '\0') {
    258		dev_err(fw_mgmt->parent, "backend-version: firmware-tag is not NULL terminated\n");
    259		return -EINVAL;
    260	}
    261
    262	ret = gb_operation_sync(connection,
    263				GB_FW_MGMT_TYPE_BACKEND_FW_VERSION, &request,
    264				sizeof(request), &response, sizeof(response));
    265	if (ret) {
    266		dev_err(fw_mgmt->parent, "failed to get version of %s backend firmware (%d)\n",
    267			fw_info->firmware_tag, ret);
    268		return ret;
    269	}
    270
    271	fw_info->status = response.status;
    272
    273	/* Reset version as that should be non-zero only for success case */
    274	fw_info->major = 0;
    275	fw_info->minor = 0;
    276
    277	switch (fw_info->status) {
    278	case GB_FW_BACKEND_VERSION_STATUS_SUCCESS:
    279		fw_info->major = le16_to_cpu(response.major);
    280		fw_info->minor = le16_to_cpu(response.minor);
    281		break;
    282	case GB_FW_BACKEND_VERSION_STATUS_NOT_AVAILABLE:
    283	case GB_FW_BACKEND_VERSION_STATUS_RETRY:
    284		break;
    285	case GB_FW_BACKEND_VERSION_STATUS_NOT_SUPPORTED:
    286		dev_err(fw_mgmt->parent,
    287			"Firmware with tag %s is not supported by Interface\n",
    288			fw_info->firmware_tag);
    289		break;
    290	default:
    291		dev_err(fw_mgmt->parent, "Invalid status received: %u\n",
    292			fw_info->status);
    293	}
    294
    295	return 0;
    296}
    297
    298static int fw_mgmt_backend_fw_update_operation(struct fw_mgmt *fw_mgmt,
    299					       char *tag)
    300{
    301	struct gb_fw_mgmt_backend_fw_update_request request;
    302	int ret;
    303
    304	strncpy(request.firmware_tag, tag, GB_FIRMWARE_TAG_MAX_SIZE);
    305
    306	/*
    307	 * The firmware-tag should be NULL terminated, otherwise throw error and
    308	 * fail.
    309	 */
    310	if (request.firmware_tag[GB_FIRMWARE_TAG_MAX_SIZE - 1] != '\0') {
    311		dev_err(fw_mgmt->parent, "backend-update: firmware-tag is not NULL terminated\n");
    312		return -EINVAL;
    313	}
    314
    315	/* Allocate ids from 1 to 255 (u8-max), 0 is an invalid id */
    316	ret = ida_simple_get(&fw_mgmt->id_map, 1, 256, GFP_KERNEL);
    317	if (ret < 0) {
    318		dev_err(fw_mgmt->parent, "failed to allocate request id (%d)\n",
    319			ret);
    320		return ret;
    321	}
    322
    323	fw_mgmt->backend_fw_request_id = ret;
    324	request.request_id = ret;
    325
    326	ret = gb_operation_sync(fw_mgmt->connection,
    327				GB_FW_MGMT_TYPE_BACKEND_FW_UPDATE, &request,
    328				sizeof(request), NULL, 0);
    329	if (ret) {
    330		ida_simple_remove(&fw_mgmt->id_map,
    331				  fw_mgmt->backend_fw_request_id);
    332		fw_mgmt->backend_fw_request_id = 0;
    333		dev_err(fw_mgmt->parent,
    334			"backend %s firmware update request failed (%d)\n", tag,
    335			ret);
    336		return ret;
    337	}
    338
    339	return 0;
    340}
    341
    342static int fw_mgmt_backend_fw_updated_operation(struct gb_operation *op)
    343{
    344	struct gb_connection *connection = op->connection;
    345	struct fw_mgmt *fw_mgmt = gb_connection_get_data(connection);
    346	struct gb_fw_mgmt_backend_fw_updated_request *request;
    347
    348	/* No pending load and validate request ? */
    349	if (!fw_mgmt->backend_fw_request_id) {
    350		dev_err(fw_mgmt->parent, "unexpected backend firmware updated request received\n");
    351		return -ENODEV;
    352	}
    353
    354	if (op->request->payload_size != sizeof(*request)) {
    355		dev_err(fw_mgmt->parent, "illegal size of backend firmware updated request (%zu != %zu)\n",
    356			op->request->payload_size, sizeof(*request));
    357		return -EINVAL;
    358	}
    359
    360	request = op->request->payload;
    361
    362	/* Invalid request-id ? */
    363	if (request->request_id != fw_mgmt->backend_fw_request_id) {
    364		dev_err(fw_mgmt->parent, "invalid request id for backend firmware updated request (%02u != %02u)\n",
    365			fw_mgmt->backend_fw_request_id, request->request_id);
    366		return -ENODEV;
    367	}
    368
    369	ida_simple_remove(&fw_mgmt->id_map, fw_mgmt->backend_fw_request_id);
    370	fw_mgmt->backend_fw_request_id = 0;
    371	fw_mgmt->backend_fw_status = request->status;
    372
    373	if ((fw_mgmt->backend_fw_status != GB_FW_BACKEND_FW_STATUS_SUCCESS) &&
    374	    (fw_mgmt->backend_fw_status != GB_FW_BACKEND_FW_STATUS_RETRY))
    375		dev_err(fw_mgmt->parent,
    376			"failed to load backend firmware: %02x\n",
    377			fw_mgmt->backend_fw_status);
    378
    379	complete(&fw_mgmt->completion);
    380
    381	return 0;
    382}
    383
    384/* Char device fops */
    385
    386static int fw_mgmt_open(struct inode *inode, struct file *file)
    387{
    388	struct fw_mgmt *fw_mgmt = get_fw_mgmt(inode->i_cdev);
    389
    390	/* fw_mgmt structure can't get freed until file descriptor is closed */
    391	if (fw_mgmt) {
    392		file->private_data = fw_mgmt;
    393		return 0;
    394	}
    395
    396	return -ENODEV;
    397}
    398
    399static int fw_mgmt_release(struct inode *inode, struct file *file)
    400{
    401	struct fw_mgmt *fw_mgmt = file->private_data;
    402
    403	put_fw_mgmt(fw_mgmt);
    404	return 0;
    405}
    406
    407static int fw_mgmt_ioctl(struct fw_mgmt *fw_mgmt, unsigned int cmd,
    408			 void __user *buf)
    409{
    410	struct fw_mgmt_ioc_get_intf_version intf_fw_info;
    411	struct fw_mgmt_ioc_get_backend_version backend_fw_info;
    412	struct fw_mgmt_ioc_intf_load_and_validate intf_load;
    413	struct fw_mgmt_ioc_backend_fw_update backend_update;
    414	unsigned int timeout;
    415	int ret;
    416
    417	/* Reject any operations after mode-switch has started */
    418	if (fw_mgmt->mode_switch_started)
    419		return -EBUSY;
    420
    421	switch (cmd) {
    422	case FW_MGMT_IOC_GET_INTF_FW:
    423		ret = fw_mgmt_interface_fw_version_operation(fw_mgmt,
    424							     &intf_fw_info);
    425		if (ret)
    426			return ret;
    427
    428		if (copy_to_user(buf, &intf_fw_info, sizeof(intf_fw_info)))
    429			return -EFAULT;
    430
    431		return 0;
    432	case FW_MGMT_IOC_GET_BACKEND_FW:
    433		if (copy_from_user(&backend_fw_info, buf,
    434				   sizeof(backend_fw_info)))
    435			return -EFAULT;
    436
    437		ret = fw_mgmt_backend_fw_version_operation(fw_mgmt,
    438							   &backend_fw_info);
    439		if (ret)
    440			return ret;
    441
    442		if (copy_to_user(buf, &backend_fw_info,
    443				 sizeof(backend_fw_info)))
    444			return -EFAULT;
    445
    446		return 0;
    447	case FW_MGMT_IOC_INTF_LOAD_AND_VALIDATE:
    448		if (copy_from_user(&intf_load, buf, sizeof(intf_load)))
    449			return -EFAULT;
    450
    451		ret = fw_mgmt_load_and_validate_operation(fw_mgmt,
    452				intf_load.load_method, intf_load.firmware_tag);
    453		if (ret)
    454			return ret;
    455
    456		if (!wait_for_completion_timeout(&fw_mgmt->completion,
    457						 fw_mgmt->timeout_jiffies)) {
    458			dev_err(fw_mgmt->parent, "timed out waiting for firmware load and validation to finish\n");
    459			return -ETIMEDOUT;
    460		}
    461
    462		intf_load.status = fw_mgmt->intf_fw_status;
    463		intf_load.major = fw_mgmt->intf_fw_major;
    464		intf_load.minor = fw_mgmt->intf_fw_minor;
    465
    466		if (copy_to_user(buf, &intf_load, sizeof(intf_load)))
    467			return -EFAULT;
    468
    469		return 0;
    470	case FW_MGMT_IOC_INTF_BACKEND_FW_UPDATE:
    471		if (copy_from_user(&backend_update, buf,
    472				   sizeof(backend_update)))
    473			return -EFAULT;
    474
    475		ret = fw_mgmt_backend_fw_update_operation(fw_mgmt,
    476				backend_update.firmware_tag);
    477		if (ret)
    478			return ret;
    479
    480		if (!wait_for_completion_timeout(&fw_mgmt->completion,
    481						 fw_mgmt->timeout_jiffies)) {
    482			dev_err(fw_mgmt->parent, "timed out waiting for backend firmware update to finish\n");
    483			return -ETIMEDOUT;
    484		}
    485
    486		backend_update.status = fw_mgmt->backend_fw_status;
    487
    488		if (copy_to_user(buf, &backend_update, sizeof(backend_update)))
    489			return -EFAULT;
    490
    491		return 0;
    492	case FW_MGMT_IOC_SET_TIMEOUT_MS:
    493		if (get_user(timeout, (unsigned int __user *)buf))
    494			return -EFAULT;
    495
    496		if (!timeout) {
    497			dev_err(fw_mgmt->parent, "timeout can't be zero\n");
    498			return -EINVAL;
    499		}
    500
    501		fw_mgmt->timeout_jiffies = msecs_to_jiffies(timeout);
    502
    503		return 0;
    504	case FW_MGMT_IOC_MODE_SWITCH:
    505		if (!fw_mgmt->intf_fw_loaded) {
    506			dev_err(fw_mgmt->parent,
    507				"Firmware not loaded for mode-switch\n");
    508			return -EPERM;
    509		}
    510
    511		/*
    512		 * Disallow new ioctls as the fw-core bundle driver is going to
    513		 * get disconnected soon and the character device will get
    514		 * removed.
    515		 */
    516		fw_mgmt->mode_switch_started = true;
    517
    518		ret = gb_interface_request_mode_switch(fw_mgmt->connection->intf);
    519		if (ret) {
    520			dev_err(fw_mgmt->parent, "Mode-switch failed: %d\n",
    521				ret);
    522			fw_mgmt->mode_switch_started = false;
    523			return ret;
    524		}
    525
    526		return 0;
    527	default:
    528		return -ENOTTY;
    529	}
    530}
    531
    532static long fw_mgmt_ioctl_unlocked(struct file *file, unsigned int cmd,
    533				   unsigned long arg)
    534{
    535	struct fw_mgmt *fw_mgmt = file->private_data;
    536	struct gb_bundle *bundle = fw_mgmt->connection->bundle;
    537	int ret = -ENODEV;
    538
    539	/*
    540	 * Serialize ioctls.
    541	 *
    542	 * We don't want the user to do few operations in parallel. For example,
    543	 * updating Interface firmware in parallel for the same Interface. There
    544	 * is no need to do things in parallel for speed and we can avoid having
    545	 * complicated code for now.
    546	 *
    547	 * This is also used to protect ->disabled, which is used to check if
    548	 * the connection is getting disconnected, so that we don't start any
    549	 * new operations.
    550	 */
    551	mutex_lock(&fw_mgmt->mutex);
    552	if (!fw_mgmt->disabled) {
    553		ret = gb_pm_runtime_get_sync(bundle);
    554		if (!ret) {
    555			ret = fw_mgmt_ioctl(fw_mgmt, cmd, (void __user *)arg);
    556			gb_pm_runtime_put_autosuspend(bundle);
    557		}
    558	}
    559	mutex_unlock(&fw_mgmt->mutex);
    560
    561	return ret;
    562}
    563
    564static const struct file_operations fw_mgmt_fops = {
    565	.owner		= THIS_MODULE,
    566	.open		= fw_mgmt_open,
    567	.release	= fw_mgmt_release,
    568	.unlocked_ioctl	= fw_mgmt_ioctl_unlocked,
    569};
    570
    571int gb_fw_mgmt_request_handler(struct gb_operation *op)
    572{
    573	u8 type = op->type;
    574
    575	switch (type) {
    576	case GB_FW_MGMT_TYPE_LOADED_FW:
    577		return fw_mgmt_interface_fw_loaded_operation(op);
    578	case GB_FW_MGMT_TYPE_BACKEND_FW_UPDATED:
    579		return fw_mgmt_backend_fw_updated_operation(op);
    580	default:
    581		dev_err(&op->connection->bundle->dev,
    582			"unsupported request: %u\n", type);
    583		return -EINVAL;
    584	}
    585}
    586
    587int gb_fw_mgmt_connection_init(struct gb_connection *connection)
    588{
    589	struct fw_mgmt *fw_mgmt;
    590	int ret, minor;
    591
    592	if (!connection)
    593		return 0;
    594
    595	fw_mgmt = kzalloc(sizeof(*fw_mgmt), GFP_KERNEL);
    596	if (!fw_mgmt)
    597		return -ENOMEM;
    598
    599	fw_mgmt->parent = &connection->bundle->dev;
    600	fw_mgmt->timeout_jiffies = msecs_to_jiffies(FW_MGMT_TIMEOUT_MS);
    601	fw_mgmt->connection = connection;
    602
    603	gb_connection_set_data(connection, fw_mgmt);
    604	init_completion(&fw_mgmt->completion);
    605	ida_init(&fw_mgmt->id_map);
    606	mutex_init(&fw_mgmt->mutex);
    607	kref_init(&fw_mgmt->kref);
    608
    609	mutex_lock(&list_mutex);
    610	list_add(&fw_mgmt->node, &fw_mgmt_list);
    611	mutex_unlock(&list_mutex);
    612
    613	ret = gb_connection_enable(connection);
    614	if (ret)
    615		goto err_list_del;
    616
    617	minor = ida_simple_get(&fw_mgmt_minors_map, 0, NUM_MINORS, GFP_KERNEL);
    618	if (minor < 0) {
    619		ret = minor;
    620		goto err_connection_disable;
    621	}
    622
    623	/* Add a char device to allow userspace to interact with fw-mgmt */
    624	fw_mgmt->dev_num = MKDEV(MAJOR(fw_mgmt_dev_num), minor);
    625	cdev_init(&fw_mgmt->cdev, &fw_mgmt_fops);
    626
    627	ret = cdev_add(&fw_mgmt->cdev, fw_mgmt->dev_num, 1);
    628	if (ret)
    629		goto err_remove_ida;
    630
    631	/* Add a soft link to the previously added char-dev within the bundle */
    632	fw_mgmt->class_device = device_create(fw_mgmt_class, fw_mgmt->parent,
    633					      fw_mgmt->dev_num, NULL,
    634					      "gb-fw-mgmt-%d", minor);
    635	if (IS_ERR(fw_mgmt->class_device)) {
    636		ret = PTR_ERR(fw_mgmt->class_device);
    637		goto err_del_cdev;
    638	}
    639
    640	return 0;
    641
    642err_del_cdev:
    643	cdev_del(&fw_mgmt->cdev);
    644err_remove_ida:
    645	ida_simple_remove(&fw_mgmt_minors_map, minor);
    646err_connection_disable:
    647	gb_connection_disable(connection);
    648err_list_del:
    649	mutex_lock(&list_mutex);
    650	list_del(&fw_mgmt->node);
    651	mutex_unlock(&list_mutex);
    652
    653	put_fw_mgmt(fw_mgmt);
    654
    655	return ret;
    656}
    657
    658void gb_fw_mgmt_connection_exit(struct gb_connection *connection)
    659{
    660	struct fw_mgmt *fw_mgmt;
    661
    662	if (!connection)
    663		return;
    664
    665	fw_mgmt = gb_connection_get_data(connection);
    666
    667	device_destroy(fw_mgmt_class, fw_mgmt->dev_num);
    668	cdev_del(&fw_mgmt->cdev);
    669	ida_simple_remove(&fw_mgmt_minors_map, MINOR(fw_mgmt->dev_num));
    670
    671	/*
    672	 * Disallow any new ioctl operations on the char device and wait for
    673	 * existing ones to finish.
    674	 */
    675	mutex_lock(&fw_mgmt->mutex);
    676	fw_mgmt->disabled = true;
    677	mutex_unlock(&fw_mgmt->mutex);
    678
    679	/* All pending greybus operations should have finished by now */
    680	gb_connection_disable(fw_mgmt->connection);
    681
    682	/* Disallow new users to get access to the fw_mgmt structure */
    683	mutex_lock(&list_mutex);
    684	list_del(&fw_mgmt->node);
    685	mutex_unlock(&list_mutex);
    686
    687	/*
    688	 * All current users of fw_mgmt would have taken a reference to it by
    689	 * now, we can drop our reference and wait the last user will get
    690	 * fw_mgmt freed.
    691	 */
    692	put_fw_mgmt(fw_mgmt);
    693}
    694
    695int fw_mgmt_init(void)
    696{
    697	int ret;
    698
    699	fw_mgmt_class = class_create(THIS_MODULE, "gb_fw_mgmt");
    700	if (IS_ERR(fw_mgmt_class))
    701		return PTR_ERR(fw_mgmt_class);
    702
    703	ret = alloc_chrdev_region(&fw_mgmt_dev_num, 0, NUM_MINORS,
    704				  "gb_fw_mgmt");
    705	if (ret)
    706		goto err_remove_class;
    707
    708	return 0;
    709
    710err_remove_class:
    711	class_destroy(fw_mgmt_class);
    712	return ret;
    713}
    714
    715void fw_mgmt_exit(void)
    716{
    717	unregister_chrdev_region(fw_mgmt_dev_num, NUM_MINORS);
    718	class_destroy(fw_mgmt_class);
    719	ida_destroy(&fw_mgmt_minors_map);
    720}