

#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/sched.h>
#include <linux/device.h>
#include <linux/err.h>
#include "asm/uaccess.h"
#include <linux/delay.h>

#include "com_gpio.h"
#include "stm32f4xx.h"
#include "stm32_hal_legacy.h"
#include "stm32f4xx_hal_spi.h"
#include "stm32f4xx_hal_gpio.h"
#include "driver.h"

static int major = MAJOR_SPI;
static int ret=0;
#define DEVNAME "spi"
#define NUM_OF_DEVICES	6

struct spi_device {
	dev_t devno;
	struct cdev cdev;
	char name[16];
	//IRQn_Type	Event_IRQn;
	//IRQn_Type	Err_IRQn;
} spi_dev[NUM_OF_DEVICES];

SPI_HandleTypeDef hspi1, hspi2, hspi3, hspi4, hspi5, hspi6;

static int spi_open(struct inode *inode, struct file *filep);
static int spi_close(struct inode *inode, struct file *filep);
static ssize_t spi_read(struct file *filep, char __user *buf, size_t size, loff_t *offset);
static ssize_t spi_write(struct file *filep, const char __user *buf, size_t size, loff_t *offset);
static int spi_ioctl(struct inode *inode, struct file *filep, unsigned int cmd, unsigned long arg);
static struct file_operations spi_ops = 
{
	.owner = THIS_MODULE,
	.open = spi_open,
	.release = spi_close,
	.ioctl = spi_ioctl,
	.read = spi_read,
	.write = spi_write,
	
};

static int spi_open(struct inode *inode, struct file *filep)
{
	SPI_HandleTypeDef *pHspi;
	int imajor,iminor;
	imajor = MAJOR(inode->i_rdev);
	iminor = MINOR(inode->i_rdev);
	if(imajor != major)
	{
		printk("Error: %s %d spi major fail! %d\n",__FILE__,__LINE__,imajor);
		return -1;
	}
	//printk("---> open major=%d, minor=%d\n",imajor,iminor);
	switch(iminor)
	{
	case 0:		
      	pHspi = &hspi1;
		break;
	case 1:
      	pHspi = &hspi2;
		break;
	case 2:
      	pHspi = &hspi3;
		break;
	case 3:
        pHspi = &hspi4;
        break;	
	case 4:
        pHspi = &hspi5;
        break;	
	case 5:
        pHspi = &hspi6;
        break;
	default:
		printk("Error:%s %d Invalid minor %d",__FILE__,__LINE__,iminor);
		return -1;
	}

	__HAL_SPI_ENABLE(pHspi);
	return 0;
}

static int spi_close(struct inode *inode, struct file *filep)
{
	SPI_HandleTypeDef *pHspi;
	int imajor,iminor;
	imajor = MAJOR(inode->i_rdev);
	iminor = MINOR(inode->i_rdev);
	if(imajor != major)
	{
		printk("Error: %s %d spi major fail! %d\n",__FILE__,__LINE__,imajor);
		return -1;
	}

	//printk("---> close major=%d, minor=%d\n",imajor,iminor);
	switch(iminor)
	{
	case 0:		
      	pHspi = &hspi1;
		break;
	case 1:
      	pHspi = &hspi2;
		break;
	case 2:
      	pHspi = &hspi3;
		break;
	case 3:
        pHspi = &hspi4;
        break;	
	case 4:
        pHspi = &hspi5;
        break;	
	case 5:
        pHspi = &hspi6;
        break;
	default:
		printk("Error:%s %d Invalid minor %d",__FILE__,__LINE__,iminor);
		return -1;
	}

	__HAL_SPI_DISABLE(pHspi);	
	return 0;
}


static int spi_ioctl(struct inode *inode, struct file *filep, unsigned int cmd, unsigned long arg)
{
	HAL_StatusTypeDef ret = HAL_OK;
	spi_arg_t spi_args;
	int imajor,iminor;
	SPI_HandleTypeDef *pHspi;
	int i;

	imajor = MAJOR(inode->i_rdev);
	iminor = MINOR(inode->i_rdev);

	if(imajor != MAJOR_SPI)
	{
		printk("Error: %s %d spi major fail! %d\n",__FILE__,__LINE__,imajor);
		return -EFAULT;
	}

	switch(iminor)
	{
	case 0:		
      	pHspi = &hspi1;
		break;
	case 1:
      	pHspi = &hspi2;
		break;
	case 2:
      	pHspi = &hspi3;
		break;
	case 3:
        pHspi = &hspi4;
        break;	
	case 4:
        pHspi = &hspi5;
        break;	
	case 5:
        pHspi = &hspi6;
        break;
	default:
		printk("Error:%s %d Invalid minor %d",__FILE__,__LINE__,iminor);
		return -1;
	}


	if ( copy_from_user(&spi_args, (void*)arg, sizeof(spi_arg_t)) != 0)
	{
		printk("---> copy form user space fail!\n");
		return -1;
	}

	switch(cmd)
	{
	case SPI_MASTER_TRANSFER:
		// printk("SPI%d_transfer %d: ", iminor+1, spi_args.Size);
		// for(i=0;i<spi_args.Size;i++)
		// {
		// 	printk("%#x ", spi_args.buf[i]);
		// }
		// printk("\n");

		ret = HAL_SPI_Transmit( pHspi, spi_args.buf, spi_args.Size, HAL_MAX_DELAY);
		if(ret != HAL_OK)
		{
			printk("SPI master transfer error, %d\n", ret);
		}
		break;
	case SPI_MASTER_RECEIVE:
		ret = HAL_SPI_Receive(pHspi, spi_args.buf, spi_args.Size, HAL_MAX_DELAY);
		if(ret != HAL_OK)
		{
			printk("SPI master transfer error, %d\n", ret);
		}
		
		// printk("SPI%d_receive %d: ", iminor+1, spi_args.Size);
		// for(i=0;i<spi_args.Size;i++)
		// {
		// 	printk("%#x ", spi_args.buf[i]);
		// }
		// printk("\n");
		
		if ( copy_to_user((void*)arg, &spi_args, sizeof(spi_arg_t)) != 0)
		{
			printk("---> copy to user space fail!\n");
			return -1;
		}

		break;
	default:
		printk("---> Invalid action\n");
		ret = -1;
		break;
	}	
	return ret;
}

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

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

void spi_hw_init(void)
{
	GPIO_InitTypeDef spi_gpio;

	//SPI1

	//SPI2
	__HAL_RCC_GPIOI_CLK_ENABLE();
	spi_gpio.Pin 	= GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3;
	spi_gpio.Mode	= GPIO_MODE_AF_PP;
	spi_gpio.Pull   = GPIO_PULLUP;
	spi_gpio.Speed 	= GPIO_SPEED_FREQ_HIGH;
	spi_gpio.Alternate	=	GPIO_AF5_SPI2;
	HAL_GPIO_Init(GPIOI, &spi_gpio);

	//NSS
	HAL_GPIO_WritePin(GPIOI, GPIO_PIN_0, GPIO_PIN_SET);
	spi_gpio.Pin 	= GPIO_PIN_0;
	spi_gpio.Mode	= GPIO_MODE_OUTPUT_PP;
	spi_gpio.Pull   = GPIO_PULLUP;
	spi_gpio.Speed 	= GPIO_SPEED_FREQ_HIGH;
	HAL_GPIO_Init(GPIOI, &spi_gpio);

	__HAL_RCC_SPI2_CLK_ENABLE();
	hspi2.Instance 			= SPI2;
	hspi2.Init.Direction 	= SPI_DIRECTION_2LINES;
	hspi2.Init.Mode 		= SPI_MODE_MASTER;
	hspi2.Init.DataSize 	= SPI_DATASIZE_8BIT;
	hspi2.Init.CLKPolarity 	= SPI_POLARITY_HIGH;
	hspi2.Init.CLKPhase 	= SPI_PHASE_2EDGE;
	hspi2.Init.NSS 			= SPI_NSS_SOFT;
	hspi2.Init.BaudRatePrescaler 	= SPI_BAUDRATEPRESCALER_32;
	hspi2.Init.FirstBit 	= SPI_FIRSTBIT_MSB;
	hspi2.Init.TIMode 		= SPI_TIMODE_DISABLE;
	hspi2.Init.CRCCalculation 	= SPI_CRCCALCULATION_DISABLE;
	hspi2.Init.CRCPolynomial	= 0;	
	HAL_SPI_Init(&hspi2);

	//SPI3

	//SPI4

	//SPI5
	__HAL_RCC_GPIOF_CLK_ENABLE();
	spi_gpio.Pin 	= GPIO_PIN_7 | GPIO_PIN_8 | GPIO_PIN_9;
	spi_gpio.Mode	= GPIO_MODE_AF_PP;
	spi_gpio.Pull   = GPIO_PULLUP;
	spi_gpio.Speed 	= GPIO_SPEED_FREQ_HIGH;
	spi_gpio.Alternate	=	GPIO_AF5_SPI5;
	HAL_GPIO_Init(GPIOF, &spi_gpio);

	//NSS
	HAL_GPIO_WritePin(GPIOF, GPIO_PIN_6, GPIO_PIN_SET);
	spi_gpio.Pin 	= GPIO_PIN_6;
	spi_gpio.Mode	= GPIO_MODE_OUTPUT_PP;
	spi_gpio.Pull   = GPIO_PULLUP;
	spi_gpio.Speed 	= GPIO_SPEED_FREQ_HIGH;
	HAL_GPIO_Init(GPIOF, &spi_gpio);

	__HAL_RCC_SPI5_CLK_ENABLE();
	hspi5.Instance 			= SPI5;
	hspi5.Init.Direction 	= SPI_DIRECTION_2LINES;
	hspi5.Init.Mode 		= SPI_MODE_MASTER;
	hspi5.Init.DataSize 	= SPI_DATASIZE_8BIT;
	hspi5.Init.CLKPolarity 	= SPI_POLARITY_HIGH;
	hspi5.Init.CLKPhase 	= SPI_PHASE_2EDGE;
	hspi5.Init.NSS 			= SPI_NSS_SOFT;
	hspi5.Init.BaudRatePrescaler 	= SPI_BAUDRATEPRESCALER_8;
	hspi5.Init.FirstBit 	= SPI_FIRSTBIT_MSB;
	hspi5.Init.TIMode 		= SPI_TIMODE_DISABLE;
	hspi5.Init.CRCCalculation 	= SPI_CRCCALCULATION_DISABLE;
	hspi5.Init.CRCPolynomial	= 0;	
	HAL_SPI_Init(&hspi5);
	
	//SPI6

}


static int __init spi_init(void)
{
	int ret;
	
	//SPI1


	//SPI2
	printk("spi2 module start ...\n");
	spi_dev[1].devno = MKDEV(MAJOR_SPI, 1);
	sprintf(spi_dev[1].name, "%s%d", DEVNAME, 2);
	register_chrdev_region(spi_dev[1].devno, 1, spi_dev[1].name);
	cdev_add(&spi_dev[1].cdev, spi_dev[1].devno, 1);
	cdev_init(&spi_dev[1].cdev, &spi_ops);

	//SPI3
	
	//SPI4

	//SPI5
	printk("spi5 module start ...\n");
	spi_dev[4].devno = MKDEV(MAJOR_SPI, 4);
	sprintf(spi_dev[4].name, "%s%d", DEVNAME, 5);
	register_chrdev_region(spi_dev[4].devno, 1, spi_dev[4].name);
	cdev_add(&spi_dev[4].cdev, spi_dev[4].devno, 1);
	cdev_init(&spi_dev[4].cdev, &spi_ops);

	//SPI6

	spi_hw_init();
	return 0;

ERR_STEP1:
	unregister_chrdev_region(spi_dev[1].devno, 1);
	unregister_chrdev_region(spi_dev[4].devno, 1);
ERR_STEP:
	cdev_del(&spi_dev[1].cdev);
	cdev_del(&spi_dev[4].cdev);
	return ret;
}

static void __exit spi_exit(void)
{
	unregister_chrdev_region(spi_dev[1].devno,1);
	cdev_del(&spi_dev[1].cdev);
	unregister_chrdev_region(spi_dev[4].devno,1);
	cdev_del(&spi_dev[4].cdev);
}

module_init(spi_init);
module_exit(spi_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("jimbo");
MODULE_DESCRIPTION("this is spi module");



