/*
 * Copyright (C) 2011 Freescale Semiconductor, Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 *
 */

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h> /* printk() */
#include <linux/slab.h> /* kmalloc() */
#include <linux/fs.h> /* everything... */
#include <linux/errno.h> /* error codes */
#include <linux/types.h> /* size_t */
#include <asm/uaccess.h> /* copy_from/to_user */
#include <linux/input.h>/*input_allocate_device*/
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/sysfs.h>
#include <linux/string.h>
#include <linux/ctype.h>
#include <linux/cdev.h>		/* char devices structure and aux functions */
#include <linux/gpio.h>

//DEV Name
#define DEVICE_NAME "MC1323x_dev"

#define MX53_SMD_ZIGBEE_INT             (1*32 + 6)      /* GPIO2_6 */
#define MX53_SMD_ZIGBEE_RESET_B         (1*32 + 7)      /* GPIO2_7 */
#define MX53_SMD_WAKEUP_ZIGBEE		(1*32 + 13)	/* GPIO2_13 */

#define ZIGBEE_RESET_PIN 	0
#define ZIGBEE_WAKE_PIN 	1
#define ZIGBEE_INT_PIN 		2


#define SET_PIN 	_IOW(0, 0, unsigned char *)
#define CLEAR_PIN 	_IOW(0, 1, unsigned char *)
#define GET_PIN 	_IOWR(0, 2, unsigned char *)

#define HIGH 				1
#define LOW				0


/*Private Struct MC1323*/
struct  MC1323_structure {
	struct input_dev *input_dev;
	struct i2c_client *client;
};

struct MC1323_structure *MC1323_data;
struct i2c_client *clientPublic;
static unsigned major;					/* major number */
static struct cdev myMC1323;			/* to register with kernel */
static struct class *myMC1323_class;		/* to register with sysfs */
static struct device *myMC1323_device;		/* to register with sysfs */

static int __devinit MC1323_probe(struct i2c_client *client, const struct i2c_device_id *id);
static int MC1323_resume(struct i2c_client *client);
static int MC1323_suspend(struct i2c_client *client, pm_message_t mesg);
static int __devexit MC1323_remove(struct i2c_client *client);


static const struct i2c_device_id MC1323_idtable[] = {
	{"MC1323", 0},
};

/*FOPS-------------------------------------------*/
static struct i2c_driver MC1323_fops = {

  .driver = {
		   .owner = THIS_MODULE,
		   .name = "MC1323",
	    },
  .id_table = MC1323_idtable,
  .probe = MC1323_probe, 
  .resume = MC1323_resume,
  .suspend = MC1323_suspend,
  .remove = __devexit_p(MC1323_remove),

};


int MC1323_open(struct inode *inode, struct file *filp)
{

	printk(KERN_DEBUG "\nOPEN DEVICE********************|\n\n");

	return 0;
}

ssize_t MC1323_read(struct file *filep, char *buf, size_t size, loff_t *fpos)
{

	char dataRead[size];
	char read,i;
	printk(KERN_DEBUG "\nREAD DEVICE********************\n");
	read = 0;
	if(buf != NULL)
	{
		read = i2c_master_recv(clientPublic,&dataRead[0],size);	
		if (read > 0 )
		{
			for(i=0; i<size;i++)
				printk(KERN_DEBUG "Data %d: %x\n",i+1, dataRead[i]);
			if(copy_to_user(buf, dataRead, size)) {
				printk(KERN_DEBUG "Error reading\n");
				read=  -1;
			}
		}
	}
	return read;
}

int MC1323_ioctl(struct inode *inode, struct file *filp, unsigned int ioctl_num, unsigned long arg)
{
	unsigned char pin[0];
	int value;

	printk(KERN_INFO "\nIOCTL********************\n\n");

switch(ioctl_num)
{
						
	case SET_PIN:		if (copy_from_user(pin, (unsigned char *)arg ,sizeof(unsigned char *)))
				{	
							printk(KERN_INFO "ERROR COPYING ARGUMENT in SET_PIN\n");
							return -1;
				}
						printk(KERN_INFO "SET_PIN %d",*pin);
						switch (*pin)
						{
							case ZIGBEE_RESET_PIN : 
									gpio_set_value(MX53_SMD_ZIGBEE_RESET_B,HIGH);
									printk(KERN_INFO "SET RESET PIN");
								break;
							
							case ZIGBEE_WAKE_PIN: 
									gpio_set_value(MX53_SMD_WAKEUP_ZIGBEE,HIGH);
									printk(KERN_INFO "SET WAKEUP PIN");
									break;

							default:
									printk(KERN_INFO "PIN %d is invalid",*pin);
									return -1;
									break;
						}
						break;
						
	case CLEAR_PIN:			
						if (copy_from_user(pin, (unsigned char *)arg ,sizeof(unsigned char *)))
						{	
							printk(KERN_INFO "ERROR COPYING ARGUMENT in SET_PIN\n");
							return -1;
						}
						printk(KERN_INFO "CLEAR_PIN %d",*pin);
						switch (*pin)
						{
							case ZIGBEE_WAKE_PIN: 
									gpio_set_value(MX53_SMD_WAKEUP_ZIGBEE,LOW);
									printk(KERN_INFO "CLEAR WAKEUP PIN");
									break;
							
							case ZIGBEE_RESET_PIN : 
									gpio_set_value(MX53_SMD_ZIGBEE_RESET_B,LOW);
									printk(KERN_INFO "CLEAR RESET PIN");
									break;
							default:
									printk(KERN_INFO "PIN %d is invalid",pin);
									return -1;
									break;
						}
						break;
						
	case GET_PIN:		if (copy_from_user(pin, (unsigned char *)arg ,sizeof(unsigned char *)))
						{	
							printk(KERN_INFO "ERROR COPYING ARGUMENT in SET_PIN\n");
							return -1;
						}
						printk(KERN_INFO "GET_PIN %d",*pin);
						switch (*pin)
						{
							case ZIGBEE_WAKE_PIN: 
									value=gpio_get_value(MX53_SMD_WAKEUP_ZIGBEE);
									printk(KERN_INFO "ZIGBEE WAKEUP PIN is %d",value );
									break;
							
							case ZIGBEE_RESET_PIN : 
									value=gpio_get_value(MX53_SMD_ZIGBEE_RESET_B);
									printk(KERN_INFO "ZIGBEE_RESET PIN is %d",value );
									break;
							
							case ZIGBEE_INT_PIN : 
									value=gpio_get_value(MX53_SMD_ZIGBEE_INT );
									printk(KERN_INFO "ZIGBEE_INT PIN is %d",value );
									break;
									
							default:
									printk(KERN_INFO "PIN %d is invalid",pin);
									return -1;
									break;
						}
						*pin=(unsigned char)value;
						return (copy_to_user((unsigned char *)arg, pin, sizeof(unsigned char *)));
						break;
						
						

	default:		    printk(KERN_DEBUG "INVALID ARGUMENTS\n");	
						return EINVAL;
	}
	return 0;
						
}


ssize_t MC1323_write(struct file *filep, const char *buf, size_t size, loff_t *fpos)
{

	const char copyBuf[size];
	char writen,i;
	printk(KERN_DEBUG "\nWRITE DEVICE********************\n\n");
        if(copy_from_user(&copyBuf[0], buf, size) ) {
        	printk(KERN_DEBUG "Error writing\n");
                return -1;
        }
	for(i=0; i<size;i++)
		printk(KERN_DEBUG "Data %d: %x\n",i+1,copyBuf[i]);
	writen = i2c_master_send(clientPublic,&copyBuf[0], size);
	return writen; //Data written
}

int MC1323_release(struct inode *inode, struct file *filp)
{

	printk(KERN_DEBUG "RELEASE\n");
	printk(KERN_DEBUG "\nRELEASE DEVICE*****************|\n\n");
	return 0;
}

struct file_operations file_ops_MC1323 = {

	open:		MC1323_open,
	release:	MC1323_release,
	write:		MC1323_write,
	read:		MC1323_read,
	ioctl:		MC1323_ioctl
};

static int MC1323_resume(struct i2c_client *client)
{
	printk(KERN_DEBUG "***RESUME***\n");
	return 0;
}

static int MC1323_suspend(struct i2c_client *client, pm_message_t mesg)
{
	printk(KERN_DEBUG "***SUSPEND***\n");
	return 0;
}

static int __devinit MC1323_probe(struct i2c_client *client,
				 const struct i2c_device_id *id)
{
	int result;
	dev_t devid;
	clientPublic = client;
        printk(KERN_DEBUG "MC1323 PROBE\n");
	//Allocate Private Structure
	MC1323_data = kzalloc(sizeof(struct MC1323_structure), GFP_KERNEL);
	if (!MC1323_data)
			return -ENOMEM;
	result = alloc_chrdev_region(&devid, 0, 1, DEVICE_NAME);
	if (result < 0) {
		printk(KERN_INFO "The MC1323 driver could not be registered\n");
		return -1;
	}

	/* save MAJOR number */
	major = MAJOR(devid);
	printk(KERN_INFO "The MC1323 MAJOR is %d\n",major);

	/* Initalize the cdev structure */
	cdev_init(&myMC1323, &file_ops_MC1323);
	myMC1323.owner = THIS_MODULE;

	/* Register the char device with the kernel */
	result = cdev_add(&myMC1323, devid, 1);
	if (result) {
		printk(KERN_INFO "Error: Failed registering MC1323 with the kernel\n");
		unregister_chrdev_region(devid, 1);
		cdev_del(&myMC1323);
		return result;
	}
	printk(KERN_INFO "The MC1323 registered\n");

	/* Create a class and register with the sysfs. (Failure is not fatal) */
	myMC1323_class = class_create(THIS_MODULE, DEVICE_NAME);
	if (IS_ERR(myMC1323_class)) {
		printk(KERN_INFO "class_create() failed: %ld\n",
							PTR_ERR(myMC1323_class));
		myMC1323_class = NULL;
	} else {
		/* Register device with sysfs (creates device node in /dev) */
		myMC1323_device = device_create(myMC1323_class, NULL, devid, NULL,
								DEVICE_NAME);
		if (IS_ERR(myMC1323_device)) {
			printk(KERN_INFO "device_create() failed: %ld\n",
							PTR_ERR(myMC1323_device));
			myMC1323_device = NULL;
		}
		printk(KERN_INFO "The MC1323 sysfs\n");
	}
    result=gpio_request(MX53_SMD_ZIGBEE_INT,"ZigBee-Int");
    if (result<0 )
                printk(KERN_INFO "Error: Failed regestirin Zigee-Int %d\n",result);
	else {
		gpio_direction_input(MX53_SMD_ZIGBEE_INT);
		 printk(KERN_INFO "ZIGBEE INT as input");

	}
    result=gpio_request(MX53_SMD_WAKEUP_ZIGBEE,"ZigBee-Wake");
    if (result<0 )
               printk(KERN_INFO "Error: Failed regestirin Zigee-Wake up  %d\n",result);
	else {
		gpio_direction_output(MX53_SMD_WAKEUP_ZIGBEE, LOW);
		printk(KERN_INFO "The value of wake up is %d\n",gpio_get_value(MX53_SMD_WAKEUP_ZIGBEE));
	}
	result=gpio_request(MX53_SMD_ZIGBEE_RESET_B,"ZigBee-Reset");
    if (result<0 )
               printk(KERN_INFO "Error: Failed regestirin Zigee-Reset up  %d\n",result);
	else {
		gpio_direction_output(MX53_SMD_ZIGBEE_RESET_B, HIGH);
		printk(KERN_INFO "The value of reset is %d\n",gpio_get_value(MX53_SMD_ZIGBEE_RESET_B));
	}
	return 0;
}

static int __devexit MC1323_remove(struct i2c_client *client)
{
	cdev_del(&myMC1323);
	unregister_chrdev_region(MKDEV(major, 0), 1);
	if (myMC1323_class != NULL) {
		device_destroy(myMC1323_class, MKDEV(major, 0));
		class_destroy(myMC1323_class);
	}
	kfree(MC1323_data);
	return 0;
}




MODULE_DEVICE_TABLE(i2c, MC1323_idtable);

static int __init MC1323_init(void)
{
	printk(KERN_DEBUG "MC1323 INIT\n");
	return i2c_add_driver(&MC1323_fops);
}

static void __exit MC1323_exit(void)
{
	i2c_del_driver(&MC1323_fops);
}


module_init(MC1323_init);
module_exit(MC1323_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Freescale Semiconductor, Inc.");
MODULE_DESCRIPTION("MC1323 chip driver");
