I2C IOCTL写失败

时间:2017-10-24 14:16:31

标签: c linux embedded-linux i2c petalinux

嘿我正在尝试编写一个用户空间应用程序,将一些数据移动到I2C,用于运行PetaLinux的嵌入式系统,这是一个嵌入式Linux的操作系统,尽管我认为这不会影响这个问题。我收到连接超时和分段错误。

该函数具有指示它写入第一个I2C总线的宏。我指定要在main中写入的数据并将其传递给i2c_write,然后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上运行程序时,我得到以下输出:

  

主   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;
}

我能够通过删除free()来摆脱seg错误,所以谢谢。我已经确定了Cadence I2C驱动程序中出现的超时问题:

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

仍在发生。

如上所述,我写入slave的方式可能存在一些问题,导致slave无法发送ACK,从而导致超时。我不确定我需要写哪些寄存器来写什么。我有一种感觉,需要更改I2C_DEVICE宏和addr以及reg变量。

1 个答案:

答案 0 :(得分:0)

  

cdns-i2c e0004000.i2c:超时等待完成

似乎i2c驱动程序(cdns-i2s)没有收到来自奴隶的确认。当您使用I2C从机地址为0x00(general call address)时,可能会发生这种情况。使用通用调用地址时,发送的第二个字节有一个特殊用途,在i2c-specification(第3.1.13节)中提到。

如果使用通用呼叫地址,则需要遵循规范,否则请尝试使用确切的i2c从地址而不是通用呼叫地址(0x00)。