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