linux-device-driveri2cgpio

How to interact with the Linux PCA953X.c driver? How does one utilize this driver?


I have a PCA9535 GPIO Expander board connected through I²C to my Raspberry Pi. I am able to set the GPIO expander output pins (to high) using the i2cset command:

sudo i2cset 1 0x20 0x02 0xFF   // 0x20 (gpio expander), and register 0x02

I came across a kernel driver for the PCA953X and loaded the kernel module gpio-pca953x.ko + modified the /boot/config.txt to include the dts overlay for the expander. When I run i2detect -y 1 (for i2c-1 bus), I can see "UU" at the 0x20 slave address, which corroborates that the driver is managing it.

If I wanted to do something comparable to what I did with the i2cset command programmatically, what API / interface should I use such that I make use of the PCA953X driver that's installed? As you can see from the code below, nothing there suggests that I would be utilizing PCA953X:

  int file;
  int adapter_nr = 1; 
  char filename[20];

  snprintf(filename, 19, "/dev/i2c-%d", adapter_nr);
  file = open(filename, O_RDWR);
  if (file < 0) {
    exit(1);
  }

  // Writes 0xFF to register 0x02
  uint8_t cmd[2]  = {0x02, 0XFF};
  struct i2c_msg msg = {
    .addr = 0x20,
    .flags = 0,
    .len = sizeof(cmd)/sizeof(uint8_t),
    .buf = cmd
  };
 
  struct i2c_rdwr_ioctl_data tx = {
    .msgs = &msg,
    .nmsgs = 1
  };

  ioctl(file, I2C_RDWR, &tx);

Is there a programming route that involves utilizing the PCA953X driver? What does having that module actually do?


Solution

  • Thanks to @TomV for pointing it out in the comments. The PCA953X driver provides a new character device in the form of "gpiochipN". In my case, it was gpiochip2. I had not noticed this extra gpiochip in /dev as I was not looking for it. Because this is a character device, I was able to use ioctl to interact with the lines:

     int fd, ret;
     fd = open("/dev/gpiochip2", O_RDONLY);
     if (fd < 0) {
         printf("Unable to open: %s", strerror(errno));
         return;
     }
    
     struct gpiohandle_request req;
     req.lineoffsets[0] = 0;
     req.lineoffsets[1] = 1;
     req.lineoffsets[2] = 2;
     req.lineoffsets[3] = 3;
     req.lineoffsets[4] = 4;
     req.lineoffsets[5] = 5;
     req.lineoffsets[6] = 6;
     req.lineoffsets[7] = 7;
     req.lines = 8;
     req.flags = GPIOHANDLE_REQUEST_OUTPUT;
     ret = ioctl(fd, GPIO_GET_LINEHANDLE_IOCTL, &req);
     if (-1 == ret) {
       printf("Failed to get line handle:%s\n", strerror(ret));
       close(fd);
       return;
     }
    
     // Sets all 8 lines to high (equivalent to setting register 0x3 to 0b11111111 or 0xFF)
     struct gpiohandle_data data;
     data.values[0] = 1;
     data.values[1] = 1;
     data.values[2] = 1;
     data.values[3] = 1;
     data.values[4] = 1;
     data.values[5] = 1;
     data.values[6] = 1;
     data.values[7] = 1;
     ret = ioctl(req.fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, &data);
     if (-1 == ret) {
       printf("Failed to set line value\n");
     }
     else {
       close(req.fd);
     }
    
     close(fd);
    

    The above code interacts with the gpiochip2 character device, which is made possible by having the PCA953X installed. Without it, I would have to read and write to the registers through the i2c bus as was posted in my original question.