#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <asm/current.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/uaccess.h>

#include <linux/irqreturn.h>
#include <linux/interrupt.h>

#include "stm32f4xx.h"
#include "stm32f4xx_hal_cortex.h"
#include "driver.h"
#include "stm32f4xx_hal_rng.h"
#include "stm32_hal_legacy.h"

static int major = MAJOR_PLATFORM;
static int minor = 0;
static dev_t devno;
static struct cdev *platform_cdev = NULL;
static int count = 1;
#define DEVNAME "Platform_GD32F450I"
struct device *dev = NULL;

RNG_HandleTypeDef hrng;

static int platform_open(struct inode *inode, struct file *filep);
static int platform_close(struct inode *inode, struct file *filep);
static ssize_t platform_read(struct file *filep, char __user *buf, size_t size, loff_t *offset);
static ssize_t platform_write(struct file *filep, const char __user *buf, size_t size, loff_t *offset);
static int platform_ioctl(struct inode *indoe, struct file *filep, unsigned int cmd, unsigned long arg);


static struct file_operations platform_ops = 
{
	.owner = THIS_MODULE,
	.open = platform_open,
	.release = platform_close,
	.read = platform_read,
	.write = platform_write,
	.ioctl	= platform_ioctl,
	
};

static int platform_open(struct inode *inode, struct file *filep)
{
	//printk("---> platform open\n");
	
	return 0;
}

static int platform_close(struct inode *inode, struct file *filep)
{
	//printk("---> platform close\n");
	
	return 0;
}

static ssize_t platform_read(struct file *filep, char __user *buf, size_t size, loff_t *offset)
{
	printk("---> platform read\n");
	return 0;
}

static ssize_t platform_write(struct file *filep, const char __user *buf, size_t size, loff_t *offset)
{
	printk("---> platform write\n");
	return 0;
}


static int platform_ioctl(struct inode *indoe, struct file *filep, unsigned int cmd, unsigned long arg)
{
	platform_arg_t platform_arg;
	if(copy_from_user( &platform_arg, (void*)arg, sizeof(platform_arg_t)))
		return -EFAULT;
	switch(cmd)
	{
	case SET_PRIORITY_GROUP:
		if((platform_arg.priority_group != NVIC_PRIORITYGROUP_0) 
				&& (platform_arg.priority_group != NVIC_PRIORITYGROUP_1) 
				&& (platform_arg.priority_group != NVIC_PRIORITYGROUP_2) 
				&& (platform_arg.priority_group != NVIC_PRIORITYGROUP_3))
		{
			printk(KERN_ERR"Invalid priority group\n");
			return -EFAULT;
		}

		HAL_NVIC_SetPriorityGrouping(platform_arg.priority_group);
		break;
	case GET_PRIORITY_GROUP:
		platform_arg.priority_group = HAL_NVIC_GetPriorityGrouping();
		if(copy_to_user((void*)arg, &platform_arg, sizeof(platform_arg_t)))
			return -EFAULT;
		break;
	case RESET_MCU:
		NVIC_SystemReset();
		break;
	case GET_32BIT_RANDOM:
		HAL_RNG_GenerateRandomNumber(&hrng, &platform_arg.random_32bit);
		if(copy_to_user((void*)arg, &platform_arg, sizeof(platform_arg_t)))
			return -EFAULT;
		break;
	default:
		printk(KERN_ERR"Invalid cmd\n");
		break;
	}
	return 0;
}

//extern __IO uint32_t uwTick;
//irqreturn_t SysTick_Handler(int irqno, void *dev_id)
//{
//	 HAL_IncTick();
//	 if((uwTick % 1000) == 0)
//		 printk("uwTick = %d\n", uwTick);
//	 return IRQ_HANDLED;
//}

extern uint32_t SystemCoreClock;
void platform_hw_init(void)
{
	HAL_StatusTypeDef retVal;
	/* Set Interrupt Group Priority */
	HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);

	SystemCoreClockUpdate();
	
	__RNG_CLK_ENABLE();
	//Init random generator
	hrng.Instance = RNG;
	retVal = HAL_RNG_Init(&hrng);	
}

static int __init platform_init(void)
{
	int ret;
	platform_cdev = cdev_alloc();
	if(platform_cdev == NULL){
		return -ENOMEM;
	}

	cdev_init(platform_cdev,&platform_ops);
	devno = MKDEV(major,minor);
	ret = register_chrdev_region(devno, count, DEVNAME);
	if(ret){
		goto ERR_STEP;
	}
	
	ret = cdev_add(platform_cdev, devno, count);
	if(ret){
		goto ERR_STEP1;
	}
	
	platform_hw_init();

//	ret = request_irq(SysTick_IRQn, 
//						SysTick_Handler,
//						IRQF_SHARED,
//						DEVNAME,
//						&devno);
//	if(ret) {
//		printk("request_irq() failed! %d\n", ret);
//		goto ERR_STEP1;
//	}
	printk("Platform_GD32F450I module start...\n");
	return 0;

ERR_STEP1:
	unregister_chrdev_region(devno,count);
ERR_STEP:
	cdev_del(platform_cdev);

	return ret;
}

static void __exit platform_exit(void)
{
	unregister_chrdev_region(MKDEV(major,minor),count);
	cdev_del(platform_cdev);
}

module_init(platform_init);
module_exit(platform_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jimbo");
MODULE_DESCRIPTION("This module init STM32F429 interrupt group.\n");