text: #include <stdlib.h>
  #include <stdio.h>
  #include <string.h>
  #include <errno.h>
  #include <unistd.h>
  #include <signal.h>
  #include <assert.h>
  #include <fcntl.h>
  #include <sys/ioctl.h>
  #include <sys/mman.h>
  #include <poll.h>
  
  #include "common/util.h"
  #include "common/swaglog.h"
  #include "camera_qcom2.h"
  
  #include "media/cam_defs.h"
  #include "media/cam_isp.h"
  #include "media/cam_isp_ife.h"
  #include "media/cam_sensor_cmn_header.h"
  #include "media/cam_sensor.h"
  #include "media/cam_sync.h"
  
  #include "sensor2_i2c.h"
  
  #define FRAME_WIDTH  1928
  #define FRAME_HEIGHT 1208
  //#define FRAME_STRIDE 1936 // for 8 bit output
  #define FRAME_STRIDE 2416  // for 10 bit output
  
  static void hexdump(uint8_t *data, int len) {
    for (int i = 0; i < len; i++) {
      if (i!=0&&i%0x10==0) printf("\n");
      printf("%02X ", data[i]);
    }
    printf("\n");
  }
  
  
  extern volatile sig_atomic_t do_exit;
  
  CameraInfo cameras_supported[CAMERA_ID_MAX] = {
    [CAMERA_ID_AR0231] = {
      .frame_width = FRAME_WIDTH,
      .frame_height = FRAME_HEIGHT,
      .frame_stride = FRAME_STRIDE,
      .bayer = true,
      .bayer_flip = 0,
      .hdr = false
    },
  };
  
  // ************** low level camera helpers ****************
  
  int cam_control(int fd, int op_code, void *handle, int size) {
    struct cam_control camcontrol = {0};
    camcontrol.op_code = op_code;
    camcontrol.handle = (uint64_t)handle;
    if (size == 0) { camcontrol.size = 8;
      camcontrol.handle_type = CAM_HANDLE_MEM_HANDLE;
    } else {
      camcontrol.size = size;
      camcontrol.handle_type = CAM_HANDLE_USER_POINTER;
    }
  
    int ret = ioctl(fd, VIDIOC_CAM_CONTROL, &camcontrol);
    if (ret == -1) {
      perror("wat");
    }
    return ret;
  }
  
  int device_control(int fd, int op_code, int session_handle, int dev_handle) {
    // start stop and release are all the same
    static struct cam_release_dev_cmd release_dev_cmd;
    release_dev_cmd.session_handle = session_handle;
    release_dev_cmd.dev_handle = dev_handle;
    return cam_control(fd, op_code, &release_dev_cmd, sizeof(release_dev_cmd));
  }
  
  void *alloc_w_mmu_hdl(int video0_fd, int len, int align, int flags, uint32_t *handle, int mmu_hdl, int mmu_hdl2) {
    int ret;
  
    static struct cam_mem_mgr_alloc_cmd mem_mgr_alloc_cmd = {0};
    mem_mgr_alloc_cmd.len = len;
    mem_mgr_alloc_cmd.align = align;
    mem_mgr_alloc_cmd.flags = flags;
    mem_mgr_alloc_cmd.num_hdl = 0;
    if (mmu_hdl != 0) {
      mem_mgr_alloc_cmd.mmu_hdls[0] = mmu_hdl;
      mem_mgr_alloc_cmd.num_hdl++;
    }
    if (mmu_hdl2 != 0) {
      mem_mgr_alloc_cmd.mmu_hdls[1] = mmu_hdl2;
      mem_mgr_alloc_cmd.num_hdl++;
    }
  
    cam_control(video0_fd, CAM_REQ_MGR_ALLOC_BUF, &mem_mgr_alloc_cmd, sizeof(mem_mgr_alloc_cmd));
    *handle = mem_mgr_alloc_cmd.out.buf_handle;
  
    void *ptr = NULL;
    if (mem_mgr_alloc_cmd.out.fd > 0) {
      ptr = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, mem_mgr_alloc_cmd.out.fd, 0);
      assert(ptr != MAP_FAILED);
    }
  
    LOGD("alloced: %x %d %llx mapped %p", mem_mgr_alloc_cmd.out.buf_handle, mem_mgr_alloc_cmd.out.fd, mem_mgr_alloc_cmd.out.vaddr, ptr);
  
    return ptr;
  }
  
  void *alloc(int video0_fd, int len, int align, int flags, uint32_t *handle) {
    return alloc_w_mmu_hdl(video0_fd, len, align, flags, handle, 0, 0);
  }
  
  void release(int video0_fd, uint32_t handle) {
    int ret;
    static struct cam_mem_mgr_release_cmd mem_mgr_release_cmd = {0};
    mem_mgr_release_cmd.buf_handle = handle;
  
    ret = cam_control(video0_fd, CAM_REQ_MGR_RELEASE_BUF, &mem_mgr_release_cmd, sizeof(mem_mgr_release_cmd));
    assert(ret == 0);
  }
  
  
  void release_fd(int video0_fd, uint32_t handle) {
    // handle to fd
    close(handle>>16);
    release(video0_fd, handle);
  }
  
  // ************** high level camera helpers ****************
  
  void sensors_poke(struct CameraState *s, int request_id) {
    uint32_t cam_packet_handle = 0;
    int size = sizeof(struct cam_packet);
    struct cam_packet *pkt = alloc(s->video0_fd, size, 8,
      CAM_MEM_FLAG_KMD_ACCESS | CAM_MEM_FLAG_UMD_ACCESS | CAM_MEM_FLAG_CMD_BUF_TYPE, &cam_packet_handle);
    pkt->num_cmd_buf = 1;
    pkt->kmd_cmd_buf_index = -1;
    pkt->header.size = size;
    pkt->header.op_code = 0x7f;
    pkt->header.request_id = request_id;
    struct cam_cmd_buf_desc *buf_desc = (struct cam_cmd_buf_desc *)&pkt->payload;
  
    static struct cam_config_dev_cmd config_dev_cmd = {};
    config_dev_cmd.session_handle = s->session_handle;
    config_dev_cmd.dev_handle = s->sensor_dev_handle;
    config_dev_cmd.offset = 0;
    config_dev_cmd.packet_handle = cam_packet_handle;
  
    int ret = cam_control(s->sensor_fd, CAM_CONFIG_DEV, &config_dev_cmd, sizeof(config_dev_cmd));
    assert(ret == 0);
  
    release_fd(s->video0_fd, cam_packet_handle);
  }
  
  void sensors_i2c(struct CameraState *s, struct i2c_random_wr_payload* dat, int len, int op_code) {
    LOGD("sensors_i2c: %d", len);
    uint32_t cam_packet_handle = 0;
    int size = sizeof(struct cam_packet)+sizeof(struct cam_cmd_buf_desc)*1;
    struct cam_packet *pkt = alloc(s->video0_fd, size, 8,
      CAM_MEM_FLAG_KMD_ACCESS | CAM_MEM_FLAG_UMD_ACCESS | CAM_MEM_FLAG_CMD_BUF_TYPE, &cam_packet_handle);
    pkt->num_cmd_buf = 1;
    pkt->kmd_cmd_buf_index = -1;
    pkt->header.size = size;
    pkt->header.op_code = op_code;
    struct cam_cmd_buf_desc *buf_desc = (struct cam_cmd_buf_desc *)&pkt->payload;
  
    buf_desc[0].size = buf_desc[0].length = sizeof(struct cam_cmd_i2c_random_wr) + (len-1)*sizeof(struct i2c_random_wr_payload);
    buf_desc[0].type = CAM_CMD_BUF_I2C;
    struct cam_cmd_power *power = alloc(s->video0_fd, buf_desc[0].size, 8, CAM_MEM_FLAG_KMD_ACCESS | CAM_MEM_FLAG_UMD_ACCESS | CAM_MEM_FLAG_CMD_BUF_TYPE, &buf_desc[0].mem_handle);
    struct cam_cmd_i2c_random_wr *i2c_random_wr = (void*)power;
    i2c_random_wr->header.count = len;
    i2c_random_wr->header.op_code = 1;
    i2c_random_wr->header.cmd_type = CAMERA_SENSOR_CMD_TYPE_I2C_RNDM_WR;
    i2c_random_wr->header.data_type = CAMERA_SENSOR_I2C_TYPE_WORD;
    i2c_random_wr->header.addr_type = CAMERA_SENSOR_I2C_TYPE_WORD;
    memcpy(i2c_random_wr->random_wr_payload, dat, len*sizeof(struct i2c_random_wr_payload));
  
    static struct cam_config_dev_cmd config_dev_cmd = {};
    config_dev_cmd.session_handle = s->session_handle;
    config_dev_cmd.dev_handle = s->sensor_dev_handle;
    config_dev_cmd.offset = 0;
    config_dev_cmd.packet_handle = cam_packet_handle;
  
    int ret = cam_control(s->sensor_fd, CAM_CONFIG_DEV, &config_dev_cmd, sizeof(config_dev_cmd));
    assert(ret == 0);
  
    release_fd(s->video0_fd, buf_desc[0].mem_handle);
    release_fd(s->video0_fd, cam_packet_handle);
  }
  
  void sensors_init(int video0_fd, int sensor_fd, int camera_num) {
    uint32_t cam_packet_handle = 0;
    int size = sizeof(struct cam_packet)+sizeof(struct cam_cmd_buf_desc)*2;
    struct cam_packet *pkt = alloc(video0_fd, size, 8,
      CAM_MEM_FLAG_KMD_ACCESS | CAM_MEM_FLAG_UMD_ACCESS | CAM_MEM_FLAG_CMD_BUF_TYPE, &cam_packet_handle);
    pkt->num_cmd_buf = 2;
    pkt->kmd_cmd_buf_index = -1;
    pkt->header.op_code = 0x1000003;
    pkt->header.size = size;
    struct cam_cmd_buf_desc *buf_desc = (struct cam_cmd_buf_desc *)&pkt->payload;
  
    buf_desc[0].size = buf_desc[0].length = sizeof(struct cam_cmd_i2c_info) + sizeof(struct cam_cmd_probe);
    buf_desc[0].type = CAM_CMD_BUF_LEGACY;
    struct cam_cmd_i2c_info *i2c_info = alloc(video0_fd, buf_desc[0].size, 8, CAM_MEM_FLAG_KMD_ACCESS | CAM_MEM_FLAG_UMD_ACCESS | CAM_MEM_FLAG_CMD_BUF_TYPE, &buf_desc[0].mem_handle);
    struct cam_cmd_probe *probe = (struct cam_cmd_probe *)((uint8_t *)i2c_info) + sizeof(struct cam_cmd_i2c_info);
  
    switch (camera_num) {
      case 0:
        // port 0
        i2c_info->slave_addr = 0x20;
        probe->camera_id = 0;
        break;
      case 1:
        // port 1
        i2c_info->slave_addr = 0x30;
        probe->camera_id = 1;
        break;
      case 2:
        // port 2
        i2c_info->slave_addr = 0x20;
        probe->camera_id = 2;
        break;
    }
  
    // 0(I2C_STANDARD_MODE) = 100khz, 1(I2C_FAST_MODE) = 400khz
    //i2c_info->i2c_freq_mode = I2C_STANDARD_MODE;
    i2c_info->i2c_freq_mode = I2C_FAST_MODE;
    i2c_info->cmd_type = CAMERA_SENSOR_CMD_TYPE_I2C_INFO;
  
    probe->data_type = CAMERA_SENSOR_I2C_TYPE_WORD;
    probe->addr_type = CAMERA_SENSOR_I2C_TYPE_WORD;
    probe->op_code = 3;   // don't care?
    probe->cmd_type = CAMERA_SENSOR_CMD_TYPE_PROBE;
    probe->reg_addr = 0x3000; //0x300a; //0x300b;
    probe->expected_data = 0x354; //0x7750; //0x885a;
    probe->data_mask = 0;
  
    //buf_desc[1].size = buf_desc[1].length = 148;
    buf_desc[1].size = buf_desc[1].length = 196;
    buf_desc[1].type = CAM_CMD_BUF_I2C;
    struct cam_cmd_power *power = alloc(video0_fd, buf_desc[1].size, 8, CAM_MEM_FLAG_KMD_ACCESS | CAM_MEM_FLAG_UMD_ACCESS | CAM_MEM_FLAG_CMD_BUF_TYPE, &buf_desc[1].mem_handle);
    memset(power, 0, buf_desc[1].size);
    struct cam_cmd_unconditional_wait *unconditional_wait;
  
    void *ptr = power;
    // 7750
    /*power->count = 2;
    power->cmd_type = CAMERA_SENSOR_CMD_TYPE_PWR_UP;
    power->power_settings[0].power_seq_type = 2;
    power->power_settings[1].power_seq_type = 8;
    power = (void*)power + (sizeof(struct cam_cmd_power) + (power->count-1)*sizeof(struct cam_power_settings));*/
  
    // 885a
    power->count = 4;
    power->cmd_type = CAMERA_SENSOR_CMD_TYPE_PWR_UP;
    power->power_settings[0].power_seq_type = 3; // clock??
    power->power_settings[1].power_seq_type = 1; // analog
    power->power_settings[2].power_seq_type = 2; // digital
    power->power_settings[3].power_seq_type = 8; // reset low
    power = (void*)power + (sizeof(struct cam_cmd_power) + (power->count-1)*sizeof(struct cam_power_settings));
  
    unconditional_wait = (void*)power;
    unconditional_wait->cmd_type = CAMERA_SENSOR_CMD_TYPE_WAIT;
    unconditional_wait->delay = 5;
    unconditional_wait->op_code = CAMERA_SENSOR_WAIT_OP_SW_UCND;
    power = (void*)power + sizeof(struct cam_cmd_unconditional_wait);
  
    // set clock
    power->count = 1;
    power->cmd_type = CAMERA_SENSOR_CMD_TYPE_PWR_UP;
    power->power_settings[0].power_seq_type = 0;
    power->power_settings[0].config_val_low = 24000000; //Hz
    power = (void*)power + (sizeof(struct cam_cmd_power) + (power->count-1)*sizeof(struct cam_power_settings));
  
    unconditional_wait = (void*)power;
    unconditional_wait->cmd_type = CAMERA_SENSOR_CMD_TYPE_WAIT;
    unconditional_wait->delay = 10; // ms
    unconditional_wait->op_code = CAMERA_SENSOR_WAIT_OP_SW_UCND;
    power = (void*)power + sizeof(struct cam_cmd_unconditional_wait);
  
    // 8,1 is this reset?
    power->count = 1;
    power->cmd_type = CAMERA_SENSOR_CMD_TYPE_PWR_UP;
    power->power_settings[0].power_seq_type = 8;
    power->power_settings[0].config_val_low = 1;
    power = (void*)power + (sizeof(struct cam_cmd_power) + (power->count-1)*sizeof(struct cam_power_settings));
  
    unconditional_wait = (void*)power;
    unconditional_wait->cmd_type = CAMERA_SENSOR_CMD_TYPE_WAIT;
    unconditional_wait->delay = 100; // ms
    unconditional_wait->op_code = CAMERA_SENSOR_WAIT_OP_SW_UCND;
    power = (void*)power + sizeof(struct cam_cmd_unconditional_wait);
  
    // probe happens here
  
    // disable clock
    power->count = 1;
    power->cmd_type = CAMERA_SENSOR_CMD_TYPE_PWR_DOWN;
    power->power_settings[0].power_seq_type = 0;
    power->power_settings[0].config_val_low = 0;
    power = (void*)power + (sizeof(struct cam_cmd_power) + (power->count-1)*sizeof(struct cam_power_settings));
  
    unconditional_wait = (void*)power;
    unconditional_wait->cmd_type = CAMERA_SENSOR_CMD_TYPE_WAIT;
    unconditional_wait->delay = 1;
    unconditional_wait->op_code = CAMERA_SENSOR_WAIT_OP_SW_UCND;
    power = (void*)power + sizeof(struct cam_cmd_unconditional_wait);
  
    // reset high
    power->count = 1;
    power->cmd_type = CAMERA_SENSOR_CMD_TYPE_PWR_DOWN;
    power->power_settings[0].power_seq_type = 8;
    power->power_settings[0].config_val_low = 1;
    power = (void*)power + (sizeof(struct cam_cmd_power) + (power->count-1)*sizeof(struct cam_power_settings));
  
    unconditional_wait = (void*)power;
    unconditional_wait->cmd_type = CAMERA_SENSOR_CMD_TYPE_WAIT;
    unconditional_wait->delay = 1;
    unconditional_wait->op_code = CAMERA_SENSOR_WAIT_OP_SW_UCND;
    power = (void*)power + sizeof(struct cam_cmd_unconditional_wait);
  
    // reset low
    power->count = 1;
    power->cmd_type = CAMERA_SENSOR_CMD_TYPE_PWR_DOWN;
    power->power_settings[0].power_seq_type = 8;
    power->power_settings[0].config_val_low = 0;
    power = (void*)power + (sizeof(struct cam_cmd_power) + (power->count-1)*sizeof(struct cam_power_settings));
  
    unconditional_wait = (void*)power;
    unconditional_wait->cmd_type = CAMERA_SENSOR_CMD_TYPE_WAIT;
    unconditional_wait->delay = 1;
    unconditional_wait->op_code = CAMERA_SENSOR_WAIT_OP_SW_UCND;
    power = (void*)power + sizeof(struct cam_cmd_unconditional_wait);
  
    // 7750
    /*power->count = 1;
    power->cmd_type = CAMERA_SENSOR_CMD_TYPE_PWR_DOWN;
    power->power_settings[0].power_seq_type = 2;
    power = (void*)power + (sizeof(struct cam_cmd_power) + (power->count-1)*sizeof(struct cam_power_settings));*/
  
    // 885a
    power->count = 3;
    power->cmd_type = CAMERA_SENSOR_CMD_TYPE_PWR_DOWN;
    power->power_settings[0].power_seq_type = 2;
    power->power_settings[1].power_seq_type = 1;
    power->power_settings[2].power_seq_type = 3;
    power = (void*)power + (sizeof(struct cam_cmd_power) + (power->count-1)*sizeof(struct cam_power_settings));
  
    LOGD("probing the sensor");
    int ret = cam_control(sensor_fd, CAM_SENSOR_PROBE_CMD, (void *)cam_packet_handle, 0);
    assert(ret == 0);
  
    release_fd(video0_fd, buf_desc[0].mem_handle);
    release_fd(video0_fd, buf_desc[1].mem_handle);
    release_fd(video0_fd, cam_packet_handle);
  }
  
  void config_isp(struct CameraState *s, int io_mem_handle, int fence, int request_id, int buf0_mem_handle, int buf0_offset) {
    uint32_t cam_packet_handle = 0;
    int size = sizeof(struct cam_packet)+sizeof(struct cam_cmd_buf_desc)*2;
    if (io_mem_handle != 0) {
      size += sizeof(struct cam_buf_io_cfg);
    }
    struct cam_packet *pkt = alloc(s->video0_fd, size, 8,
      CAM_MEM_FLAG_KMD_ACCESS | CAM_MEM_FLAG_UMD_ACCESS | CAM_MEM_FLAG_CMD_BUF_TYPE, &cam_packet_handle);
    pkt->num_cmd_buf = 2;
    pkt->kmd_cmd_buf_index = 0;
  
    if (io_mem_handle != 0) {
      pkt->io_configs_offset = sizeof(struct cam_cmd_buf_desc)*2;
      pkt->num_io_configs = 1;
    }
  
    if (io_mem_handle != 0) {
      pkt->header.op_code = 0xf000001;
      pkt->header.request_id = request_id;
    } else {
      pkt->header.op_code = 0xf000000;
    }
    pkt->header.size = size;
    struct cam_cmd_buf_desc *buf_desc = (struct cam_cmd_buf_desc *)&pkt->payload;
    struct cam_buf_io_cfg *io_cfg = (void*)&pkt->payload + pkt->io_configs_offset;
  
    // TODO: support MMU
    buf_desc[0].size = 65624;
    buf_desc[0].length = 0;
    buf_desc[0].type = CAM_CMD_BUF_DIRECT;
    buf_desc[0].meta_data = 3;
    buf_desc[0].mem_handle = buf0_mem_handle;
    buf_desc[0].offset = buf0_offset;
  
    buf_desc[1].size = 324;
  	if (io_mem_handle != 0) {
      buf_desc[1].length = 228; // 0 works here too
      buf_desc[1].offset = 0x60;
  	} else {
      buf_desc[1].length = 324;
    }
    buf_desc[1].type = CAM_CMD_BUF_GENERIC;
    buf_desc[1].meta_data = CAM_ISP_PACKET_META_GENERIC_BLOB_COMMON;
    uint32_t *buf2 = alloc(s->video0_fd, buf_desc[1].size, 0x20, CAM_MEM_FLAG_KMD_ACCESS | CAM_MEM_FLAG_UMD_ACCESS | CAM_MEM_FLAG_CMD_BUF_TYPE, &buf_desc[1].mem_handle);
  
    // cam_isp_packet_generic_blob_handler
    uint32_t tmp[] = {
      // size is 0x20, type is 0(CAM_ISP_GENERIC_BLOB_TYPE_HFR_CONFIG)
      0x2000,
      0x1, 0x0, CAM_ISP_IFE_OUT_RES_RDI_0, 0x1, 0x0, 0x1, 0x0, 0x0, // 1 port, CAM_ISP_IFE_OUT_RES_RDI_0
      // size is 0x38, type is 1(CAM_ISP_GENERIC_BLOB_TYPE_CLOCK_CONFIG), clocks
      0x3801,
      0x1, 0x4, // Dual mode, 4 RDI wires
      0x18148d00, 0x0, 0x18148d00, 0x0, 0x18148d00, 0x0, // rdi clock
      0x0, 0x0, 0x0, 0x0, 0x0, 0x0,  // junk?
      // offset 0x60
      // size is 0xe0, type is 2(CAM_ISP_GENERIC_BLOB_TYPE_BW_CONFIG), bandwidth
      0xe002,
      0x1, 0x4, // 4 RDI
      0x0, 0x0, 0x1ad27480, 0x0, 0x1ad27480, 0x0, // left_pix_vote
      0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // right_pix_vote
      0x0, 0x0, 0x6ee11c0, 0x2, 0x6ee11c0, 0x2,  // rdi_vote
      0x0, 0x0, 0x0, 0x0,
      0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
      0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
      0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
      0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
    memcpy(buf2, tmp, sizeof(tmp));
  
    if (io_mem_handle != 0) {
      io_cfg[0].mem_handle[0] = io_mem_handle;
  		io_cfg[0].planes[0] = (struct cam_plane_cfg){
  		 .width = FRAME_WIDTH,
  		 .height = FRAME_HEIGHT,
  		 .plane_stride = FRAME_STRIDE,
  		 .slice_height = FRAME_HEIGHT,
  		 .meta_stride = 0x0,
  		 .meta_size = 0x0,
  		 .meta_offset = 0x0,
  		 .packer_config = 0x0,
  		 .mode_config = 0x0,
  		 .tile_config = 0x0,
  		 .h_init = 0x0,
  		 .v_init = 0x0,
  		};
      io_cfg[0].format = 0x3;
      io_cfg[0].color_pattern = 0x5;
      io_cfg[0].bpp = 0xc;
      io_cfg[0].resource_type = CAM_ISP_IFE_OUT_RES_RDI_0;
      io_cfg[0].fence = fence;
      io_cfg[0].direction = 0x2;
      io_cfg[0].subsample_pattern = 0x1;
      io_cfg[0].framedrop_pattern = 0x1;
    }
  
    static struct cam_config_dev_cmd config_dev_cmd = {};
    config_dev_cmd.session_handle = s->session_handle;
    config_dev_cmd.dev_handle = s->isp_dev_handle;
    config_dev_cmd.offset = 0;
    config_dev_cmd.packet_handle = cam_packet_handle;
  
    int ret = cam_control(s->isp_fd, CAM_CONFIG_DEV, &config_dev_cmd, sizeof(config_dev_cmd));
    if (ret != 0) {
      printf("ISP CONFIG FAILED\n");
    }
  
    release_fd(s->video0_fd, buf_desc[1].mem_handle);
    //release(s->video0_fd, buf_desc[0].mem_handle);
    release_fd(s->video0_fd, cam_packet_handle);
  }
  
  void enqueue_buffer(struct CameraState *s, int i) {
    int ret;
    int request_id = (++s->sched_request_id);
    bool first = true;
  
    if (s->buf_handle[i]) {
      first = false;
      release(s->video0_fd, s->buf_handle[i]);
  
      // destroy old output fence
      static struct cam_sync_info sync_destroy = {0};
      strcpy(sync_destroy.name, "NodeOutputPortFence");
      sync_destroy.sync_obj = s->sync_objs[i];
      ret = cam_control(s->video1_fd, CAM_SYNC_DESTROY, &sync_destroy, sizeof(sync_destroy));
      LOGD("fence destroy: %d %d", ret, sync_destroy.sync_obj);
    }
  
    // new request_ids
    s->request_ids[i] = request_id;
  
    // do stuff
    static struct cam_req_mgr_sched_request req_mgr_sched_request = {0};
    req_mgr_sched_request.session_hdl = s->session_handle;
    req_mgr_sched_request.link_hdl = s->link_handle;
    req_mgr_sched_request.req_id = request_id;
    ret = cam_control(s->video0_fd, CAM_REQ_MGR_SCHED_REQ, &req_mgr_sched_request, sizeof(req_mgr_sched_request));
    LOGD("sched req: %d %d", ret, request_id);
  
    // create output fence
    static struct cam_sync_info sync_create = {0};
    strcpy(sync_create.name, "NodeOutputPortFence");
    ret = cam_control(s->video1_fd, CAM_SYNC_CREATE, &sync_create, sizeof(sync_create));
    LOGD("fence req: %d %d", ret, sync_create.sync_obj);
    s->sync_objs[i] = sync_create.sync_obj;
  
    // configure ISP to put the image in place
    static struct cam_mem_mgr_map_cmd mem_mgr_map_cmd = {0};
    mem_mgr_map_cmd.mmu_hdls[0] = s->device_iommu;
    mem_mgr_map_cmd.num_hdl = 1;
    mem_mgr_map_cmd.flags = 1;
    mem_mgr_map_cmd.fd = s->bufs[i].fd;
    ret = cam_control(s->video0_fd, CAM_REQ_MGR_MAP_BUF, &mem_mgr_map_cmd, sizeof(mem_mgr_map_cmd));
    LOGD("map buf req: (fd: %d) 0x%x %d", s->bufs[i].fd, mem_mgr_map_cmd.out.buf_handle, ret);
    s->buf_handle[i] = mem_mgr_map_cmd.out.buf_handle;
  
    // poke sensor
    sensors_poke(s, request_id);
    LOGD("Poked sensor");
  
    // push the buffer
    config_isp(s, s->buf_handle[i], s->sync_objs[i], request_id, s->buf0_handle, 65632*(i+1));
  }
  
  
  // ******************* camera *******************
  
  static void camera_release_buffer(void* cookie, int i) {
    int ret;
    CameraState *s = cookie;
    enqueue_buffer(s, i);
  }
  
  static void camera_init(CameraState *s, int camera_id, int camera_num, unsigned int fps) {
    LOGD("camera init %d", camera_num);
  
    // TODO: this is copied code from camera_webcam
    assert(camera_id < ARRAYSIZE(cameras_supported));
    s->ci = cameras_supported[camera_id];
    assert(s->ci.frame_width != 0);
  
    s->camera_num = camera_num;
    s->frame_size = s->ci.frame_height * s->ci.frame_stride;
  
    tbuffer_init2(&s->camera_tb, FRAME_BUF_COUNT, "frame", camera_release_buffer, s);
  
    s->transform = (mat3){{
      1.0, 0.0, 0.0,
      0.0, 1.0, 0.0,
      0.0, 0.0, 1.0,
    }};
    s->digital_gain = 1.0;
  }
  
  static void camera_open(CameraState *s, VisionBuf* b) {
    int ret;
    s->bufs = b;
  
    // /dev/v4l-subdev10 is sensor, 11, 12, 13 are the other sensors
    switch (s->camera_num) {
      case 0:
        s->sensor_fd = open("/dev/v4l-subdev10", O_RDWR | O_NONBLOCK);
        break;
      case 1:
        s->sensor_fd = open("/dev/v4l-subdev11", O_RDWR | O_NONBLOCK);
        break;
      case 2:
        s->sensor_fd = open("/dev/v4l-subdev12", O_RDWR | O_NONBLOCK);
        break;
    }
    assert(s->sensor_fd >= 0);
    LOGD("opened sensor");
  
    // also at /dev/v4l-subdev3, 4, 5, 6
    switch (s->camera_num) {
      case 0:
        s->csiphy_fd = open("/dev/v4l-subdev3", O_RDWR | O_NONBLOCK);
        break;
      case 1:
        s->csiphy_fd = open("/dev/v4l-subdev4", O_RDWR | O_NONBLOCK);
        break;
      case 2:
        s->csiphy_fd = open("/dev/v4l-subdev5", O_RDWR | O_NONBLOCK);
        break;
    }
    assert(s->csiphy_fd >= 0);
    LOGD("opened csiphy");
  
    // probe the sensor
    LOGD("-- Probing sensor %d", s->camera_num);
    sensors_init(s->video0_fd, s->sensor_fd, s->camera_num);
  
    memset(&s->req_mgr_session_info, 0, sizeof(s->req_mgr_session_info));
    ret = cam_control(s->video0_fd, CAM_REQ_MGR_CREATE_SESSION, &s->req_mgr_session_info, sizeof(s->req_mgr_session_info));
    LOGD("get session: %d 0x%X", ret, s->req_mgr_session_info.session_hdl);
    s->session_handle = s->req_mgr_session_info.session_hdl;
  
    // access the sensor
    LOGD("-- Accessing sensor");
    static struct cam_acquire_dev_cmd acquire_dev_cmd = {0};
    acquire_dev_cmd.session_handle = s->session_handle;
    acquire_dev_cmd.handle_type = CAM_HANDLE_USER_POINTER;
    ret = cam_control(s->sensor_fd, CAM_ACQUIRE_DEV, &acquire_dev_cmd, sizeof(acquire_dev_cmd));
    LOGD("acquire sensor dev: %d", ret);
    s->sensor_dev_handle = acquire_dev_cmd.dev_handle;
  
    static struct cam_isp_resource isp_resource = {0};
  
    acquire_dev_cmd.session_handle = s->session_handle;
    acquire_dev_cmd.handle_type = CAM_HANDLE_USER_POINTER;
    acquire_dev_cmd.num_resources = 1;
    acquire_dev_cmd.resource_hdl = (uint64_t)&isp_resource;
  
    isp_resource.resource_id = CAM_ISP_RES_ID_PORT;
    isp_resource.length = sizeof(struct cam_isp_in_port_info) + sizeof(struct cam_isp_out_port_info)*(1-1);
    isp_resource.handle_type = CAM_HANDLE_USER_POINTER;
  
    struct cam_isp_in_port_info *in_port_info = malloc(isp_resource.length);
    isp_resource.res_hdl = (uint64_t)in_port_info;
  
    switch (s->camera_num) {
      case 0:
        in_port_info->res_type = CAM_ISP_IFE_IN_RES_PHY_0;
        break;
      case 1:
        in_port_info->res_type = CAM_ISP_IFE_IN_RES_PHY_1;
        break;
      case 2:
        in_port_info->res_type = CAM_ISP_IFE_IN_RES_PHY_2;
        break;
    }
  
    in_port_info->lane_type = CAM_ISP_LANE_TYPE_DPHY;
    in_port_info->lane_num = 4;
    in_port_info->lane_cfg = 0x3210;
  
    in_port_info->vc = 0x0;
    //in_port_info->dt = 0x2C; //CSI_RAW12
    //in_port_info->format = CAM_FORMAT_MIPI_RAW_12;
  
    in_port_info->dt = 0x2B; //CSI_RAW10
    in_port_info->format = CAM_FORMAT_MIPI_RAW_10;
  
    in_port_info->test_pattern = 0x2; // 0x3?
    in_port_info->usage_type = 0x0;
  
    in_port_info->left_start = 0x0;
    in_port_info->left_stop = FRAME_WIDTH - 1;
    in_port_info->left_width = FRAME_WIDTH;
  
    in_port_info->right_start = 0x0;
    in_port_info->right_stop = FRAME_WIDTH - 1;
    in_port_info->right_width = FRAME_WIDTH;
  
    in_port_info->line_start = 0x0;
    in_port_info->line_stop = FRAME_HEIGHT - 1;
    in_port_info->height = FRAME_HEIGHT;
  
    in_port_info->pixel_clk = 0x0;
    in_port_info->batch_size = 0x0;
    in_port_info->dsp_mode = 0x0;
    in_port_info->hbi_cnt = 0x0;
    in_port_info->custom_csid = 0x0;
  
    in_port_info->num_out_res = 0x1;
    in_port_info->data[0] = (struct cam_isp_out_port_info){
      .res_type = CAM_ISP_IFE_OUT_RES_RDI_0,
      //.format = CAM_FORMAT_MIPI_RAW_12,
      .format = CAM_FORMAT_MIPI_RAW_10,
      .width = FRAME_WIDTH,
      .height = FRAME_HEIGHT,
      .comp_grp_id = 0x0, .split_point = 0x0, .secure_mode = 0x0,
    };
  
    ret = cam_control(s->isp_fd, CAM_ACQUIRE_DEV, &acquire_dev_cmd, sizeof(acquire_dev_cmd));
    LOGD("acquire isp dev: %d", ret);
    free(in_port_info);
    s->isp_dev_handle = acquire_dev_cmd.dev_handle;
  
    static struct cam_csiphy_acquire_dev_info csiphy_acquire_dev_info = {0};
    csiphy_acquire_dev_info.combo_mode = 0;
  
    acquire_dev_cmd.session_handle = s->session_handle;
    acquire_dev_cmd.handle_type = CAM_HANDLE_USER_POINTER;
    acquire_dev_cmd.num_resources = 1;
    acquire_dev_cmd.resource_hdl = (uint64_t)&csiphy_acquire_dev_info;
  
    ret = cam_control(s->csiphy_fd, CAM_ACQUIRE_DEV, &acquire_dev_cmd, sizeof(acquire_dev_cmd));
    LOGD("acquire csiphy dev: %d", ret);
    s->csiphy_dev_handle = acquire_dev_cmd.dev_handle;
  
    // acquires done
  
    // config ISP
    void *buf0 = alloc_w_mmu_hdl(s->video0_fd, 984480, 0x20, CAM_MEM_FLAG_HW_READ_WRITE | CAM_MEM_FLAG_KMD_ACCESS | CAM_MEM_FLAG_UMD_ACCESS | CAM_MEM_FLAG_CMD_BUF_TYPE, &s->buf0_handle, s->device_iommu, s->cdm_iommu);
    config_isp(s, 0, 0, 1, s->buf0_handle, 0);
  
    LOG("-- Configuring sensor");
    sensors_i2c(s, init_array_ar0231, sizeof(init_array_ar0231)/sizeof(struct i2c_random_wr_payload),
      CAM_SENSOR_PACKET_OPCODE_SENSOR_CONFIG);
    sensors_i2c(s, start_reg_array, sizeof(start_reg_array)/sizeof(struct i2c_random_wr_payload),
      CAM_SENSOR_PACKET_OPCODE_SENSOR_STREAMON);
    sensors_i2c(s, stop_reg_array, sizeof(stop_reg_array)/sizeof(struct i2c_random_wr_payload),
      CAM_SENSOR_PACKET_OPCODE_SENSOR_STREAMOFF);
  
    // config csiphy
    LOG("-- Config CSI PHY");
    {
      uint32_t cam_packet_handle = 0;
      struct cam_packet *pkt = alloc(s->video0_fd, sizeof(struct cam_packet)+sizeof(struct cam_cmd_buf_desc)*1, 8,
        CAM_MEM_FLAG_KMD_ACCESS | CAM_MEM_FLAG_UMD_ACCESS | CAM_MEM_FLAG_CMD_BUF_TYPE, &cam_packet_handle);
      pkt->num_cmd_buf = 1;
      pkt->kmd_cmd_buf_index = -1;
      struct cam_cmd_buf_desc *buf_desc = (struct cam_cmd_buf_desc *)&pkt->payload;
  
      buf_desc[0].size = buf_desc[0].length = sizeof(struct cam_csiphy_info);
      buf_desc[0].type = CAM_CMD_BUF_GENERIC;
      struct cam_csiphy_info *csiphy_info = alloc(s->video0_fd, buf_desc[0].size, 8, CAM_MEM_FLAG_KMD_ACCESS | CAM_MEM_FLAG_UMD_ACCESS | CAM_MEM_FLAG_CMD_BUF_TYPE, &buf_desc[0].mem_handle);
  
      csiphy_info->lane_mask = 0x1f;
      csiphy_info->lane_assign = 0x3210;// skip clk. How is this 16 bit for 5 channels??
      csiphy_info->csiphy_3phase = 0x0; // no 3 phase, only 2 conductors per lane
      csiphy_info->combo_mode = 0x0;
      csiphy_info->lane_cnt = 0x4;
      csiphy_info->secure_mode = 0x0;
      csiphy_info->settle_time = 2800000000;
      csiphy_info->data_rate = 44000000;
  
      static struct cam_config_dev_cmd config_dev_cmd = {};
      config_dev_cmd.session_handle = s->session_handle;
      config_dev_cmd.dev_handle = s->csiphy_dev_handle;
      config_dev_cmd.offset = 0;
      config_dev_cmd.packet_handle = cam_packet_handle;
  
      int ret = cam_control(s->csiphy_fd, CAM_CONFIG_DEV, &config_dev_cmd, sizeof(config_dev_cmd));
      assert(ret == 0);
  
      release(s->video0_fd, buf_desc[0].mem_handle);
      release(s->video0_fd, cam_packet_handle);
    }
  
    // link devices
    LOG("-- Link devices");
    static struct cam_req_mgr_link_info req_mgr_link_info = {0};
    req_mgr_link_info.session_hdl = s->session_handle;
    req_mgr_link_info.num_devices = 2;
    req_mgr_link_info.dev_hdls[0] = s->isp_dev_handle;
    req_mgr_link_info.dev_hdls[1] = s->sensor_dev_handle;
    ret = cam_control(s->video0_fd, CAM_REQ_MGR_LINK, &req_mgr_link_info, sizeof(req_mgr_link_info));
    LOGD("link: %d", ret);
    s->link_handle = req_mgr_link_info.link_hdl;
  
    static struct cam_req_mgr_link_control req_mgr_link_control = {0};
    req_mgr_link_control.ops = 0;
    req_mgr_link_control.session_hdl = s->session_handle;
    req_mgr_link_control.num_links = 1;
    req_mgr_link_control.link_hdls[0] = s->link_handle;
    ret = cam_control(s->video0_fd, CAM_REQ_MGR_LINK_CONTROL, &req_mgr_link_control, sizeof(req_mgr_link_control));
    LOGD("link control: %d", ret);
  
    // start devices
    LOG("-- Start devices");
    ret = device_control(s->isp_fd, CAM_START_DEV, s->session_handle, s->isp_dev_handle);
    LOGD("start isp: %d", ret);
    ret = device_control(s->csiphy_fd, CAM_START_DEV, s->session_handle, s->csiphy_dev_handle);
    LOGD("start csiphy: %d", ret);
    ret = device_control(s->sensor_fd, CAM_START_DEV, s->session_handle, s->sensor_dev_handle);
    LOGD("start sensor: %d", ret);
  
    for (int i = 0; i < FRAME_BUF_COUNT; i++) {
      LOG("-- Initting buffer %d", i);
      enqueue_buffer(s, i);
    }
  }
  
  void cameras_init(DualCameraState *s) {
    camera_init(&s->rear, CAMERA_ID_AR0231, 0, 20);
    camera_init(&s->wide, CAMERA_ID_AR0231, 1, 20);
    camera_init(&s->front, CAMERA_ID_AR0231, 2, 20);
  }
  
  void cameras_open(DualCameraState *s, VisionBuf *camera_bufs_rear, VisionBuf *camera_bufs_focus, VisionBuf *camera_bufs_stats, VisionBuf *camera_bufs_front) {
    int ret;
  
    LOG("-- Opening devices");
    // video0 is the target of many ioctls
    s->video0_fd = open("/dev/video0", O_RDWR | O_NONBLOCK);
    assert(s->video0_fd >= 0);
    LOGD("opened video0");
    s->rear.video0_fd = s->front.video0_fd = s->wide.video0_fd = s->video0_fd;
  
    // video1 is the target of some ioctls
    s->video1_fd = open("/dev/video1", O_RDWR | O_NONBLOCK);
    assert(s->video1_fd >= 0);
    LOGD("opened video1");
    s->rear.video1_fd = s->front.video1_fd = s->wide.video1_fd = s->video1_fd;
  
    // looks like there's only one of these
    s->isp_fd = open("/dev/v4l-subdev1", O_RDWR | O_NONBLOCK);
    assert(s->isp_fd >= 0);
    LOGD("opened isp");
    s->rear.isp_fd = s->front.isp_fd = s->wide.isp_fd = s->isp_fd;
  
    // query icp for MMU handles
    LOG("-- Query ICP for MMU handles");
    static struct cam_isp_query_cap_cmd isp_query_cap_cmd = {0};
    static struct cam_query_cap_cmd query_cap_cmd = {0};
    query_cap_cmd.handle_type = 1;
    query_cap_cmd.caps_handle = (uint64_t)&isp_query_cap_cmd;
    query_cap_cmd.size = sizeof(isp_query_cap_cmd);
    ret = cam_control(s->isp_fd, CAM_QUERY_CAP, &query_cap_cmd, sizeof(query_cap_cmd));
    assert(ret == 0);
    LOGD("using MMU handle: %x", isp_query_cap_cmd.device_iommu.non_secure);
    LOGD("using MMU handle: %x", isp_query_cap_cmd.cdm_iommu.non_secure);
    int device_iommu = isp_query_cap_cmd.device_iommu.non_secure;
    int cdm_iommu = isp_query_cap_cmd.cdm_iommu.non_secure;
    s->rear.device_iommu = s->front.device_iommu = s->wide.device_iommu = device_iommu;
    s->rear.cdm_iommu = s->front.cdm_iommu = s->wide.cdm_iommu = cdm_iommu;
  
    // subscribe
    LOG("-- Subscribing");
    static struct v4l2_event_subscription sub = {0};
    sub.type = 0x8000000;
    sub.id = 0;
    ret = ioctl(s->video0_fd, VIDIOC_SUBSCRIBE_EVENT, &sub);
    LOGD("isp subscribe: %d", ret);
    sub.id = 1;
    ret = ioctl(s->video0_fd, VIDIOC_SUBSCRIBE_EVENT, &sub);
    LOGD("isp subscribe: %d", ret);
  
    camera_open(&s->rear, camera_bufs_rear);
    //camera_open(&s->front, camera_bufs_front);
    // TODO: add bufs for camera wide
  }
  
  static void camera_close(CameraState *s) {
    int ret;
  
    // stop devices
    LOG("-- Stop devices");
    ret = device_control(s->sensor_fd, CAM_STOP_DEV, s->session_handle, s->sensor_dev_handle);
    LOGD("stop sensor: %d", ret);
    ret = device_control(s->isp_fd, CAM_STOP_DEV, s->session_handle, s->isp_dev_handle);
    LOGD("stop isp: %d", ret);
    ret = device_control(s->csiphy_fd, CAM_STOP_DEV, s->session_handle, s->csiphy_dev_handle);
    LOGD("stop csiphy: %d", ret);
  
    // link control stop
    LOG("-- Stop link control");
    static struct cam_req_mgr_link_control req_mgr_link_control = {0};
    req_mgr_link_control.ops = 1;
    req_mgr_link_control.session_hdl = s->session_handle;
    req_mgr_link_control.num_links = 1;
    req_mgr_link_control.link_hdls[0] = s->link_handle;
    ret = cam_control(s->video0_fd, CAM_REQ_MGR_LINK_CONTROL, &req_mgr_link_control, sizeof(req_mgr_link_control));
    LOGD("link control stop: %d", ret);
  
    // unlink
    LOG("-- Unlink");
    static struct cam_req_mgr_unlink_info req_mgr_unlink_info = {0};
    req_mgr_unlink_info.session_hdl = s->session_handle;
    req_mgr_unlink_info.link_hdl = s->link_handle;
    ret = cam_control(s->video0_fd, CAM_REQ_MGR_UNLINK, &req_mgr_unlink_info, sizeof(req_mgr_unlink_info));
    LOGD("unlink: %d", ret);
  
    // release devices
    LOGD("-- Release devices");
    ret = device_control(s->sensor_fd, CAM_RELEASE_DEV, s->session_handle, s->sensor_dev_handle);
    LOGD("release sensor: %d", ret);
    ret = device_control(s->isp_fd, CAM_RELEASE_DEV, s->session_handle, s->isp_dev_handle);
    LOGD("release isp: %d", ret);
    ret = device_control(s->csiphy_fd, CAM_RELEASE_DEV, s->session_handle, s->csiphy_dev_handle);
    LOGD("release csiphy: %d", ret);
  
    ret = cam_control(s->video0_fd, CAM_REQ_MGR_DESTROY_SESSION, &s->req_mgr_session_info, sizeof(s->req_mgr_session_info));
    LOGD("destroyed session: %d", ret);
  
    tbuffer_stop(&s->camera_tb);
  }
  
  static void cameras_close(DualCameraState *s) {
    camera_close(&s->rear);
    //camera_close(&s->front);
    //camera_close(&s->wide);
  }
  
  struct video_event_data {
    int32_t   session_hdl;
    int32_t   link_hdl;
    int32_t   frame_id;
    int32_t   reserved;
    uint64_t  tv_sec;
    uint64_t  tv_usec;
  };
  
  void cameras_run(DualCameraState *s) {
    LOG("-- Dequeueing Video events");
    int frame_id = 1;
  
    while (!do_exit) {
      struct pollfd fds[2] = {{0}};
  
      fds[0].fd = s->video0_fd;
      fds[0].events = POLLPRI;
  
      fds[1].fd = s->video1_fd;
      fds[1].events = POLLPRI;
  
      int ret = poll(fds, ARRAYSIZE(fds), 1000);
      if (ret <= 0) {
        if (errno == EINTR || errno == EAGAIN) continue;
        LOGE("poll failed (%d - %d)", ret, errno);
        break;
      }
  
      for (int i=0; i<2; i++) {
        if (!fds[i].revents) continue;
        static struct v4l2_event ev = {0};
        ret = ioctl(fds[i].fd, VIDIOC_DQEVENT, &ev);
        if (ev.type == 0x8000000) {
          struct video_event_data *event_data = (struct video_event_data *)ev.u.data;
          uint64_t timestamp = (event_data->tv_sec*1000000000ULL
                                + event_data->tv_usec*1000);
          LOGD("video%d dqevent: %d type:0x%x frame_id:%d timestamp: %llu", i, ret, ev.type, event_data->frame_id, timestamp);
  
          if (event_data->frame_id != 0) {
            for (int j = 0; j < FRAME_BUF_COUNT; j++) {
              if (s->rear.request_ids[j] == event_data->frame_id) {
                // TODO: support more than rear camera
                tbuffer_dispatch(&s->rear.camera_tb, j);
                s->rear.camera_bufs_metadata[j].frame_id = frame_id++;
                break;
              }
            }
          }
        }
      }
    }
  
    LOG(" ************** STOPPING **************");
    cameras_close(s);
  }
  
  void camera_autoexposure(CameraState *s, float grey_frac) {
  }
  
  