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