On 9/13/21 2:36 AM, Xu Yilun wrote: > On Mon, Sep 13, 2021 at 02:48:45PM +0800, Xu Yilun wrote: >> On Fri, Sep 10, 2021 at 04:18:26PM -0700, Russ Weight wrote: >>> >>> On 9/10/21 1:22 AM, Xu Yilun wrote: >>>> On Wed, Sep 08, 2021 at 07:18:42PM -0700, Russ Weight wrote: >>>>> Extend the FPGA Image Load class driver to include IOCTL support >>>>> (FPGA_IMAGE_LOAD_WRITE) for initiating an upload of an image to a device. >>>>> The IOCTL will return immediately, and the update will begin in the >>>>> context of a kernel worker thread. >>>>> >>>>> Signed-off-by: Russ Weight <russell.h.weight@xxxxxxxxx> >>>>> --- >>>>> v15: >>>>> - Compare to previous patch: >>>>> [PATCH v14 2/6] fpga: sec-mgr: enable secure updates >>>>> - Changed file, symbol, and config names to reflect the new driver name >>>>> - Removed update/filename sysfs file and added the FPGA_IMAGE_LOAD_WRITE >>>>> IOCTL for writing the image data to the FPGA card. The driver no longer >>>>> uses the request_firmware framework. >>>>> - Fixed some error return values in fpga_image_load_register() >>>>> - Removed signed-off/reviewed-by tags >>>>> v14: >>>>> - Added MAINTAINERS reference for >>>>> Documentation/ABI/testing/sysfs-class-fpga-sec-mgr >>>>> - Updated ABI documentation date and kernel version >>>>> - Updated copyright to 2021 >>>>> v13: >>>>> - Change "if (count == 0 || " to "if (!count || " >>>>> - Improve error message: "Attempt to register without all required ops\n" >>>>> v12: >>>>> - Updated Date and KernelVersion fields in ABI documentation >>>>> - Removed size parameter from write_blk() op - it is now up to >>>>> the lower-level driver to determine the appropriate size and >>>>> to update smgr->remaining_size accordingly. >>>>> v11: >>>>> - Fixed a spelling error in a comment >>>>> - Initialize smgr->err_code and smgr->progress explicitly in >>>>> fpga_sec_mgr_create() instead of accepting the default 0 value. >>>>> v10: >>>>> - Rebased to 5.12-rc2 next >>>>> - Updated Date and KernelVersion in ABI documentation >>>>> v9: >>>>> - Updated Date and KernelVersion in ABI documentation >>>>> v8: >>>>> - No change >>>>> v7: >>>>> - Changed Date in documentation file to December 2020 >>>>> - Changed filename_store() to use kmemdup_nul() instead of >>>>> kstrndup() and changed the count to not assume a line-return. >>>>> v6: >>>>> - Changed "security update" to "secure update" in commit message >>>>> v5: >>>>> - When checking the return values for functions of type enum >>>>> fpga_sec_err err_code, test for FPGA_SEC_ERR_NONE instead of 0 >>>>> v4: >>>>> - Changed from "Intel FPGA Security Manager" to FPGA Security Manager" >>>>> and removed unnecessary references to "Intel". >>>>> - Changed: iops -> sops, imgr -> smgr, IFPGA_ -> FPGA_, ifpga_ to fpga_ >>>>> v3: >>>>> - Removed unnecessary "goto done" >>>>> - Added a comment to explain imgr->driver_unload in >>>>> ifpga_sec_mgr_unregister() >>>>> v2: >>>>> - Bumped documentation date and version >>>>> - Removed explicit value assignments in enums >>>>> - Other minor code cleanup per review comments >>>>> --- >>>>> Documentation/fpga/fpga-image-load.rst | 21 +++ >>>>> MAINTAINERS | 1 + >>>>> drivers/fpga/fpga-image-load.c | 224 ++++++++++++++++++++++++- >>>>> include/linux/fpga/fpga-image-load.h | 29 ++++ >>>>> include/uapi/linux/fpga-image-load.h | 58 +++++++ >>>>> 5 files changed, 329 insertions(+), 4 deletions(-) >>>>> create mode 100644 include/uapi/linux/fpga-image-load.h >>>>> >>>>> diff --git a/Documentation/fpga/fpga-image-load.rst b/Documentation/fpga/fpga-image-load.rst >>>>> index a6e53ac66026..2ca8d2f0212d 100644 >>>>> --- a/Documentation/fpga/fpga-image-load.rst >>>>> +++ b/Documentation/fpga/fpga-image-load.rst >>>>> @@ -8,3 +8,24 @@ The FPGA Image Load class driver provides a common API for user-space >>>>> tools to manage image uploads to FPGA devices. Device drivers that >>>>> instantiate the FPGA Image Load class driver will interact with the >>>>> target device to transfer and authenticate the image data. >>>>> + >>>>> +User API >>>>> +======== >>>>> + >>>>> +open >>>>> +---- >>>>> + >>>>> +An FPGA Image Load device is opened exclusively to control an image load. >>>>> +Image loads are processed by a kernel worker thread. A user may choose >>>>> +close the device while the upload continues. >>>> Why we allow the user to close the dev while the uploading is ongoing? >>>> Seems it introduces more checking effort when another user open the dev >>>> again and try another uploading. >>> A user could choose to write their own tools. How would we prevent >>> them from closing the device while an update is in progress? Should >>> we attempt to cancel the update if they close the device? >> I think we could try to cancel the update when close(), if we cannot >> cancel we block on close(). >> >> The driver should be responsible for the integrity of the update flow, >> it is not prefered a user starts an update and leaves, but canceled by >> another user. OK - I can make those changes for the next version. >>> Concurrent updates are already prevented by returning EBUSY for the >>> FPGA_IMAGE_LOAD_WRITE IOCTL if an update is already in progress. >>> >>> A single IOCTL is sufficient to do a full update. Maybe a user >>> would want to have three small tools: update_start, update_status, >>> update_cancel, each of which would open the device, do an IOCTL, >>> and then close the device. This is analogous to the sysfs >>> implementation (no open/close that bounds the entire sequence). >>> >>> With the current design, we do an exclusive open. As long as the user >>> keeps the device open, no other process can open the device to start >>> a new update, cancel, or collect status. >>> >>> Those are my thoughts. What do you think? Is it OK as is? Or should >>> I make some changes here? >>> >>>>> + >>>>> +ioctl >>>>> +----- >>>>> + >>>>> +FPGA_IMAGE_LOAD_WRITE: >>>>> + >>>>> +Start an image load with the provided image buffer. This IOCTL returns >>>>> +immediately after starting a kernel worker thread to process the image load >>>>> +which could take as long a 40 minutes depending on the actual device being >>>>> +updated. This is an exclusive operation; an attempt to start concurrent image >>>>> +load for the same device will fail with EBUSY. >>>>> diff --git a/MAINTAINERS b/MAINTAINERS >>>>> index 4e7f48fa7e5c..637bc003ca81 100644 >>>>> --- a/MAINTAINERS >>>>> +++ b/MAINTAINERS >>>>> @@ -7365,6 +7365,7 @@ S: Maintained >>>>> F: Documentation/fpga/fpga-image-load.rst >>>>> F: drivers/fpga/fpga-image-load.c >>>>> F: include/linux/fpga/fpga-image-load.h >>>>> +F: include/uapi/linux/fpga-image-load.h >>>>> >>>>> FPU EMULATOR >>>>> M: Bill Metzenthen <billm@xxxxxxxxxxxxx> >>>>> diff --git a/drivers/fpga/fpga-image-load.c b/drivers/fpga/fpga-image-load.c >>>>> index 7d75bbcff541..f5ccfa9dd977 100644 >>>>> --- a/drivers/fpga/fpga-image-load.c >>>>> +++ b/drivers/fpga/fpga-image-load.c >>>>> @@ -5,18 +5,181 @@ >>>>> * Copyright (C) 2019-2021 Intel Corporation, Inc. >>>>> */ >>>>> >>>>> +#include <linux/delay.h> >>>>> #include <linux/fpga/fpga-image-load.h> >>>>> +#include <linux/fs.h> >>>>> +#include <linux/kernel.h> >>>>> #include <linux/module.h> >>>>> #include <linux/slab.h> >>>>> +#include <linux/uaccess.h> >>>>> #include <linux/vmalloc.h> >>>>> >>>>> #define IMAGE_LOAD_XA_LIMIT XA_LIMIT(0, INT_MAX) >>>>> static DEFINE_XARRAY_ALLOC(fpga_image_load_xa); >>>>> >>>>> static struct class *fpga_image_load_class; >>>>> +static dev_t fpga_image_devt; >>>>> >>>>> #define to_image_load(d) container_of(d, struct fpga_image_load, dev) >>>>> >>>>> +static void fpga_image_dev_error(struct fpga_image_load *imgld, >>>>> + enum fpga_image_err err_code) >>>>> +{ >>>>> + imgld->err_code = err_code; >>>>> + imgld->lops->cancel(imgld); >>>>> +} >>>>> + >>>>> +static void fpga_image_prog_complete(struct fpga_image_load *imgld) >>>>> +{ >>>>> + mutex_lock(&imgld->lock); >>>>> + imgld->progress = FPGA_IMAGE_PROG_IDLE; >>>>> + complete_all(&imgld->update_done); >>>>> + mutex_unlock(&imgld->lock); >>>>> +} >>>>> + >>>>> +static void fpga_image_do_load(struct work_struct *work) >>>>> +{ >>>>> + struct fpga_image_load *imgld; >>>>> + enum fpga_image_err ret; >>>>> + u32 size, offset = 0; >>>>> + >>>>> + imgld = container_of(work, struct fpga_image_load, work); >>>>> + size = imgld->remaining_size; >>>>> + >>>>> + get_device(&imgld->dev); >>>>> + if (!try_module_get(imgld->dev.parent->driver->owner)) { >>>>> + imgld->err_code = FPGA_IMAGE_ERR_BUSY; >>>>> + goto idle_exit; >>>>> + } >>>>> + >>>>> + imgld->progress = FPGA_IMAGE_PROG_PREPARING; >>>>> + ret = imgld->lops->prepare(imgld); >>>>> + if (ret != FPGA_IMAGE_ERR_NONE) { >>>>> + fpga_image_dev_error(imgld, ret); >>>>> + goto modput_exit; >>>>> + } >>>>> + >>>>> + imgld->progress = FPGA_IMAGE_PROG_WRITING; >>>>> + while (imgld->remaining_size) { >>>>> + ret = imgld->lops->write_blk(imgld, offset); >>>>> + if (ret != FPGA_IMAGE_ERR_NONE) { >>>>> + fpga_image_dev_error(imgld, ret); >>>>> + goto done; >>>>> + } >>>>> + >>>>> + offset = size - imgld->remaining_size; >>>> The low level driver is required to update the "remaining_size" in >>>> write_blk ops? >>>> >>>> The API seems ambiguous. The framework asks for writing a block of data, >>>> but no block size is specified. >>> This change was made two or three iterations ago at Richard Gong's >>> suggestion. He asserted that the lower level driver should be in >>> control of the block size (based on write speeds). What do you >> I think it is reasonable. >> >>> think? Should the class driver impose a fixed size? Or allow the >> But the definition of the API is hard to comprehend, either we input the >> offset & the blk size, or we record them all in imgld structure, is it >> better. >> >> If the framework provides the blk size, the low level driver could >> return the written size so it could still control its own batch. I'll clean up the API for this function in the next version of the patches. >> >>> lower-level driver to determine the size? Is it OK to update >>> remaining_size in the lower-level driver? Or should there be >>> another call-back to request the size? >>> >>>>> + } >>>>> + >>>>> + imgld->progress = FPGA_IMAGE_PROG_PROGRAMMING; >>>>> + ret = imgld->lops->poll_complete(imgld); >>>>> + if (ret != FPGA_IMAGE_ERR_NONE) >>>>> + fpga_image_dev_error(imgld, ret); >>>>> + >>>>> +done: >>>>> + if (imgld->lops->cleanup) >>>>> + imgld->lops->cleanup(imgld); >>>>> + >>>>> +modput_exit: >>>>> + module_put(imgld->dev.parent->driver->owner); >>>>> + >>>>> +idle_exit: >>>>> + /* >>>>> + * Note: imgld->remaining_size is left unmodified here to provide >>>>> + * additional information on errors. It will be reinitialized when >>>>> + * the next image load begins. >>>>> + */ >>>>> + vfree(imgld->data); >>>>> + imgld->data = NULL; >>>>> + put_device(&imgld->dev); >>>>> + fpga_image_prog_complete(imgld); >>>>> +} >>>>> + >>>>> +static int fpga_image_load_ioctl_write(struct fpga_image_load *imgld, >>>>> + unsigned long arg) >>>>> +{ >>>>> + struct fpga_image_write wb; >>>>> + unsigned long minsz; >>>>> + u8 *buf; >>>>> + >>>>> + if (imgld->driver_unload || imgld->progress != FPGA_IMAGE_PROG_IDLE) >>>>> + return -EBUSY; >>>>> + >>>>> + minsz = offsetofend(struct fpga_image_write, buf); >>>>> + if (copy_from_user(&wb, (void __user *)arg, minsz)) >>>>> + return -EFAULT; >>>>> + >>>>> + if (wb.flags) >>>>> + return -EINVAL; >>>>> + >>>>> + /* Enforce 32-bit alignment on the write data */ >>>>> + if (wb.size & 0x3) >>>>> + return -EINVAL; >>>>> + >>>>> + buf = vzalloc(wb.size); >>>>> + if (!buf) >>>>> + return -ENOMEM; >>>>> + >>>>> + if (copy_from_user(buf, u64_to_user_ptr(wb.buf), wb.size)) { >>>>> + vfree(buf); >>>>> + return -EFAULT; >>>>> + } >>>>> + >>>>> + imgld->data = buf; >>>>> + imgld->remaining_size = wb.size; >>>>> + imgld->err_code = FPGA_IMAGE_ERR_NONE; >>>>> + imgld->progress = FPGA_IMAGE_PROG_STARTING; >>>>> + reinit_completion(&imgld->update_done); >>>>> + schedule_work(&imgld->work); >>>>> + >>>>> + return 0; >>>>> +} >>>>> + >>>>> +static long fpga_image_load_ioctl(struct file *filp, unsigned int cmd, >>>>> + unsigned long arg) >>>>> +{ >>>>> + struct fpga_image_load *imgld = filp->private_data; >>>>> + int ret = -ENOTTY; >>>>> + >>>>> + switch (cmd) { >>>>> + case FPGA_IMAGE_LOAD_WRITE: >>>>> + mutex_lock(&imgld->lock); >>>>> + ret = fpga_image_load_ioctl_write(imgld, arg); >>>>> + mutex_unlock(&imgld->lock); >>>>> + break; >>>>> + } >>>>> + >>>>> + return ret; >>>>> +} >>>>> + >>>>> +static int fpga_image_load_open(struct inode *inode, struct file *filp) >>>>> +{ >>>>> + struct fpga_image_load *imgld = container_of(inode->i_cdev, >>>>> + struct fpga_image_load, cdev); >>>>> + >>>>> + if (test_and_set_bit(0, &imgld->opened)) >>>> Some more flags to add for "opened" field? But the field name indicates >>>> it is a single flag. >>> Can you explain your comment further? I'm not understanding. What >>> "more flags" are you referring to? >>> >> The test_and_set_bit() makes me feel there are more bits to be >> controlled in "opened" for other purposes. The name "opened" indicates >> this is a single state, maybe you don't have to use bitops for it, >> atomic ops for int maybe better. OK - I'll use an atomic operation here. >> >>> This test_and_set_bit() operation and the "opened" structure member >>> were borrowed from the production driver implementation. opened is a >>> single value that is expected to be either 1 or 0. >>> >>>>> + return -EBUSY; >>>>> + >>>>> + filp->private_data = imgld; >>>>> + >>>>> + return 0; >>>>> +} >>>>> + >>>>> +static int fpga_image_load_release(struct inode *inode, struct file *filp) >>>>> +{ >>>>> + struct fpga_image_load *imgld = filp->private_data; >>>>> + >>>>> + clear_bit(0, &imgld->opened); >>>>> + >>>>> + return 0; >>>>> +} >>>>> + >>>>> +static const struct file_operations fpga_image_load_fops = { >>>>> + .owner = THIS_MODULE, >>>>> + .open = fpga_image_load_open, >>>>> + .release = fpga_image_load_release, >>>>> + .unlocked_ioctl = fpga_image_load_ioctl, >>>>> +}; >>>>> + >>>>> /** >>>>> * fpga_image_load_register - create and register an FPGA Image Load Device >>>>> * >>>>> @@ -33,11 +196,17 @@ fpga_image_load_register(struct device *parent, >>>>> const struct fpga_image_load_ops *lops, void *priv) >>>>> { >>>>> struct fpga_image_load *imgld; >>>>> - int id, ret; >>>>> + int ret; >>>>> + >>>>> + if (!lops || !lops->cancel || !lops->prepare || >>>>> + !lops->write_blk || !lops->poll_complete) { >>>>> + dev_err(parent, "Attempt to register without all required ops\n"); >>>>> + return ERR_PTR(-ENOMEM); >>>>> + } >>>>> >>>>> imgld = kzalloc(sizeof(*imgld), GFP_KERNEL); >>>>> if (!imgld) >>>>> - return NULL; >>>>> + return ERR_PTR(-ENOMEM); >>>> This is the fix for Patch #1? If yes please merge it to Patch #1. >>> Good catch. Yes, I'll move it to patch #1. >>>>> >>>>> ret = xa_alloc(&fpga_image_load_xa, &imgld->dev.id, imgld, IMAGE_LOAD_XA_LIMIT, >>>>> GFP_KERNEL); >>>>> @@ -48,13 +217,19 @@ fpga_image_load_register(struct device *parent, >>>>> >>>>> imgld->priv = priv; >>>>> imgld->lops = lops; >>>>> + imgld->err_code = FPGA_IMAGE_ERR_NONE; >>>>> + imgld->progress = FPGA_IMAGE_PROG_IDLE; >>>>> + init_completion(&imgld->update_done); >>>>> + INIT_WORK(&imgld->work, fpga_image_do_load); >>>>> >>>>> imgld->dev.class = fpga_image_load_class; >>>>> imgld->dev.parent = parent; >>>>> + imgld->dev.devt = MKDEV(MAJOR(fpga_image_devt), imgld->dev.id); >>>>> >>>>> - ret = dev_set_name(&imgld->dev, "fpga_image%d", id); >>>>> + ret = dev_set_name(&imgld->dev, "fpga_image%d", imgld->dev.id); >>>> Another fix? Please merge it to Patch #1. >>> Yes. >>> >>>>> if (ret) { >>>>> - dev_err(parent, "Failed to set device name: fpga_image%d\n", id); >>>>> + dev_err(parent, "Failed to set device name: fpga_image%d\n", >>>>> + imgld->dev.id); >>>> Ditto >>> Yes. >>>>> goto error_device; >>>>> } >>>>> >>>>> @@ -64,6 +239,16 @@ fpga_image_load_register(struct device *parent, >>>>> return ERR_PTR(ret); >>>>> } >>>>> >>>>> + cdev_init(&imgld->cdev, &fpga_image_load_fops); >>>>> + imgld->cdev.owner = parent->driver->owner; >>>>> + imgld->cdev.kobj.parent = &imgld->dev.kobj; >>>>> + >>>>> + ret = cdev_add(&imgld->cdev, imgld->dev.devt, 1); >>>>> + if (ret) { >>>>> + put_device(&imgld->dev); >>>>> + return ERR_PTR(ret); >>>>> + } >>>>> + >>>>> return imgld; >>>>> >>>>> error_device: >>>>> @@ -83,9 +268,29 @@ EXPORT_SYMBOL_GPL(fpga_image_load_register); >>>>> * >>>>> * This function is intended for use in an FPGA Image Load driver's >>>>> * remove() function. >>>>> + * >>>>> + * For some devices, once authentication of the uploaded image has begun, >>>>> + * the hardware cannot be signaled to stop, and the driver will not exit >>>>> + * until the hardware signals completion. This could be 30+ minutes of >>>>> + * waiting. The driver_unload flag enables a force-unload of the driver >>>>> + * (e.g. modprobe -r) by signaling the parent driver to exit even if the >>>> How does the driver_unload enables the force unload of the parent >>>> driver? I didn't find the code. >>> The driver_unload field is tested in the lower-level (parent) driver in >>> m10bmc_sec_poll_complete(), allowing the kernel worker thread to exit >>> even if the firmware update is still in progress. >> I didn't see the signaling implementation here. Maybe you need to move the >> comments somewhere else, or in another patch. > I have another concern. the design requires the low level driver poll > for a field of the input parameter that the framework manages. It is > unnormal, like an implicit cancel call to the driver. I'm cleaning up the driver_unload. I'm also changing the release (close) function to set the request_cancel flag when need. I think I have addressed all of your concerns for the next version of the patches. Thanks, - Russ > > How about the framework explicitly call cancel() when needed? Of cource > we need to declare the cancel() could be called when other callback is > in progress. > > Thanks, > Yilun > >> Thanks, >> Yilun >> >>>>> + * hardware update is incomplete. The driver_unload flag also prevents >>>>> + * new updates from starting once the unregister process has begun. >>>>> */ >>>>> void fpga_image_load_unregister(struct fpga_image_load *imgld) >>>>> { >>>>> + mutex_lock(&imgld->lock); >>>>> + imgld->driver_unload = true; >>>>> + if (imgld->progress == FPGA_IMAGE_PROG_IDLE) { >>>>> + mutex_unlock(&imgld->lock); >>>>> + goto unregister; >>>>> + } >>>>> + >>>>> + mutex_unlock(&imgld->lock); >>>>> + wait_for_completion(&imgld->update_done); >>>>> + >>>>> +unregister: >>>>> + cdev_del(&imgld->cdev); >>>>> device_unregister(&imgld->dev); >>>>> } >>>>> EXPORT_SYMBOL_GPL(fpga_image_load_unregister); >>>>> @@ -100,19 +305,30 @@ static void fpga_image_load_dev_release(struct device *dev) >>>>> >>>>> static int __init fpga_image_load_class_init(void) >>>>> { >>>>> + int ret; >>>>> pr_info("FPGA Image Load Driver\n"); >>>>> >>>>> fpga_image_load_class = class_create(THIS_MODULE, "fpga_image_load"); >>>>> if (IS_ERR(fpga_image_load_class)) >>>>> return PTR_ERR(fpga_image_load_class); >>>>> >>>>> + ret = alloc_chrdev_region(&fpga_image_devt, 0, MINORMASK, >>>>> + "fpga_image_load"); >>>>> + if (ret) >>>>> + goto exit_destroy_class; >>>>> + >>>>> fpga_image_load_class->dev_release = fpga_image_load_dev_release; >>>>> >>>>> return 0; >>>>> + >>>>> +exit_destroy_class: >>>>> + class_destroy(fpga_image_load_class); >>>>> + return ret; >>>>> } >>>>> >>>>> static void __exit fpga_image_load_class_exit(void) >>>>> { >>>>> + unregister_chrdev_region(fpga_image_devt, MINORMASK); >>>>> class_destroy(fpga_image_load_class); >>>>> WARN_ON(!xa_empty(&fpga_image_load_xa)); >>>>> } >>>>> diff --git a/include/linux/fpga/fpga-image-load.h b/include/linux/fpga/fpga-image-load.h >>>>> index a9cef9e1056b..b3d790e5d943 100644 >>>>> --- a/include/linux/fpga/fpga-image-load.h >>>>> +++ b/include/linux/fpga/fpga-image-load.h >>>>> @@ -7,22 +7,51 @@ >>>>> #ifndef _LINUX_FPGA_IMAGE_LOAD_H >>>>> #define _LINUX_FPGA_IMAGE_LOAD_H >>>>> >>>>> +#include <linux/cdev.h> >>>>> +#include <linux/completion.h> >>>>> #include <linux/device.h> >>>>> #include <linux/mutex.h> >>>>> #include <linux/types.h> >>>>> +#include <uapi/linux/fpga-image-load.h> >>>>> >>>>> struct fpga_image_load; >>>>> >>>>> /** >>>>> * struct fpga_image_load_ops - device specific operations >>>>> + * @prepare: Required: Prepare secure update >>>>> + * @write_blk: Required: Write a block of data >>>>> + * @poll_complete: Required: Check for the completion of the >>>>> + * HW authentication/programming process. This >>>>> + * function should check for imgld->driver_unload >>>>> + * and abort with FPGA_IMAGE_ERR_CANCELED when true. >>>>> + * @cancel: Required: Signal HW to cancel update >>>>> + * @cleanup: Optional: Complements the prepare() >>>>> + * function and is called at the completion >>>>> + * of the update, whether success or failure, >>>>> + * if the prepare function succeeded. >>>>> */ >>>>> struct fpga_image_load_ops { >>>>> + enum fpga_image_err (*prepare)(struct fpga_image_load *imgld); >>>>> + enum fpga_image_err (*write_blk)(struct fpga_image_load *imgld, u32 offset); >>>>> + enum fpga_image_err (*poll_complete)(struct fpga_image_load *imgld); >>>>> + enum fpga_image_err (*cancel)(struct fpga_image_load *imgld); >>>>> + void (*cleanup)(struct fpga_image_load *imgld); >>>>> }; >>>>> >>>>> struct fpga_image_load { >>>>> struct device dev; >>>>> + struct cdev cdev; >>>>> const struct fpga_image_load_ops *lops; >>>>> struct mutex lock; /* protect data structure contents */ >>>>> + unsigned long opened; >>>>> + struct work_struct work; >>>>> + struct completion update_done; >>>>> + const u8 *data; /* pointer to update data */ >>>>> + u32 remaining_size; /* size remaining to transfer */ >>>>> + enum fpga_image_prog progress; >>>>> + enum fpga_image_prog err_progress; /* progress at time of failure */ >>>> This field is not used in this patch? So could you introduce it later? >>> Yes - I'll move it. Thanks. >>>>> + enum fpga_image_err err_code; /* image load error code */ >>>>> + bool driver_unload; >>>>> void *priv; >>>>> }; >>>>> >>>>> diff --git a/include/uapi/linux/fpga-image-load.h b/include/uapi/linux/fpga-image-load.h >>>>> new file mode 100644 >>>>> index 000000000000..4146a0a9e408 >>>>> --- /dev/null >>>>> +++ b/include/uapi/linux/fpga-image-load.h >>>>> @@ -0,0 +1,58 @@ >>>>> +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ >>>>> +/* >>>>> + * Header File for FPGA Image Load User API >>>>> + * >>>>> + * Copyright (C) 2019-2021 Intel Corporation, Inc. >>>>> + * >>>>> + */ >>>>> + >>>>> +#ifndef _UAPI_LINUX_FPGA_IMAGE_LOAD_H >>>>> +#define _UAPI_LINUX_FPGA_IMAGE_LOAD_H >>>>> + >>>>> +#include <linux/types.h> >>>>> +#include <linux/ioctl.h> >>>>> + >>>>> +#define FPGA_IMAGE_LOAD_MAGIC 0xB9 >>>>> + >>>>> +/* Image load progress codes */ >>>>> +enum fpga_image_prog { >>>>> + FPGA_IMAGE_PROG_IDLE, >>>>> + FPGA_IMAGE_PROG_STARTING, >>>>> + FPGA_IMAGE_PROG_PREPARING, >>>>> + FPGA_IMAGE_PROG_WRITING, >>>>> + FPGA_IMAGE_PROG_PROGRAMMING, >>>>> + FPGA_IMAGE_PROG_MAX >>>>> +}; >>>>> + >>>>> +/* Image error progress codes */ >>>>> +enum fpga_image_err { >>>>> + FPGA_IMAGE_ERR_NONE, >>>>> + FPGA_IMAGE_ERR_HW_ERROR, >>>>> + FPGA_IMAGE_ERR_TIMEOUT, >>>>> + FPGA_IMAGE_ERR_CANCELED, >>>>> + FPGA_IMAGE_ERR_BUSY, >>>>> + FPGA_IMAGE_ERR_INVALID_SIZE, >>>>> + FPGA_IMAGE_ERR_RW_ERROR, >>>>> + FPGA_IMAGE_ERR_WEAROUT, >>>>> + FPGA_IMAGE_ERR_MAX >>>>> +}; >>>>> + >>>>> +#define FPGA_IMAGE_LOAD_WRITE _IOW(FPGA_IMAGE_LOAD_MAGIC, 0, struct fpga_image_write) >>>> Put the cmd word definition under the comments and parameter definition. >>> OK >>>>> + >>>>> +/** >>>>> + * FPGA_IMAGE_LOAD_WRITE - _IOW(FPGA_IMAGE_LOAD_MAGIC, 0, >>>>> + * struct fpga_image_write) >>>>> + * >>>>> + * Upload a data buffer to the target device. The user must provide the >>>>> + * data buffer, size, and an eventfd file descriptor. >>>> I didn't find the eventfd. >>> It is added in a later patch. I'll change the comment accordingly. >>> >>> Thanks, >>> - Russ >>>> Thanks, >>>> Yilun >>>> >>>>> + * >>>>> + * Return: 0 on success, -errno on failure. >>>>> + */ >>>>> +struct fpga_image_write { >>>>> + /* Input */ >>>>> + __u32 flags; /* Zero for now */ >>>>> + __u32 size; /* Data size (in bytes) to be written */ >>>>> + __u64 buf; /* User space address of source data */ >>>>> +}; >>>>> + >>>>> +#endif /* _UAPI_LINUX_FPGA_IMAGE_LOAD_H */ >>>>> -- >>>>> 2.25.1