| 1 | /* SPDX-License-Identifier: GPL-2.0-only */ |
| 2 | /* |
| 3 | * Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES |
| 4 | */ |
| 5 | #ifndef __LINUX_FWCTL_H |
| 6 | #define __LINUX_FWCTL_H |
| 7 | #include <linux/device.h> |
| 8 | #include <linux/cdev.h> |
| 9 | #include <linux/cleanup.h> |
| 10 | #include <uapi/fwctl/fwctl.h> |
| 11 | |
| 12 | struct fwctl_device; |
| 13 | struct fwctl_uctx; |
| 14 | |
| 15 | /** |
| 16 | * struct fwctl_ops - Driver provided operations |
| 17 | * |
| 18 | * fwctl_unregister() will wait until all excuting ops are completed before it |
| 19 | * returns. Drivers should be mindful to not let their ops run for too long as |
| 20 | * it will block device hot unplug and module unloading. |
| 21 | */ |
| 22 | struct fwctl_ops { |
| 23 | /** |
| 24 | * @device_type: The drivers assigned device_type number. This is uABI. |
| 25 | */ |
| 26 | enum fwctl_device_type device_type; |
| 27 | /** |
| 28 | * @uctx_size: The size of the fwctl_uctx struct to allocate. The first |
| 29 | * bytes of this memory will be a fwctl_uctx. The driver can use the |
| 30 | * remaining bytes as its private memory. |
| 31 | */ |
| 32 | size_t uctx_size; |
| 33 | /** |
| 34 | * @open_uctx: Called when a file descriptor is opened before the uctx |
| 35 | * is ever used. |
| 36 | */ |
| 37 | int (*open_uctx)(struct fwctl_uctx *uctx); |
| 38 | /** |
| 39 | * @close_uctx: Called when the uctx is destroyed, usually when the FD |
| 40 | * is closed. |
| 41 | */ |
| 42 | void (*close_uctx)(struct fwctl_uctx *uctx); |
| 43 | /** |
| 44 | * @info: Implement FWCTL_INFO. Return a kmalloc() memory that is copied |
| 45 | * to out_device_data. On input length indicates the size of the user |
| 46 | * buffer on output it indicates the size of the memory. The driver can |
| 47 | * ignore length on input, the core code will handle everything. |
| 48 | */ |
| 49 | void *(*info)(struct fwctl_uctx *uctx, size_t *length); |
| 50 | /** |
| 51 | * @fw_rpc: Implement FWCTL_RPC. Deliver rpc_in/in_len to the FW and |
| 52 | * return the response and set out_len. rpc_in can be returned as the |
| 53 | * response pointer. Otherwise the returned pointer is freed with |
| 54 | * kvfree(). |
| 55 | */ |
| 56 | void *(*fw_rpc)(struct fwctl_uctx *uctx, enum fwctl_rpc_scope scope, |
| 57 | void *rpc_in, size_t in_len, size_t *out_len); |
| 58 | }; |
| 59 | |
| 60 | /** |
| 61 | * struct fwctl_device - Per-driver registration struct |
| 62 | * @dev: The sysfs (class/fwctl/fwctlXX) device |
| 63 | * |
| 64 | * Each driver instance will have one of these structs with the driver private |
| 65 | * data following immediately after. This struct is refcounted, it is freed by |
| 66 | * calling fwctl_put(). |
| 67 | */ |
| 68 | struct fwctl_device { |
| 69 | struct device dev; |
| 70 | /* private: */ |
| 71 | struct cdev cdev; |
| 72 | |
| 73 | /* Protect uctx_list */ |
| 74 | struct mutex uctx_list_lock; |
| 75 | struct list_head uctx_list; |
| 76 | /* |
| 77 | * Protect ops, held for write when ops becomes NULL during unregister, |
| 78 | * held for read whenever ops is loaded or an ops function is running. |
| 79 | */ |
| 80 | struct rw_semaphore registration_lock; |
| 81 | const struct fwctl_ops *ops; |
| 82 | }; |
| 83 | |
| 84 | struct fwctl_device *_fwctl_alloc_device(struct device *parent, |
| 85 | const struct fwctl_ops *ops, |
| 86 | size_t size); |
| 87 | /** |
| 88 | * fwctl_alloc_device - Allocate a fwctl |
| 89 | * @parent: Physical device that provides the FW interface |
| 90 | * @ops: Driver ops to register |
| 91 | * @drv_struct: 'struct driver_fwctl' that holds the struct fwctl_device |
| 92 | * @member: Name of the struct fwctl_device in @drv_struct |
| 93 | * |
| 94 | * This allocates and initializes the fwctl_device embedded in the drv_struct. |
| 95 | * Upon success the pointer must be freed via fwctl_put(). Returns a 'drv_struct |
| 96 | * \*' on success, NULL on error. |
| 97 | */ |
| 98 | #define fwctl_alloc_device(parent, ops, drv_struct, member) \ |
| 99 | ({ \ |
| 100 | static_assert(__same_type(struct fwctl_device, \ |
| 101 | ((drv_struct *)NULL)->member)); \ |
| 102 | static_assert(offsetof(drv_struct, member) == 0); \ |
| 103 | (drv_struct *)_fwctl_alloc_device(parent, ops, \ |
| 104 | sizeof(drv_struct)); \ |
| 105 | }) |
| 106 | |
| 107 | static inline struct fwctl_device *fwctl_get(struct fwctl_device *fwctl) |
| 108 | { |
| 109 | get_device(dev: &fwctl->dev); |
| 110 | return fwctl; |
| 111 | } |
| 112 | static inline void fwctl_put(struct fwctl_device *fwctl) |
| 113 | { |
| 114 | put_device(dev: &fwctl->dev); |
| 115 | } |
| 116 | DEFINE_FREE(fwctl, struct fwctl_device *, if (_T) fwctl_put(_T)); |
| 117 | |
| 118 | int fwctl_register(struct fwctl_device *fwctl); |
| 119 | void fwctl_unregister(struct fwctl_device *fwctl); |
| 120 | |
| 121 | /** |
| 122 | * struct fwctl_uctx - Per user FD context |
| 123 | * @fwctl: fwctl instance that owns the context |
| 124 | * |
| 125 | * Every FD opened by userspace will get a unique context allocation. Any driver |
| 126 | * private data will follow immediately after. |
| 127 | */ |
| 128 | struct fwctl_uctx { |
| 129 | struct fwctl_device *fwctl; |
| 130 | /* private: */ |
| 131 | /* Head at fwctl_device::uctx_list */ |
| 132 | struct list_head uctx_list_entry; |
| 133 | }; |
| 134 | |
| 135 | #endif |
| 136 | |