public MCP23S17GpioProvider(byte spiAddress, int spiChannel, int spiSpeed) throws IOException {

    // setup SPI for communication
    int fd = Spi.wiringPiSPISetup(spiChannel, spiSpeed);
    if (fd <= -1) {
      throw new IOException("SPI port setup failed.");
    }

    // set all default pins directions
    write(REGISTER_IODIR_A, (byte) currentDirectionA);
    write(REGISTER_IODIR_B, (byte) currentDirectionB);

    // set all default pin interrupts
    write(REGISTER_GPINTEN_A, (byte) currentDirectionA);
    write(REGISTER_GPINTEN_B, (byte) currentDirectionB);

    // set all default pin interrupt default values
    write(REGISTER_DEFVAL_A, (byte) 0x00);
    write(REGISTER_DEFVAL_B, (byte) 0x00);

    // set all default pin interrupt comparison behaviors
    write(REGISTER_INTCON_A, (byte) 0x00);
    write(REGISTER_INTCON_B, (byte) 0x00);

    // set all default pin states
    write(REGISTER_GPIO_A, (byte) currentStatesA);
    write(REGISTER_GPIO_B, (byte) currentStatesB);

    // set all default pin pull up resistors
    write(REGISTER_GPPU_A, (byte) currentPullupA);
    write(REGISTER_GPPU_B, (byte) currentPullupB);
  }
  protected void write(byte register, byte data) {

    // create packet in data buffer
    byte packet[] = new byte[3];
    packet[0] = (byte) (address | WRITE_FLAG); // address byte
    packet[1] = register; // register byte
    packet[2] = data; // data byte

    // send data packet
    Spi.wiringPiSPIDataRW(0, packet, 3);
  }
  protected byte read(byte register) {

    // create packet in data buffer
    byte packet[] = new byte[3];
    packet[0] = (byte) (address | READ_FLAG); // address byte
    packet[1] = register; // register byte
    packet[2] = 0b00000000; // data byte

    int result = Spi.wiringPiSPIDataRW(0, packet, 3);
    if (result >= 0) return packet[2];
    else throw new RuntimeException("Invalid SPI read operation: " + result);
  }