static bool iotjs_spi_open(iotjs_spi_t* spi) {
  IOTJS_VALIDATED_STRUCT_METHOD(iotjs_spi_t, spi);

  struct iotbus_spi_config_s cfg = {.bits_per_word = _this->bits_per_word,
                                    .chip_select =
                                        _this->chip_select == kSpiCsNone ? 0
                                                                         : 1,
                                    .frequency = _this->max_speed };

  switch (_this->mode) {
    case kSpiMode_0:
      cfg.mode = IOTBUS_SPI_MODE0;
      break;
    case kSpiMode_1:
      cfg.mode = IOTBUS_SPI_MODE1;
      break;
    case kSpiMode_2:
      cfg.mode = IOTBUS_SPI_MODE2;
      break;
    case kSpiMode_3:
      cfg.mode = IOTBUS_SPI_MODE3;
      break;
    default:
      cfg.mode = IOTBUS_SPI_MODE0;
  }

  _this->hSpi = iotbus_spi_open(_this->bus, &cfg);
  if (_this->hSpi == NULL) {
    return false;
  }

  DDLOG(
      "SPI Options \n mode: %d\n chipSelect: %d\n bitOrder: %d\n "
      "maxSpeed: %d\n bitPerWord: %d\n loopback: %d",
      _this->mode, _this->chip_select, _this->bit_order, _this->max_speed,
      _this->bits_per_word, _this->loopback);

  return true;
}


bool iotjs_spi_transfer(iotjs_spi_t* spi) {
  IOTJS_VALIDATED_STRUCT_METHOD(iotjs_spi_t, spi);

  int err =
      iotbus_spi_transfer_buf(_this->hSpi, (unsigned char*)_this->tx_buf_data,
                              (unsigned char*)_this->rx_buf_data,
                              _this->buf_len);
  if (err != 0) {
    DDLOG("%s - transfer failed: %d", __func__, err);
    return false;
  }

  return true;
}
void WriteWorker(uv_work_t* work_req) {
  I2C_WORKER_INIT_TEMPLATE;
  iotjs_i2c_t* i2c = iotjs_i2c_instance_from_reqwrap(req_wrap);
  IOTJS_VALIDATED_STRUCT_METHOD(iotjs_i2c_t, i2c);

  uint8_t len = req_data->buf_len;
  uint8_t* data = (uint8_t*)req_data->buf_data;

  IOTJS_ASSERT(!_this->i2c_master);
  IOTJS_ASSERT(len > 0);

  int ret = i2c_write(_this->i2c_master, &_this->config, data, len);
  if (ret < 0) {
    DDLOG("I2C WriteWorker : cannot write - %d", ret);
    req_data->error = kI2cErrWrite;
  } else {
    req_data->error = kI2cErrOk;
  }

  if (req_data->buf_data != NULL) {
    iotjs_buffer_release(req_data->buf_data);
  }

  req_data->error = kI2cErrOk;
}
void WriteBlockWorker(uv_work_t* work_req) {
  I2C_WORKER_INIT_TEMPLATE;
  iotjs_i2c_t* i2c = iotjs_i2c_instance_from_reqwrap(req_wrap);
  IOTJS_VALIDATED_STRUCT_METHOD(iotjs_i2c_t, i2c);

  uint8_t cmd = req_data->cmd;
  uint8_t len = req_data->buf_len;
  char* data = req_data->buf_data;

  // The first element of data array is command.
  iotjs_buffer_reallocate(data, len + 1);
  memmove(data + 1, data, len * sizeof(char));
  data[0] = cmd;

  IOTJS_ASSERT(!_this->i2c_master);

  int ret =
      i2c_write(_this->i2c_master, &_this->config, &req_data->byte, len + 1);
  if (ret < 0) {
    DDLOG("I2C WriteBlockWorker : cannot write - %d", ret);
    req_data->error = kI2cErrWrite;
    return;
  }
  req_data->error = kI2cErrOk;
}
static iotjs_error_t tizen_send_launch_request(const char* json,
                                               void* hbridge) {
  DDDLOG("%s", __func__);

  bundle* b;
  int ret;

  ret = bundle_from_json(json, &b);
  if (ret != BUNDLE_ERROR_NONE) {
    DDLOG("bundle_from_json failed");
    return IOTJS_ERROR_INVALID_PARAMETER;
  }

  app_control_h app_control = NULL;

  app_control_create(&app_control);
  app_control_import_from_bundle(app_control, b);

  ret = app_control_send_launch_request(app_control, NULL, NULL);

  if (ret != APP_CONTROL_ERROR_NONE) {
    DDDLOG("app_control_send_launch_request failed");
    switch (ret) {
      case APP_CONTROL_ERROR_INVALID_PARAMETER:
        iotjs_bridge_set_err(hbridge, "APP_CONTROL_ERROR_INVALID_PARAMETER");
        break;
      case APP_CONTROL_ERROR_OUT_OF_MEMORY:
        iotjs_bridge_set_err(hbridge, "APP_CONTROL_ERROR_OUT_OF_MEMORY");
        break;
      case APP_CONTROL_ERROR_APP_NOT_FOUND:
        iotjs_bridge_set_err(hbridge, "APP_CONTROL_ERROR_APP_NOT_FOUND");
        break;
      case APP_CONTROL_ERROR_LAUNCH_REJECTED:
        iotjs_bridge_set_err(hbridge, "APP_CONTROL_ERROR_LAUNCH_REJECTED");
        break;
      case APP_CONTROL_ERROR_LAUNCH_FAILED:
        iotjs_bridge_set_err(hbridge, "APP_CONTROL_ERROR_LAUNCH_FAILED");
        break;
      case APP_CONTROL_ERROR_TIMED_OUT:
        iotjs_bridge_set_err(hbridge, "APP_CONTROL_ERROR_TIMED_OUT");
        break;
      case APP_CONTROL_ERROR_PERMISSION_DENIED:
        iotjs_bridge_set_err(hbridge, "APP_CONTROL_ERROR_PERMISSION_DENIED");
        break;
      default:
        iotjs_bridge_set_err(hbridge, "APP_CONTROL_ERROR_UNKNOWN");
        break;
    }
    return IOTJS_ERROR_RESULT_FAILED;
  }

  bundle_free(b);
  app_control_destroy(app_control);

  return IOTJS_ERROR_NONE;
}
void iotjs_spi_open_worker(uv_work_t* work_req) {
  SPI_WORKER_INIT;
  IOTJS_VALIDATED_STRUCT_METHOD(iotjs_spi_t, spi);

  if (!iotjs_spi_open(spi)) {
    DDLOG("%s - SPI open failed %d", __func__, _this->bus);
    req_data->result = false;
    return;
  }

  req_data->result = true;
}
bool iotjs_spi_close(iotjs_spi_t* spi) {
  IOTJS_VALIDATED_STRUCT_METHOD(iotjs_spi_t, spi);

  if (_this->hSpi != NULL) {
    int err = iotbus_spi_close(_this->hSpi);
    if (err != 0) {
      DDLOG("%s - close failed: %d", __func__, err);
      return false;
    }
    _this->hSpi = NULL;
  }

  return true;
}
void WriteByteWorker(uv_work_t* work_req) {
  I2C_WORKER_INIT_TEMPLATE;
  iotjs_i2c_t* i2c = iotjs_i2c_instance_from_reqwrap(req_wrap);
  IOTJS_VALIDATED_STRUCT_METHOD(iotjs_i2c_t, i2c);

  IOTJS_ASSERT(!_this->i2c_master);

  int ret = i2c_write(_this->i2c_master, &_this->config, &req_data->byte, 1);
  if (ret < 0) {
    DDLOG("I2C WriteByteWorker : cannot write - %d", ret);
    req_data->error = kI2cErrWrite;
    return;
  }
  req_data->error = kI2cErrOk;
}
void iotjs_tizen_app_control_cb(app_control_h app_control, void* user_data) {
  DDDLOG("%s", __func__);

  iotjs_environment_t* env = iotjs_environment_get();

  if (env->state != kRunningMain && env->state != kRunningLoop) {
    return;
  }

  const char* event_emitter_name = IOTJS_MAGIC_STRING_TIZEN;
  const char* event_name = IOTJS_MAGIC_STRING_APP_CONTROL;

  jerry_value_t tizen = iotjs_module_get(event_emitter_name);
  jerry_value_t fn = iotjs_jval_get_property(tizen, IOTJS_MAGIC_STRING_EMIT);

  if (jerry_value_is_function(fn) == false) {
    DDDLOG("tizen module is not loaded");
    goto exit;
  }

  // parse app control
  char* json = NULL;
  bundle* b = NULL;

  app_control_export_as_bundle(app_control, &b);

  if (BUNDLE_ERROR_NONE != bundle_to_json(b, &json)) {
    DDLOG("bundle_to_json failed");
    bundle_free(b);
    return;
  }
  DDDLOG("JSON: %s", json);

  // call emit
  jerry_value_t jargv[2] = { jerry_create_string(
                                 (const jerry_char_t*)event_name),
                             jerry_create_string((const jerry_char_t*)json) };

  iotjs_invoke_callback(fn, tizen, jargv, 2);
  jerry_release_value(jargv[0]);
  jerry_release_value(jargv[1]);

  free(json);
  bundle_free(b);

exit:
  jerry_release_value(fn);
}
void OpenWorker(uv_work_t* work_req) {
  I2C_WORKER_INIT_TEMPLATE;
  iotjs_i2c_t* i2c = iotjs_i2c_instance_from_reqwrap(req_wrap);

  IOTJS_VALIDATED_STRUCT_METHOD(iotjs_i2c_t, i2c);
  _this->i2c_master = iotjs_i2c_config_nuttx(req_data->device);
  if (!_this->i2c_master) {
    DDLOG("I2C OpenWorker : cannot open");
    req_data->error = kI2cErrOpen;
    return;
  }

  _this->config.frequency = I2C_DEFAULT_FREQUENCY;

  req_data->error = kI2cErrOk;
}
void ReadWorker(uv_work_t* work_req) {
  I2C_WORKER_INIT_TEMPLATE;
  iotjs_i2c_t* i2c = iotjs_i2c_instance_from_reqwrap(req_wrap);
  IOTJS_VALIDATED_STRUCT_METHOD(iotjs_i2c_t, i2c);

  uint8_t len = req_data->buf_len;
  req_data->buf_data = iotjs_buffer_allocate(len);

  IOTJS_ASSERT(!_this->i2c_master);
  IOTJS_ASSERT(len > 0);

  int ret = i2c_read(_this->i2c_master, &_this->config,
                     (uint8_t*)req_data->buf_data, len);
  if (ret != 0) {
    DDLOG("I2C ReadWorker : cannot read - %d", ret);
    req_data->error = kI2cErrRead;
    return;
  }
  req_data->error = kI2cErrOk;
}