#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 <asm/uaccess.h>
#include "stm32f4xx.h"
#include "stm32f4xx_hal_gpio.h"
#include "stm32_hal_legacy.h"
#include "driver.h"
#include <linux/delay.h>

static int major = MAJOR_GPIO_I2C;
static int minor = 0;
static dev_t devno;
static struct cdev *gpio_i2c_cdev = NULL;
static int count = 1;
#define DEVNAME "gpio_i2c"

static int gpio_i2c_open(struct inode *inode, struct file *filep);
static int gpio_i2c_close(struct inode *inode, struct file *filep);
static ssize_t gpio_i2c_read(struct file *filep, char __user *buf, size_t size, loff_t *offset);
static ssize_t gpio_i2c_write(struct file *filep, const char __user *buf, size_t size, loff_t *offset);
static int gpio_i2c_ioctl(struct inode *inode, struct file *filep, unsigned int cmd, unsigned long arg);
static struct file_operations gpio_i2c_ops = 
{
	.owner = THIS_MODULE,
	.open = gpio_i2c_open,
	.release = gpio_i2c_close,
	.ioctl = gpio_i2c_ioctl,
	.read = gpio_i2c_read,
	.write = gpio_i2c_write,
	
};

#define SCL_PORT	GPIOC
#define SCL_PIN 	GPIO_PIN_9
#define SDA_PORT 	GPIOA
#define SDA_PIN 	GPIO_PIN_8

#define SCLK_Set() HAL_GPIO_WritePin(SCL_PORT, SCL_PIN, 1);
#define SCLK_Clr() HAL_GPIO_WritePin(SCL_PORT, SCL_PIN, 0);
#define SDIN_Set() HAL_GPIO_WritePin(SDA_PORT, SDA_PIN, 1);
#define SDIN_Clr() HAL_GPIO_WritePin(SDA_PORT, SDA_PIN, 0);

void IIC_Start(void)
{
	SCLK_Set();
	SDIN_Set();
	udelay(1);
	SDIN_Clr();
}

/**********************************************
//IIC Stop
**********************************************/
void IIC_Stop(void)
{
	SCLK_Clr();
	SDIN_Clr();
	SCLK_Set();
	udelay(1);
	SDIN_Set();
}

void IIC_Ack(void)
{
	SCLK_Clr();
	SDIN_Clr();
	udelay(1); 
	SCLK_Set();
	udelay(1);
	SCLK_Clr();	
	SDIN_Set();	//release SDA
}

void IIC_NoAck(void)
{
	SCLK_Clr();
	SDIN_Set();
	udelay(1); 
	SCLK_Set();
	udelay(1);
	SCLK_Clr();
	SDIN_Set();	//release SDA
}

int IIC_Wait_Ack(void)
{
	uint8_t ack = 0;
	SCLK_Clr();
	SDIN_Set();	//release SDA
	udelay(1);
	SCLK_Set() ;
	udelay(1);
	ack = HAL_GPIO_ReadPin(SDA_PORT, SDA_PIN);
	SCLK_Clr();
	return ack;
}

void Write_IIC_Byte(unsigned char IIC_Byte)
{
	unsigned char i;
	unsigned char m,da;
	da=IIC_Byte;
	SCLK_Clr();
	for(i=0;i<8;i++)		
	{
		m=da;
		m=m&0x80;
		if(m==0x80)
		{
			SDIN_Set();
		}
		else 
		{
			SDIN_Clr(); 
		}
		
		da=da<<1;
		udelay(1);
		SCLK_Set();
		udelay(1);
		SCLK_Clr();
	}
}

unsigned char Read_IIC_Byte(void)
{
	unsigned char i;
	unsigned int da = 0;

	SCLK_Clr();
	SDIN_Set();	//release SDA

	for(i=0;i<8;i++)	//8	
	{
		da=da<<1 ;
		udelay(1);
		SCLK_Set();
		if(HAL_GPIO_ReadPin(SDA_PORT, SDA_PIN) == 1)
		{da++;}
		udelay(1);
		SCLK_Clr();
	}

	return da;
}

static int master_write(uint8_t slave_addr, uint8_t *buf, uint32_t size)
{
	int i;
	IIC_Start();  		 
	Write_IIC_Byte(slave_addr);            //Slave address,SA0=0	   90
	if(IIC_Wait_Ack() != 0)
	{
		IIC_Stop();
		return -1;
	}	

	for(i=0;i<size;i++)
	{
		Write_IIC_Byte(buf[i]);			//write command
		IIC_Wait_Ack();
	}

	IIC_Stop();	
	return 0;
}

static int master_read(uint8_t slave_addr, uint8_t *buf, uint32_t size)
{
	int i;
	IIC_Start();
	Write_IIC_Byte(slave_addr+1);            //Slave address
	if(IIC_Wait_Ack() != 0)
	{
		IIC_Stop();
		return -1;
	}

	for(i=0;i<size-1;i++)
	{
		udelay(1);
		buf[i] = Read_IIC_Byte();	
		IIC_Ack();	
	}				

	udelay(1);
	buf[i] = Read_IIC_Byte();	
	IIC_NoAck();
	IIC_Stop();
	
	return 0;
}


static int master_write_read(uint8_t slave_addr, uint8_t *wrbuf, uint8_t wrsize, uint8_t *rdbuf, uint32_t rdsize)
{
	int i;
	IIC_Start();  		 
	Write_IIC_Byte(slave_addr);            //Slave address,SA0=0	   90
	if(IIC_Wait_Ack() != 0)
	{
		IIC_Stop();
		return -1;
	}


	for(i=0;i<wrsize;i++)
	{
		Write_IIC_Byte(wrbuf[i]);			//write command
		IIC_Wait_Ack();
	}

	udelay(100);

	IIC_Start();
	Write_IIC_Byte(slave_addr+1);            //Slave address
	if(IIC_Wait_Ack() != 0)
	{
		IIC_Stop();
		return -1;
	}
	
	for(i=0;i<rdsize-1;i++)
	{
		udelay(1);
		rdbuf[i] = Read_IIC_Byte();	
		IIC_Ack();	
	}				

	udelay(1);
	rdbuf[i] = Read_IIC_Byte();	
	IIC_NoAck();
	IIC_Stop();
	
	return 0;
}


static int gpio_i2c_open(struct inode *inode, struct file *filep)
{
	//__HAL_RCC_GPIOC_CLK_ENABLE();
	return 0;
}

static int gpio_i2c_close(struct inode *inode, struct file *filep)
{
	return 0;
}

static int gpio_i2c_ioctl(struct inode *inode, struct file *filep, unsigned int cmd, unsigned long arg)
{
	int ret = 0;
	gpio_i2c_t gpio_i2c_arg;
	
	
	switch(cmd)
	{
		case GPIO_I2C_MASTER_TRANSFER:
			if ( copy_from_user(&gpio_i2c_arg, (void*)arg, sizeof(gpio_i2c_t)) )
				return -EFAULT;
			ret = master_write(gpio_i2c_arg.DevAddress&0xFE, gpio_i2c_arg.Buf, gpio_i2c_arg.WriteLen );			
			break;
		case GPIO_I2C_MASTER_RECEIVE:
			if ( copy_from_user(&gpio_i2c_arg, (void*)arg, 3) )
				return -EFAULT;
			ret = master_read(gpio_i2c_arg.DevAddress&0xFE, gpio_i2c_arg.Buf, gpio_i2c_arg.ReadLen );
			if(ret != 0)
			{
				break;
			}
			if ( copy_to_user((void*)arg, (void*)&gpio_i2c_arg, 3+gpio_i2c_arg.ReadLen))
				return -EFAULT;
			break;
		case GPIO_I2C_MASTER_TRANSFER_RECV:
			if ( copy_from_user(&gpio_i2c_arg, (void*)arg, sizeof(gpio_i2c_t)) )
				return -EFAULT;
			ret = master_write_read(gpio_i2c_arg.DevAddress&0xFE, gpio_i2c_arg.Buf, gpio_i2c_arg.WriteLen, gpio_i2c_arg.Buf, gpio_i2c_arg.ReadLen);
			if(ret != 0)
			{
				break;
			}
			if ( copy_to_user((void*)arg, (void*)&gpio_i2c_arg, 3+gpio_i2c_arg.ReadLen))
				return -EFAULT;
			break;
	default:
		printk("---> Invalid action\n");
		return -EINVAL;
		break;		

	}
	return ret;
}

static ssize_t gpio_i2c_read(struct file *filep, char __user *buf, size_t size, loff_t *offset)
{
	return 0;
}

static ssize_t gpio_i2c_write(struct file *filep, const char __user *buf, size_t size, loff_t *offset)
{
	return 0;
}

static void gpio_i2c_hw_init(void)
{
	GPIO_InitTypeDef  GPIO_InitStruct;
	//config gpio
	GPIO_InitStruct.Mode	= GPIO_MODE_OUTPUT_OD;
	GPIO_InitStruct.Pull    = GPIO_PULLUP;
	GPIO_InitStruct.Speed   = GPIO_SPEED_FAST;
	
	//SCL
	GPIO_InitStruct.Pin		= SCL_PIN;
	HAL_GPIO_Init(SCL_PORT, &GPIO_InitStruct);
	//SDA
	GPIO_InitStruct.Pin		= SDA_PIN;
	HAL_GPIO_Init(SDA_PORT, &GPIO_InitStruct);

}

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

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

	gpio_i2c_hw_init();

	printk("gpio_i2c module start...\n");
	return 0;

ERR_STEP1:
	unregister_chrdev_region(devno,count);
ERR_STEP:
	cdev_del(gpio_i2c_cdev);
	return ret;
}

static void __exit gpio_i2c_exit(void)
{
	unregister_chrdev_region(MKDEV(major,minor),count);
	cdev_del(gpio_i2c_cdev);
}

module_init(gpio_i2c_init);
module_exit(gpio_i2c_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jimbo");
MODULE_DESCRIPTION("this is gpio_i2c module");



