mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-10 15:14:43 +00:00
Merge branch 'master' of git://git.denx.de/u-boot-spi
This commit is contained in:
commit
cc555bd4f4
14 changed files with 93 additions and 94 deletions
11
README
11
README
|
@ -3096,17 +3096,6 @@ CBFS (Coreboot Filesystem) support
|
|||
memories can be connected with a given cs line.
|
||||
Currently Xilinx Zynq qspi supports these type of connections.
|
||||
|
||||
CONFIG_SYS_SPI_ST_ENABLE_WP_PIN
|
||||
enable the W#/Vpp signal to disable writing to the status
|
||||
register on ST MICRON flashes like the N25Q128.
|
||||
The status register write enable/disable bit, combined with
|
||||
the W#/VPP signal provides hardware data protection for the
|
||||
device as follows: When the enable/disable bit is set to 1,
|
||||
and the W#/VPP signal is driven LOW, the status register
|
||||
nonvolatile bits become read-only and the WRITE STATUS REGISTER
|
||||
operation will not execute. The only way to exit this
|
||||
hardware-protected mode is to drive W#/VPP HIGH.
|
||||
|
||||
- SystemACE Support:
|
||||
CONFIG_SYSTEMACE
|
||||
|
||||
|
|
|
@ -164,6 +164,8 @@ static int do_spi_flash_probe(int argc, char * const argv[])
|
|||
static const char *spi_flash_update_block(struct spi_flash *flash, u32 offset,
|
||||
size_t len, const char *buf, char *cmp_buf, size_t *skipped)
|
||||
{
|
||||
char *ptr = (char *)buf;
|
||||
|
||||
debug("offset=%#x, sector_size=%#x, len=%#zx\n",
|
||||
offset, flash->sector_size, len);
|
||||
/* Read the entire sector so to allow for rewriting */
|
||||
|
@ -179,16 +181,14 @@ static const char *spi_flash_update_block(struct spi_flash *flash, u32 offset,
|
|||
/* Erase the entire sector */
|
||||
if (spi_flash_erase(flash, offset, flash->sector_size))
|
||||
return "erase";
|
||||
/* Write the initial part of the block from the source */
|
||||
if (spi_flash_write(flash, offset, len, buf))
|
||||
return "write";
|
||||
/* If it's a partial sector, rewrite the existing part */
|
||||
/* If it's a partial sector, copy the data into the temp-buffer */
|
||||
if (len != flash->sector_size) {
|
||||
/* Rewrite the original data to the end of the sector */
|
||||
if (spi_flash_write(flash, offset + len,
|
||||
flash->sector_size - len, &cmp_buf[len]))
|
||||
return "write";
|
||||
memcpy(cmp_buf, buf, len);
|
||||
ptr = cmp_buf;
|
||||
}
|
||||
/* Write one complete sector */
|
||||
if (spi_flash_write(flash, offset, flash->sector_size, ptr))
|
||||
return "write";
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -97,10 +97,6 @@ enum {
|
|||
#define STATUS_QEB_MXIC (1 << 6)
|
||||
#define STATUS_PEC (1 << 7)
|
||||
|
||||
#ifdef CONFIG_SYS_SPI_ST_ENABLE_WP_PIN
|
||||
#define STATUS_SRWD (1 << 7) /* SR write protect */
|
||||
#endif
|
||||
|
||||
/* Flash timeout values */
|
||||
#define SPI_FLASH_PROG_TIMEOUT (2 * CONFIG_SYS_HZ)
|
||||
#define SPI_FLASH_PAGE_ERASE_TIMEOUT (5 * CONFIG_SYS_HZ)
|
||||
|
@ -123,7 +119,8 @@ int sst_write_bp(struct spi_flash *flash, u32 offset, size_t len,
|
|||
* @name: Device name ([MANUFLETTER][DEVTYPE][DENSITY][EXTRAINFO])
|
||||
* @jedec: Device jedec ID (0x[1byte_manuf_id][2byte_dev_id])
|
||||
* @ext_jedec: Device ext_jedec ID
|
||||
* @sector_size: Sector size of this device
|
||||
* @sector_size: Isn't necessarily a sector size from vendor,
|
||||
* the size listed here is what works with CMD_ERASE_64K
|
||||
* @nr_sectors: No.of sectors on this device
|
||||
* @e_rd_cmd: Enum list for read commands
|
||||
* @flags: Important param, for flash specific behaviour
|
||||
|
|
|
@ -154,21 +154,17 @@ static void spi_flash_dual_flash(struct spi_flash *flash, u32 *addr)
|
|||
}
|
||||
#endif
|
||||
|
||||
int spi_flash_cmd_wait_ready(struct spi_flash *flash, unsigned long timeout)
|
||||
static int spi_flash_poll_status(struct spi_slave *spi, unsigned long timeout,
|
||||
u8 cmd, u8 poll_bit)
|
||||
{
|
||||
struct spi_slave *spi = flash->spi;
|
||||
unsigned long timebase;
|
||||
unsigned long flags = SPI_XFER_BEGIN;
|
||||
int ret;
|
||||
u8 status;
|
||||
u8 check_status = 0x0;
|
||||
u8 poll_bit = STATUS_WIP;
|
||||
u8 cmd = flash->poll_cmd;
|
||||
|
||||
if (cmd == CMD_FLAG_STATUS) {
|
||||
poll_bit = STATUS_PEC;
|
||||
if (cmd == CMD_FLAG_STATUS)
|
||||
check_status = poll_bit;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SF_DUAL_FLASH
|
||||
if (spi->flags & SPI_XFER_U_PAGE)
|
||||
|
@ -204,6 +200,28 @@ int spi_flash_cmd_wait_ready(struct spi_flash *flash, unsigned long timeout)
|
|||
return -1;
|
||||
}
|
||||
|
||||
int spi_flash_cmd_wait_ready(struct spi_flash *flash, unsigned long timeout)
|
||||
{
|
||||
struct spi_slave *spi = flash->spi;
|
||||
int ret;
|
||||
u8 poll_bit = STATUS_WIP;
|
||||
u8 cmd = CMD_READ_STATUS;
|
||||
|
||||
ret = spi_flash_poll_status(spi, timeout, cmd, poll_bit);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (flash->poll_cmd == CMD_FLAG_STATUS) {
|
||||
poll_bit = STATUS_PEC;
|
||||
cmd = CMD_FLAG_STATUS;
|
||||
ret = spi_flash_poll_status(spi, timeout, cmd, poll_bit);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int spi_flash_write_common(struct spi_flash *flash, const u8 *cmd,
|
||||
size_t cmd_len, const void *buf, size_t buf_len)
|
||||
{
|
||||
|
|
|
@ -132,6 +132,9 @@ static int spi_flash_validate_params(struct spi_slave *spi, u8 *idcode,
|
|||
flash->name = params->name;
|
||||
flash->memory_map = spi->memory_map;
|
||||
flash->dual_flash = flash->spi->option;
|
||||
#ifdef CONFIG_DM_SPI_FLASH
|
||||
flash->flags = params->flags;
|
||||
#endif
|
||||
|
||||
/* Assign spi_flash ops */
|
||||
#ifndef CONFIG_DM_SPI_FLASH
|
||||
|
@ -184,6 +187,9 @@ static int spi_flash_validate_params(struct spi_slave *spi, u8 *idcode,
|
|||
flash->erase_size = flash->sector_size;
|
||||
}
|
||||
|
||||
/* Now erase size becomes valid sector size */
|
||||
flash->sector_size = flash->erase_size;
|
||||
|
||||
/* Look for the fastest read cmd */
|
||||
cmd = fls(params->e_rd_cmd & flash->spi->op_mode_rx);
|
||||
if (cmd) {
|
||||
|
@ -288,34 +294,6 @@ int spi_flash_decode_fdt(const void *blob, struct spi_flash *flash)
|
|||
}
|
||||
#endif /* CONFIG_OF_CONTROL */
|
||||
|
||||
#ifdef CONFIG_SYS_SPI_ST_ENABLE_WP_PIN
|
||||
/* enable the W#/Vpp signal to disable writing to the status register */
|
||||
static int spi_enable_wp_pin(struct spi_flash *flash)
|
||||
{
|
||||
u8 status;
|
||||
int ret;
|
||||
|
||||
ret = spi_flash_cmd_read_status(flash, &status);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = spi_flash_cmd_write_status(flash, STATUS_SRWD);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = spi_flash_cmd_write_disable(flash);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static int spi_enable_wp_pin(struct spi_flash *flash)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* spi_flash_probe_slave() - Probe for a SPI flash device on a bus
|
||||
*
|
||||
|
@ -394,8 +372,6 @@ int spi_flash_probe_slave(struct spi_slave *spi, struct spi_flash *flash)
|
|||
puts(" Full access #define CONFIG_SPI_FLASH_BAR\n");
|
||||
}
|
||||
#endif
|
||||
if (spi_enable_wp_pin(flash))
|
||||
puts("Enable WP pin failed\n");
|
||||
|
||||
/* Release spi bus */
|
||||
spi_release_bus(spi);
|
||||
|
@ -434,6 +410,8 @@ struct spi_flash *spi_flash_probe(unsigned int busnum, unsigned int cs,
|
|||
struct spi_slave *bus;
|
||||
|
||||
bus = spi_setup_slave(busnum, cs, max_hz, spi_mode);
|
||||
if (!bus)
|
||||
return NULL;
|
||||
return spi_flash_probe_tail(bus);
|
||||
}
|
||||
|
||||
|
@ -444,6 +422,8 @@ struct spi_flash *spi_flash_probe_fdt(const void *blob, int slave_node,
|
|||
struct spi_slave *bus;
|
||||
|
||||
bus = spi_setup_slave_fdt(blob, slave_node, spi_node);
|
||||
if (!bus)
|
||||
return NULL;
|
||||
return spi_flash_probe_tail(bus);
|
||||
}
|
||||
#endif
|
||||
|
@ -469,6 +449,15 @@ int spi_flash_std_write(struct udevice *dev, u32 offset, size_t len,
|
|||
{
|
||||
struct spi_flash *flash = dev_get_uclass_priv(dev);
|
||||
|
||||
#if defined(CONFIG_SPI_FLASH_SST)
|
||||
if (flash->flags & SST_WR) {
|
||||
if (flash->spi->op_mode_tx & SPI_OPM_TX_BP)
|
||||
return sst_write_bp(flash, offset, len, buf);
|
||||
else
|
||||
return sst_write_wp(flash, offset, len, buf);
|
||||
}
|
||||
#endif
|
||||
|
||||
return spi_flash_cmd_write_ops(flash, offset, len, buf);
|
||||
}
|
||||
|
||||
|
|
|
@ -296,8 +296,9 @@ static int exynos_spi_probe(struct udevice *bus)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int exynos_spi_claim_bus(struct udevice *bus)
|
||||
static int exynos_spi_claim_bus(struct udevice *dev)
|
||||
{
|
||||
struct udevice *bus = dev->parent;
|
||||
struct exynos_spi_priv *priv = dev_get_priv(bus);
|
||||
|
||||
exynos_pinmux_config(priv->periph_id, PINMUX_FLAG_NONE);
|
||||
|
@ -308,8 +309,9 @@ static int exynos_spi_claim_bus(struct udevice *bus)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int exynos_spi_release_bus(struct udevice *bus)
|
||||
static int exynos_spi_release_bus(struct udevice *dev)
|
||||
{
|
||||
struct udevice *bus = dev->parent;
|
||||
struct exynos_spi_priv *priv = dev_get_priv(bus);
|
||||
|
||||
spi_flush_fifo(priv->regs);
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
#include <asm/io.h>
|
||||
#include "omap3_spi.h"
|
||||
|
||||
#define SPI_WAIT_TIMEOUT 3000000
|
||||
#define SPI_WAIT_TIMEOUT 10
|
||||
|
||||
static void spi_reset(struct omap3_spi_slave *ds)
|
||||
{
|
||||
|
@ -227,7 +227,7 @@ int omap3_spi_write(struct spi_slave *slave, unsigned int len, const void *txp,
|
|||
{
|
||||
struct omap3_spi_slave *ds = to_omap3_spi(slave);
|
||||
int i;
|
||||
int timeout = SPI_WAIT_TIMEOUT;
|
||||
ulong start;
|
||||
int chconf = readl(&ds->regs->channel[ds->slave.cs].chconf);
|
||||
|
||||
/* Enable the channel */
|
||||
|
@ -241,9 +241,10 @@ int omap3_spi_write(struct spi_slave *slave, unsigned int len, const void *txp,
|
|||
|
||||
for (i = 0; i < len; i++) {
|
||||
/* wait till TX register is empty (TXS == 1) */
|
||||
start = get_timer(0);
|
||||
while (!(readl(&ds->regs->channel[ds->slave.cs].chstat) &
|
||||
OMAP3_MCSPI_CHSTAT_TXS)) {
|
||||
if (--timeout <= 0) {
|
||||
if (get_timer(start) > SPI_WAIT_TIMEOUT) {
|
||||
printf("SPI TXS timed out, status=0x%08x\n",
|
||||
readl(&ds->regs->channel[ds->slave.cs].chstat));
|
||||
return -1;
|
||||
|
@ -280,7 +281,7 @@ int omap3_spi_read(struct spi_slave *slave, unsigned int len, void *rxp,
|
|||
{
|
||||
struct omap3_spi_slave *ds = to_omap3_spi(slave);
|
||||
int i;
|
||||
int timeout = SPI_WAIT_TIMEOUT;
|
||||
ulong start;
|
||||
int chconf = readl(&ds->regs->channel[ds->slave.cs].chconf);
|
||||
|
||||
/* Enable the channel */
|
||||
|
@ -295,10 +296,11 @@ int omap3_spi_read(struct spi_slave *slave, unsigned int len, void *rxp,
|
|||
writel(0, &ds->regs->channel[ds->slave.cs].tx);
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
start = get_timer(0);
|
||||
/* Wait till RX register contains data (RXS == 1) */
|
||||
while (!(readl(&ds->regs->channel[ds->slave.cs].chstat) &
|
||||
OMAP3_MCSPI_CHSTAT_RXS)) {
|
||||
if (--timeout <= 0) {
|
||||
if (get_timer(start) > SPI_WAIT_TIMEOUT) {
|
||||
printf("SPI RXS timed out, status=0x%08x\n",
|
||||
readl(&ds->regs->channel[ds->slave.cs].chstat));
|
||||
return -1;
|
||||
|
@ -332,7 +334,7 @@ int omap3_spi_txrx(struct spi_slave *slave, unsigned int len,
|
|||
const void *txp, void *rxp, unsigned long flags)
|
||||
{
|
||||
struct omap3_spi_slave *ds = to_omap3_spi(slave);
|
||||
int timeout = SPI_WAIT_TIMEOUT;
|
||||
ulong start;
|
||||
int chconf = readl(&ds->regs->channel[ds->slave.cs].chconf);
|
||||
int irqstatus = readl(&ds->regs->irqstatus);
|
||||
int i=0;
|
||||
|
@ -350,9 +352,10 @@ int omap3_spi_txrx(struct spi_slave *slave, unsigned int len,
|
|||
for (i=0; i < len; i++){
|
||||
/* Write: wait for TX empty (TXS == 1)*/
|
||||
irqstatus |= (1<< (4*(ds->slave.bus)));
|
||||
start = get_timer(0);
|
||||
while (!(readl(&ds->regs->channel[ds->slave.cs].chstat) &
|
||||
OMAP3_MCSPI_CHSTAT_TXS)) {
|
||||
if (--timeout <= 0) {
|
||||
if (get_timer(start) > SPI_WAIT_TIMEOUT) {
|
||||
printf("SPI TXS timed out, status=0x%08x\n",
|
||||
readl(&ds->regs->channel[ds->slave.cs].chstat));
|
||||
return -1;
|
||||
|
@ -368,9 +371,10 @@ int omap3_spi_txrx(struct spi_slave *slave, unsigned int len,
|
|||
writel(((u8 *)txp)[i], tx);
|
||||
|
||||
/*Read: wait for RX containing data (RXS == 1)*/
|
||||
start = get_timer(0);
|
||||
while (!(readl(&ds->regs->channel[ds->slave.cs].chstat) &
|
||||
OMAP3_MCSPI_CHSTAT_RXS)) {
|
||||
if (--timeout <= 0) {
|
||||
if (get_timer(start) > SPI_WAIT_TIMEOUT) {
|
||||
printf("SPI RXS timed out, status=0x%08x\n",
|
||||
readl(&ds->regs->channel[ds->slave.cs].chstat));
|
||||
return -1;
|
||||
|
|
|
@ -67,7 +67,7 @@ int spi_claim_bus(struct spi_slave *slave)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
return ops->claim_bus ? ops->claim_bus(bus) : 0;
|
||||
return ops->claim_bus ? ops->claim_bus(dev) : 0;
|
||||
}
|
||||
|
||||
void spi_release_bus(struct spi_slave *slave)
|
||||
|
@ -77,7 +77,7 @@ void spi_release_bus(struct spi_slave *slave)
|
|||
struct dm_spi_ops *ops = spi_get_ops(bus);
|
||||
|
||||
if (ops->release_bus)
|
||||
ops->release_bus(bus);
|
||||
ops->release_bus(dev);
|
||||
}
|
||||
|
||||
int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
|
||||
|
|
|
@ -153,8 +153,9 @@ static int tegra114_spi_probe(struct udevice *bus)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int tegra114_spi_claim_bus(struct udevice *bus)
|
||||
static int tegra114_spi_claim_bus(struct udevice *dev)
|
||||
{
|
||||
struct udevice *bus = dev->parent;
|
||||
struct tegra114_spi_priv *priv = dev_get_priv(bus);
|
||||
struct spi_regs *regs = priv->regs;
|
||||
|
||||
|
|
|
@ -125,8 +125,9 @@ static int tegra20_sflash_probe(struct udevice *bus)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int tegra20_sflash_claim_bus(struct udevice *bus)
|
||||
static int tegra20_sflash_claim_bus(struct udevice *dev)
|
||||
{
|
||||
struct udevice *bus = dev->parent;
|
||||
struct tegra20_sflash_priv *priv = dev_get_priv(bus);
|
||||
struct spi_regs *regs = priv->regs;
|
||||
u32 reg;
|
||||
|
|
|
@ -141,8 +141,9 @@ static int tegra30_spi_probe(struct udevice *bus)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int tegra30_spi_claim_bus(struct udevice *bus)
|
||||
static int tegra30_spi_claim_bus(struct udevice *dev)
|
||||
{
|
||||
struct udevice *bus = dev->parent;
|
||||
struct tegra30_spi_priv *priv = dev_get_priv(bus);
|
||||
struct spi_regs *regs = priv->regs;
|
||||
u32 reg;
|
||||
|
|
|
@ -227,9 +227,6 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,
|
|||
debug("spi_xfer: bus:%i cs:%i bitlen:%i len:%i flags:%lx\n",
|
||||
slave->bus, slave->cs, bitlen, len, flags);
|
||||
|
||||
if (bitlen == 0)
|
||||
return -1;
|
||||
|
||||
if (bitlen % 8) {
|
||||
debug("spi_xfer: Non byte aligned SPI transfer\n");
|
||||
return -1;
|
||||
|
|
|
@ -38,11 +38,12 @@
|
|||
|
||||
/* SPI RX operation modes */
|
||||
#define SPI_OPM_RX_AS (1 << 0)
|
||||
#define SPI_OPM_RX_DOUT (1 << 1)
|
||||
#define SPI_OPM_RX_DIO (1 << 2)
|
||||
#define SPI_OPM_RX_QOF (1 << 3)
|
||||
#define SPI_OPM_RX_QIOF (1 << 4)
|
||||
#define SPI_OPM_RX_EXTN (SPI_OPM_RX_AS | SPI_OPM_RX_DOUT | \
|
||||
#define SPI_OPM_RX_AF (1 << 1)
|
||||
#define SPI_OPM_RX_DOUT (1 << 2)
|
||||
#define SPI_OPM_RX_DIO (1 << 3)
|
||||
#define SPI_OPM_RX_QOF (1 << 4)
|
||||
#define SPI_OPM_RX_QIOF (1 << 5)
|
||||
#define SPI_OPM_RX_EXTN (SPI_OPM_RX_AS | SPI_OPM_RX_AF | SPI_OPM_RX_DOUT | \
|
||||
SPI_OPM_RX_DIO | SPI_OPM_RX_QOF | \
|
||||
SPI_OPM_RX_QIOF)
|
||||
|
||||
|
@ -385,12 +386,12 @@ struct dm_spi_ops {
|
|||
* allowed to claim the same bus for several slaves without releasing
|
||||
* the bus in between.
|
||||
*
|
||||
* @bus: The SPI slave
|
||||
* @dev: The SPI slave
|
||||
*
|
||||
* Returns: 0 if the bus was claimed successfully, or a negative value
|
||||
* if it wasn't.
|
||||
*/
|
||||
int (*claim_bus)(struct udevice *bus);
|
||||
int (*claim_bus)(struct udevice *dev);
|
||||
|
||||
/**
|
||||
* Release the SPI bus
|
||||
|
@ -399,9 +400,9 @@ struct dm_spi_ops {
|
|||
* all transfers have finished. It may disable any SPI hardware as
|
||||
* appropriate.
|
||||
*
|
||||
* @bus: The SPI slave
|
||||
* @dev: The SPI slave
|
||||
*/
|
||||
int (*release_bus)(struct udevice *bus);
|
||||
int (*release_bus)(struct udevice *dev);
|
||||
|
||||
/**
|
||||
* Set the word length for SPI transactions
|
||||
|
@ -413,7 +414,7 @@ struct dm_spi_ops {
|
|||
*
|
||||
* Returns: 0 on success, -ve on failure.
|
||||
*/
|
||||
int (*set_wordlen)(struct udevice *bus, unsigned int wordlen);
|
||||
int (*set_wordlen)(struct udevice *dev, unsigned int wordlen);
|
||||
|
||||
/**
|
||||
* SPI transfer
|
||||
|
|
|
@ -62,11 +62,10 @@ struct spi_slave;
|
|||
* return 0 - Success, 1 - Failure
|
||||
*/
|
||||
struct spi_flash {
|
||||
struct spi_slave *spi;
|
||||
#ifdef CONFIG_DM_SPI_FLASH
|
||||
struct spi_slave *spi;
|
||||
struct udevice *dev;
|
||||
#else
|
||||
struct spi_slave *spi;
|
||||
u16 flags;
|
||||
#endif
|
||||
const char *name;
|
||||
u8 dual_flash;
|
||||
|
@ -91,13 +90,13 @@ struct spi_flash {
|
|||
#ifndef CONFIG_DM_SPI_FLASH
|
||||
/*
|
||||
* These are not strictly needed for driver model, but keep them here
|
||||
* whilt the transition is in progress.
|
||||
* while the transition is in progress.
|
||||
*
|
||||
* Normally each driver would provide its own operations, but for
|
||||
* SPI flash most chips use the same algorithms. One approach is
|
||||
* to create a 'common' SPI flash device which knows how to talk
|
||||
* to most devices, and then allow other drivers to be used instead
|
||||
* if requird, perhaps with a way of scanning through the list to
|
||||
* if required, perhaps with a way of scanning through the list to
|
||||
* find the driver that matches the device.
|
||||
*/
|
||||
int (*read)(struct spi_flash *flash, u32 offset, size_t len, void *buf);
|
||||
|
|
Loading…
Reference in a new issue