diff options
| author | Paulo Alcantara <pc@manguebit.org> | 2026-09-26 19:20:30 -0300 |
|---|---|---|
| committer | Paulo Alcantara <pc@manguebit.org> | 2026-09-30 14:24:12 -0300 |
| commit | dd05add7b2b6004c5fa3a1f276f56d8668f86b65 (patch) | |
| tree | 23efdcaa0b8031f6ac6f57b6734d18d6c1adc6f2 /drivers/usb/misc/ezusb.c | |
| download | linux-stable-dd05add7b2b6004c5fa3a1f276f56d8668f86b65.tar.gz linux-stable-dd05add7b2b6004c5fa3a1f276f56d8668f86b65.zip | |
netfs: zero the tail of a short DIO/unbuffered readgrafted
The buffered read collector zero-fills the tail of a short read that
stops below the inode's i_size (netfs_clear_unread()), so a read that
races an extending write still returns the expected number of bytes.
The non-buffered collector path does no such thing: it just records
how much was transferred.
Add the same zero-fill for the non-buffered case, gated on
NETFS_SREQ_CLEAR_TAIL: a subreq's source sets that flag when a short
result from it is known to be safe to treat as a hole, as opposed to
NETFS_SREQ_HIT_EOF, which means the read genuinely ran off the end of
the file and should be reported short as-is. Only CLEAR_TAIL should
zero-fill here; a real EOF must stay a real short read.
No source currently sets CLEAR_TAIL on an unbuffered/DIO subrequest,
so this is inert on its own -- a following change teaches cifs to set
it in the one case that needs it.
Fixes: e2d46f2ec332 ("netfs: Change the read result collector to only use one work item")
Reviewed-by: David Howells <dhowells@redhat.com>
Reviewed-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Paulo Alcantara <pc@manguebit.org>
Cc: Christian Brauner <brauner@kernel.org>
Cc: Matthew Wilcox <willy@infradead.org>
Cc: Ronnie Sahlberg <ronniesahlberg@gmail.com>
Cc: Shyam Prasad N <sprasad@microsoft.com>
Cc: Tom Talpey <tom@talpey.com>
Cc: Bharath SM <bharathsm@microsoft.com>
Cc: stable@vger.kernel.org
Diffstat (limited to 'drivers/usb/misc/ezusb.c')
| -rw-r--r-- | drivers/usb/misc/ezusb.c | 152 |
1 files changed, 152 insertions, 0 deletions
diff --git a/drivers/usb/misc/ezusb.c b/drivers/usb/misc/ezusb.c new file mode 100644 index 000000000..7ab4856ab --- /dev/null +++ b/drivers/usb/misc/ezusb.c @@ -0,0 +1,152 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * EZ-USB specific functions used by some of the USB to Serial drivers. + * + * Copyright (C) 1999 - 2002 Greg Kroah-Hartman (greg@kroah.com) + */ + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/usb.h> +#include <linux/firmware.h> +#include <linux/ihex.h> +#include <linux/usb/ezusb.h> + +struct ezusb_fx_type { + /* EZ-USB Control and Status Register. Bit 0 controls 8051 reset */ + unsigned short cpucs_reg; + unsigned short max_internal_adress; +}; + +static const struct ezusb_fx_type ezusb_fx1 = { + .cpucs_reg = 0x7F92, + .max_internal_adress = 0x1B3F, +}; + +/* Commands for writing to memory */ +#define WRITE_INT_RAM 0xA0 +#define WRITE_EXT_RAM 0xA3 + +static int ezusb_writememory(struct usb_device *dev, int address, + unsigned char *data, int length, __u8 request) +{ + if (!dev) + return -ENODEV; + + return usb_control_msg_send(dev, 0, request, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + address, 0, data, length, 3000, GFP_KERNEL); +} + +static int ezusb_set_reset(struct usb_device *dev, unsigned short cpucs_reg, + unsigned char reset_bit) +{ + int response = ezusb_writememory(dev, cpucs_reg, &reset_bit, 1, WRITE_INT_RAM); + if (response < 0) + dev_err(&dev->dev, "%s-%d failed: %d\n", + __func__, reset_bit, response); + return response; +} + +int ezusb_fx1_set_reset(struct usb_device *dev, unsigned char reset_bit) +{ + return ezusb_set_reset(dev, ezusb_fx1.cpucs_reg, reset_bit); +} +EXPORT_SYMBOL_GPL(ezusb_fx1_set_reset); + +static int ezusb_ihex_firmware_download(struct usb_device *dev, + struct ezusb_fx_type fx, + const char *firmware_path) +{ + int ret = -ENOENT; + const struct firmware *firmware = NULL; + const struct ihex_binrec *record; + + if (request_ihex_firmware(&firmware, firmware_path, + &dev->dev)) { + dev_err(&dev->dev, + "%s - request \"%s\" failed\n", + __func__, firmware_path); + goto out; + } + + ret = ezusb_set_reset(dev, fx.cpucs_reg, 0); + if (ret < 0) + goto out; + + record = (const struct ihex_binrec *)firmware->data; + for (; record; record = ihex_next_binrec(record)) { + if (be32_to_cpu(record->addr) > fx.max_internal_adress) { + ret = ezusb_writememory(dev, be32_to_cpu(record->addr), + (unsigned char *)record->data, + be16_to_cpu(record->len), WRITE_EXT_RAM); + if (ret < 0) { + dev_err(&dev->dev, "%s - ezusb_writememory " + "failed writing internal memory " + "(%d %04X %p %d)\n", __func__, ret, + be32_to_cpu(record->addr), record->data, + be16_to_cpu(record->len)); + goto out; + } + } + } + + ret = ezusb_set_reset(dev, fx.cpucs_reg, 1); + if (ret < 0) + goto out; + record = (const struct ihex_binrec *)firmware->data; + for (; record; record = ihex_next_binrec(record)) { + if (be32_to_cpu(record->addr) <= fx.max_internal_adress) { + ret = ezusb_writememory(dev, be32_to_cpu(record->addr), + (unsigned char *)record->data, + be16_to_cpu(record->len), WRITE_INT_RAM); + if (ret < 0) { + dev_err(&dev->dev, "%s - ezusb_writememory " + "failed writing external memory " + "(%d %04X %p %d)\n", __func__, ret, + be32_to_cpu(record->addr), record->data, + be16_to_cpu(record->len)); + goto out; + } + } + } + ret = ezusb_set_reset(dev, fx.cpucs_reg, 0); +out: + release_firmware(firmware); + return ret; +} + +int ezusb_fx1_ihex_firmware_download(struct usb_device *dev, + const char *firmware_path) +{ + return ezusb_ihex_firmware_download(dev, ezusb_fx1, firmware_path); +} +EXPORT_SYMBOL_GPL(ezusb_fx1_ihex_firmware_download); + +#if 0 +/* + * Once someone one needs these fx2 functions, uncomment them + * and add them to ezusb.h and all should be good. + */ +static struct ezusb_fx_type ezusb_fx2 = { + .cpucs_reg = 0xE600, + .max_internal_adress = 0x3FFF, +}; + +int ezusb_fx2_set_reset(struct usb_device *dev, unsigned char reset_bit) +{ + return ezusb_set_reset(dev, ezusb_fx2.cpucs_reg, reset_bit); +} +EXPORT_SYMBOL_GPL(ezusb_fx2_set_reset); + +int ezusb_fx2_ihex_firmware_download(struct usb_device *dev, + const char *firmware_path) +{ + return ezusb_ihex_firmware_download(dev, ezusb_fx2, firmware_path); +} +EXPORT_SYMBOL_GPL(ezusb_fx2_ihex_firmware_download); +#endif + +MODULE_DESCRIPTION("EZUSB device support"); +MODULE_LICENSE("GPL"); |
