blob: b45eb5cde1a6445a1b6bde7997bd0b3cd8c378ca [file] [log] [blame]
/*******************************************************************************
* SUBSYSTEM£º User Line Driver
* MOUDLE: USL
* FILE NAME: usr_line.c
* PURPOSE: To scan and control the user line(slic/daa).
* Author: jiang.yuelong
* Version£º 1.0
* Date£º 24/12/2004
*------------------------------------------------------------------------------
* DEVELOPMENT HISTORY
* Date Version Modifier Description of Mpdify
* mm/dd/yyyy x.x X XX xxxxxxxxxxxxx
*******************************************************************************/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/init.h>
#include <linux/list.h>
#include <linux/slab.h>
#include <linux/signal.h>
//#include <linux/smp_lock.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <linux/delay.h>
#include <linux/version.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
#include <mach/gpio.h>
#include <linux/irqreturn.h>
#include <mach/pcu.h>
#include <linux/irq.h>
#include <linux/platform_device.h>
#include <linux/device.h>
#include <linux/wakelock.h>
#include <linux/soc/zte/pm/drv_idle.h>
#include "usr_line.h"
#include "si3217x_constants.h"
//#include <linux/soc/zte/rpm/rpmsg.h>
#include <mach/gpio_cfg.h>
#include "i2s.h"
/* ---- Public Variables ------------------------------------------------- */
USL_PORT *pstUslPort = NULL; /* global USL_PORT pointer */
u8 si_usl_debuglvl = 3; /* control whether to print message to serial port, init to enable printk */
int dtmf_mute = 0;
int slic_pcm_open = 0;
struct spi_device *pslicSpi;
static struct class *slic_dev_class;
static struct device *slic_device;
static int fskbuf_avail_flag = 0;
/* ---- Private Variables ------------------------------------------------ */
static u8 *pIoctlData = NULL; /* global pointer for ioctl parameters */
static USL_MSG *msg_queue;
static DECLARE_WAIT_QUEUE_HEAD(msg_wait_queue); /* for message report */
static u8 timer_run = 1;
static u8 scan_over = 0;
static u8 self_test = 0;
u8 slic_offhook = 0;
u8 init_flg = 0;
slic_state current_state = NONE;
int irq_num = 0;
#define MAX_INT_STRINGS 38
#define FSK_DEPTH_TRIG 4
#define SLIC_INT_GPIO ZX29_GPIO_53
#define FNC_SLIC_INT_GPIO GPIO53_EXT_INT6
#define SLIC_RST_GPIO ZX29_GPIO_122
#define FNC_SLIC_RST_GPIO GPIO122_GPIO122
static struct wake_lock slic_wakelock;
static char *intMapStrings[] =
{
"IRQ_OSC1_T1",
"IRQ_OSC1_T2",
"IRQ_OSC2_T1",
"IRQ_OSC2_T2",
"IRQ_RING_T1",
"IRQ_RING_T2",
"IRQ_PM_T1",
"IRQ_PM_T2",
"IRQ_FSKBUF_AVAIL", /**< FSK FIFO depth reached */
"IRQ_VBAT",
"IRQ_RING_TRIP", /**< Ring Trip detected */
"IRQ_LOOP_STATUS", /**< Loop Current changed */
"IRQ_LONG_STAT",
"IRQ_VOC_TRACK",
"IRQ_DTMF", /**< DTMF Detected - call @ref ProSLIC_DTMFReadDigit to decode the value */
"IRQ_INDIRECT", /**< Indirect/RAM access completed */
"IRQ_TXMDM",
"IRQ_RXMDM",
"IRQ_PQ1", /**< Power alarm 1 */
"IRQ_PQ2", /**< Power alarm 2 */
"IRQ_PQ3", /**< Power alarm 3 */
"IRQ_PQ4", /**< Power alarm 4 */
"IRQ_PQ5", /**< Power alarm 5 */
"IRQ_PQ6", /**< Power alarm 6 */
"IRQ_RING_FAIL",
"IRQ_CM_BAL",
"IRQ_USER_0",
"IRQ_USER_1",
"IRQ_USER_2",
"IRQ_USER_3",
"IRQ_USER_4",
"IRQ_USER_5",
"IRQ_USER_6",
"IRQ_USER_7",
"IRQ_DSP",
"IRQ_MADC_FS",
"IRQ_P_HVIC",
"IRQ_P_THERM", /**< Thermal alarm */
"IRQ_P_OFFLD"
};
/*--------extern variables---------------------------------------------*/
/* ---- Public functions ------------------------------------------------- */
/* ---- Private functions ------------------------------------------------ */
static s8 SlicMallocMemory(void); /* */
static void SlicFreeMemory(void); /* */
static void zx29_i2s_tdm_pin_cfg(void);
//volatile static T_ZDrvRpMsg_Msg icp_pMsg;
volatile static int rpMsgBuf[8] = {1,2};
/*--------extern functions---------------------------------------------*/
/*---------------------func define--------------------------------*/
/*
*****************************************************************************
** FUNCTION: SlicMallocMemory
**
** PURPOSE: request memory for global variables pstUslPort and pIoctlData,
** used in si_usrline_init only
**
** PARAMETERS: none
**
** RETURNS: 0 on success, else -1
**
*****************************************************************************
*/
static s8 SlicMallocMemory(void)
{
/* USL_PORT used */
pstUslPort = (USL_PORT *)kmalloc(sizeof(USL_PORT)*MAX_PORT_NUM, GFP_KERNEL);
if (NULL != pstUslPort)
{
memset(pstUslPort, 0, sizeof(USL_PORT)*MAX_PORT_NUM);
}
else
{
return -1;
}
/* ioctl used */
pIoctlData = (u8 *)kmalloc(512, GFP_KERNEL);
if (NULL != pIoctlData)
{
memset(pIoctlData, 0, 512);
}
else
{
kfree(pstUslPort);
return -1;
}
return 0;
}
/*
*****************************************************************************
** FUNCTION: SlicFreeMemory
**
** PURPOSE: free memory requested by SlicMallocMemory
**
** PARAMETERS: none
**
** RETURNS: none
**
*****************************************************************************
*/
static void SlicFreeMemory(void)
{
if (NULL != pstUslPort)
{
kfree(pstUslPort);
}
pstUslPort = NULL;
if (NULL != pIoctlData)
{
kfree(pIoctlData);
}
pIoctlData = NULL;
if (NULL != msg_queue )
{
kfree(msg_queue);
}
msg_queue = NULL ;
return;
}
void CreatMsgQueue(void)
{
msg_queue = kzalloc(sizeof(USL_MSG),GFP_KERNEL);
}
bool QueueEmpty(void)
{
return msg_queue->tail == msg_queue->head;
}
bool QueueFull(void)
{
return (msg_queue->tail + 1)%MSGMAX == msg_queue->head;
}
s8 usrline_report(const u16 port, const u8 event, const u32 payload,u16 mask)
{
USLPUT1("->%s: port %d, ev %d, pay %d, mask %x.\n", __FUNCTION__, port, event, payload, mask );
if( GET_EV_MASK(event) & mask ) {
USLPUT1("ev %x, ~mask%x\n", GET_EV_MASK(event), mask );
return 0;
}
if (QueueFull())
{
USLPUT0(" usrline_report msgqueue full!!( event=%d,port=%d,payload=%d). \n", event, port, payload);
return -1;
}
/* add to queue tail */
//msg_queue->data[msg_queue->tail].port = port;
msg_queue->data[msg_queue->tail].msgid = event;
msg_queue->data[msg_queue->tail].payload = payload;
msg_queue->tail = (msg_queue->tail + 1)%MSGMAX;
/* wake up messages wait queue*/
wake_up_interruptible(&msg_wait_queue);
USLPUT1("\nevent:%d port:%d payload:0x%x\n", event, port, payload);
return 0;
}
static s8 usrline_ioctl_msg_rev(USL_PORT *data, SLIC_IOCTL_DATA *pstCmd)
{
printk("howard usrline_ioctl_msg_rev\n");
if (NULL == pstCmd)
{
USLPUT0("%s pstCmd NULL\n",__FUNCTION__);
return SLC_FAIL;
}
if (QueueEmpty()) /* no message. sleep */
{
interruptible_sleep_on(&msg_wait_queue);
}
while (self_test)
{
sleep_on_timeout(&msg_wait_queue, 10);
}
if (copy_to_user(pstCmd->data, &msg_queue->data[msg_queue->head], sizeof(MSG_DATA)) != 0)
{
USLPUT0("%s copy data to user fail!\n",__FUNCTION__);
}
msg_queue->head = (msg_queue->head + 1)%MSGMAX;
return SLC_SUCCESS;
}
static s8 usrline_ioctl_msg_clear(USL_PORT *data, SLIC_IOCTL_DATA *pstCmd)
{
if (NULL != msg_queue)
{
memset(msg_queue,0,sizeof(USL_MSG));
}
return SLC_SUCCESS;
}
s8 usrline_port_register( const u16 port, const u8 type,
CODEC_OPS *ops, Port_t *pchip)
{
USL_PORT *ptr;
if (NULL == pstUslPort)
{
USLPUT0("usrline_port_register pstUslPort NULL!\n");
return -1;
}
if (port >= MAX_PORT_NUM)
{
USLPUT0("usrline_port_register port %d illegal\n", port);
return -1;
}
if ((NULL == ops) || (NULL == pchip))
{
USLPUT0("usrline_port_register input pointer NULL!\n");
return -1;
}
ptr = (USL_PORT *)&pstUslPort[port];
if (NULL == ptr)
{
USLPUT0("usrline_port_register get port Para NULL!\n");
return -1;
}
ptr->port = port;
ptr->port_type = type;
ptr->codec_ops = ops;
ptr->pLine = pchip;
ptr->event_mask = 0;
ptr->signal_flag = SIGNAL_PLAY_STOP;
ptr->signal_on = RING_SIGNAL_OFF;
/*added for ring queue*/
ptr->stRingQuenePara.dwRingStop = 0;
ptr->stRingQuenePara.dwNeedSort = 0;
ptr->stRingQuenePara.dwOffCountStart = 0;
ptr->stRingQuenePara.dwOffCount = 0;
ptr->stRingQuenePara.dwOffMaxCount = 0;
ptr->stRingQuenePara.RingQueneDelay = 10;
ptr->dwIsRevPol = 0;
ptr->dwInitOK = LINE_INITOK;
ptr->flag = LINE_ENABLE;
return 0;
}
static void uslput_sig(const SIGNAL_DATA *sig)
{
USLPUT3("signal_type:%d cid:%s tone_type:%d\n",
sig->signal_type,sig->cid,sig->tone_type);
}
/* ================================================================== */
static s8 usrline_ioctl_Inf_precfg(USL_PORT *data, SLIC_IOCTL_DATA *pstCmd)
{
if (NULL == pstCmd)
{
USLPUT0("%s pstCmd NULL\n",__FUNCTION__);
return -1;
}
if (copy_from_user(pIoctlData, pstCmd->data, pstCmd->data_size) != 0)
{
USLPUT0("%s copy data from user fail!\n",__FUNCTION__);
return -1;
}
slic_inf_precfg((SLCINF_CFG *)pIoctlData);
return 0;
}
static unsigned char checkBufSum(char *str, int nLen)
{
int i=0;
uInt8 sum = 0;
for(i=0; i<nLen; i++)
{
sum += str[i];
LOGPRINT ("\nsum=%02X", sum);
}
return -sum;
}
static int WaitOnFSKBuffer (proslicChanType *pProslic){
uInt8 avail;
uInt32 i=0;
uInt8 hook_status = 0;
ProSLIC_ReadHookStatus(pProslic,&hook_status);
if(hook_status == PROSLIC_OFFHOOK)
{
ProSLIC_DisableCID(pProslic);
return -1;
}
do {
if (fskbuf_avail_flag == 1)
break;
ProSLIC_CheckCIDBuffer(pProslic,&avail);
i++;
}
while ((avail == 0)&& (i<20000));
fskbuf_avail_flag = 0;
if(i>=20000)
{
printk("\nWaitOnFSKBuffer Time = %d", i);
}
//return 0;
}
static int DoOnHookCallerIDEx (proslicChanType *pProslic, int8 *BufTel, int8 *BufTxt)
{ //SI324X/26 example code--------
char sTime[] = "01020304";
unsigned char preamble[] ={'U','U','U','U','U','U','U','U'};
int i;uInt8 data;
int j;
unsigned char TxBuf[256];
unsigned char *pTxBuf=TxBuf;
int nTxLen;
int nNumLen = strlen(BufTel);
int nTxtLen = strlen(BufTxt);
uInt8 hook_status = 0;
// Êý¾Ý×é°ü
*pTxBuf++ = 0x80; //msg type: ¸´ºÏÊý¾Ý¸ñʽ
if(1)
{
if(nNumLen==0)
{
*pTxBuf++ = (2+8)+(2+nTxtLen); //msg length
}
else if(nTxtLen==0)
{
*pTxBuf++ = (2+8)+(2+nNumLen); //msg length
}
else
{
*pTxBuf++ = (2+8)+(2+nNumLen)+(2+nTxtLen); //msg length
}
*pTxBuf++ = 0x01;//²ÎÊýÀàÐÍ £ºÈÕÆÚʱ¼ä
*pTxBuf++ = 0x08;//ʱ¼ä³¤¶È
for(i=0; i<8; i++)
{
*pTxBuf++ = sTime[i];
}
}
else
{
//MSG_FATAL("-zja--Don't SENDING current_time",0,0,0);
*pTxBuf++ = (2+nNumLen)+(2+nTxtLen); //msg length
}
if(nNumLen!=0)
{
*pTxBuf++ =0x02; //²ÎÊýÀàÐÍ £ºµç»°ºÅÂë
*pTxBuf++ =nNumLen; //²ÎÊý³¤¶È
memcpy(pTxBuf, BufTel, nNumLen);
pTxBuf += nNumLen;
}
if(nTxtLen!=0)
{
*pTxBuf++=0x07; //²ÎÊýÀàÐÍ £ºÖ÷½ÐÐÅÏ¢
*pTxBuf++=nTxtLen; //²ÎÊýÀàÐÍ £ºµç»°ºÅÂë
memcpy(pTxBuf, BufTxt, nTxtLen);
pTxBuf += nTxtLen;
}
*pTxBuf++=(checkBufSum(TxBuf, pTxBuf-TxBuf));
nTxLen = pTxBuf-TxBuf;
printk("\n Len=%d", nTxLen);
for(i=0; i<nTxLen; i++)
{
if((i&0x0F)==0)
printk("\n");
printk("0x%02X ", TxBuf[i]);
}
printk("\n");
/* The setting for FSKDEPTH will depend on your system contraints*/
//fire interrupt when FSK_DEPTH_TRIG bytes of space, set FSKDEPTH to 8-FSK_DEPTH_TRIG
ProSLIC_ReadHookStatus(pProslic,&hook_status);
if(hook_status == PROSLIC_OFFHOOK)
{
return -1;
}
ProSLIC_FSKSetup (pProslic, 1);
if (pProslic->debugMode)
LOGPRINT ("\nSending CID to channel %d\n",pProslic->channel);
(pProslic->deviceId->ctrlInterface)->Delay_fptr((pProslic->deviceId->ctrlInterface)->hTimer,50);
// (pProslic->deviceId->ctrlInterface)->Delay_fptr((pProslic->deviceId->ctrlInterface)->hTimer,130); //130ms of mark bits
ProSLIC_EnableCID(pProslic);
for (i=0;i<30;i+=FSK_DEPTH_TRIG)
{
/*if (WaitOnFSKBuffer (pProslic))
{
//printk("howard offhook during ONHOOK FSK CID\n");
return -1;
}*/
if(i>=4)
{
WaitOnFSKBuffer (pProslic);
}
ProSLIC_SendCID(pProslic,preamble,FSK_DEPTH_TRIG);
}
if (30%FSK_DEPTH_TRIG)
{
/*if (WaitOnFSKBuffer (pProslic))
{
//printk("howard offhook during ONHOOK FSK CID\n");
return -1;
}*/
WaitOnFSKBuffer (pProslic);
}
ProSLIC_SendCID(pProslic,preamble,30%FSK_DEPTH_TRIG);
WaitOnFSKBuffer (pProslic);
/*if (WaitOnFSKBuffer (pProslic))
{
//printk("howard offhook during ONHOOK FSK CID\n");
return -1;
}*/
(pProslic->deviceId->ctrlInterface)->Delay_fptr((pProslic->deviceId->ctrlInterface)->hTimer,140); //wait for 1 byte then 130ms +/- 25ms mark bits
for (i=0;i<nTxLen; i+=FSK_DEPTH_TRIG){
/*if (WaitOnFSKBuffer (pProslic))
{
//printk("howard offhook during ONHOOK FSK CID\n");
return -1;
}*/
ProSLIC_SendCID (pProslic,&(TxBuf[i]),FSK_DEPTH_TRIG);
WaitOnFSKBuffer (pProslic);
}
if (nTxLen%FSK_DEPTH_TRIG)
{
/*if (WaitOnFSKBuffer (pProslic))
{
//printk("howard offhook during ONHOOK FSK CID\n");
return -1;
}*/
ProSLIC_SendCID (pProslic,&(TxBuf[(nTxLen/FSK_DEPTH_TRIG)*FSK_DEPTH_TRIG]),nTxLen%FSK_DEPTH_TRIG);
WaitOnFSKBuffer (pProslic);
}
(pProslic->deviceId->ctrlInterface)->Delay_fptr((pProslic->deviceId->ctrlInterface)->hTimer,10*(FSK_DEPTH_TRIG));
(pProslic->deviceId->ctrlInterface)->Delay_fptr((pProslic->deviceId->ctrlInterface)->hTimer,50); //130ms of mark bits
ProSLIC_DisableCID(pProslic);
}
void ring_sendcid(proslicChanType_ptr hProslic, int8 *cid)
{
/* Ensure OFFHOOK Active */
ProSLIC_SetLinefeedStatus(hProslic,LF_FWD_ACTIVE);
msleep(500);
/* 1st Ring Burst */
ProSLIC_SetLinefeedStatus(hProslic,LF_RINGING);
msleep(2500);/* Delay 250 to 3600ms */
printk("ring_sendcid %s\n", cid);
DoOnHookCallerIDEx(hProslic, cid, "");
//DoOnHookCallerIDEx(hProslic, "15029909468", "");
}
static s8 usrline_ioctl_signal_start(USL_PORT *data, SLIC_IOCTL_DATA *pstCmd)
{
s8 i = 0;
USL_PORT *pPort = NULL;
SIGNAL_DATA *sig = NULL;
int use_cid=1;
int cid_len = 0;
//CANDENCE_ATTR *cadc = NULL;
printk("howard usrline_ioctl_signal_start\n");
if ((NULL == data) || (NULL == pstCmd))
{
USLPUT0("%s data or pstCmd NULL\n",__FUNCTION__);
return -1;
}
USLPUT2("%s: port %d \n",__FUNCTION__, data->port );
pPort = (USL_PORT *)&pstUslPort[(data->port+1)%2];
sig = (SIGNAL_DATA *) &(data->sig_data);
//cadc = sig->cadence;
if (copy_from_user(sig, pstCmd->data, pstCmd->data_size) != 0)
{
USLPUT0("%s copy data from user fail!\n",__FUNCTION__);
return -1;
}
uslput_sig(sig);
/* init signal data */
/*added for ring queue*/
//data->stRingQuenePara.dwOffMaxCount = cadc->cadence_off - cadc->cadence_on - data->stRingQuenePara.RingQueneDelay;
if (USL_RING_SIGNAL == sig->signal_type)
{
printk("ring singnal\n");
data->stRingQuenePara.dwRingStop = 0;
if((NULL == pPort) /* no other port */
|| (USL_TONE_SIGNAL == pPort->sig_data.signal_type) /* other port is playing tone */
|| ((USL_RING_SIGNAL == pPort->sig_data.signal_type) && (SIGNAL_PLAY_STOP == pPort->signal_flag))) /* other port is ringing in off state */
//|| (cadc->cadence_off < cadc->cadence_on + data->stRingQuenePara.RingQueneDelay*2)) /* not match quene rule */
{
data->codec_ops->codec_signal_ctrl(data->pLine, sig, RING_SIGNAL_INIT);
data->signal_flag = SIGNAL_PLAY_START;
}
else /* need quene to ring */
{
//USLPUT2("stRingQuenePara.dwOffMaxCount = %d\n", data->stRingQuenePara.dwOffMaxCount);
data->stRingQuenePara.dwNeedSort = 1;
}
printk("howard ring and send cid in ioctl\n");
//sendCIDStream(data->pLine->ProObj);
cid_len = strlen(sig->cid);
printk("cid_len %d\n", cid_len);
for (i=0; i < cid_len; i++)
{
if((sig->cid[i]<0x30) || (sig->cid[i]>0x39))
{
use_cid = 0;
break;
}
}
if((use_cid == 1) && (cid_len > 0))
ring_sendcid(data->pLine->ProObj, sig->cid);
else
ProSLIC_RingStart(data->pLine->ProObj);
current_state = RINGING;
} else {
printk("tone singnal\n");
data->codec_ops->codec_signal_ctrl(data->pLine, sig, TONE_SIGNAL_INIT);
data->signal_flag = SIGNAL_PLAY_START;
data->stRingQuenePara.dwRingStop = 0;
}
/*ring queue add*/
return 0;
}
/*added for ring queue*/
static s8 usrline_signal_start(USL_PORT *data)
{
SIGNAL_DATA *sig = NULL;
if (NULL == data)
{
USLPUT0("%s data NULL\n",__FUNCTION__);
return -1;
}
sig = (SIGNAL_DATA *) &(data->sig_data);
USLPUT2("%s: port %d \n",__FUNCTION__, data->port );
data->codec_ops->codec_signal_ctrl(data->pLine, sig, RING_SIGNAL_INIT);
data->signal_flag = SIGNAL_PLAY_START;
data->stRingQuenePara.dwNeedSort = 0;
data->stRingQuenePara.dwOffCount = 0;
data->stRingQuenePara.dwOffCountStart = 0;
return 0;
}
/*ring queue add*/
/*added for ring queue*/
static s8 usrline_ioctl_signal_stop(USL_PORT *data, SLIC_IOCTL_DATA *pstCmd)
{
printk("howard usrline_ioctl_signal_stop\n");
if (NULL == data)
{
USLPUT0("%s data NULL\n",__FUNCTION__);
return -1;
}
USLPUT2("%s: port %d, signal type %d\n",__FUNCTION__, data->port, data->sig_data.signal_type);
if ((data->signal_flag == SIGNAL_PLAY_START)||(1==data->stRingQuenePara.dwNeedSort)) /* stop signal */
{
data->stRingQuenePara.dwNeedSort = 0;
data->stRingQuenePara.dwRingStop = 1;
}
return 0;
}
/*added for ring queue*/
static s8 usrline_signal_stop(USL_PORT *data)
{
u8 cmd = 0;
SIGNAL_DATA *sig = NULL;
if (NULL == data)
{
USLPUT0("%s data NULL\n",__FUNCTION__);
return -1;
}
sig = (SIGNAL_DATA *) &(data->sig_data);
data->signal_flag = SIGNAL_PLAY_STOP;
if (sig->signal_type == USL_RING_SIGNAL)
{
USLPUT0("ring singal stop\n");
data->signal_on = RING_SIGNAL_OFF;
cmd = RING_SIGNAL_CLEAN_OFF;
} else {
USLPUT0("tone singal stop\n");
cmd = TONE_SIGNAL_CLEAN;
}
data->codec_ops->codec_signal_ctrl(data->pLine, sig, cmd);
data->stRingQuenePara.dwRingStop = 0;
data->stRingQuenePara.dwNeedSort = 0;
data->stRingQuenePara.dwOffCountStart = 0;
data->stRingQuenePara.dwOffCount = 0;
return 0;
}
static s8 usrline_signal_play(USL_PORT *pdata)
{
SIGNAL_DATA *data = (SIGNAL_DATA *) &(pdata->sig_data);
u16 port = pdata->port;
u16 signal_type = data->signal_type;
u32 delay;
u8 signal_clean, signal_on, signal_off;
//data->tick_count++;
if (signal_type == USL_RING_SIGNAL)
{
if (RINGING != current_state)
{
signal_on = RING_SIGNAL_ON;
signal_off = RING_SIGNAL_OFF;
signal_clean = RING_SIGNAL_CLEAN_ON;
pdata->codec_ops->codec_signal_ctrl(pdata->pLine, data, signal_on);
pdata->signal_on = signal_on;
}
} else {
if (PLAYING_TONE != current_state)
{
signal_on = TONE_SIGNAL_ON;
signal_off = TONE_SIGNAL_OFF;
signal_clean = TONE_SIGNAL_CLEAN;
pdata->codec_ops->codec_signal_ctrl(pdata->pLine, data, signal_on);
pdata->signal_on = signal_on;
}
}
return 0;
}
static s8 usrline_ioctl_pcm_open(USL_PORT *data, SLIC_IOCTL_DATA *pstCmd)
{
proslicChanType_ptr hProslic;
hProslic = data->pLine->ProObj;
printk("howard usrline_ioctl_pcm_open\n");
ProSLIC_PCMStart(hProslic);
slic_pcm_open = 1;
return 0;
}
static s8 usrline_ioctl_pcm_close(USL_PORT *data, SLIC_IOCTL_DATA *pstCmd)
{
proslicChanType_ptr hProslic;
hProslic = data->pLine->ProObj;
printk("howard usrline_ioctl_pcm_close\n");
ProSLIC_PCMStop(hProslic);
slic_pcm_open = 0;
return 0;
}
static s8 usrline_ioctl_pcm_set_nb(USL_PORT *data, SLIC_IOCTL_DATA *pstCmd)
{
proslicChanType_ptr hProslic;
hProslic = data->pLine->ProObj;
printk("howard usrline_ioctl_pcm_set nb\n");
ProSLIC_PCMSetup(hProslic, PCM_16LIN);
return 0;
}
static s8 usrline_ioctl_pcm_set_wb(USL_PORT *data, SLIC_IOCTL_DATA *pstCmd)
{
proslicChanType_ptr hProslic;
hProslic = data->pLine->ProObj;
printk("howard usrline_ioctl_pcm_set wb\n");
ProSLIC_PCMSetup(hProslic, PCM_16LIN_WB);
return 0;
}
static s8 usrline_ioctl_timeslot_set(USL_PORT *data, SLIC_IOCTL_DATA *pstCmd)
{
if ((NULL == data) || (NULL == pstCmd))
{
USLPUT0("%s data or pstCmd NULL\n",__FUNCTION__);
return -1;
}
//data->codec_ops->codec_timeslot_set(data->pLine, pstCmd->unPara.stTimeSlot.bTx, pstCmd->unPara.stTimeSlot.bRx);
return 0;
}
static s8 usrline_ioctl_timeslot_release(USL_PORT *data, SLIC_IOCTL_DATA *pstCmd)
{
if (NULL == data)
{
USLPUT0("%s data NULL\n",__FUNCTION__);
return -1;
}
USLPUT2("%s: port %d \n",__FUNCTION__, data->port );
data->codec_ops->codec_timeslot_release( data->pLine );
return 0;
}
static s8 usrline_ioctl_codec_config(USL_PORT *data, SLIC_IOCTL_DATA *pstCmd)
{
if ((NULL == data) || (NULL == pstCmd))
{
USLPUT0("%s data or pstCmd NULL\n",__FUNCTION__);
return -1;
}
if (copy_from_user(pIoctlData, pstCmd->data, pstCmd->data_size) != 0)
{
USLPUT0("%s copy data from user fail!\n",__FUNCTION__);
return -1;
}
data->codec_ops->codec_parm_cfg(data->pLine , pIoctlData, pstCmd->data_size);
return 0;
}
static s8 usrline_ioctl_codec_read(USL_PORT *data, SLIC_IOCTL_DATA *pstCmd)
{
if ((NULL == data) || (NULL == pstCmd))
{
USLPUT0("%s data or pstCmd NULL\n",__FUNCTION__);
return -1;
}
if (copy_from_user(pIoctlData, pstCmd->data, pstCmd->data_size) != 0)
{
USLPUT0("%s copy data from user fail!\n",__FUNCTION__);
return -1;
}
data->codec_ops->codec_parm_get(data->pLine, pIoctlData, pstCmd->data_size);
return 0;
}
static s8 usrline_ioctl_ram_config(USL_PORT *data, SLIC_IOCTL_DATA *pstCmd)
{
if ((NULL == data) || (NULL == pstCmd))
{
USLPUT0("%s data or pstCmd NULL\n",__FUNCTION__);
return -1;
}
if (copy_from_user(pIoctlData, pstCmd->data, pstCmd->data_size) != 0)
{
USLPUT0("%s copy data from user fail!\n",__FUNCTION__);
return -1;
}
data->codec_ops->codec_ram_cfg(data->pLine, pIoctlData, pstCmd->data_size);
return 0;
}
static s8 usrline_ioctl_mute(USL_PORT *data, SLIC_IOCTL_DATA *pstCmd)
{
proslicChanType_ptr hProslic;
hProslic = data->pLine->ProObj;
if ((NULL == data) || (NULL == pstCmd))
{
USLPUT0("%s data or pstCmd NULL\n",__FUNCTION__);
return -1;
}
if (copy_from_user(pIoctlData, pstCmd->data, pstCmd->data_size) != 0)
{
USLPUT0("%s copy data from user fail!\n",__FUNCTION__);
return -1;
}
printk("howard slic usrline_ioctl_mute %s\n", pIoctlData);
if(*pIoctlData == 't')
{
ProSLIC_SetMuteStatus(hProslic,PROSLIC_MUTE_TX);
printk("howard slic mute tx\n");
}
else if(*pIoctlData == 'r')
{
ProSLIC_SetMuteStatus(hProslic,PROSLIC_MUTE_RX);
printk("howard slic mute rx\n");
}
else if(*pIoctlData == 'n')
{
ProSLIC_SetMuteStatus(hProslic,PROSLIC_MUTE_NONE);
printk("howard slic mute none\n");
}
return 0;
}
static s8 usrline_ioctl_ram_read(USL_PORT *data, SLIC_IOCTL_DATA *pstCmd)
{
u16 addr = 0;
if ((NULL == data) || (NULL == pstCmd))
{
USLPUT0("%s data or pstCmd NULL\n",__FUNCTION__);
return -1;
}
if (copy_from_user(pIoctlData, pstCmd->data, pstCmd->data_size) != 0)
{
USLPUT0("%s copy data from user fail!\n",__FUNCTION__);
return -1;
}
addr = *(u16 *)pIoctlData;
printk("SLIC usrline_ioctl_ram_read addr %d,size %d\n", addr, pstCmd->data_size);
data->codec_ops->codec_ram_get(data->pLine, pIoctlData, pstCmd->data_size);
return 0;
}
extern void ctrl_ReadRegister(uInt8 cs, uInt8 channel, uInt8 regAddr, uInt8 *prdata);
extern void ctrl_WriteRegister(uInt8 cs, uInt8 channel, uInt8 regAddr, uInt8 wdata);
static s8 usrline_ioctl_dev_init(USL_PORT *data, SLIC_IOCTL_DATA *pstCmd)
{
int ret = 0;
printk("howard usrline_ioctl_dev_init\n");
if (0 == init_flg)
{
ret = InitSlicChip();
if (0 == ret)
{
init_flg = 1;
printk("SLIC init complete\n");
}
else
{
init_flg = 0;
printk("SLIC init NOT complete\n");
}
/* disable printk after init */
si_usl_debuglvl = 0;
}
return SLC_SUCCESS;
}
static s8 usrline_ioctl_port_reset(USL_PORT *data, SLIC_IOCTL_DATA *pstCmd)
{
if (NULL == data)
{
USLPUT0("%s data NULL\n",__FUNCTION__);
return -1;
}
USLPUT2("%s: port %d \n",__FUNCTION__, data->port );
usrline_ioctl_timeslot_release(data, pstCmd);
if ((data->signal_flag == SIGNAL_PLAY_START)||(1==data->stRingQuenePara.dwNeedSort)) /* stop signal */
{
//usrline_signal_stop(data);
data->stRingQuenePara.dwNeedSort = 0;
data->stRingQuenePara.dwRingStop = 1;
}
data->dwIsRevPol = 0;
data->flag = LINE_DISABLE;
data->codec_ops->codec_reset(data->pLine, data->port);
data->flag = LINE_ENABLE;
return SLC_SUCCESS;
}
/*added for slc time cfg*/
static s8 usrline_ioctl_time_cfg(USL_PORT *data, SLIC_IOCTL_DATA *pstCmd)
{
if ((NULL == data) || (NULL == pstCmd))
{
USLPUT0("%s data or pstCmd NULL\n",__FUNCTION__);
return -1;
}
if (copy_from_user(pIoctlData, pstCmd->data, pstCmd->data_size) != 0)
{
USLPUT0("%s copy data from user fail!\n",__FUNCTION__);
return -1;
}
/* first save the ring quene time */
data->stRingQuenePara.RingQueneDelay = ((USL_CONFIG *)pIoctlData)->RingQueneDelay;
/* then save the chip related time parameters */
data->codec_ops->codec_time_cfg(data->pLine, (USL_CONFIG *)pIoctlData);
return SLC_SUCCESS;
}
static s8 usrline_ioctl_slctool_hooklowlen_cfg(USL_PORT *data, SLIC_IOCTL_DATA *pstCmd)
{
if ((NULL == data) || (NULL == pstCmd))
{
USLPUT0("%s data or pstCmd NULL\n",__FUNCTION__);
return -1;
}
//data->codec_ops->codec_slctool_time_cfg(data->pLine, SLIC_CFG_HOOK_LOWLEN, pstCmd->unPara.wTime);
return 0;
}
static s8 usrline_ioctl_slctool_hookhiglen_cfg(USL_PORT *data, SLIC_IOCTL_DATA *pstCmd)
{
if ((NULL == data) || (NULL == pstCmd))
{
USLPUT0("%s data or pstCmd NULL\n",__FUNCTION__);
return -1;
}
//data->codec_ops->codec_slctool_time_cfg(data->pLine, SLIC_CFG_HOOK_HIGLEN, pstCmd->unPara.wTime);
return 0;
}
static s8 usrline_ioctl_slctool_prehookhiglen_cfg(USL_PORT *data, SLIC_IOCTL_DATA *pstCmd)
{
if ((NULL == data) || (NULL == pstCmd))
{
USLPUT0("%s data or pstCmd NULL\n",__FUNCTION__);
return -1;
}
//data->codec_ops->codec_slctool_time_cfg(data->pLine, SLIC_CFG_PREHOOK_HIGLEN, pstCmd->unPara.wTime);
return 0;
}
static s8 usrline_ioctl_slctool_flashlowmin_cfg(USL_PORT *data, SLIC_IOCTL_DATA *pstCmd)
{
if ((NULL == data) || (NULL == pstCmd))
{
USLPUT0("%s data or pstCmd NULL\n",__FUNCTION__);
return -1;
}
//data->codec_ops->codec_slctool_time_cfg(data->pLine, SLIC_CFG_FLASH_LMIN, pstCmd->unPara.wTime);
return 0;
}
static s8 usrline_ioctl_slctool_flashlowmax_cfg(USL_PORT *data, SLIC_IOCTL_DATA *pstCmd)
{
if ((NULL == data) || (NULL == pstCmd))
{
USLPUT0("%s data or pstCmd NULL\n",__FUNCTION__);
return -1;
}
//data->codec_ops->codec_slctool_time_cfg(data->pLine, SLIC_CFG_FLASH_LMAX, pstCmd->unPara.wTime);
return 0;
}
static s8 usrline_ioctl_slctool_flashhfix_cfg(USL_PORT *data, SLIC_IOCTL_DATA *pstCmd)
{
if ((NULL == data) || (NULL == pstCmd))
{
USLPUT0("%s data or pstCmd NULL\n",__FUNCTION__);
return -1;
}
//data->codec_ops->codec_slctool_time_cfg(data->pLine, SLIC_CFG_FLASH_HFIX, pstCmd->unPara.wTime);
return 0;
}
static s8 usrline_ioctl_slctool_dialhmin_cfg(USL_PORT *data, SLIC_IOCTL_DATA *pstCmd)
{
if ((NULL == data) || (NULL == pstCmd))
{
USLPUT0("%s data or pstCmd NULL\n",__FUNCTION__);
return -1;
}
//data->codec_ops->codec_slctool_time_cfg(data->pLine, SLIC_CFG_DIAL_HMIN, pstCmd->unPara.wTime);
return 0;
}
static s8 usrline_ioctl_slctool_dialhmax_cfg(USL_PORT *data, SLIC_IOCTL_DATA *pstCmd)
{
if ((NULL == data) || (NULL == pstCmd))
{
USLPUT0("%s data or pstCmd NULL\n",__FUNCTION__);
return -1;
}
//data->codec_ops->codec_slctool_time_cfg(data->pLine, SLIC_CFG_DIAL_HMAX, pstCmd->unPara.wTime);
return 0;
}
static s8 usrline_ioctl_slctool_diallmin_cfg(USL_PORT *data, SLIC_IOCTL_DATA *pstCmd)
{
if ((NULL == data) || (NULL == pstCmd))
{
USLPUT0("%s data or pstCmd NULL\n",__FUNCTION__);
return -1;
}
//data->codec_ops->codec_slctool_time_cfg(data->pLine, SLIC_CFG_DIAL_LMIN, pstCmd->unPara.wTime);
return 0;
}
static s8 usrline_ioctl_slctool_diallmax_cfg(USL_PORT *data, SLIC_IOCTL_DATA *pstCmd)
{
if ((NULL == data) || (NULL == pstCmd))
{
USLPUT0("%s data or pstCmd NULL\n",__FUNCTION__);
return -1;
}
//data->codec_ops->codec_slctool_time_cfg(data->pLine, SLIC_CFG_DIAL_LMAX, pstCmd->unPara.wTime);
return 0;
}
static s8 usrline_ioctl_dial_start(USL_PORT *data, SLIC_IOCTL_DATA *pstCmd)
{
if (NULL == data)
{
USLPUT0("%s data NULL\n",__FUNCTION__);
return -1;
}
USLPUT3("%s port %d \n",__FUNCTION__,data->port);
data->codec_ops->codec_dial_set(data->pLine, EV_DIAL_START);
return SLC_SUCCESS;
}
static s8 usrline_ioctl_dial_stop(USL_PORT *data, SLIC_IOCTL_DATA *pstCmd)
{
if (NULL == data)
{
USLPUT0("%s data NULL\n",__FUNCTION__);
return -1;
}
USLPUT3("%s port %d \n",__FUNCTION__,data->port);
data->codec_ops->codec_dial_set(data->pLine, EV_DIAL_STOP);
return SLC_SUCCESS;
}
#ifdef POWER_SUPPLY_05A
/*used for dinggasp deal*/
extern u8 (*slic_function)(void);
/*
function: return ring state
return : zero: not ring;
none zero: is ring
*/
u8 slic_is_ring_state(void)
{
USL_PORT *data = NULL;
u8 done_line = 0;
int i = 0;
for ( i = 0; i < MAX_PORT_NUM; i++ )
{
data = (USL_PORT *)&pstUslPort[i];
if((NULL == data) || (LINE_DISABLE == data->flag))
{
continue;
}
if (RING_SIGNAL_ON == data->signal_on)
{
data->stRingQuenePara.dwRingStop = 1;
done_line++;
}
}
USLPUT3("done_line = %d.\n",done_line);
return done_line;
}
#endif
static s8 usrline_ioctl_port_lock(USL_PORT *data, SLIC_IOCTL_DATA *pstCmd)
{
if (NULL == data)
{
USLPUT0("%s data NULL\n",__FUNCTION__);
return -1;
}
USLPUT2("%s: port %d \n",__FUNCTION__, data->port);
data->flag = LINE_DISABLE;
return 0;
}
static s8 usrline_ioctl_port_unlock(USL_PORT *data, SLIC_IOCTL_DATA *pstCmd)
{
if (NULL == data)
{
USLPUT0("%s data NULL\n",__FUNCTION__);
return -1;
}
USLPUT2("%s: port %d \n",__FUNCTION__, data->port);
data->flag = LINE_ENABLE;
return 0;
}
static s8 usrline_ioctl_polarity_reverse(USL_PORT *data, SLIC_IOCTL_DATA *pstCmd)
{
if (NULL == data)
{
USLPUT0("%s data NULL\n",__FUNCTION__);
return -1;
}
USLPUT2("%s: port %d \n",__FUNCTION__, data->port);
data->codec_ops->codec_polarity_reverse(data->pLine,data->port);
data->dwIsRevPol ^= 1;
return 0;
}
static s8 usrline_ioctl_dtmf_start(USL_PORT *data, SLIC_IOCTL_DATA *pstCmd)
{
if (NULL == data)
{
USLPUT0("%s data NULL\n",__FUNCTION__);
return -1;
}
data->codec_ops->codec_signal_ctrl(data->pLine, NULL, RING_SIGNAL_OFF_REVERSED);
USLPUT2("%s : port %d \n",__FUNCTION__, data->port);
return 0;
}
static s8 usrline_ioctl_fsk_start(USL_PORT *data, SLIC_IOCTL_DATA *pstCmd)
{
if (NULL == data)
{
USLPUT0("%s data NULL\n",__FUNCTION__);
return -1;
}
USLPUT2("%s : port %d ",__FUNCTION__, data->port);
if (0 == data->dwIsRevPol)
{
USLPUT2("set fsk OHT\n");
data->codec_ops->codec_signal_ctrl(data->pLine, NULL, RING_SIGNAL_OFF);
}
else
{
USLPUT2("set dtmf OHTREV\n");
data->codec_ops->codec_signal_ctrl(data->pLine, NULL, RING_SIGNAL_OFF_REVERSED);
}
return 0;
}
static s8 usrline_ioctl_fsk_stop(USL_PORT *data, SLIC_IOCTL_DATA *pstCmd)
{
if (NULL == data)
{
USLPUT0("%s data NULL\n",__FUNCTION__);
return -1;
}
USLPUT2("%s : port %d \n",__FUNCTION__, data->port);
data->codec_ops->codec_signal_ctrl(data->pLine, NULL, RING_SIGNAL_CLEAN_ON);
return 0;
}
static s8 usrline_ioctl_set_debuglvl(USL_PORT *data, SLIC_IOCTL_DATA *pstCmd)
{
USL_PORT *pUslPort = NULL;
int i = 0;
if (NULL == pstCmd)
{
USLPUT0("%s pstCmd NULL\n",__FUNCTION__);
return -1;
}
//si_usl_debuglvl = pstCmd->unPara.wLevel;
if(si_usl_debuglvl > 2)
{
for ( i = 0; i < MAX_PORT_NUM; i++ )
{
pUslPort = (USL_PORT *)&pstUslPort[i];
if((NULL == pUslPort) || (LINE_DISABLE == pUslPort->flag))
{
continue;
}
USLPUT0("port: %d\n",pUslPort->port);
/* print the chip relative time parameters */
pUslPort->codec_ops->codec_time_print(pUslPort->pLine);
USLPUT0("ring_quene: %d\n",pUslPort->stRingQuenePara.RingQueneDelay);
}
}
return 0;
}
static s8 usrline_ioctl_start_test(USL_PORT *data, SLIC_IOCTL_DATA *pstCmd)
{
if ((NULL == data) || (NULL == pstCmd))
{
USLPUT0("%s data or pstCmd NULL\n",__FUNCTION__);
return -1;
}
if (copy_from_user(pIoctlData, pstCmd->data, pstCmd->data_size) != 0)
{
USLPUT0("%s copy data from user fail!\n",__FUNCTION__);
return -1;
}
data->event_mask = ~(GET_EV_MASK(EV_FXS_TEST_DONE) | GET_EV_MASK(EV_FXS_TEST_ERROR));
if (-1 == data->codec_ops->codec_start_test(data->pLine, (WriteCmd_t *)pIoctlData))
{
data->event_mask = 0;
USLPUT0("%s test start error\n",__FUNCTION__);
}
return 0;
}
static s8 usrline_ioctl_stop_test(USL_PORT *data, SLIC_IOCTL_DATA *pstCmd)
{
if (NULL == data)
{
USLPUT0("%s data NULL\n",__FUNCTION__);
return -1;
}
data->codec_ops->codec_stop_test(data->pLine);
data->event_mask = 0;
USLPUT2("%s:port %d.\n", __FUNCTION__, data->port);
return 0;
}
static s8 usrline_ioctl_read_test_result(USL_PORT *data, SLIC_IOCTL_DATA *pstCmd)
{
TestResult_t *stResult = (TestResult_t *)pIoctlData;
if ((NULL == data) || (NULL == pstCmd))
{
USLPUT0("%s data or pstResult NULL\n",__FUNCTION__);
return -1;
}
data->codec_ops->codec_read_reslult(data->pLine, stResult);
data->event_mask = 0;
USLPUT3("%s:port %d.\n", __FUNCTION__, data->port);
USLPUT3("112 test result:");
USLPUT3("port %d\n", stResult->port);
USLPUT3("port_type %d\n", stResult->port_type);
USLPUT3("item %d\n", stResult->item);
USLPUT3("obligate %d\n", stResult->obligate);
USLPUT3("num %ld\n", stResult->num);
USLPUT3("omci_item %d\n", stResult->omci_item);
USLPUT3("flg %d\n", stResult->flg);
USLPUT3("user_flg %d\n", stResult->user_flg);
USLPUT3("err_num %d\n", stResult->err_num);
USLPUT3("Vac_tr %ld\n", stResult->vac_tr);
USLPUT3("vac_tg %ld\n", stResult->vac_tg);
USLPUT3("vac_rg %ld\n", stResult->vac_rg);
USLPUT3("Vdc_tr %ld\n", stResult->vdc_tr);
USLPUT3("vdc_tg %ld\n", stResult->vdc_tg);
USLPUT3("vdc_rg %ld\n", stResult->vdc_rg);
USLPUT3("res_tr %ld\n", stResult->res_tr);
USLPUT3("res_tg %ld\n", stResult->res_tg);
USLPUT3("res_rg %ld\n", stResult->res_rg);
USLPUT3("cap_tr %ld\n", stResult->cap_tr);
USLPUT3("cap_tg %ld\n", stResult->cap_tg);
USLPUT3("cap_rg %ld\n", stResult->cap_rg);
USLPUT3("ring_vol %ld\n", stResult->ring_vol);
USLPUT3("Hz %ld\n", stResult->Hz);
USLPUT3("ren %ld\n", stResult->ren);
USLPUT3("loop_curent %ld\n",stResult->loop_curent);
USLPUT3("loop_res %ld\n", stResult->loop_res);
USLPUT3("battary %ld\n", stResult->battary);
if (copy_to_user(pstCmd->data, stResult, pstCmd->data_size) != 0)
{
USLPUT0("%s copy data to user fail!\n",__FUNCTION__);
}
return 0;
}
static s8 usrline_ioctl_electric_cfg(USL_PORT *data, SLIC_IOCTL_DATA *pstCmd)
{
if ((NULL == data) || (NULL == pstCmd))
{
USLPUT0("%s data or pstCmd NULL\n",__FUNCTION__);
return -1;
}
if (copy_from_user(pIoctlData, pstCmd->data, pstCmd->data_size) != 0)
{
USLPUT0("%s copy data from user fail!\n",__FUNCTION__);
return -1;
}
data->codec_ops->codec_electric_cfg(data->pLine, data->port, (ELECTRIC_CFG_CUSTOMIZED *)pIoctlData);
return 0;
}
/*ring queue add by chenjian*/
void usrline_ring_ctrl(USL_PORT *usl_port)
{
USL_PORT *data = NULL;
data = (USL_PORT *)&pstUslPort[(usl_port->port+1)%2];
if(1 == usl_port->stRingQuenePara.dwNeedSort)
{
if ((NULL == data) || (SIGNAL_PLAY_STOP == data->signal_flag))
{
usrline_signal_start(usl_port);
}
else if ((data->stRingQuenePara.RingQueneDelay <= data->stRingQuenePara.dwOffCount) && (data->stRingQuenePara.dwOffCount <= data->stRingQuenePara.dwOffMaxCount))
{
usrline_signal_start(usl_port);
}
}
if (1==usl_port->stRingQuenePara.dwRingStop)
{
usrline_signal_stop(usl_port);
}
return;
}
/*ring queue add by chenjian*/
static int usrline_scan(void* info)
{
USL_PORT *usl_port = NULL;
sigset_t blocked;
u16 port = 0;
//lock_kernel();
sprintf(current->comm, "usl_scan"); /* comm is 16 bytes */
daemonize("usl_scan");
#if 1
/* Block and flush all signals */
sigfillset(&blocked);
sigprocmask(SIG_BLOCK, &blocked, NULL);
flush_signals(current);
#endif
/* real time task FIFO */
current->policy = SCHED_FIFO;
//unlock_kernel();
printk("howard usrline_scan, HZ is %d\n", HZ);
while (timer_run)
{
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(10);
//if(slic_offhook){
//schedule_timeout(10);
//printk("howard usrline_scan while\n");
for (port = 0; port < MAX_PORT_NUM; port++)
{
usl_port = (USL_PORT *)&pstUslPort[port];
if((NULL == usl_port) || (LINE_DISABLE == usl_port->flag))
{
continue;
}
usrline_ring_ctrl(usl_port);
if (SIGNAL_PLAY_START == usl_port->signal_flag)
{
usrline_signal_play(usl_port);
}
usl_port->codec_ops->codec_scan(usl_port);
//}
}
} /* while () */
USLPUT0("User Line scan thread exit\n\r");
scan_over = 1;
return 0;
}
/* ==================================================================== */
static int usrline_open(struct inode *inode, struct file *filp)
{
//MOD_INC_USE_COUNT;
return 0;
}
static int usrline_release(struct inode *inode, struct file *filp)
{
// MOD_DEC_USE_COUNT;
return 0;
}
static void irq_handle_interrupt(USL_PORT *data,
ProslicInt interrupt, int *hook_det)
{
proslicChanType_ptr hProslic;
hProslic = data->pLine->ProObj;
switch(interrupt)
{
case IRQ_LOOP_STATUS:
ProSLIC_ReadHookStatus(hProslic,hook_det);
if(*hook_det == PROSLIC_OFFHOOK)
{
slic_offhook = 1;
printk("OFFHOOK\n");
}
else
{
slic_offhook = 0;
printk("ONHOOK\n");
}
break;
case IRQ_P_HVIC:
case IRQ_P_THERM:
printk("IRQ_P_HVIC or IRQ_P_THERM detect, set linefeed FWD_ACTIVE\n");
ProSLIC_SetLinefeedStatus(hProslic,LF_FWD_ACTIVE);
break;
case IRQ_DTMF:
{
unsigned char digit;
char digit_char;
int ret = 0;
//ProSLIC_SetMuteStatus(hProslic,PROSLIC_MUTE_TX);
if(slic_pcm_open == 1)
{
//ret = zDrvRpMsg_Write(&icp_pMsg);
CPPS_FUNC(cpps_callbacks, zDrvVp_SetDtmfMute_Wrap)();
// printk("DTMF zDrvRpMsg_Write ret %d\n",ret);
}
dtmf_mute = 1;
ProSLIC_DTMFReadDigit(hProslic, &digit);
if( (digit >=1) && (digit <= 9 ) )
{
digit_char = digit + '0';
}
else
{
if(digit == 0)
{
digit_char = 'D';
}
else
{
char digit_decode[] = "0*#ABC";
digit_char = digit_decode[digit - 10];
}
}
printk("detected dtmf-%c\n", digit_char);
usrline_report(data->port,EV_FXS_COLLECT_DIG,digit_char, data->event_mask);
}
break;
default:
break;
}
}
static int irq_check_interrupts(USL_PORT *data, int *hook_det)
{
proslicIntType irqs;
ProslicInt arrayIrqs[MAX_PROSLIC_IRQS];
proslicChanType_ptr hProslic;
hProslic = data->pLine->ProObj;
irqs.irqs = arrayIrqs;
if (ProSLIC_GetInterrupts(hProslic, &irqs) != 0)
{
unsigned int i;
/* Iterate through the interrupts and handle */
for(i=0 ; i<irqs.number; i++)
{
if (irqs.irqs[i] == 8)
fskbuf_avail_flag = 1;
/*
if (irqs.irqs[i] < MAX_INT_STRINGS)
{
printk("detected: %s\n", intMapStrings[irqs.irqs[i]]);
}
*/
irq_handle_interrupt(data,irqs.irqs[i], hook_det);
}
}
/*
if (irqs.number)
{
printk("\n");
}
*/
return irqs.number;
}
static int slic_get_gpio_state(int gpioNum,
unsigned int gpio_sel_gpio,unsigned int gpio_sel_int)
{
unsigned int gpio_state = GPIO_HIGH;
zx29_gpio_config(gpioNum, gpio_sel_gpio);
//pcu_clr_irq_pending(irq);
zx29_gpio_set_direction(gpioNum,GPIO_IN);
msleep(30);
gpio_state = gpio_get_value(gpioNum);
msleep(30);
zx29_gpio_config(gpioNum, gpio_sel_int);/******qhf***int****/
//pcu_clr_irq_pending(irq);
//printk(KERN_INFO "gpio state=%d.\n",gpio_state);
return gpio_state; /* 0: µÍµçƽ(press), 1:¸ßµçƽ(release) */
}
static irqreturn_t slic_int_irq(int irq, void *data)
{
int ret = IRQ_HANDLED;
int hook_changed = 0;
//int gpio_state = 0;
proslicChanType_ptr hProslic;
hProslic = ((USL_PORT *)data)->pLine->ProObj;
//gpio_state = slic_get_gpio_state(SLIC_INT_GPIO, GPIO74_GPIO74, GPIO74_EXT_INT12);
//printk("howard slic irq %d, clear pending\n", irq);
pcu_clr_irq_pending(irq);
if (1 == init_flg)
irq_check_interrupts((USL_PORT *)data, &hook_changed);
/* To be done */
return ret;
}
/*******************************************************************************
* Function: slic_int_irq_handler
* Description: clear irq , wake thread irq
* Parameters:
* Input:
* Output:
********************************************************************************/
static irqreturn_t slic_int_irq_handler(int irq, void *dev_id)
{
//disable_irq_nosync(irq);
//pcu_int_clear(irq);
//printk("howard slic int handler irq=%d.\n", irq);
pcu_clr_irq_pending(irq);
return IRQ_WAKE_THREAD;
}
#if 0
static int slic_create_rpmsg()
{
int ret = 0;
icp_pMsg.actorID = PS_ID;
icp_pMsg.buf = rpMsgBuf;
icp_pMsg.len = 8;
icp_pMsg.chID = ICP_CHANNEL_DTMF;
icp_pMsg.flag |= RPMSG_WRITE_INT;
ret = zDrvRpMsg_CreateChannel(PS_ID, ICP_CHANNEL_DTMF, 0x10);
printk("create_rpmsg ret %d\n", ret);
return ret;
}
#endif
/*********************************************************************************
*{usrline_ioctl_xxxx},func deals ioctl cmd, if not realized, please fill {NULL},
*must corresponding to cmd one by one
*********************************************************************************/
static const USRLINE_IOCTL_FUNC_MAP IoctlFuncMap[] =
{
{usrline_ioctl_dev_init}, /* SLIC_DEV_INIT */
{usrline_ioctl_msg_rev}, /* SLIC_MSG_REV */
{NULL}, /* SLIC_TEST */
{usrline_ioctl_signal_start}, /* SLIC_SIGNAL_START */
{usrline_ioctl_signal_stop}, /* SLIC_SIGNAL_STOP */
{usrline_ioctl_pcm_open}, /* SLIC_PCM_OPEN */
{usrline_ioctl_pcm_close}, /* SLIC_PCM_CLOSE */
{usrline_ioctl_pcm_set_nb}, /* SLIC_PCM_SET_NB */
{usrline_ioctl_pcm_set_wb}, /* SLIC_PCM_SET_WB */
{usrline_ioctl_Inf_precfg}, /* SLIC_INF_PRECFG */
{NULL}, /* SLIC_NOTUSED */
{usrline_ioctl_port_reset}, /* SLIC_PORT_RESET */
{usrline_ioctl_msg_clear}, /* SLIC_MSG_CLR */
{usrline_ioctl_dial_start}, /* SLIC_DIAL_START */
{usrline_ioctl_dial_stop}, /* SLIC_DIAL_STOP */
{usrline_ioctl_timeslot_set}, /* SLIC_TIMESLOT_SET */
{usrline_ioctl_timeslot_release}, /* SLIC_TIMESLOT_RELEASE */
{usrline_ioctl_port_lock}, /* SLIC_PORT_LOCK */
{usrline_ioctl_port_unlock}, /* SLIC_PORT_UNLOCK */
{usrline_ioctl_fsk_start}, /* SLIC_FSK_START */
{usrline_ioctl_fsk_stop}, /* SLIC_FSK_STOP */
{usrline_ioctl_polarity_reverse}, /* SLIC_POLARITY_REVERSE */
{usrline_ioctl_dtmf_start}, /* SLIC_DTMFCID_START */
{usrline_ioctl_start_test}, /* SLIC_LINE_TEST_START */
{usrline_ioctl_stop_test}, /* SLIC_LINE_TEST_ABORT */
{usrline_ioctl_read_test_result}, /* SLIC_LINE_TEST_READ */
{usrline_ioctl_time_cfg}, /* SLIC_TIMEPARA_CFG */
{usrline_ioctl_electric_cfg}, /* SLIC_ELECTRIC_CFG */
{usrline_ioctl_set_debuglvl}, /* SLIC_DEBUG_LEVEL */
{usrline_ioctl_slctool_hooklowlen_cfg}, /* SLIC_CFG_HOOK_LOWLEN */
{usrline_ioctl_slctool_hookhiglen_cfg}, /* SLIC_CFG_HOOK_HIGLEN */
{usrline_ioctl_slctool_flashlowmin_cfg}, /* SLIC_CFG_FLASH_LMIN */
{usrline_ioctl_slctool_flashlowmax_cfg}, /* SLIC_CFG_FLASH_LMAX */
{usrline_ioctl_slctool_flashhfix_cfg}, /* SLIC_CFG_FLASH_HFIX */
{usrline_ioctl_slctool_dialhmin_cfg}, /* SLIC_CFG_DIAL_HMIN */
{usrline_ioctl_slctool_dialhmax_cfg}, /* SLIC_CFG_DIAL_HMAX */
{usrline_ioctl_slctool_diallmin_cfg}, /* SLIC_CFG_DIAL_LMIN */
{usrline_ioctl_slctool_diallmax_cfg}, /* SLIC_CFG_DIAL_LMAX */
{NULL}, /* SLIC_CFG_RINGCEASE */
{usrline_ioctl_slctool_prehookhiglen_cfg}, /* SLIC_CFG_PREHOOK_HIGLEN */
{NULL}, /* SLIC_CFG_QUEUE_DELAY */
{usrline_ioctl_codec_read}, /* SLIC_CODEC_GET */
{usrline_ioctl_codec_config}, /* SLIC_CODEC_SET */
{usrline_ioctl_ram_read}, /* SLIC_RAM_GET */
{usrline_ioctl_ram_config}, /* SLIC_RAM_SET */
{usrline_ioctl_mute}, /* SLIC_RAM_SET */
{NULL}, /* SLIC_CODEC_GETALL */
{NULL}, /* SLIC_RAM_GETALL */
{NULL}, /* SLIC_GET_CHIP_NAME */
};
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36)
static long usrline_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
#else
static int usrline_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
#endif
{
s8 rev = -1;
USL_PORT *usl_port = NULL;
SLIC_IOCTL_DATA stCmd = {0};
printk("howard usrline_ioctl cmd=%d\n", cmd);
if((0 == init_flg) && (SLIC_DEV_INIT != cmd) && (SLIC_MSG_REV != cmd))
{
printk("SLIC init NOT complete\n");
return -1;
}
if (NULL != (void *)arg)
{
if (copy_from_user(&stCmd, (SLIC_IOCTL_DATA *)arg, sizeof(SLIC_IOCTL_DATA)) != 0)
{
USLPUT0("usrline_ioctl copy data from user fail!\n");
return rev;
}
#if 0
if (stCmd.port >= MAX_PORT_NUM)
{
USLPUT0("usrline_ioctl port:%d illegal, max:%d\n", stCmd.port, MAX_PORT_NUM-1);
return rev;
}
#endif
usl_port = (USL_PORT *)&pstUslPort[0];
//ProSLIC_Init_MultiBOM(&(pstUslPort->pLine->ProObj),1,3);
if (NULL == usl_port)
{
USLPUT0("usrline_ioctl usl_port NULL\n");
return rev;
}
if (LINE_DISABLE == usl_port->flag)
{
USLPUT0("usrline_ioctl port:0 is disable now!\n");
switch (cmd)
{
case SLIC_INF_PRECFG:
case SLIC_MSG_REV:
rev = IoctlFuncMap[cmd].pIoctlFunc(usl_port, &stCmd);
break;
case SLIC_PORT_UNLOCK:
if (LINE_INITOK == usl_port->dwInitOK)
{
rev = IoctlFuncMap[cmd].pIoctlFunc(usl_port, &stCmd);
}
break;
default:
rev = IoctlFuncMap[cmd].pIoctlFunc(usl_port, &stCmd);
break;
}
return rev;
}
}
if ((cmd >= 0) && (cmd < SLIC_IOCTL_CMD_MAX))
{
if (NULL != IoctlFuncMap[cmd].pIoctlFunc)
{
rev = IoctlFuncMap[cmd].pIoctlFunc(usl_port, &stCmd);
}
else
{
USLPUT0("cmd:%d not realized!\n", cmd);
}
}
else
{
USLPUT0("cmd:%d not supprot!\n", cmd);
}
return rev;
}
static struct file_operations usrline_fops = {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36)
unlocked_ioctl: usrline_ioctl,
#else
ioctl: usrline_ioctl,
#endif
open: usrline_open,
release: usrline_release,
owner: THIS_MODULE
};
/* =================================================== */
int si_usrline_init(void)
{
int rev;
init_waitqueue_head(&msg_wait_queue);
CreatMsgQueue();
printk("howard si_usrline_init\n");
rev = register_chrdev(USL_MAJOR, USL_NAME, &usrline_fops);
if (rev < 0) {
USLPUT0("%s: can't get major %d\n", USL_NAME, USL_MAJOR);
return rev;
}
if (0 != SlicMallocMemory())
{
unregister_chrdev(USL_MAJOR, USL_NAME);
USLPUT0("Can't get USL_PORT memory!\n");
return -1;
}
slic_dev_class = class_create(THIS_MODULE, USL_NAME);
if (IS_ERR(slic_dev_class))
{
printk("howard failed in creat slic class!\n");
unregister_chrdev(USL_MAJOR, USL_NAME);
return -1;
}
slic_device = device_create(slic_dev_class, NULL, MKDEV(USL_MAJOR, 0), NULL, USL_NAME);
if (IS_ERR(slic_device))
{
printk("howard failed in creat slic device!\n");
class_destroy(slic_dev_class);
unregister_chrdev(USL_MAJOR, USL_NAME);
return -1;
}
printk("howard kernel_thread\n");
kernel_thread(usrline_scan, NULL, 0); /* fork the main thread */
SlicCfgParaBasedBoardType();
#ifdef POWER_SUPPLY_05A
slic_function = slic_is_ring_state;
#endif
SPI_Init();
//zx29_gpio_config(SLIC_POW_EN_GPIO, GPIO121_GPIO121); /* set SLIC 3.3V EN GPIO */
//zx29_gpio_set_direction(SLIC_POW_EN_GPIO, GPIO_OUT);
//gpio_direction_output(SLIC_POW_EN_GPIO, 1);
/* add by zhanghuan for INT and RST GPIO */
//zx29_gpio_config(SLIC_INT_GPIO, GPIO74_GPIO74); /* set SLIC_INT_GPIO as GPIO */
//zx29_gpio_config(SLIC_RST_GPIO, GPIO77_GPIO77); /* set SLIC_RST_GPIO as GPIO */
#if 0
rev = gpio_is_valid(SLIC_INT_GPIO);
if(rev < 0)
{
printk("SLIC_INT_GPIO is not valid\n");
//return -1;
};
rev = gpio_request(SLIC_INT_GPIO, "slic int");
if(rev < 0)
{
printk("SLIC_INT_GPIO is not valid\n");
//return -1;
};
//gpio_direction_input(SLIC_INT_GPIO);
zx29_gpio_config(SLIC_INT_GPIO, GPIO74_GPIO74); /* set SLIC_INT_GPIO as GPIO */
gpio_direction_input(SLIC_INT_GPIO);
#endif
//zx29_gpio_set_direction(SLIC_INT_GPIO, GPIO_IN);
#if 1
rev = gpio_request(SLIC_INT_GPIO, "slic_int");
if (rev) {
printk(KERN_INFO "slic_int gpio request error.\n");
return rev;
}
zx29_gpio_pd_pu_set(SLIC_INT_GPIO, IO_CFG_PULL_DISABLE);//IO_CFG_PULL_DISABLE
rev = zx29_gpio_config(SLIC_INT_GPIO,FNC_SLIC_INT_GPIO);/********V3 GPIO53:0 /EXT_INT:6*******//********V2 GPIO74:0 /EXT_INT:12*******/
zx29_gpio_set_inttype(SLIC_INT_GPIO,IRQ_TYPE_EDGE_FALLING/*IRQ_TYPE_EDGE_FALLING/*IRQ_TYPE_EDGE_RISING*/); //INT_POSEDGE
//zx29_gpio_pd_pu_set(SLIC_INT_GPIO, 0);
irq_num = gpio_to_irq(SLIC_INT_GPIO);
rev = irq_set_irq_wake(irq_num, 1);
printk("howard irq_set_irq_wake irq_num %d, ret %d\n", irq_num, rev);
pcu_clr_irq_pending(irq_num);
request_threaded_irq(irq_num, slic_int_irq_handler,
slic_int_irq, IRQF_ONESHOT, "slic int", pstUslPort);
#endif
rev = gpio_is_valid(SLIC_RST_GPIO);
if(rev < 0)
{
printk("SLIC_RST_GPIO is not valid\n");
//return -1;
};
rev = gpio_request(SLIC_RST_GPIO, "slic reset");
if(rev < 0)
{
printk("SLIC_RST_GPIO is not valid\n");
//return -1;
};
zx29_gpio_config(SLIC_RST_GPIO, FNC_SLIC_RST_GPIO); /* set SLIC_RST_GPIO as GPIO */
//zx29_gpio_set_direction(SLIC_RST_GPIO, GPIO_OUT);
gpio_direction_output(SLIC_RST_GPIO, 0);
gpio_set_value(SLIC_RST_GPIO,1);
//gpio_direction_output(SLIC_RST_GPIO, 1);
//zx29_gpio_output_data(SLIC_RST_GPIO, 1);
/* add by zhanghuan for SLIC wake lock */
wake_lock_init(&slic_wakelock, WAKE_LOCK_SUSPEND, "slic_wakelock");
wake_lock(&slic_wakelock);
zx_cpuidle_set_busy(IDLE_FLAG_SLIC);
#if 0
rev = slic_create_rpmsg();
if(rev < 0)
{
printk("slic_create_rpmsg failed\n");
};
#endif
zx29_i2s_tdm_pin_cfg();
USLPUT0("howard:User SLIC Driver V3.0.0 Init Finish.\n\r");
return 0;
}
void si_usrline_cleanup(void)
{
timer_run = 0; /* stop scan */
while(!scan_over) /* wait scan thread exit */
{
schedule();
}
printk("howard si_usrline_cleanup\n");
free_irq(irq_num, pstUslPort);
DeinitSlicChip();
SlicFreeMemory();
SPI_Exit();
gpio_free(SLIC_INT_GPIO);
gpio_free(SLIC_RST_GPIO);
printk("howard free irq\n");
device_destroy(slic_dev_class, MKDEV(USL_MAJOR, 0));
class_destroy(slic_dev_class);
unregister_chrdev(USL_MAJOR, USL_NAME);
wake_lock_destroy(&slic_wakelock);
zx_cpuidle_set_free(IDLE_FLAG_SLIC);
USLPUT0("SLC:User SLIC Driver remove OK!\n\r");
return;
}
static void zx29_i2s_tdm_pin_cfg(void)
{
unsigned int regval = 0;
int ret = 0;
printk("zx29_i2s_tdm_pin_cfg\n");
//ret = zOss_NvItemRead(OS_FLASH_VOICE_DRV_RW_NONFAC_BASE_ADDR, ((UINT8 *)(&audionvflag)), sizeof(audionvflag));
printk("after zx29_i2s_tdm_pin_cfg\n");
#if 0
ret = gpio_request(PIN_TDM_FS, "i2s0_ws");
if (ret < 0)
BUG();
ret = gpio_request(PIN_TDM_CLK, "i2s0_clk");
if (ret < 0)
BUG();
ret = gpio_request(PIN_TDM_DIN, "i2s0_din");
if (ret < 0)
BUG();
ret = gpio_request(PIN_TDM_DOUT, "i2s0_dout");
if (ret < 0)
BUG();
zx29_gpio_config(PIN_TDM_FS, FUN_TDM_FS);
zx29_gpio_config(PIN_TDM_CLK, FUN_TDM_CLK);
zx29_gpio_config(PIN_TDM_DIN, FUN_TDM_DIN);
zx29_gpio_config(PIN_TDM_DOUT, FUN_TDM_DOUT);
//zDrvRamlog_PRINTF(RAMLOG_MOD_AUDIO,"vp_SetTopTdmConfig set top TDM,ARM_TDM_LOOP_SET=0x%x\n",ARM_TDM_LOOP_SET);
// sel tdm wclk
regval = zx_read_reg(ZX29_TDM_MOD_CLK_SEL);
regval &= 0xfcffffff; //set mod_clk_sel bit 25:24 to select the tdm wclk, 0, main_clk;1,122.88m;2,mpll104m;3,mpll104m;
zx_write_reg(ZX29_TDM_MOD_CLK_SEL, regval);
//zDrvRamlog_PRINTF(RAMLOG_MOD_AUDIO,"vp_SetTopTdmConfig set top TDM,MOD_CLK_SEL=0x%x\n",TDM_MOD_CLK_SEL);
//zDrvRamlog_PRINTF(RAMLOG_MOD_AUDIO,"vp_SetTopTdmConfig set top TDM,DMA_SEL_CFG=0x%x\n",DMA_SEL_CFG);
// sel tdm use dma
regval = zx_read_reg(ZX29_I2S_DMA_SEL_CFG);
regval &= 0xffffff87;
regval |= 0x00000018; // bit3 1 tdmtx,bit4 1 tdmrx bit5 i2s1tx,bit6 i2s1rx
zx_write_reg(ZX29_I2S_DMA_SEL_CFG, regval);
printk("slic cfg tdm gpio pin end !\n");
#else
ret = gpio_request(PIN_I2S0_WS, "i2s0_ws");
if (ret < 0)
BUG();
ret = gpio_request(PIN_I2S0_CLK, "i2s0_clk");
if (ret < 0)
BUG();
ret = gpio_request(PIN_I2S0_DIN, "i2s0_din");
if (ret < 0)
BUG();
ret = gpio_request(PIN_I2S0_DOUT, "i2s0_dout");
if (ret < 0)
BUG();
zx29_gpio_config(PIN_I2S0_WS, FUN_I2S0_WS);
zx29_gpio_config(PIN_I2S0_CLK, FUN_I2S0_CLK);
zx29_gpio_config(PIN_I2S0_DIN, FUN_I2S0_DIN);
zx29_gpio_config(PIN_I2S0_DOUT, FUN_I2S0_DOUT);
// sel i2s0 use dma
regval = zx_read_reg(ZX29_I2S_DMA_SEL_CFG);
regval &= 0xffffff87; //bit3 1 i2s0tx,bit4 1 i2s0rx bit5 i2s1tx,bit6 i2s1rx
zx_write_reg(ZX29_I2S_DMA_SEL_CFG, regval);
printk("slic cfg i2s0 gpio pin end !\n");
#endif
//top i2s1 cfg
regval = zx_read_reg(ZX29_I2S_LOOP_CFG);
regval &= 0xfffffff8;
regval |= 0x00000001; // inter arm_i2s1--top i2s1
zx_write_reg(ZX29_I2S_LOOP_CFG, regval);
// inter loop
regval = zx_read_reg(ZX29_I2S_LOOP_CFG);
regval &= 0xfffffe07;
regval |= 0x000000a8; // inter arm_i2s2--afe i2s
zx_write_reg(ZX29_I2S_LOOP_CFG, regval);
printk("slic cfg top gpio end !\n");
}
/* =================================================== */
#ifdef USE_GPIO_SPI_SLIC
module_init(si_usrline_init);
module_exit(si_usrline_cleanup);
#endif
#ifdef USE_STD_SPI_SLIC
/* É豸̽²âº¯Êý */
static int slic_probe(struct spi_device *spi)
{
printk("howard slic_probe\n");
pslicSpi =spi;
si_usrline_init();
return 0;
}
static int slic_remove(struct spi_device *spi)
{
printk("howard slic_remove\n");
si_usrline_cleanup();
return 0;
}
static const struct spi_device_id slic_id[] = {
{"slic_spi", 0 },
{ }
};
MODULE_DEVICE_TABLE(spi, slic_id);
static struct spi_driver slic_spi_driver = {
.driver = {
.name = "slic_spi",
.owner = THIS_MODULE,
},
.probe = slic_probe,
.remove = slic_remove,
.id_table = slic_id,
};
static int __init slic_spi_init(void)
{
int ret;
printk("howard slic_spi_init\n");
ret = spi_register_driver(&slic_spi_driver);
if (ret != 0)
{
printk("howard slic Failed to register slic_spi_driver : %d\n", ret);
}
return ret;
}
static void __exit slic_spi_exit(void)
{
printk("howard slic_spi_exit\n");
spi_unregister_driver(&slic_spi_driver);
}
module_init(slic_spi_init);
module_exit(slic_spi_exit);
#endif
MODULE_AUTHOR("zxic");
MODULE_DESCRIPTION("SLIC Driver");
MODULE_LICENSE("GPL");