#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/cdev.h> 
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/syscalls.h>
#include <linux/sched.h>
#include <linux/version.h>
#include <linux/if_vlan.h>
#include <linux/if_ether.h>
#include <linux/atmdev.h>
#include "xdsl_types.h"
#include "xdsl_ctrl.h"
#include "xdsl_ctrl_api.h"


MODULE_LICENSE("GPL");

uint8  dslcmd_rxbuf[ DSL_MAX_BUF_SIZE ];

cmd_t xdslcmd_Table[]={
{RLCM_PHY_ENABLE_MODEM,					XDSL_CMD_SET},
{RLCM_PHY_DISABLE_MODEM,				XDSL_CMD_SET},
{RLCM_GET_DRIVER_VERSION,				XDSL_CMD_GET},
{RLCM_GET_DRIVER_BUILD,					XDSL_CMD_GET},
{RLCM_MODEM_RETRAIN,					XDSL_CMD_SET},		//?
{RLCM_GET_REHS_COUNT,					XDSL_CMD_GET},
{RLCM_GET_CHANNEL_SNR,					XDSL_CMD_GET},
{RLCM_GET_AVERAGE_SNR,					XDSL_CMD_GET},
{RLCM_GET_SNR_MARGIN,					XDSL_CMD_GET},
{RLCM_REPORT_MODEM_STATE,				XDSL_CMD_GET},
{RLCM_REPORT_PM_DATA,					XDSL_CMD_GET},
{RLCM_MODEM_NEAR_END_ID_REQ,			XDSL_CMD_GET},		//?
{RLCM_MODEM_FAR_END_ID_REQ,				XDSL_CMD_GET},		//?
{RLCM_MODEM_NEAR_END_LINE_DATA_REQ,		XDSL_CMD_GET},		//?
{RLCM_MODEM_FAR_END_LINE_DATA_REQ,		XDSL_CMD_GET},		//?
{RLCM_MODEM_NEAR_END_FAST_CH_DATA_REQ,	XDSL_CMD_GET},		//?
{RLCM_MODEM_NEAR_END_INT_CH_DATA_REQ,	XDSL_CMD_GET},		//?
{RLCM_MODEM_FAR_END_FAST_CH_DATA_REQ,	XDSL_CMD_GET},		//?
{RLCM_MODEM_FAR_END_INT_CH_DATA_REQ,	XDSL_CMD_GET},		//?
{RLCM_SET_ADSL_MODE,					XDSL_CMD_SET},
{RLCM_GET_ADSL_MODE,					XDSL_CMD_GET},
{RLCM_GET_CURRENT_LOSS_DATA,			XDSL_CMD_GET},
{RLCM_GET_LINK_SPEED,					XDSL_CMD_GET},
{RLCM_GET_CHANNEL_MODE,					XDSL_CMD_GET},
{RLCM_GET_LOOP_ATT,						XDSL_CMD_GET},
{RLCM_INC_TX_POWER,						XDSL_CMD_SET},
{RLCM_TUNE_PERF,						XDSL_CMD_SET},
{RLCM_ENABLE_BIT_SWAP,					XDSL_CMD_SET},
{RLCM_DISABLE_BIT_SWAP,					XDSL_CMD_SET},
{RLCM_ENABLE_PILOT_RELOCATION,			XDSL_CMD_SET},
{RLCM_DISABLE_PILOT_RELOCATION,			XDSL_CMD_SET},
{RLCM_ENABLE_TRELLIS,					XDSL_CMD_SET},
{RLCM_DISABLE_TRELLIS,					XDSL_CMD_SET},
{RLCM_SET_VENDOR_ID,					XDSL_CMD_SET},
{RLCM_MODEM_READ_CONFIG,				XDSL_CMD_GET},
{RLCM_MODEM_WRITE_CONFIG,				XDSL_CMD_SET},
{RLCM_DEBUG_MODE,						XDSL_CMD_SET},
{RLCM_TEST_PSD,							XDSL_CMD_SET},
{RLCM_GET_CHANNEL_BH,					XDSL_CMD_GET},
{RLCM_PHY_START_MODEM,					XDSL_CMD_SET},
{RLCM_ENABLE_ADSL_LOG,					XDSL_CMD_SET},
{RLCM_DISABLE_ADSL_LOG,					XDSL_CMD_SET},
{RLCM_GET_VENDOR_ID,					XDSL_CMD_GET},
{RLCM_GET_TX_POWER,						XDSL_CMD_GET},
{RLCM_GET_PERF_VALUE,					XDSL_CMD_GET},
{RLCM_GET_15MIN_LOSS_DATA,				XDSL_CMD_GET},
{RLCM_GET_1DAY_LOSS_DATA,				XDSL_CMD_GET},
{RLCM_GET_CHANNEL_BITLOAD,				XDSL_CMD_GET},
{RLCM_GET_TRAP_THRESHOLD,				XDSL_CMD_GET},
{RLCM_SET_TRAP_THRESHOLD,				XDSL_CMD_SET},
{RLCM_15MIN_WAIT_TRAP,					XDSL_CMD_SET},
{RLCM_ENABLE_ATM_LOOPBACK,				XDSL_CMD_SET},
{RLCM_DISABLE_ATM_LOOPBACK,				XDSL_CMD_SET},
{RLCM_MSGMODE,							XDSL_CMD_SET},		//?
{RLCM_CMD_API,							XDSL_CMD_SET},
{RLCM_GET_CURRENT_LOSS_DATA,			XDSL_CMD_GET},
{RLCM_GET_CHANNEL_BH,					XDSL_CMD_GET},
{RLCM_GET_TRAP_15MIN_LOSS_DATA,			XDSL_CMD_GET},
{RLCM_SEND_DYING_GASP,					XDSL_CMD_SET},		//?
{RLCM_TEST_HW,							XDSL_CMD_SET},		//?
{RLCM_SET_LOOPBACK,						XDSL_CMD_SET},		//?
{RLCM_INIT_ADSL_MODE,					XDSL_CMD_SET},
{RLCM_ENABLE_POM_ACCESS,				XDSL_CMD_SET},
{RLCM_DISABLE_POM_ACCESS,				XDSL_CMD_SET},
{RLCM_MASK_TONE,						XDSL_CMD_SET},		//?
{RLCM_GET_CAPABILITY,					XDSL_CMD_GET},
{RLCM_VERIFY_HW,						XDSL_CMD_SET},
{RLCM_TRIG_OLR_TYPE,					XDSL_CMD_SET},
{RLCM_TRIG_OLR_TYPE1,					XDSL_CMD_SET},
{RLCM_ENABLE_AEQ,						XDSL_CMD_SET},
{RLCM_ENABLE_HPF,						XDSL_CMD_SET},
{RLCM_SET_HPF_FC,						XDSL_CMD_SET},
{RLCM_SET_HYBRID,						XDSL_CMD_SET},
{RLCM_SET_RX_GAIN,						XDSL_CMD_SET},
{RLCM_SET_AFE_REG,						XDSL_CMD_SET},
{RLCM_SET_FOBASE,						XDSL_CMD_SET},
//yaru + for web data
{RLCM_SET_XDSL_MODE,					XDSL_CMD_SET},
{RLCM_GET_SHOWTIME_XDSL_MODE,			XDSL_CMD_GET},
{RLCM_GET_XDSL_MODE,					XDSL_CMD_GET},
{RLCM_SET_OLR_TYPE,						XDSL_CMD_SET},
{RLCM_GET_OLR_TYPE,						XDSL_CMD_GET},
{RLCM_GET_LINE_RATE,					XDSL_CMD_GET},
{RLCM_GET_DS_ERROR_COUNT,				XDSL_CMD_GET},
{RLCM_GET_US_ERROR_COUNT,				XDSL_CMD_GET},
{RLCM_GET_DIAG_QLN,						XDSL_CMD_GET},
{RLCM_GET_DIAG_HLOG,					XDSL_CMD_GET},
{RLCM_GET_DIAG_SNR,						XDSL_CMD_GET},
{RLCM_GET_DS_PMS_PARAM1,				XDSL_CMD_GET},
{RLCM_GET_US_PMS_PARAM1,				XDSL_CMD_GET},
{RLCM_SET_ANNEX_L,						XDSL_CMD_SET},
{RLCM_GET_ANNEX_L,						XDSL_CMD_GET},
{RLCM_GET_LINK_POWER_STATE,				XDSL_CMD_GET},
{RLCM_GET_ATT_RATE,						XDSL_CMD_GET},
{RLCM_LOADCARRIERMASK,					XDSL_CMD_GET},
{RLCM_SET_ANNEX_M,						XDSL_CMD_SET},
{RLCM_GET_ANNEX_M,						XDSL_CMD_GET},
{RLCM_SET_8671_REV,						XDSL_CMD_SET},
{RLCM_GET_8671_REV,						XDSL_CMD_GET},
{RLCM_SET_HIGH_INP,						XDSL_CMD_SET},
{RLCM_GET_HIGH_INP,						XDSL_CMD_GET},
{RLCM_GET_LD_STATE,						XDSL_CMD_GET},
{RLCM_SET_ANNEX_B,						XDSL_CMD_SET},
{RLCM_GET_ANNEX_B,						XDSL_CMD_GET},
{RLCM_GET_DSL_STAT_SHOWTIME,			XDSL_CMD_GET},
{RLCM_GET_DSL_STAT_TOTAL,				XDSL_CMD_GET},
{RLCM_GET_DSL_PSD,						XDSL_CMD_GET},
{RLCM_GET_DSL_ORHERS,					XDSL_CMD_GET},
{RLCM_GET_DSL_GI,						XDSL_CMD_GET},
{RLCM_SET_ADSL_LAST_OPMode,				XDSL_CMD_SET},
{RLCM_SET_ADSL_PMS_CONFIG,				XDSL_CMD_SET},
{RLCM_GET_ADSL2WAN_IFCFG,				XDSL_CMD_GET},
{RLCM_ENABLE_DIAGNOSTIC,				XDSL_CMD_SET},
#ifdef FIELD_TRY_SAFE_MODE
{RLCM_GET_SAFEMODE_CTRL,				XDSL_CMD_GET},
{RLCM_SET_SAFEMODE_CTRL,				XDSL_CMD_SET},
#endif
{RLCM_GET_DSL_STAT_15MIN,				XDSL_CMD_GET},
{RLCM_GET_DSL_STAT_1DAY,				XDSL_CMD_GET},
{RLCM_GET_INIT_COUNT_LAST_LINK_RATE,	XDSL_CMD_GET},
{RLCM_ENABLE_NODROPLINEFLAG,			XDSL_CMD_SET},
{RLCM_DISABLE_NODROPLINEFLAG,			XDSL_CMD_SET},
{RLCM_SET_D_CONFIG,						XDSL_CMD_SET},
{RLCM_GET_DSL_STAT_LAST_SHOWTIME,		XDSL_CMD_GET},
{RLCM_GET_DSL_STAT_SHOWTIME_REV1P4,		XDSL_CMD_GET},
{RLCM_GET_DSL_STAT_TOTAL_REV1P4,		XDSL_CMD_GET},
{RLCM_GET_DSL_STAT_LAST_SHOWTIME_REV1P4,XDSL_CMD_GET},
{RLCM_GET_DSL_STAT_15MIN_REV1P4,		XDSL_CMD_GET},
{RLCM_GET_DSL_STAT_1DAY_REV1P4,			XDSL_CMD_GET},
{RLCM_GET_DSL_STAT_FLAG_LOF,			XDSL_CMD_GET},
{RLCM_GET_FRAME_COUNT,					XDSL_CMD_GET},
{RLCM_DBG_DATA,							XDSL_CMD_SET},
{RLCM_SET_DSL_FUNC,						XDSL_CMD_SET},
{RLCM_GET_DSL_STAT_PRE_15MIN,			XDSL_CMD_GET},
{RLCM_GET_CHANNEL_TABLE,				XDSL_CMD_GET},
{RLCM_GET_PREV1DAY_LOSS_DATA,			XDSL_CMD_GET},
{RLCM_GET_ANY_15MIN_LOSS_DATA,			XDSL_CMD_GET},
{RLCM_GET_ADSLLINE_ENABLED,				XDSL_CMD_GET},
{RLCM_SET_ADSLLINE_ENABLED,				XDSL_CMD_SET},
{RLCM_GET_ADSLLINE_CAP_ACT,				XDSL_CMD_GET},
{RLCM_GET_ADSLLINE_MODE_CAP,			XDSL_CMD_GET},
{RLCM_GET_ADSLLINE_MODE_ACT,			XDSL_CMD_GET},
#ifdef CONFIG_VDSL
{RLCM_DEBUG_DATA ,						XDSL_CMD_SET},
{RLCM_GET_VDSL2_MREFPSD_DS,				XDSL_CMD_GET},
{RLCM_GET_VDSL2_TXREFVNPSD_DS,			XDSL_CMD_GET},
{RLCM_GET_VDSL2_MREFPSD_US,				XDSL_CMD_GET},
{RLCM_GET_VDSL2_TXREFVNPSD_US,			XDSL_CMD_GET},
{RLCM_GET_ADSL_DIAG_HLIN,				XDSL_CMD_GET},
{RLCM_GET_ADSL_DIAG_GI,					XDSL_CMD_GET},
{RLCM_GET_ADSL_DIAG_BI,					XDSL_CMD_GET},
{RLCM_GET_ADSL_DIAG_OTHER,				XDSL_CMD_GET},
{RLCM_GET_VDSL2_DIAG_QLN,				XDSL_CMD_GET},
{RLCM_GET_VDSL2_DIAG_HLOG,				XDSL_CMD_GET},
{RLCM_GET_VDSL2_DIAG_SNR,				XDSL_CMD_GET},
{RLCM_GET_VDSL2_DIAG_HLIN,				XDSL_CMD_GET},
{RLCM_GET_VDSL2_DIAG_HLIN_SCALE,		XDSL_CMD_GET},
{RLCM_GET_VDSL2_DIAG_OTHER,				XDSL_CMD_GET},
{RLCM_GET_VDSL2_SNR_MARGIN,				XDSL_CMD_GET},
{RLCM_GET_VDSL2_LOOP_ATT,				XDSL_CMD_GET},
{RLCM_GET_VDSL2_CHANNEL_SNR,			XDSL_CMD_GET},
{RLCM_GET_VDSL2_CHANNEL_BITLOAD,		XDSL_CMD_GET},
{RLCM_WANBonderDSLConfig_Enable,		XDSL_CMD_SET},
{RLCM_WANBonderDSLConfig_Status,		XDSL_CMD_GET},
{RLCM_WANBonderDSLConfig_GroupBondScheme,XDSL_CMD_GET},
{RLCM_WANBonderDSLConfig_GroupCapacity, XDSL_CMD_GET},
{RLCM_WANBonderDSLConfig_aGroupTargetUpRate, XDSL_CMD_GET},
{RLCM_WANBonderDSLConfig_aGroupTargetDownRate, XDSL_CMD_GET},
{RLCM_WANConfig_LineNumber,				XDSL_CMD_GET},
{RLCM_UserGetDslData,					XDSL_CMD_GET},
{RLCM_UserSetDslData,					XDSL_CMD_SET},
#endif
{0,0}
};


#define PTM_MAGIC           					(('R'+'T'+'L'+'P'+'T'+'M') << 8)
#define PTM_SET_DEFAULT_TABLE   				(PTM_MAGIC+1)
#define PTM_GET_TABLE          					(PTM_MAGIC+2)
#define PTM_READ_DATA           				(PTM_MAGIC+3)
#define PTM_WRITE_DATA          				(PTM_MAGIC+4)
#define PTM_SET_HW              				(PTM_MAGIC+5)
#define PTM_GET_QMAP            				(PTM_MAGIC+6)
#define PTM_STOP_HW             				(PTM_MAGIC+7)
#define PTM_START_HW            				(PTM_MAGIC+8)
#define PTM_CLEAN_WANIF         				(PTM_MAGIC+9)
#define PTM_CLEAN_QMAP          				(PTM_MAGIC+10)
#define PTM_SET_WANIF           				(PTM_MAGIC+11)
#define PTM_SET_QMAP            				(PTM_MAGIC+12)
#define PTM_SET_STAGTYPE        				(PTM_MAGIC+13)
#define PTM_SET_DUMMYVID        				(PTM_MAGIC+14)
#define PTM_SET_QMAPATSTAG      				(PTM_MAGIC+15)
#define PTM_SET_MODE            				(PTM_MAGIC+16)
#define PTM_SET_SYSTEM          				(PTM_MAGIC+17)
#define PTM_ENABLE_TPSTC						(PTM_MAGIC+18)
#define PTM_DISABLE_TPSTC						(PTM_MAGIC+19)
#define PTM_RESET_TPSTC							(PTM_MAGIC+20)
#define PTM_GET_TPSTC_TX_CNT					(PTM_MAGIC+21)
#define PTM_GET_UTOPIA_TX_CNT					(PTM_MAGIC+22)

cmd_t ptmcmd_Table[]={
{PTM_SET_DEFAULT_TABLE, PTM_CMD_SET},
{PTM_GET_TABLE,			PTM_CMD_GET},
{PTM_READ_DATA,			PTM_CMD_SET},
{PTM_WRITE_DATA,		PTM_CMD_SET},
{PTM_SET_HW,			PTM_CMD_SET},
{PTM_GET_QMAP,			PTM_CMD_GET},
{PTM_STOP_HW,			PTM_CMD_SET},
{PTM_START_HW,			PTM_CMD_SET},
{PTM_CLEAN_WANIF,		PTM_CMD_SET},
{PTM_CLEAN_QMAP,		PTM_CMD_SET},
{PTM_SET_WANIF,			PTM_CMD_SET},
{PTM_SET_QMAP  ,		PTM_CMD_SET},
{PTM_SET_STAGTYPE,		PTM_CMD_SET},
{PTM_SET_DUMMYVID,		PTM_CMD_SET},
{PTM_SET_QMAPATSTAG,	PTM_CMD_SET},
{PTM_SET_MODE,			PTM_CMD_SET},
{PTM_SET_SYSTEM ,		PTM_CMD_SET},
{PTM_ENABLE_TPSTC,		PTM_CMD_SET},
{PTM_DISABLE_TPSTC,		PTM_CMD_SET},
{PTM_RESET_TPSTC,		PTM_CMD_SET},
{PTM_GET_TPSTC_TX_CNT,	PTM_CMD_GET},
{PTM_GET_UTOPIA_TX_CNT,	PTM_CMD_GET},
{0,0}
};

int get_cmd_protocol(char type, int command)
{
	cmd_t *pCmd;
	if (type == XDSL_CMD){
		pCmd = xdslcmd_Table;
	} else {
		pCmd = ptmcmd_Table;
	}

	while (pCmd->cmdId != 0){
		if (pCmd->cmdId == command){
			return pCmd->setFlag;
		}
		pCmd++;
	}

	return -1;
}

unsigned char *get_cmd_rxbuf(unsigned char protocol, unsigned int command, unsigned long inarg)
{
	if (protocol == XDSL_CMD_GET){
		if (inarg==0) 
			return NULL;
		else if ((*(uint32*)inarg)==0) 
			return NULL;
		else if (command == RLCM_UserGetDslData){		
			return (*(char**)((int*)(*((int*)inarg+1))+1));
		} else {
			return ((char*)(*((int*)inarg+1)));
		}		
	}
	return NULL;
}


int send_dslcmd(unsigned char category, unsigned char protocol, unsigned int command, int dev_num, unsigned long inarg)
{
	extern int xdsl_ctrl_tx_final(uint8 category, uint8 protocol, uint8 devnum, uint32 command, 
									uint32 arg, uint32 argsize, uint32 ret, uint32 seg_num);
	int retVal=0, argsize=0;
	unsigned int *argp;	

	if (inarg == 0){
		argsize = 0;
		retVal = xdsl_ctrl_tx_final(category, protocol, dev_num, command, 0, 0, 0, 0);
	} else {
		argp = (uint32*)inarg;
		argsize = (*argp);
		if (argsize == 0){
			retVal = xdsl_ctrl_tx_final(category, protocol, dev_num, command, 0, 0, 0, 0);
		} else {
			retVal = xdsl_ctrl_tx_final(category, protocol, dev_num, command, (*(argp+1)), argsize, 0, 0);
		}
	}

	return retVal;
}

void return_data_to_user(char* user_buf, char* dsl_data, int command, unsigned long inarg)
{
	unsigned int return_size;

	if (inarg==0)
		return_size = 0;
	else
		return_size = *((unsigned int *)inarg);
	
	if (command == RLCM_UserGetDslData)
		return_size -= 4;

	#if defined(CONFIG_PTM_BONDING_MASTER)
	if (copy_to_user(user_buf, dsl_data, return_size)){
		memcpy(user_buf, dsl_data, return_size);
	}
	#else
	memcpy(user_buf, dsl_data, return_size);
	#endif
}

#if defined(CONFIG_XDSL_CTRL_ON_DSL)
struct file *xdsl_dev[1];
struct file *ptm_dev[1];

#if defined(CONFIG_PTM_BONDING_SLAVE)
struct mutex slave_dsl_mutex;

long sendcmd_to_master(int dev_num, unsigned int command, unsigned long inarg)
{
	char category;
	unsigned char *user_rxbuf;
	int retVal=0, protocol, argsize, data_size=0;

	mutex_lock(&slave_dsl_mutex);
	protocol = get_cmd_protocol(XDSL_CMD, command);
	if (protocol == -1){
		mutex_unlock(&slave_dsl_mutex);
		return -1;
	}

	user_rxbuf = NULL;

	if (protocol == XDSL_CMD_GET){
		category = CTRL_2MASTER_GET;
		user_rxbuf = get_cmd_rxbuf(protocol, command, inarg);
	} else {
		category = CTRL_2MASTER_SET;
	}

	send_dslcmd(category, protocol, command, dev_num, inarg);
	if (protocol == XDSL_CMD_GET){
		if (retVal==0){
			return_data_to_user(user_rxbuf, dslcmd_rxbuf, command, inarg);
		}
	}
	mutex_unlock(&slave_dsl_mutex);
	return retVal;
}
EXPORT_SYMBOL(sendcmd_to_master);
#endif
#endif

#if defined(CONFIG_XDSL_CTRL_ON_SOC)
#define XDSL_DEV_NUM	2
#define PTM_DEV_NUM		2
extern char dmac[6];

int xdsl_major_num[ XDSL_DEV_NUM ] = {260, 261};
char xdsl_dev_name[ XDSL_DEV_NUM ][6]={"xdsl0", "xdsl1"};
static struct class *xdsl_class[ XDSL_DEV_NUM ];
struct file *xdsl_dev[ XDSL_DEV_NUM ];

int ptm_major_num[ PTM_DEV_NUM ] = {264, 265};
char ptm_dev_name[ PTM_DEV_NUM ][6]={"ptm0", "ptm1"};
static struct class *ptm_class[ PTM_DEV_NUM ];
struct file *ptm_dev[ PTM_DEV_NUM ];

/* Prototype */
static int xdsl_open( struct inode *inode, struct file *filp );
static int xdsl_close( struct inode *inode, struct file *filp );
long xdsl_ioctl(struct file *flip, int devnum, unsigned int command, unsigned long inarg);
long xdsl0_ioctl(struct file *flip, unsigned int command, unsigned long inarg);
long xdsl1_ioctl(struct file *flip, unsigned int command, unsigned long inarg);
long ptm_ioctl(struct file *flip, int devnum, unsigned int command, unsigned long inarg);
long ptm0_ioctl(struct file *flip, unsigned int command, unsigned long inarg);
long ptm1_ioctl(struct file *flip, unsigned int command, unsigned long inarg);

EXPORT_SYMBOL(ptm1_ioctl);

static struct file_operations xdsl_dev_fops[XDSL_DEV_NUM] =
{
	{
		unlocked_ioctl: xdsl0_ioctl,
    	open:       	xdsl_open,
	    release:		xdsl_close,
	},

	{
		unlocked_ioctl: xdsl1_ioctl,
    	open:       	xdsl_open,
	    release:		xdsl_close,
	}
};

static struct file_operations ptm_dev_fops[PTM_DEV_NUM] =
{
	{
		unlocked_ioctl: ptm0_ioctl,
    	open:       	xdsl_open,
	    release:		xdsl_close,
	},

	{
		unlocked_ioctl: ptm1_ioctl,
    	open:       	xdsl_open,
	    release:		xdsl_close,
	}
};

/***************************************************************************
 * Function Name: xdsl_open
 * Description  : Called when an application opens this device.
 * Returns      : 0 - success
 ***************************************************************************/
static int xdsl_open( struct inode *inode, struct file *filp )
{
	return( 0 );
} /* dsl_ipc_open */

/***************************************************************************
 * Function Name: xdsl_close
 * Description  : Called when an application stops this device.
 * Returns      : 0 - success
 ***************************************************************************/
static int xdsl_close( struct inode *inode, struct file *filp )
{

	return( 0 );
} /* dsl_ipc_close */


static struct mutex dsl_mutex;
extern void msleep(unsigned int millisecs);
extern unsigned long msleep_interruptible(unsigned int msecs);


/***************************************************************************
 * Function Name: ptm_ioctl
 * Description  : Main entry point for an application send issue ATM API
 *                requests.
 * Returns      : 0 - success or error
 ***************************************************************************/
long ptm_ioctl(struct file *flip, int dev_num, unsigned int command, unsigned long inarg)
{
	extern int xdsl_ctrl_tx_final(uint8 category, uint8 protocol, uint8 devnum, uint32 command, 
									uint32 arg, uint32 argsize, uint32 ret, uint32 seg_num);

	char category;
	int retVal=0, protocol;
	unsigned int *argp;	
	int argsize = sizeof(ptm_arg);
	
	#if defined(CONFIG_BONDING_MASTER_ON_SOC)
	int PTMCommand(unsigned int command, unsigned long inarg);
	if (dev_num==0){
		retVal = PTMCommand(command, inarg);
		return retVal;
	}
	#endif

	mutex_lock(&dsl_mutex);
	protocol = get_cmd_protocol(PTM_CMD, command);
	if (protocol == -1){
		mutex_unlock(&dsl_mutex);
		return retVal;
	}

	if (protocol == PTM_CMD_GET){
		category = CTRL_2DSL_GET;
	} else {
		category = CTRL_2DSL_SET;
	}

	if (inarg){
		argp = (unsigned int*)(inarg);
		argsize = sizeof(ptm_arg);
	} else {
		argp = NULL;
		argsize = 0;
	}

	retVal = xdsl_ctrl_tx_final(category, protocol, dev_num, command, argp, argsize, 0, 0);

	if (protocol==PTM_CMD_GET){
		retVal = *(int*)dslcmd_rxbuf;
	}

	mutex_unlock(&dsl_mutex);
	return retVal;
}

long ptm0_ioctl(struct file *flip, unsigned int command, unsigned long inarg)
{
	int retVal=0;
	retVal = ptm_ioctl(flip, 0, command, inarg);
	return retVal;
}

long ptm1_ioctl(struct file *flip, unsigned int command, unsigned long inarg)
{
	int retVal=0;
	retVal = ptm_ioctl(flip, 1, command, inarg);
	return retVal;
}


/***************************************************************************
 * Function Name: xdsl_ioctl
 * Description  : Main entry point for an application send issue ATM API
 *                requests.
 * Returns      : 0 - success or error
 ***************************************************************************/
long xdsl_ioctl(struct file *flip, int dev_num, unsigned int command, unsigned long inarg)
{
	char category;
	unsigned char *user_rxbuf;
	int retVal=0, protocol, argsize, data_size=0;

	#if defined(CONFIG_BONDING_MASTER_ON_SOC)
	int XDSLCommand(unsigned int command, unsigned long inarg);
	if (dev_num==0){
		retVal = XDSLCommand(command, inarg);
		return retVal;
	}
	#endif

	mutex_lock(&dsl_mutex);
	protocol = get_cmd_protocol(XDSL_CMD, command);
	if (protocol == -1){
		mutex_unlock(&dsl_mutex);
		return -1;
	}

	user_rxbuf = NULL;

	if (protocol == XDSL_CMD_GET){
		category = CTRL_2DSL_GET;
		user_rxbuf = get_cmd_rxbuf(protocol, command, inarg);
	} else {
		category = CTRL_2DSL_SET;
	}

	send_dslcmd(category, protocol, command, dev_num, inarg);

	if (protocol == XDSL_CMD_GET){
		if (retVal==0){
			return_data_to_user(user_rxbuf, dslcmd_rxbuf, command, inarg);
		}
	}

	mutex_unlock(&dsl_mutex);
	return retVal;
}

long xdsl0_ioctl(struct file *flip, unsigned int command, unsigned long inarg)
{
	int retVal=0;
	retVal = xdsl_ioctl(flip, 0, command, inarg);
	return retVal;
}

long xdsl1_ioctl(struct file *flip, unsigned int command, unsigned long inarg)
{
	int retVal=0;
	retVal = xdsl_ioctl(flip, 1, command, inarg);
	return retVal;
}
#endif


int XDSLCommand(unsigned int command, unsigned long inarg )
{
	int ret=0, err=0;
	if (IS_ERR(xdsl_dev[0])){	
		xdsl_dev[0] = filp_open("/dev/adsl0", O_RDONLY, 0);
		if (IS_ERR(xdsl_dev[0])) {
			err = PTR_ERR(xdsl_dev[0]);
			printk("dev adsl0: unable to open\n");
		} else{
			ret = xdsl_dev[0]->f_op->unlocked_ioctl(xdsl_dev[0], command, inarg);
		}
	}
	else if (xdsl_dev[0]){
		ret = xdsl_dev[0]->f_op->unlocked_ioctl(xdsl_dev[0], command, inarg);
	} 
	return ret;
}

int PTMCommand(unsigned int command, unsigned long inarg )
{
	int ret=0, err=0;
	if (IS_ERR(ptm_dev[0])){	
		ptm_dev[0] = filp_open("/dev/ptm", O_RDONLY, 0);
		if (IS_ERR(ptm_dev[0])) {
			err = PTR_ERR(ptm_dev[0]);
			printk("dev ptm: unable to open\n");
		} else{
			ret = ptm_dev[0]->f_op->unlocked_ioctl(ptm_dev[0], command, inarg);
		}
	}
	else if (ptm_dev[0]){
		ret = ptm_dev[0]->f_op->unlocked_ioctl(ptm_dev[0], command, inarg);
	} 
	return ret;
}

static int __init xdsl_dev_init( void )
{
	int err;
	extern void xdsl_ctrl_tx_init(void);
	extern void xdsl_ctrl_rx_init(void);
	#if defined(CONFIG_XDSL_CTRL_ON_SOC)
	int i;
	struct device *dp;

	printk("XDSL DEV Init !!\n");

	mutex_init(&dsl_mutex);

	for (i=0; i<XDSL_DEV_NUM; i++){
		err=register_chrdev(xdsl_major_num[i], xdsl_dev_name[i], &xdsl_dev_fops[i] );

		if (err) {
			printk(KERN_ERR "failed to register xdsl%d device (%d)\n", i, err);
			return -1;
		}

		xdsl_class[i] = class_create(THIS_MODULE, xdsl_dev_name[i]);
		if (IS_ERR(xdsl_class[i])) {
			err = PTR_ERR(xdsl_class[i]);
			goto out_class;
		}

		dp=device_create(xdsl_class[i], NULL, MKDEV(xdsl_major_num[i], 0), NULL, xdsl_dev_name[i]);

		if (IS_ERR(dp))
			printk( "xdsl: create device failed\n");
		else
			printk( "xdsl: create device successed\n");
	}

	for (i=0; i<PTM_DEV_NUM; i++){
		err=register_chrdev(ptm_major_num[i], ptm_dev_name[i], &ptm_dev_fops[i] );

		if (err) {
			printk(KERN_ERR "failed to register ptm%d device (%d)\n", i, err);
			return -1;
		}

		ptm_class[i] = class_create(THIS_MODULE, ptm_dev_name[i]);
		if (IS_ERR(ptm_class[i])) {
			err = PTR_ERR(ptm_class[i]);
			goto out_class;
		}

		dp = device_create(ptm_class[i], NULL, MKDEV(ptm_major_num[i], 0), NULL, ptm_dev_name[i]);

		if (IS_ERR(dp))
			printk( "ptm: create device failed\n");
		else
			printk( "ptm: create device successed\n");

	}


	xdsl_ctrl_tx_init();
	xdsl_ctrl_rx_init();
	#endif

	#if defined(CONFIG_XDSL_CTRL_ON_DSL) || defined(CONFIG_BONDING_MASTER_ON_SOC)
	#if defined(CONFIG_PTM_BONDING_SLAVE)
	mutex_init(&slave_dsl_mutex);
	#endif
	xdsl_dev[0] = filp_open("/dev/adsl0", O_RDONLY, 0);
	if (IS_ERR(xdsl_dev[0])) {
		err = PTR_ERR(xdsl_dev[0]);
		printk("dev adsl0: unable to open, waiting dsllib bring up\n");
	} 

	ptm_dev[0] = filp_open("/dev/ptm", O_RDONLY, 0);
	if (IS_ERR(ptm_dev[0])) {
		err = PTR_ERR(ptm_dev[0]);
		printk("dev ptm: unable to open, waiting dsllib bring up\n");
	} 
	#endif

	#if defined(CONFIG_XDSL_CTRL_ON_DSL)
	xdsl_ctrl_rx_init();
	#endif

#if defined(CONFIG_PTM_BONDING_SLAVE)
	erbTester_proc_init();
#endif

	xdsl_ctrl_setup();

	return 0;
out_class:
	#if defined(CONFIG_XDSL_CTRL_ON_SOC)
	for (i=0; i<XDSL_DEV_NUM; i++){
		unregister_chrdev(xdsl_major_num[i], xdsl_dev_name[i]);
	}
	
	for (i=0; i<PTM_DEV_NUM; i++){
		unregister_chrdev(ptm_major_num[i], ptm_dev_name[i]);
	}
	
	#endif
	printk("xdsl_ctrl module init fail\n");
	
	return -1;
}


static void __exit xdsl_dev_exit(void)
{
}

module_init(xdsl_dev_init);
module_exit(xdsl_dev_exit);