File Index
Files and directories
Sign in
FILE

fw-management.c

File preview with syntax highlighting.

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