I2C IOCTL Ошибка записи

Эй, я пытаюсь написать приложение из пользовательского пространства, чтобы переместить некоторые данные в I2C для встроенной системы, работающей под управлением PetaLinux, операционной системы для встроенного Linux, хотя я не думаю, что именно это влияет на проблему. Я получаю тайм-аут соединения и ошибку сегментации.

Функция имеет макросы, которые направляют ее на запись в первую шину I2C. Я указываю данные, которые хочу записать в main, и передаю их в i2c_write, которая затем передает их в i2c_ioctl_write.

Вот код:

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

#include <errno.h>
#include <string.h>

#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <sys/ioctl.h>

#define I2C_ADAPTER "/dev/i2c-0"
#define I2C_DEVICE  0x00
#define REG_ADDR 0x00


int i2c_ioctl_write (int fd, uint8_t dev, uint8_t regaddr, uint16_t *data)
{
    printf("i2c_ioctl_write\n");
    int i, j = 0;
    int ret;
    uint8_t *buf;

    buf = malloc(1 + 2 * (sizeof(data) / sizeof(data[0])));
    if (buf == NULL) {
        return -ENOMEM;
    }
    printf("\tBuffer Allocation Successful...\n");

    buf[j ++] = regaddr;
    for (i = 0; i < (sizeof(data) / sizeof(data[0])); i ++) {
        buf[j ++] = (data[i] & 0xff00) >> 8;
        buf[j ++] = data[i] & 0xff;
    }
    printf("\tBuffer Setup Successful...\n");

    struct i2c_msg messages[] = {
        {
            .addr = dev,
            .buf = buf,
            .len = sizeof(buf) / sizeof(buf[0]),
        },
    };
    printf("\tSetup I2C Messages...\n");

    struct i2c_rdwr_ioctl_data payload = {
        .msgs = messages,
        .nmsgs = sizeof(messages) / sizeof(messages[0]),
    };
    printf("\tSetup I2C IOCTL Payload...\n");


    ret = ioctl(fd, I2C_RDWR, &payload);
    printf("\tWrote with IOCTL...\n");
    if (ret < 0) {
        ret = -errno;
    }

    free (buf);
    return ret;
}

int i2c_ioctl_smbus_write (int fd, uint8_t dev, uint8_t regaddr, uint16_t *data)
{
    printf("i2c_ioctl_smbus_write\n");
    int i, j = 0;
    int ret;
    uint8_t *buf;

    buf = malloc(2 * (sizeof(data) / sizeof(data[0])));
    if (buf == NULL) {
        return -ENOMEM;
    }

    for (i = 0; i < (sizeof(data) / sizeof(data[0])); i ++) {
        buf[j ++] = (data[i] & 0xff00) >> 8;
        buf[j ++] = data[i] & 0xff;
    }

    struct i2c_smbus_ioctl_data payload = {
        .read_write = I2C_SMBUS_WRITE,
        .size = I2C_SMBUS_WORD_DATA,
        .command = regaddr,
        .data = (void *) buf,
    };

    ret = ioctl (fd, I2C_SLAVE_FORCE, dev);
    if (ret < 0)
    {
        ret = -errno;
        goto exit;
    }

    ret = ioctl (fd, I2C_SMBUS, &payload);
    if (ret < 0)
    {
        ret = -errno;
        goto exit;
    }

exit:
    free(buf);
    return ret;
}

int i2c_write (int fd, uint8_t dev, uint8_t regaddr, uint16_t *data)
{
    printf("i2x_write\n");
    uint64_t funcs;

    if (ioctl(fd, I2C_FUNCS, &funcs) < 0) {
        return -errno;
    }

    if (funcs & I2C_FUNC_I2C) {
        return i2c_ioctl_write (fd, dev, regaddr, data);
    } else if (funcs & I2C_FUNC_SMBUS_WORD_DATA) {
        return i2c_ioctl_smbus_write (fd, dev, regaddr, data);
    } else {
        return -ENOSYS;
    }
}

int main (int argc, char *argv[])
{
    printf("main\n");
    uint8_t regaddr;
    int fd;
    int ret = 0;

    uint16_t data[] = {1, 2, 4};

    fd = open(I2C_ADAPTER, O_RDWR | O_NONBLOCK);
    ret = i2c_write(fd, I2C_DEVICE, REG_ADDR, data);
    close(fd);

    if (ret) {
        fprintf (stderr, "%s.\n", strerror(-ret));
    }

    free(data);

    return ret;
}

Когда я запускаю программу в QEMU, я получаю следующий вывод:

main i2x_write i2c_ioctl_write Распределение буфера выполнено успешно... Настройка буфера выполнена успешно... Настройка I2C-сообщений Настройка полезной нагрузки I2C IOCTL cdns-i2c e0004000.i2c: время ожидания истекло по завершении Писал с тайм-аутом соединения IOCTL. Ошибка сегментации

Я предполагаю, что это терпит неудачу на линии

ret = ioctl(fd, I2C_RDWR, &payload);

но я не уверен почему. Была ли полезная нагрузка построена неправильно?

Обновление: вот текущий код:

 #include <stdio.h>
 #include <stdlib.h>
 #include <stdint.h>
 #include <inttypes.h>

 #include <errno.h>
 #include <string.h>

 #include <sys/stat.h>
 #include <fcntl.h>
 #include <unistd.h>

 #include <linux/i2c.h>
 #include <linux/i2c-dev.h>
 #include <sys/ioctl.h>

 #define I2C_ADAPTER "/dev/i2c-0"
 #define I2C_DEVICE  0x00

 int main (int argc, char *argv[])
{
    int fd;
    int ret = 0;


    fd = open(I2C_ADAPTER, O_RDWR | O_NONBLOCK);

    uint64_t funcs;

    int addr = 0X00;

    if (ioctl(fd, I2C_SLAVE, addr) < 0) {
        /* ERROR HANDLING; you can check errno to see what went wrong */
            printf("Cannot setup as slave");
            exit(1);
         }

    if (ioctl(fd, I2C_FUNCS, &funcs) < 0) {
        printf("ioctl failed");
        return -errno;
    }

    printf("funcs & I2C_FUNC_I2C:   %llu\n", funcs & I2C_FUNC_I2C);
    printf("funcs & I2C_FUNC_SMBUS_WORD_DATA:   %llu\n", funcs & I2C_FUNC_SMBUS_WORD_DATA);

    __u8 reg = 0x10;
    __s32 res;

    if (funcs & I2C_FUNC_I2C) {
        char buf[10];
        printf("Attempting to write to I2C bus via I2C protocol...\n");
        buf[0] = reg;
        buf[1] = 0x43;
        buf[2] = 0x65;
        int bytes_written = write(fd, buf, 3);
        if(bytes_written != 3) {
            printf("Wrote %d bytes", bytes_written);
            printf("\tFailed to write to I2C Bus\n");
            close(fd);
            return -1;
        }
        else {
            printf("\tSuccesful write to I2C Bus\n");
        }

        char buf2[10];
        printf("Attempting to read from I2C bus via I2C protocol...\n");
        if(read(fd, buf2, 1) != 1) {
            printf("\tFailed to do I2C read from Bus\n");
            close(fd);
            return -1;
        }
        else {
            printf("\tRead successful. Comparing read results from original write buffer...");
            printf("\t\tWritten value: %c", buf[0]);
            printf("\t\tRead value: %c", buf2[0]);
        }


        return 0;


    } else if (funcs & I2C_FUNC_SMBUS_WORD_DATA) {
        printf("Attempting to write to I2C bus via SMBus protocol...\n");
        //res = i2c_smbus_write_word_data(fd, REG_ADDR, 0x6543);
        res = 1;
        if(res < 0) {
            printf("\tFailed to write to I2C Bus\n");
            close(fd);
            return -1;
        }
        else {
            printf("\tSuccesful write to I2C Bus\n");
        }

        //res = i2c_smbus_read_word_data(fd, REG_ADDR);
        if(res < 0) {
            printf("\tFailed to read from I2C Bus\n");
            close(fd);
            return -1;
        }
        else {
            printf("\tRead successful. Comparing read results from original write buffer...");
            printf("\t\tWritten value: %c", 0x6543);
            printf("\t\tRead value: %c", res);
        }
    } else {
        printf("Cannot write to I2C");
        return -ENOSYS;
    }

    close(fd);

    if (ret) {
        fprintf (stderr, "%s.\n", strerror(-ret));
    }

    return ret;
}

Я смог избавиться от ошибки seg, удалив free(), так что спасибо. Я точно определил проблему тайм-аута, которая возникает в драйвере Cadence I2C здесь:

https://github.com/Xilinx/linux-xlnx/blob/3f3c7b60919d56119a68813998d3005bca501a40/drivers/i2c/busses/i2c-cadence.c#L825

который все еще происходит.

Как уже упоминалось, вероятно, есть некоторая проблема с тем, как я пишу на ведомое устройство, в результате чего ведомое устройство не отправляет ACK, что приводит к тайм-ауту. Я не уверен, какие регистры мне нужно написать, что для. У меня есть ощущение, что макрос I2C_DEVICE и переменные addr и reg нужно будет изменить.

1 ответ

cdns-i2c e0004000.i2c: время ожидания по завершении

Кажется, что драйвер i2c (cdns-i2s) не получает подтверждение от подчиненного устройства. Это может произойти, если вы используете адрес ведомого устройства I2C в качестве 0x00, который является общим адресом вызова. При использовании общего адреса вызова второй отправляемый байт имеет специальное назначение, упомянутое в спецификации i2c (раздел 3.1.13).

Если вы используете общий адрес вызова, вам нужно следовать спецификации, иначе попробуйте использовать точный подчиненный адрес i2c вместо общего адреса вызова (0x00).

Другие вопросы по тегам