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

Viewed 68

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?

1 Answers

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.

Related