3

I am trying to write and read from the I2C bus using C++. My I2C bus is virtual and the first thing is to load the kernel module i2c_stub. I can do everything through bash and now I am porting it to C++. I can open the i2c bus, acquire the i2c bus with a specific address, but I cannot write and read.

I am virtualizing /dev/i2c-3. These are the commands if I do in bash:

sudo make -C /lib/modules/$(uname -r)/build M=$(pwd) clean
sudo make -C /lib/modules/$(uname -r)/build M=$(pwd) modules
sudo make -C /lib/modules/$(uname -r)/build M=$(pwd) modules_install

sudo modprobe -r i2c_stub
sudo insmod i2c-stub.ko chip_addr=0x20
sudo i2cset -y 3 0x20 0x00 0x01
sudo i2cget -y 3 0x20 0x00

And this is the C++ code. The writing is failing, also the reading if I change it to be first. I am not sure if it is when I use ioctl and the address I2C_SLAVE, 0x20. I don't know where to use the address 0x00.

TEST_F(I2CTest, TestReadAndWriteI2C) {
// ------- LOAD i2c_stub KERNEL MODULE -------
char *params;
int fd;
size_t image_size;
struct stat st;
void *image;

// command: sudo insmod /root/i2c-tests/i2c-stub.ko chip_addr=0x20
params = "chip_addr=0x20";
fd = open("/root/i2c-tests/i2c-stub.ko", O_RDONLY);
fstat(fd, &st);
image_size = st.st_size;
image = malloc(image_size);
read(fd, image, image_size);
close(fd);
if (init_module(image, image_size, params) != 0) {
    perror("init_module");
    std::cout
            << "Please make sure that the following commands were executed " <<
            "on the directory [/root/i2c-tests/] before to run the unit test TestAddKernelModule " <<
            "and the file [/root/i2c-tests/i2c-stub.ko] exists." << std::endl;
    std::cout << "sudo rmmod i2c_stub" << std::endl;
    std::cout << "sudo make -C /lib/modules/$(uname -r)/build M=$(pwd) clean" << std::endl;
    std::cout << "sudo make -C /lib/modules/$(uname -r)/build M=$(pwd) modules" << std::endl;
    std::cout << "sudo make -C /lib/modules/$(uname -r)/build M=$(pwd) modules_install" << std::endl;
    GTEST_FAIL();
}
free(image);
GTEST_SUCCESS_("Kernel module loaded.");


//----- OPEN THE I2C BUS -----
int file_i2c = open("/dev/i2c-3", O_RDWR);
ASSERT_GT(file_i2c, 0);
if (file_i2c < 0) {
    GTEST_FAIL(); // Failed to open the i2c bus
} else {
    // std::cout << "Opened i2c port: /dev/i2c-3" << std::endl;
    GTEST_SUCCESS_("Opened i2c port: /dev/i2c-3");
}

// <<<<< The I2C address of the slave
if (ioctl(file_i2c, I2C_SLAVE, 0x20) < 0) {
    std::cout << "ioctl error: " << strerror(errno) << std::endl;
    GTEST_FAIL(); // Failed to acquire bus access and/or talk to slave
} else {
    std::cout << "Acquired bus access to i2c address: " << I2C_ADDR << std::endl;
    GTEST_SUCCESS_("Acquired bus access to i2c address: " + I2C_ADDR);
}

//----- WRITE BYTES -----
char bufferToWrite[1];
bufferToWrite[0] = 0x01;
// <<< Number of bytes to write
if (write(file_i2c, bufferToWrite, 1) != 1) {
    GTEST_FAIL(); // Failed to write to the i2c bus
} else {
    GTEST_SUCCESS_("success writing on i2c");
}

//----- READ BYTES -----
char bufferToRead[1];
int numberOfBytesRead = read(file_i2c, bufferToRead, 1);
std::cout << "Data read: " << bufferToRead[0] << std::endl;
printf("0x%02X\n", bufferToRead[0]);
GTEST_SUCCESS_("Data read: " + bufferToRead[0]);
}
Felipe
  • 7,013
  • 8
  • 44
  • 102

1 Answers1

1

I solved looking how wiringPiI2C (https://github.com/WiringPi/WiringPi/blob/master/wiringPi/wiringPiI2C.c) does and modified few points on my code.

TEST_F(I2CTest, TestReadAndWriteI2C) {
    // ------- LOAD i2c_stub KERNEL MODULE -------
    char *params;
    int fd;
    size_t image_size;
    struct stat st;
    void *image;

    // command: sudo insmod /root/i2c-tests/i2c-stub.ko chip_addr=0x20
    params = "chip_addr=0x20";
    fd = open("/root/i2c-tests/i2c-stub.ko", O_RDONLY);
    fstat(fd, &st);
    image_size = st.st_size;
    image = malloc(image_size);
    read(fd, image, image_size);
    close(fd);
    if (init_module(image, image_size, params) != 0) {
        perror("init_module");
        std::cout
                << "Please make sure that the following commands were executed " <<
                "on the directory [/root/i2c-tests/] before to run the unit test TestAddKernelModule " <<
                "and the file [/root/i2c-tests/i2c-stub.ko] exists." << std::endl;
        std::cout << "sudo rmmod i2c_stub" << std::endl;
        std::cout << "sudo make -C /lib/modules/$(uname -r)/build M=$(pwd) clean" << std::endl;
        std::cout << "sudo make -C /lib/modules/$(uname -r)/build M=$(pwd) modules" << std::endl;
        std::cout << "sudo make -C /lib/modules/$(uname -r)/build M=$(pwd) modules_install" << std::endl;
        GTEST_FAIL();
    }
    free(image);
    GTEST_SUCCESS_("Kernel module loaded.");

    // This initialises the I2C system with your given device identifier.
    int i2cFileDescriptor;
    if ((i2cFileDescriptor = open("/dev/i2c-3", O_RDWR)) < 0) {
        GTEST_FAIL();
    }
    if (ioctl(i2cFileDescriptor, I2C_SLAVE, 0x20) < 0) {
        GTEST_FAIL();
    }

    // Write bytes on I2C bus
    union i2c_smbus_data data;
    data.byte = 5;
    i2c_smbus_access(i2cFileDescriptor, I2C_SMBUS_WRITE, 0x00, I2C_SMBUS_BYTE_DATA, &data);

    // Read bytes from I2C bus
    union i2c_smbus_data dataRead;
    if (i2c_smbus_access(i2cFileDescriptor, I2C_SMBUS_READ, 0x00, I2C_SMBUS_BYTE_DATA, &dataRead)) {
        GTEST_FAIL();
    } else {
        ASSERT_EQ(data.byte, dataRead.byte);
        std::cout << "Read: " << std::hex << static_cast<int>(dataRead.byte ) << std::endl;
        std::cout << "Read: " << (int) dataRead.byte << std::endl;
        GTEST_SUCCESS_("Read bytes from I2C bus.");
    }

    // Write word on I2C bus
    data.word = 556;
    i2c_smbus_access(i2cFileDescriptor, I2C_SMBUS_WRITE, 0x00, I2C_SMBUS_WORD_DATA, &data);

    // Read word from I2C bus
    union i2c_smbus_data wordRead;
    if (i2c_smbus_access(fd, I2C_SMBUS_READ, 0x00, I2C_SMBUS_WORD_DATA, &wordRead)) {
        GTEST_FAIL();
    } else {
        ASSERT_EQ(data.word, wordRead.word);
        std::cout << "Read: " << std::hex << static_cast<int>(wordRead.word ) << std::endl;
        std::cout << "Read: " << (int) wordRead.word << std::endl;
        std::cout << "Read: " << wordRead.word << std::endl;
        GTEST_SUCCESS_("Read word from I2C bus.");
    }
    // return data.word & 0xFFFF ;
}
Felipe
  • 7,013
  • 8
  • 44
  • 102
  • 1
    in reply to: https://stackoverflow.com/questions/5947286/how-to-load-linux-kernel-modules-from-c-code/38606527?noredirect=1#comment92849418_38606527 Let's keep the technical talk in English and on this thread. :-) Sorry but I don't really know this stuff well, but most likely you forgot to free some resource that was allocated? I'd look at the cleanup code of i2cset. – Ciro Santilli OurBigBook.com Oct 24 '18 at 14:34
  • I guess the problem is related to `init_module` and the way I am loading the kernel module. If I execute this piece of code and comment the rest (everything after `// This initialises the I2C system with your given device identifier.`) I get this error: `*** Error in `./test/sensorTests': free(): invalid pointer: 0x01b64f50 *** Aborted`. – Felipe Oct 24 '18 at 15:04
  • I am really sorry. I isolated the `init_module` code again in different test and actually my error is this: `*** Error in ./test/sensorTests: malloc(): memory corruption: 0x010b38c8 *** Aborted` – Felipe Oct 24 '18 at 15:18