linux/drivers/staging/greybus/fw-management.c
<<
>>
Prefs
   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}
 721