1
2
3
4
5
6
7
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
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;
39
40
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
49 u8 backend_fw_request_id;
50 u8 backend_fw_status;
51};
52
53
54
55
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
75
76
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
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
129
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
158
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
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
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
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
256
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
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
309
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
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
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
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
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
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
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
514
515
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
542
543
544
545
546
547
548
549
550
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
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
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
674
675
676 mutex_lock(&fw_mgmt->mutex);
677 fw_mgmt->disabled = true;
678 mutex_unlock(&fw_mgmt->mutex);
679
680
681 gb_connection_disable(fw_mgmt->connection);
682
683
684 mutex_lock(&list_mutex);
685 list_del(&fw_mgmt->node);
686 mutex_unlock(&list_mutex);
687
688
689
690
691
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