clinuxdumpi2ceeprom

Reading a 32k i2c eeprom from userland


I need to read an eeprom in an embedded device.

So far this "almost" worked:

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

#include <linux/i2c-dev.h>


#define READ_SIZE   (256)
#define NB_PAGES    (128)



void dump_to_file(const char *output_file_path,
          const uint8_t *buffer, const int buffer_length)
{
    int output_file = open(output_file_path, O_RDWR|O_APPEND|O_CREAT);
    if (output_file < 0) {
        printf("Failed opening output file %s\n", output_file_path);
        return;
    }

    write(output_file, buffer, buffer_length);
}


int main(int argc, char *argv[])
{
    /* got these values from i2cdetect */
    const char *i2c_device = "/dev/i2c-4";
    const int device_address = 0x50;

    /* open the i2c device file */
    int file = open(i2c_device, O_RDWR);
    if (file < 0) {
        printf("Failed opening %s\n", i2c_device);
        return 1;
    }

    if (ioctl(file, I2C_SLAVE, device_address) < 0) {
        printf("Failed addressing device at %02X\n", device_address);
        close(file);
        return 1;
    }

    int i = 0;
    for (i = 0; i < NB_PAGES; i++) {
        char buf[READ_SIZE] = {0};

        if (read(file, buf, READ_SIZE) != READ_SIZE) {
            printf("Failed reading\n");
            close(file);
            return 1;
        }

        dump_to_file(argv[1], buf, READ_SIZE);
    }

    close(file);

    return 0;
}

By "almost" I mean that it dumps the full eeprom but the START depends on the last block read..
It's not always the same.
If I read 10 blocks. then run the program again I read the next ones and not the first 10.

How to set the starting address?

Update: if I do:

i2cset -y 4 0x50 0x00 0x00

and the run the above code, it works. so how can I put the equivalent of the i2cset command in the code?


Solution

  • Done! It wasn't easy because I could not find documentations anywhere.. but I thought that since the eeprom is 32K, maybe it was "like" a 24c256. But even in that case I found nothing in userspace until I decided to go by instinct. I studied i2cset source, understood what it did and put it in the code.

    Here is the result, which dumps a full i2c 32k eprom from userspace.

    Note a full backup and restore utility can be found here: https://gist.github.com/Zibri/cf8ac0b311301aeeaa8910c7da824bff

    #include <stdio.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <sys/ioctl.h>
    #include <linux/i2c-dev.h>
    
    
    #define READ_SIZE       (256)
    #define NB_PAGES        (128)
    
    void dump_to_file(const char *output_file_path,
                      const uint8_t *buffer, const int buffer_length)
    {
            int output_file = open(output_file_path, O_RDWR|O_APPEND|O_CREAT);
            if (output_file < 0) {
                    printf("Failed opening output file %s\n", output_file_path);
                    return;
            }
    
            write(output_file, buffer, buffer_length);
    }
    
    
    int main(int argc, char *argv[])
    {
    
            const char *i2c_device = "/dev/i2c-4";
            const int device_address = 0x50;
    
            int file = open(i2c_device, O_RDWR);
            if (file < 0) {
                    printf("Failed opening %s\n", i2c_device);
                    return 1;
            }
    
            if (ioctl(file, I2C_SLAVE, device_address) < 0) {
                    printf("Failed addressing device at %02X\n", device_address);
                    close(file);
                    return 1;
            }
    
            int i = 0;
    
            write(file,'\x00\x00',2); // ADDRESS
    
            for (i = 0; i < NB_PAGES; i++) {
                    char buf[READ_SIZE] = {0};
    
                    if (read(file, buf, READ_SIZE) != READ_SIZE) {
                            printf("Failed reading\n");
                            close(file);
                            return 1;
                    }
    
                    dump_to_file(argv[1], buf, READ_SIZE);
            }
    
            close(file);
    
            return 0;
    }