#include <stdio.h> 
 | 
#include <stdlib.h> 
 | 
#include <string.h> 
 | 
#include <getopt.h> /* getopt_long() */ 
 | 
#include <fcntl.h> /* low-level i/o */ 
 | 
#include <inttypes.h> 
 | 
#include <unistd.h> 
 | 
#include <errno.h> 
 | 
#include <sys/ioctl.h> 
 | 
#include <pthread.h> 
 | 
  
 | 
#include <linux/videodev2.h> 
 | 
#include "rk_aiq_user_api_sysctl.h" 
 | 
#include "common/mediactl/mediactl.h" 
 | 
  
 | 
#define CLEAR(x) memset(&(x), 0, sizeof(x)) 
 | 
#define DBG(...) do { if(!silent) printf("DBG: " __VA_ARGS__);} while(0) 
 | 
#define ERR(...) do { fprintf(stderr, "ERR: " __VA_ARGS__); } while (0) 
 | 
  
 | 
  
 | 
/* Private v4l2 event */ 
 | 
#define CIFISP_V4L2_EVENT_STREAM_START  \ 
 | 
                    (V4L2_EVENT_PRIVATE_START + 1) 
 | 
#define CIFISP_V4L2_EVENT_STREAM_STOP   \ 
 | 
                    (V4L2_EVENT_PRIVATE_START + 2) 
 | 
  
 | 
#define RKAIQ_FILE_PATH_LEN                       64 
 | 
#define RKAIQ_FLASH_NUM_MAX                       2 
 | 
  
 | 
/* 1 vicap + 2 mipi + 1 bridge + 1 redundance */ 
 | 
#define MAX_MEDIA_NODES                           5 
 | 
  
 | 
#define IQ_PATH "/etc/iqfiles/" 
 | 
  
 | 
static int silent = 0; 
 | 
static int width = 2688; 
 | 
static int height = 1520; 
 | 
static int has_mul_cam = 0; 
 | 
  
 | 
struct rkaiq_media_info { 
 | 
    char sd_isp_path[RKAIQ_FILE_PATH_LEN]; 
 | 
    char vd_params_path[RKAIQ_FILE_PATH_LEN]; 
 | 
    char vd_stats_path[RKAIQ_FILE_PATH_LEN]; 
 | 
    char mainpath[RKAIQ_FILE_PATH_LEN]; 
 | 
    char sensor_entity_name[32]; 
 | 
  
 | 
    char mdev_path[32]; 
 | 
    int available; 
 | 
    rk_aiq_sys_ctx_t* aiq_ctx; 
 | 
  
 | 
    pthread_t pid; 
 | 
}; 
 | 
  
 | 
static struct rkaiq_media_info media_infos[MAX_MEDIA_NODES]; 
 | 
  
 | 
static void errno_exit(const char *s) 
 | 
{ 
 | 
    ERR("%s error %d, %s\n", s, errno, strerror(errno)); 
 | 
    exit(EXIT_FAILURE); 
 | 
} 
 | 
  
 | 
static int xioctl(int fh, int request, void *arg) 
 | 
{ 
 | 
    int r; 
 | 
  
 | 
    do { 
 | 
        r = ioctl(fh, request, arg); 
 | 
    } while (-1 == r && EINTR == errno); 
 | 
  
 | 
    return r; 
 | 
} 
 | 
  
 | 
  
 | 
static int rkaiq_get_devname(struct media_device *device, const char *name, char *dev_name) 
 | 
{ 
 | 
    const char *devname; 
 | 
    struct media_entity *entity =  NULL; 
 | 
  
 | 
    entity = media_get_entity_by_name(device, name, strlen(name)); 
 | 
    if (!entity) 
 | 
        return -1; 
 | 
  
 | 
    devname = media_entity_get_devname(entity); 
 | 
  
 | 
    if (!devname) { 
 | 
        fprintf(stderr, "can't find %s device path!", name); 
 | 
        return -1; 
 | 
    } 
 | 
  
 | 
    strncpy(dev_name, devname, RKAIQ_FILE_PATH_LEN); 
 | 
  
 | 
    DBG("get %s devname: %s\n", name, dev_name); 
 | 
  
 | 
    return 0; 
 | 
} 
 | 
  
 | 
int rkaiq_get_media_info(struct rkaiq_media_info *media_info) 
 | 
{ 
 | 
    struct media_device *device = NULL; 
 | 
    const char *sensor_name; 
 | 
    int ret; 
 | 
  
 | 
    device = media_device_new (media_info->mdev_path); 
 | 
    if (!device) 
 | 
        return -ENOMEM; 
 | 
    /* Enumerate entities, pads and links. */ 
 | 
    ret = media_device_enumerate (device); 
 | 
    if (ret) 
 | 
        return ret; 
 | 
    if (!ret) { 
 | 
        /* Try rkisp */ 
 | 
        ret = rkaiq_get_devname(device, "rkisp-isp-subdev", 
 | 
                                media_info->sd_isp_path); 
 | 
        ret |= rkaiq_get_devname(device, "rkisp-input-params", 
 | 
                                media_info->vd_params_path); 
 | 
        ret |= rkaiq_get_devname(device, "rkisp-statistics", 
 | 
                                media_info->vd_stats_path); 
 | 
        ret |= rkaiq_get_devname(device, "rkisp_mainpath", 
 | 
                                media_info->mainpath); 
 | 
    } 
 | 
    if (ret) { 
 | 
        fprintf(stderr, "Cound not find rkisp dev names, skipped %s\n", media_info->mdev_path); 
 | 
        media_device_unref (device); 
 | 
        return ret; 
 | 
    } 
 | 
  
 | 
    sensor_name = rk_aiq_uapi_sysctl_getBindedSnsEntNmByVd(media_info->mainpath); 
 | 
    if (sensor_name == NULL || strlen(sensor_name) == 0) { 
 | 
        fprintf(stderr, "ERR: No sensor attached to %s\n", media_info->mdev_path); 
 | 
        media_device_unref (device); 
 | 
        return -EINVAL; 
 | 
    } 
 | 
  
 | 
    strcpy(media_info->sensor_entity_name, sensor_name); 
 | 
  
 | 
    media_device_unref (device); 
 | 
  
 | 
    return ret; 
 | 
} 
 | 
  
 | 
static void init_engine(struct rkaiq_media_info *media_info) 
 | 
{ 
 | 
    int index; 
 | 
  
 | 
    media_info->aiq_ctx = rk_aiq_uapi_sysctl_init(media_info->sensor_entity_name, 
 | 
                                                  IQ_PATH, NULL, NULL); 
 | 
    if (has_mul_cam) 
 | 
        rk_aiq_uapi_sysctl_setMulCamConc(media_info->aiq_ctx, 1); 
 | 
  
 | 
    if (rk_aiq_uapi_sysctl_prepare(media_info->aiq_ctx, 
 | 
            width, height, RK_AIQ_WORKING_MODE_NORMAL)) { 
 | 
        ERR("rkaiq engine prepare failed !\n"); 
 | 
        exit(-1); 
 | 
    } 
 | 
} 
 | 
  
 | 
static void start_engine(struct rkaiq_media_info *media_info) 
 | 
{ 
 | 
    DBG("device manager start\n"); 
 | 
    rk_aiq_uapi_sysctl_start(media_info->aiq_ctx); 
 | 
    if (media_info->aiq_ctx == NULL) { 
 | 
        ERR("rkisp_init engine failed\n"); 
 | 
        exit(-1); 
 | 
    } else { 
 | 
        DBG("rkisp_init engine succeed\n"); 
 | 
    } 
 | 
} 
 | 
  
 | 
static void stop_engine(struct rkaiq_media_info *media_info) 
 | 
{ 
 | 
    rk_aiq_uapi_sysctl_stop(media_info->aiq_ctx, false); 
 | 
} 
 | 
  
 | 
static void deinit_engine(struct rkaiq_media_info *media_info) 
 | 
{ 
 | 
    rk_aiq_uapi_sysctl_deinit(media_info->aiq_ctx); 
 | 
} 
 | 
  
 | 
// blocked func 
 | 
static int wait_stream_event(int fd, unsigned int event_type, int time_out_ms) 
 | 
{ 
 | 
    int ret; 
 | 
    struct v4l2_event event; 
 | 
  
 | 
    CLEAR(event); 
 | 
  
 | 
    do { 
 | 
        /* 
 | 
         * xioctl instead of poll. 
 | 
         * Since poll() cannot wait for input before stream on, 
 | 
         * it will return an error directly. So, use ioctl to 
 | 
         * dequeue event and block until sucess. 
 | 
         */ 
 | 
        ret = xioctl(fd, VIDIOC_DQEVENT, &event); 
 | 
        if (ret == 0 && event.type == event_type) 
 | 
            return 0; 
 | 
    } while (true); 
 | 
  
 | 
    return -1; 
 | 
  
 | 
} 
 | 
  
 | 
static int subscrible_stream_event(struct rkaiq_media_info *media_info, int fd, bool subs) 
 | 
{ 
 | 
    struct v4l2_event_subscription sub; 
 | 
    int ret = 0; 
 | 
  
 | 
    CLEAR(sub); 
 | 
    sub.type = CIFISP_V4L2_EVENT_STREAM_START; 
 | 
    ret = xioctl(fd, 
 | 
                 subs ? VIDIOC_SUBSCRIBE_EVENT : VIDIOC_UNSUBSCRIBE_EVENT, 
 | 
                 &sub); 
 | 
    if (ret) { 
 | 
        ERR("can't subscribe %s start event!\n", media_info->vd_params_path); 
 | 
        exit(EXIT_FAILURE); 
 | 
    } 
 | 
  
 | 
    CLEAR(sub); 
 | 
    sub.type = CIFISP_V4L2_EVENT_STREAM_STOP; 
 | 
    ret = xioctl(fd, 
 | 
                 subs ? VIDIOC_SUBSCRIBE_EVENT : VIDIOC_UNSUBSCRIBE_EVENT, 
 | 
                 &sub); 
 | 
    if (ret) { 
 | 
        ERR("can't subscribe %s stop event!\n", media_info->vd_params_path); 
 | 
    } 
 | 
  
 | 
    DBG("subscribe events from %s success !\n", media_info->vd_params_path); 
 | 
  
 | 
    return 0; 
 | 
} 
 | 
  
 | 
void parse_args(int argc, char **argv) 
 | 
{ 
 | 
   int c; 
 | 
   int digit_optind = 0; 
 | 
  
 | 
   while (1) { 
 | 
       int this_option_optind = optind ? optind : 1; 
 | 
       int option_index = 0; 
 | 
       static struct option long_options[] = { 
 | 
           {"silent",    no_argument,       0, 's' }, 
 | 
           {"help",      no_argument,       0, 'h' }, 
 | 
           {0,           0,                 0,  0  } 
 | 
       }; 
 | 
  
 | 
       c = getopt_long(argc, argv, "sh", long_options, &option_index); 
 | 
       if (c == -1) 
 | 
           break; 
 | 
  
 | 
       switch (c) { 
 | 
       case 'w': 
 | 
           width = atoi(optarg); 
 | 
           break; 
 | 
       case 'h': 
 | 
           height = atoi(optarg); 
 | 
           break; 
 | 
       case 's': 
 | 
           silent = 1; 
 | 
           break; 
 | 
       case '?': 
 | 
           ERR("Usage: %s to start 3A engine\n" 
 | 
               "         --silent,  optional, subpress debug log\n", 
 | 
               argv[0]); 
 | 
           exit(-1); 
 | 
  
 | 
       default: 
 | 
           ERR("?? getopt returned character code %c ??\n", c); 
 | 
       } 
 | 
   } 
 | 
} 
 | 
  
 | 
void *engine_thread(void *arg) 
 | 
{ 
 | 
    int ret = 0; 
 | 
    int isp_fd; 
 | 
    unsigned int stream_event = -1; 
 | 
    struct rkaiq_media_info *media_info; 
 | 
  
 | 
    media_info = (struct rkaiq_media_info *) arg; 
 | 
  
 | 
    isp_fd = open(media_info->vd_params_path, O_RDWR); 
 | 
    if (isp_fd < 0) { 
 | 
        ERR("open %s failed %s\n", media_info->vd_params_path, strerror(errno)); 
 | 
        return NULL; 
 | 
    } 
 | 
  
 | 
    subscrible_stream_event(media_info, isp_fd, true); 
 | 
    init_engine(media_info); 
 | 
  
 | 
    for (;;) { 
 | 
        DBG("%s: wait stream start event...\n", media_info->mdev_path); 
 | 
        wait_stream_event(isp_fd, CIFISP_V4L2_EVENT_STREAM_START, -1); 
 | 
        DBG("%s: wait stream start event success ...\n", media_info->mdev_path); 
 | 
  
 | 
        start_engine(media_info); 
 | 
  
 | 
        DBG("%s: wait stream stop event...\n", media_info->mdev_path); 
 | 
        wait_stream_event(isp_fd, CIFISP_V4L2_EVENT_STREAM_STOP, -1); 
 | 
        DBG("%s: wait stream stop event success ...\n", media_info->mdev_path); 
 | 
  
 | 
        stop_engine(media_info); 
 | 
    } 
 | 
  
 | 
    deinit_engine(media_info); 
 | 
    subscrible_stream_event(media_info, isp_fd, false); 
 | 
    close(isp_fd); 
 | 
  
 | 
    return NULL; 
 | 
} 
 | 
  
 | 
int main(int argc, char **argv) 
 | 
{ 
 | 
    int ret, i; 
 | 
    int threads = 0; 
 | 
  
 | 
    /* Line buffered so that printf can flash every line if redirected to 
 | 
     * no-interactive device. 
 | 
     */ 
 | 
    setlinebuf(stdout); 
 | 
  
 | 
    parse_args(argc, argv); 
 | 
  
 | 
    for (i = 0; i < MAX_MEDIA_NODES; i++) { 
 | 
        sprintf(media_infos[i].mdev_path, "/dev/media%d", i); 
 | 
        if (rkaiq_get_media_info(&media_infos[i])) { 
 | 
            ERR("Bad media topology for: %s\n", media_infos[i].mdev_path); 
 | 
            media_infos[i].available = 0; 
 | 
            continue; 
 | 
        } 
 | 
        media_infos[i].available = 1; 
 | 
        threads++; 
 | 
    } 
 | 
  
 | 
    if (1 || threads > 1) 
 | 
        has_mul_cam = 1; 
 | 
  
 | 
    for (i = 0; i < MAX_MEDIA_NODES; i++) { 
 | 
        if (!media_infos[i].available) 
 | 
            continue; 
 | 
        ret = pthread_create(&media_infos[i].pid, NULL, engine_thread, &media_infos[i]); 
 | 
        if (ret) { 
 | 
            media_infos[i].pid = 0; 
 | 
            ERR("Failed to create camera engine thread for: %s\n", media_infos[i].mdev_path); 
 | 
            errno_exit("Create thread failed"); 
 | 
        } 
 | 
    } 
 | 
  
 | 
    for (i = 0; i < MAX_MEDIA_NODES; i++) { 
 | 
        if (!media_infos[i].available || media_infos[i].pid == 0) 
 | 
            continue; 
 | 
        pthread_join(media_infos[i].pid, NULL); 
 | 
    } 
 | 
  
 | 
    return 0; 
 | 
} 
 |