Загрузка данных


#include <linux/slab.h>

static int sysconfig_i2c_bitstream_burst_write(struct sysconfig_priv *priv, const char *buf, size_t len)
{
    struct i2c_client *client = to_i2c_client(priv->dev);
    const u8 lsc_burst_cmd[] = SYSCONFIG_LSC_BITSTREAM_BURST; // {0x7A, 0x00, 0x00, 0x00}
    
    size_t aligned_len = (len + 3) & ~3;
    size_t total_padding = aligned_len - len;
    size_t offset = 0;
    
    /* Ограничиваем пачку 35 сообщениями, чтобы гарантированно не упереться в лимиты ядра */
    const int MAX_MSGS_PER_TRANSFER = 35; 
    struct i2c_msg *msgs;
    int ret;

    if (len == 0)
        return 0;

    msgs = kmalloc_array(MAX_MSGS_PER_TRANSFER, sizeof(struct i2c_msg), GFP_KERNEL);
    if (!msgs)
        return -ENOMEM;

    printk("%s(%d) Multi-packet Burst Mode. Total len = %zu, Aligned = %zu\n", 
            __FUNCTION__, __LINE__, len, aligned_len);

    while (offset < len) {
        int msg_idx = 0;

        /* Если это самый первый запуск всей прошивки, принудительно вшиваем команду 0x7A */
        if (offset == 0) {
            msgs[msg_idx].addr = client->addr;
            msgs[msg_idx].flags = client->flags & I2C_M_TEN;
            msgs[msg_idx].len = sizeof(lsc_burst_cmd);
            msgs[msg_idx].buf = (u8 *)lsc_burst_cmd;
            msg_idx++;
        }

        /* Набиваем текущую пачку сообщениями строго по 4096 байт */
        while (offset < len && msg_idx < MAX_MSGS_PER_TRANSFER) {
            size_t chunk_size = min((size_t)4096, len - offset);

            msgs[msg_idx].addr = client->addr;
            msgs[msg_idx].flags = (client->flags & I2C_M_TEN) | I2C_M_NOSTART;

            /* Если это самый последний кусок прошивки во всей сессии */
            if (offset + chunk_size == len && total_padding > 0) {
                size_t padded_size = chunk_size + total_padding;
                u8 *padded_buf = kmalloc(padded_size, GFP_KERNEL);
                if (!padded_buf) {
                    kfree(msgs);
                    return -ENOMEM;
                }
                memcpy(padded_buf, buf + offset, chunk_size);
                memset(padded_buf + chunk_size, 0xFF, total_padding);

                msgs[msg_idx].len = padded_size;
                msgs[msg_idx].buf = padded_buf;
            } else {
                /* Обычный чанк пейлоада, строго соответствующий странице DMA в 4КБ */
                msgs[msg_idx].len = chunk_size;
                msgs[msg_idx].buf = (u8 *)(buf + offset);
            }

            offset += chunk_size;
            msg_idx++;
        }

        /* Выстреливаем сформированную пачку (до 35 дескрипторов по 4КБ) монолитно через DMA */
        ret = i2c_transfer(client->adapter, msgs, msg_idx);

        /* Если на последней итерации выделялся padded_buf, освобождаем его */
        if (offset >= len && total_padding > 0) {
            kfree(msgs[msg_idx - 1].buf);
        }

        if (ret != msg_idx) {
            dev_err(priv->dev, "I2C packet group transfer failed: %d\n", ret);
            kfree(msgs);
            return (ret < 0) ? ret : -EIO;
        }
    }

    kfree(msgs);
    printk("%s(%d) High-speed structured stream successfully sent!\n", __FUNCTION__, __LINE__);
    return 0;
}