Search code examples
clinuxi2c

write() returns -1 when writing to I2C_SLAVE device


I've read through the Linux kernel documents on i2c and written a code to try to replicate the command i2cset -y 0 0x60 0x05 0xff

The code that I've written is here:

#include <stdio.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <stdint.h>
#include <string.h>

int main(){

 int file;    
 file = open("/dev/i2c-0", O_RDWR);
 if (file < 0) {
  exit(1);
 }

 int addr = 0x60;

 if(ioctl(file, I2C_SLAVE, addr) < 0){
 exit(1);
 }

__u8 reg = 0x05;
__u8 res;
__u8 data = 0xff;

int written = write(file, &reg, 1); 
printf("write returned %d\n", written);

written = write(file, &data, 1); 
printf("write returned %d\n", written);

}

When I compile and run this code I get: write returned -1
write returned -1

I've tried to follow exactly what the docs tell me, my understanding is that the address is set first with the call to ioctl, then I need to write() the register and then the data that I want sent to the register.

I've also tried to use use SMbus, but I can't get my code to compile using this, it complains at the linking stage that it can't find the functions.

Have I made any mistakes in this code? I'm a beginner to i2c and don't have a lot of experience with c either.

EDIT: errno give the following message: Operation not supported. I am logged in as root on this machine though, so I don't think it can be a permissions thing, although I may be wrong.


Solution

  • The way I got around this problem was to use SMBus, in particular the functions i2c_smbus_write_byte_data and i2c_smbus_read_byte_data. I was able to use these functions to successfully read and write to the device.

    I did have a little trouble finding these functions, I kept trying to download libraries using apt-get to install the appropriate header files. In the end I simply downloaded the files smbus.c and smbus.h.

    Then the code I needed was:

    #include <stdio.h>
    #include <linux/i2c.h>
    #include <linux/i2c-dev.h>
    #include "smbus.h"
    #include <fcntl.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <sys/ioctl.h>
    #include <stdint.h>
    #include <string.h>
    #include <errno.h>
    
    
    int main(){
    
    int file;     
    file = open("/dev/i2c-0", O_RDWR);
    if (file < 0) {
        exit(1);
    }
    
    int addr = 0x60;
    
    if(ioctl(file, I2C_SLAVE, addr) < 0){
        exit(1);
    }
    
    __u8 reg = 0x05; /* Device register to access */
    __s32 res;
    
    res = i2c_smbus_write_byte_data(file, reg, 0xff);
    close(file);
    }
    

    Then if I compile the smbus.c file: gcc -c smbus.c and myfile: gcc -c myfile.c, then link them: gcc smbus.o myfile.o -o myexe I get a working executable that runs my I2C command. Ofcourse, I have smbus.c and smbus.h in the same directory as myfile.c.