static fm_s32 fm_proc_read(char *page, char **start, off_t off, fm_s32 count, fm_s32 *eof, void *data)
{
    fm_s32 cnt = 0;
    struct fm *fm  = g_fm;

    WCN_DBG(FM_NTC | MAIN, "Enter fm_proc_read.\n");

    if (off != 0)
        return 0;

    if (!fm) {
        WCN_DBG(FM_ALT | MAIN, "para err\n");
        return 0;
    }

    if (fm->chipon && (fm_pwr_state_get(fm) == FM_PWR_RX_ON)) 
	{
        cnt = sprintf(page, "1\n");
		WCN_DBG(FM_NTC | MAIN, " FM_PWR_RX_ON\n");
    } 
	else if (fm->chipon && (fm_pwr_state_get(fm) == FM_PWR_TX_ON)) 
	{
		WCN_DBG(FM_NTC | MAIN, " FM_PWR_TX_ON\n");
        cnt = sprintf(page, "2\n");
    } 
	else 
	{
        cnt = sprintf(page, "0\n");
    }

    *eof = 1;
    WCN_DBG(FM_NTC | MAIN, "Leave fm_proc_read. cnt = %d\n", cnt);
    return cnt;
}
static ssize_t fm_proc_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
	struct fm *fm = g_fm;
	ssize_t length = 0;
	char tmpbuf[3];
	unsigned long pos = *ppos;

	WCN_DBG(FM_NTC | MAIN, "Enter fm_proc_read.\n");
	/* WCN_DBG(FM_NTC | MAIN, "count = %d\n", count); */
	/* WCN_DBG(FM_NTC | MAIN, "ppos = %d\n", pos); */

	if (pos != 0)
		return 0;

	if (!fm) {
		WCN_DBG(FM_ALT | MAIN, "para err\n");
		return 0;
	}

	if (fm->chipon && (fm_pwr_state_get(fm) == FM_PWR_RX_ON)) {
		length = sprintf(tmpbuf, "1\n");
		WCN_DBG(FM_NTC | MAIN, " FM_PWR_RX_ON\n");
	} else if (fm->chipon && (fm_pwr_state_get(fm) == FM_PWR_TX_ON)) {
		length = sprintf(tmpbuf, "2\n");
		WCN_DBG(FM_NTC | MAIN, " FM_PWR_TX_ON\n");
	} else {
		length = sprintf(tmpbuf, "0\n");
		WCN_DBG(FM_NTC | MAIN, " FM POWER OFF\n");
	}

	if (copy_to_user(buf, tmpbuf, length)) {
		WCN_DBG(FM_NTC | MAIN, " Read FM status fail!\n");
		return 0;
	}

	pos += length;
	*ppos = pos;
	WCN_DBG(FM_NTC | MAIN, "Leave fm_proc_read. length = %zu\n", length);

	return length;
}
static long fm_ops_ioctl(struct file *filp, fm_u32 cmd, unsigned long arg)
{
    fm_s32 ret = 0;
    struct fm_platform *plat = container_of(filp->f_dentry->d_inode->i_cdev, struct fm_platform, cdev);
    struct fm *fm = container_of(plat, struct fm, platform);

    WCN_DBG(FM_NTC | MAIN, "%s---pid(%d)---cmd(0x%08x)---arg(0x%08x)\n", current->comm, current->pid, cmd, (fm_u32)arg);

    if (fm_sys_state_get(fm) != FM_SUBSYS_RST_OFF) {
        WCN_DBG(FM_ALT | MAIN, "FM subsys is resetting, retry later\n");
        ret = -FM_ESRST;
        return ret;
    }
    
    switch (cmd) {
    case FM_IOCTL_POWERUP: {
        struct fm_tune_parm parm;
        WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_POWERUP:0\n");

        if (copy_from_user(&parm, (void*)arg, sizeof(struct fm_tune_parm))) {
            ret = -EFAULT;
            goto out;
        }

        ret = fm_powerup(fm, &parm);
        if (ret < 0) goto out;
        ret = fm_tune(fm, &parm);
        if (ret < 0) goto out;

        if (copy_to_user((void*)arg, &parm, sizeof(struct fm_tune_parm))) {
            ret = -EFAULT;
            goto out;
        }
		WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_POWERUP:1\n");

        break;
    }

    case FM_IOCTL_POWERDOWN: {
        WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_POWERDOWN:0\n");
        ret = fm_powerdown(fm);
		WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_POWERDOWN:1\n");
        break;
    }

    case FM_IOCTL_TUNE: {
        struct fm_tune_parm parm;
        WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_TUNE:0\n");

        if (copy_from_user(&parm, (void*)arg, sizeof(struct fm_tune_parm))) {
            ret = -EFAULT;
            goto out;
        }

        ret = fm_tune(fm, &parm);
        if (ret < 0) {
            goto out;
        }

        if (copy_to_user((void*)arg, &parm, sizeof(struct fm_tune_parm))) {
            ret = -EFAULT;
            goto out;
        }

		WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_TUNE:1\n");
        break;
    }

	case FM_IOCTL_SOFT_MUTE_TUNE:
	{
        struct fm_softmute_tune_t parm;
        fm_cqi_log();//cqi log tool
        WCN_DBG(FM_NTC | MAIN,"......FM_IOCTL_SOFT_MUTE_TUNE......\n");
        if(copy_from_user(&parm, (void*)arg, sizeof(struct fm_softmute_tune_t)))
        {
            ret = -EFAULT;
            goto out;
        }

        ret = fm_soft_mute_tune(fm, &parm);
        if (ret < 0) {
            goto out;
        }

        if(copy_to_user((void*)arg, &parm, sizeof(struct fm_softmute_tune_t)))
        {
            ret = -EFAULT;
            goto out;
        }
        break;
	}    
	case FM_IOCTL_SEEK: {
        struct fm_seek_parm parm;
        WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_SEEK:0\n");

        if (copy_from_user(&parm, (void*)arg, sizeof(struct fm_seek_parm))) {
            ret = -EFAULT;
            goto out;
        }

        ret = fm_seek(fm, &parm);
        if (ret < 0) {
            goto out;
        }

        if (copy_to_user((void*)arg, &parm, sizeof(struct fm_seek_parm))) {
            ret = -EFAULT;
            goto out;
        }
		WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_SEEK:1\n");
        break;
    }

    case FM_IOCTL_SCAN: {
        struct fm_scan_parm parm;
        WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_SCAN start\n");

        if (copy_from_user(&parm, (void*)arg, sizeof(struct fm_scan_parm))) {
            WCN_DBG(FM_ALT | MAIN, "copy_from_user failed\n");
            ret = -EFAULT;
            goto out;
        }

        ret = fm_scan(fm, &parm);
        if (ret < 0) goto out;

        if (copy_to_user((void*)arg, &parm, sizeof(struct fm_scan_parm))) {
            ret = -EFAULT;
            goto out;
        }

		WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_SCAN end\n");
        break;
    }

    case FM_IOCTL_TUNE_NEW: {
        struct fm_tune_t tune_tmp;
    
        WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_TUNE_NEW\n");
    
        if (copy_from_user(&tune_tmp, (void*)arg, sizeof(struct fm_tune_t))) {
            WCN_DBG(FM_ERR | MAIN, "tune new copy_from_user error\n");
            ret = -EFAULT;
            goto out;
        }
    
        ret = fm_tune_new(fm, &tune_tmp);
        if (ret < 0) {
            goto out;
        }
            
        if (copy_to_user((void*)arg, &tune_tmp, sizeof(struct fm_tune_t))) {
            WCN_DBG(FM_ERR | MAIN, "tune new copy_to_user error\n");
            ret = -EFAULT;
            goto out;
        }
            
        break;        
    }

    case FM_IOCTL_SEEK_NEW: {
        struct fm_seek_t seek_tmp;

        WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_SEEK_NEW\n");
        
        if (copy_from_user(&seek_tmp, (void*)arg, sizeof(struct fm_seek_t))) {
            WCN_DBG(FM_ERR | MAIN, "seek new copy_from_user error\n");
            ret = -EFAULT;
            goto out;
        }
        
        ret = fm_seek_new(fm, &seek_tmp);
        if (ret < 0) {
            goto out;
        }

        if (copy_to_user((void*)arg, &seek_tmp, sizeof(struct fm_seek_t))) {
            WCN_DBG(FM_ERR | MAIN, "seek new copy_to_user error\n");
            ret = -EFAULT;
            goto out;
        }
        
        break;
    }
    
    case FM_IOCTL_SCAN_NEW: {
        struct fm_scan_t tmp;
        
        WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_SCAN_NEW\n");

        if (copy_from_user(&tmp, (void*)arg, sizeof(struct fm_scan_t))) {
            WCN_DBG(FM_ERR | MAIN, "copy_from_user error\n");
            ret =  -EFAULT;
            goto out;
        }

        switch (tmp.cmd) {
            case FM_SCAN_CMD_START: 
                if ((tmp.upper > 10800) || (tmp.lower < 7600) || (tmp.space < 5) || (tmp.space > 20)) {
                    WCN_DBG(FM_ERR | MAIN, "scan para error\n");
                    ret = -EFAULT;
                    goto out;
                }
                parm.cmd = tmp.cmd;
                parm.lower = tmp.lower;
                parm.upper = tmp.upper;
                parm.space = tmp.space;

                ret = fm_scan_new(fm, &parm);
                if (ret < 0) {
                    goto out;
                }
                break;
                
            case FM_SCAN_CMD_GET_CH_RSSI:
                if (parm.sr_size && parm.sr.ch_rssi_buf) {
                    if (copy_to_user(tmp.sr.ch_rssi_buf, parm.sr.ch_rssi_buf, parm.num * sizeof(struct fm_ch_rssi))) {
                        WCN_DBG(FM_ERR | MAIN, "scan copy_to_user err\n");
                        ret = -EFAULT;
                        goto out;
                    }
                }
                break;
                
            default:
                break;
        }

        tmp.num = parm.num;
        if (copy_to_user((void*)arg, &tmp, sizeof(struct fm_scan_t))) {
            WCN_DBG(FM_ERR | MAIN, "copy_to_user error\n");
            ret = -EFAULT;
            goto out;
        }
        break;
    }   
    
    case FM_IOCTL_CQI_GET: {
        struct fm_cqi_req cqi_req;
        fm_s8 *buf = NULL;
        fm_s32 tmp;

        WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_CQI_GET\n");

        if (copy_from_user(&cqi_req, (void*)arg, sizeof(struct fm_cqi_req))) {
            WCN_DBG(FM_ALT | MAIN, "copy_from_user failed\n");
            ret = -EFAULT;
            goto out;
        }

        if ((cqi_req.ch_num*sizeof(struct fm_cqi) > cqi_req.buf_size) || !cqi_req.cqi_buf) {
            ret = -FM_EPARA;
            goto out;
        }

        tmp = cqi_req.ch_num / 16 + ((cqi_req.ch_num % 16) ? 1 : 0);
        tmp = tmp * 16 * sizeof(struct fm_cqi);
        buf = fm_zalloc(tmp);

        if (!buf) {
            ret = -FM_ENOMEM;
            goto out;
        }

        ret = fm_cqi_get(fm, cqi_req.ch_num, buf, tmp);

        if (ret) {
            fm_free(buf);
            WCN_DBG(FM_ALT | MAIN, "get cqi failed\n");
            goto out;
        }

        if (copy_to_user((void*)cqi_req.cqi_buf, buf, cqi_req.ch_num*sizeof(struct fm_cqi))) {
            fm_free(buf);
            ret = -EFAULT;
            goto out;
        }

        fm_free(buf);
        break;
    }

    case FM_IOCTL_GET_HW_INFO: {
        struct fm_hw_info info;

        WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_GET_HW_INFO\n");

        ret = fm_get_hw_info(fm, &info);

        if (ret) {
            WCN_DBG(FM_ALT | MAIN, "get hw info failed\n");
            goto out;
        }

        if (copy_to_user((void*)arg, &info, sizeof(struct fm_hw_info))) {
            ret = -EFAULT;
            goto out;
        }

        break;
    }

    case FM_IOCTL_GET_I2S_INFO: {
        struct fm_i2s_info i2sinfo;

        WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_GET_I2S_INFO\n");

        ret = fm_get_i2s_info(fm, &i2sinfo);

        if (ret) {
            WCN_DBG(FM_ALT | MAIN, "get i2s info failed\n");
            goto out;
        }

        if (copy_to_user((void*)arg, &i2sinfo, sizeof(struct fm_i2s_info))) {
            ret = -EFAULT;
            goto out;
        }

        break;
    }

    case FM_IOCTL_SETVOL: {
        fm_u32 vol;

        WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_SETVOL start\n");
        if (copy_from_user(&vol, (void*)arg, sizeof(fm_u32))) {
            WCN_DBG(FM_ALT | MAIN, "copy_from_user failed\n");
            ret = -EFAULT;
            goto out;
        }

        ret = fm_setvol(fm, vol);
        WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_SETVOL end:%d\n", vol);
        break;
    }
    case FM_IOCTL_GETVOL: {
        fm_u32 vol;
        WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_GETVOL start\n");
        ret = fm_getvol(fm, &vol);
        if (ret < 0) goto out;

        if (copy_to_user((void*)arg, &vol, sizeof(fm_u32))) {
            ret = -EFAULT;
            goto out;
        }

        WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_GETVOL end=%d\n",vol);
        break;
    }

    case FM_IOCTL_MUTE: {
        fm_u32 bmute;

        WCN_DBG(FM_NTC| MAIN, "FM_IOCTL_MUTE start\n");
        if (copy_from_user(&bmute, (void*)arg, sizeof(fm_u32))) {
            ret = -EFAULT;
            goto out;
        }

        ret = fm_mute(fm, bmute);
        WCN_DBG(FM_NTC| MAIN, "FM_IOCTL_MUTE end-%d\n", bmute);
        break;
    }

    case FM_IOCTL_GETRSSI: {
        fm_s32 rssi = 0;

        ret = fm_getrssi(fm, &rssi);
        if (ret < 0) goto out;

        if (copy_to_user((void*)arg, &rssi, sizeof(fm_s32))) {
            ret = -EFAULT;
            goto out;
        }

        WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_GETRSSI:%d\n",rssi);
        break;
    }

    case FM_IOCTL_RW_REG: {
        struct fm_ctl_parm parm_ctl;
        WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_RW_REG\n");

        if (copy_from_user(&parm_ctl, (void*)arg, sizeof(struct fm_ctl_parm))) {
            ret = -EFAULT;
            goto out;
        }

        if (parm_ctl.rw_flag == 0) {
            ret = fm_reg_write(fm, parm_ctl.addr, parm_ctl.val);
        } else {
            ret = fm_reg_read(fm, parm_ctl.addr, &parm_ctl.val);
        }
        if (ret < 0) goto out;

        if ((parm_ctl.rw_flag == 0x01) && (!ret)) {
            if (copy_to_user((void*)arg, &parm_ctl, sizeof(struct fm_ctl_parm))) {
                ret = -EFAULT;
                goto out;
            }
        }

        break;
    }

    case FM_IOCTL_GETCHIPID: {
        fm_u16 chipid;

        ret = fm_chipid_get(fm, &chipid);
        WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_GETCHIPID:%04x\n", chipid);
        if (ret < 0) goto out;

        if (copy_to_user((void*)arg, &chipid, sizeof(fm_u16))) {
            ret = -EFAULT;
            goto out;
        }
        break;
    }

    case FM_IOCTL_GETMONOSTERO: {
        fm_u16 usStereoMono;

        ret = fm_monostereo_get(fm, &usStereoMono);
        WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_GETMONOSTERO:%04x\n", usStereoMono);
        if (ret < 0) goto out;

        if (copy_to_user((void*)arg, &usStereoMono, sizeof(fm_u16))) {
            ret = -EFAULT;
            goto out;
        }
        break;
    }

    case FM_IOCTL_SETMONOSTERO: {
        WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_SETMONOSTERO, %d\n", (fm_s32)arg);
        ret = fm_monostereo_set(fm, (fm_s32)arg);
        break;
    }

    case FM_IOCTL_GETCURPAMD: {
        fm_u16 PamdLevl;

        ret = fm_pamd_get(fm, &PamdLevl);
        WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_GETCURPAMD:%d\n", PamdLevl);
        if (ret < 0) goto out;

        if (copy_to_user((void*)arg, &PamdLevl, sizeof(fm_u16)))
            ret = -EFAULT;
            goto out;

        break;
    }

    case FM_IOCTL_GETCAPARRAY: {
        fm_s32 ca;

        ret = fm_caparray_get(fm, &ca);
        WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_GETCAPARRAY:%d\n", ca);
        if (ret < 0) goto out;

        if (copy_to_user((void*)arg, &ca, sizeof(fm_s32))) {
            ret = -EFAULT;
            goto out;
        }
        break;
    }

    case FM_IOCTL_EM_TEST: {
        struct fm_em_parm parm_em;

        WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_EM_TEST\n");

        if (copy_from_user(&parm_em, (void*)arg, sizeof(struct fm_em_parm))) {
            ret = -EFAULT;
            goto out;
        }
        ret = fm_em_test(fm, parm_em.group_idx, parm_em.item_idx, parm_em.item_value);
        break;
    }

    case FM_IOCTL_RDS_SUPPORT: {
        fm_s32 support = FM_RDS_ENABLE;
        WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_RDS_SUPPORT\n");

        if (copy_to_user((void*)arg, &support, sizeof(fm_s32))) {
            ret = -EFAULT;
            goto out;
        }
        break;
    }

    case FM_IOCTL_IS_FM_POWERED_UP: {
        fm_u32 powerup;
        WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_IS_FM_POWERED_UP");

        if (fm->chipon && fm_pwr_state_get(fm)) {
            powerup = 1;
        } else {
            powerup = 0;
        }

        if (copy_to_user((void*)arg, &powerup, sizeof(fm_u32))) {
            ret = -EFAULT;
            goto out;
        }
        break;
    }

    case FM_IOCTL_RDS_ONOFF: {
        fm_u16 rdson_off = 0;
        WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_RDS_ONOFF start\n");

        if (copy_from_user(&rdson_off, (void*)arg, sizeof(fm_u16))) {
            ret = -EFAULT;
            goto out;
        }
        ret = fm_rds_onoff(fm, rdson_off);
        WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_RDS_ONOFF end:%d\n",rdson_off);
        break;
    }

    case FM_IOCTL_GETGOODBCNT: {
        fm_u16 uGBLCNT = 0;

        ret = fm_rds_good_bc_get(fm, &uGBLCNT);
        WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_GETGOODBCNT:%d\n", uGBLCNT);
        if (ret < 0) goto out;

        if (copy_to_user((void*)arg, &uGBLCNT, sizeof(fm_u16))) {
            ret = -EFAULT;
            goto out;
        }
        break;
    }

    case FM_IOCTL_GETBADBNT: {
        fm_u16 uBadBLCNT = 0;

        ret = fm_rds_bad_bc_get(fm, &uBadBLCNT);
        WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_GETBADBNT:%d\n", uBadBLCNT);
        if (ret < 0) goto out;

        if (copy_to_user((void*)arg, &uBadBLCNT, sizeof(fm_u16))) {
            ret = -EFAULT;
            goto out;
        }
        break;
    }

    case FM_IOCTL_GETBLERRATIO: {
        fm_u16 uBlerRatio = 0;

        ret = fm_rds_bler_ratio_get(fm, &uBlerRatio);
        WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_GETBLERRATIO:%d\n", uBlerRatio);
        if (ret < 0) goto out;

        if (copy_to_user((void*)arg, &uBlerRatio, sizeof(fm_u16))) {
            ret = -EFAULT;
            goto out;
        }
        break;
    }

    case FM_IOCTL_ANA_SWITCH: {
        fm_s32 antenna = -1;
        WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_ANA_SWITCH\n");

        if (copy_from_user(&antenna, (void*)arg, sizeof(fm_s32))) {
            WCN_DBG(FM_ALT | MAIN, "copy from user error\n");
            ret = -EFAULT;
            goto out;
        }

        ret = fm_ana_switch(fm, antenna);
        break;
    }

    case FM_IOCTL_RDS_GROUPCNT: {
        struct rds_group_cnt_req_t gc_req;
        WCN_DBG(FM_DBG | MAIN, "......FM_IOCTL_RDS_GROUPCNT......\n");

        if (copy_from_user(&gc_req, (void*)arg, sizeof(struct rds_group_cnt_req_t))) {
            WCN_DBG(FM_ALT | MAIN, "copy_from_user error\n");
            ret = -EFAULT;
            goto out;
        }

        //handle group counter request
        switch (gc_req.op) {
        case RDS_GROUP_CNT_READ:
            ret = fm_rds_group_cnt_get(fm, &gc_req.gc);
            break;
        case RDS_GROUP_CNT_WRITE:
            break;
        case RDS_GROUP_CNT_RESET:
            ret = fm_rds_group_cnt_reset(fm);
            break;
        default:
            break;
        }

        if (copy_to_user((void*)arg, &gc_req, sizeof(struct rds_group_cnt_req_t))) {
            WCN_DBG(FM_ALT | MAIN, "copy_to_user error\n");
            ret = -EFAULT;
            goto out;
        }

        break;
    }

    case FM_IOCTL_RDS_GET_LOG: {
        struct rds_raw_t rds_log;
        fm_s32 len;
        WCN_DBG(FM_DBG | MAIN, "......FM_IOCTL_RDS_GET_LOG......\n");
        //fetch a record form RDS log buffer
        ret = fm_rds_log_get(fm, (struct rds_rx_t*) & (rds_log.data), &len);
        rds_log.dirty = TRUE;
        rds_log.len = (len < sizeof(rds_log.data)) ? len : sizeof(rds_log.data);
        if (ret < 0) goto out;

        if (copy_to_user((void*)arg, &rds_log, rds_log.len + 2*sizeof(fm_s32))) {
            WCN_DBG(FM_ALT | MAIN, "copy_to_user error\n");
            ret = -EFAULT;
            goto out;
        }

        break;
    }

    case FM_IOCTL_RDS_BC_RST: {
        WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_RDS_BC_RST\n");
        ret = fm_rds_block_cnt_reset(fm);
        break;
    }

    case FM_IOCTL_I2S_SETTING: {
        struct fm_i2s_setting i2s_cfg;
        WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_I2S_SETTING\n");

        if (copy_from_user(&i2s_cfg, (void*)arg, sizeof(struct fm_i2s_setting))) {
            WCN_DBG(FM_ALT | MAIN, "i2s set, copy_from_user err\n");
            ret = -EFAULT;
            goto out;
        }

        ret = fm_i2s_set(fm, i2s_cfg.onoff, i2s_cfg.mode, i2s_cfg.sample);

        if (ret) {
            WCN_DBG(FM_ALT | MAIN, "Set i2s err\n");
            goto out;
        }

        break;
    }

    case FM_IOCTL_IS_DESE_CHAN: {
        fm_s32 tmp;
        WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_IS_DESE_CHAN\n");

        if (copy_from_user(&tmp, (void*)arg, sizeof(fm_s32))) {
            WCN_DBG(FM_ALT | MAIN, "is dese chan, copy_from_user err\n");
            ret = -EFAULT;
            goto out;
        }

        tmp = fm_is_dese_chan(fm, (fm_u16)tmp);

        if (copy_to_user((void*)arg, &tmp, sizeof(fm_s32))) {
            WCN_DBG(FM_ALT | MAIN, "is dese chan, copy_to_user err\n");
            ret = -EFAULT;
            goto out;
        }

        break;
    }
    case FM_IOCTL_DESENSE_CHECK: 
    {
        fm_desense_check_t tmp;
        WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_IS_DESE_CHAN\n");

        if (copy_from_user(&tmp, (void*)arg, sizeof(fm_desense_check_t))) 
        {
            WCN_DBG(FM_ALT | MAIN, "desene check, copy_from_user err\n");
            ret = -EFAULT;
            goto out;
        }
        ret = fm_desense_check(fm, (fm_u16)tmp.freq,tmp.rssi);

        /*if (copy_to_user((void*)arg, &tmp, sizeof(fm_desense_check_t))) {
            WCN_DBG(FM_ALT | MAIN, "desene check, copy_to_user err\n");
            ret = -EFAULT;
            goto out;
        }*/

        break;
    }
	case FM_IOCTL_SCAN_GETRSSI:
	{
		/*struct fm_rssi_req *req;
        WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_SCAN_GETRSSI\n");
		if(!(req = fm_vmalloc(sizeof(struct fm_rssi_req))))
		{
            WCN_DBG(FM_ALT | MAIN, "fm_vmalloc err\n");
            ret = -EFAULT;
            goto out;
		}
		if(copy_from_user(req, (void*)arg, sizeof(struct fm_rssi_req)))
		{
            WCN_DBG(FM_ALT | MAIN, "copy_from_user err\n");
            ret = -EFAULT;
			fm_vfree(req);
            goto out;
		}
		ret = fm_get_rssi_after_scan(fm, req);
		if(-ERR_FW_NORES == ret){
            WCN_DBG(FM_ALT | MAIN, "fm_get_rssi_after_scan err\n");
		}
		if(copy_to_user((void*)arg, req, sizeof(struct fm_rssi_req)))
		{
            WCN_DBG(FM_ALT | MAIN, "copy_to_user err\n");
            ret = -EFAULT;
			fm_vfree(req);
            goto out;
		}
		*/
        WCN_DBG(FM_ALT | MAIN, "FM_IOCTL_SCAN_GETRSSI:not support\n");
		break;
	}

	case FM_IOCTL_DUMP_REG:
	{
		WCN_DBG(FM_NTC | MAIN,"......FM_IOCTL_DUMP_REG......\n");

		ret = fm_dump_reg();
		if(ret)
		{
			WCN_DBG(FM_ALT | MAIN, "fm_dump_reg err\n");
		}
		break;
	}
	case FM_IOCTL_GPS_RTC_DRIFT:{
		struct fm_gps_rtc_info rtc_info;
		WCN_DBG(FM_NTC | MAIN,"......FM_IOCTL_GPS_RTC_DRIFT......\n");
	
		if (fm_false == fm->chipon){
			WCN_DBG(FM_ERR | MAIN,"ERROR, FM chip is OFF\n");
            ret = -EFAULT;
            goto out;
		}
		if(copy_from_user(&rtc_info, (void*)arg, sizeof(struct fm_gps_rtc_info))){
			WCN_DBG(FM_ERR | MAIN,"copy_from_user error\n");
            ret = -EFAULT;
            goto out;
		}
		
		ret = fm_get_gps_rtc_info(&rtc_info);
		if(ret){
			WCN_DBG(FM_ERR | MAIN,"fm_get_gps_rtc_info error\n");
            goto out;
		}
		break;
	}
	case FM_IOCTL_OVER_BT_ENABLE:
	{
		fm_s32 fm_via_bt = -1;
		WCN_DBG(FM_NTC | MAIN,"......FM_IOCTL_OVER_BT_ENABLE......\n");
		
		if(copy_from_user(&fm_via_bt, (void*)arg, sizeof(int32_t))){
			WCN_DBG(FM_ERR | MAIN,"copy_from_user error\n");
			ret = -EFAULT;
			goto out;
		}
	
		ret = fm_over_bt(fm, fm_via_bt);
		if(ret)
		{
			WCN_DBG(FM_ERR | MAIN, "fm_over_bt err\n");
		}
		break;
	}
	
    /***************************FM Tx function************************************/
	case FM_IOCTL_TX_SUPPORT:
	{
		fm_s32 tx_support = -1;
		WCN_DBG(FM_NTC | MAIN,"......FM_IOCTL_TX_SUPPORT......\n");

		ret = fm_tx_support(fm,&tx_support);
		if(ret)
		{
			WCN_DBG(FM_ERR | MAIN, "fm_tx_support err\n");
		}
		if (copy_to_user((void*)arg, &tx_support, sizeof(fm_s32)))
		{
			WCN_DBG(FM_ERR | MAIN,"copy_to_user error\n");
			ret = -EFAULT;
			goto out;
		}
		break;
	}
	case FM_IOCTL_POWERUP_TX:
	{
		struct fm_tune_parm parm;
        WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_POWERUP_TX:0\n");
        if (copy_from_user(&parm, (void*)arg, sizeof(struct fm_tune_parm))) {
            ret = -EFAULT;
            goto out;
        }

		ret = fm_powerup_tx(fm, &parm);
        if (ret < 0) {
            goto out;
        }
        ret = fm_tune_tx(fm, &parm);
        if (ret < 0) goto out;

        if (copy_to_user((void*)arg, &parm, sizeof(struct fm_tune_parm))) {
            ret = -EFAULT;
            goto out;
        }
        WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_POWERUP_TX:1\n");
		break;
	}
	
	case FM_IOCTL_TUNE_TX:
	{
		struct fm_tune_parm parm;
        WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_TUNE_TX:0\n");

        if (copy_from_user(&parm, (void*)arg, sizeof(struct fm_tune_parm))) {
            ret = -EFAULT;
            goto out;
        }

        ret = fm_tune_tx(fm, &parm);
        if (ret < 0) {
            goto out;
        }

        if (copy_to_user((void*)arg, &parm, sizeof(struct fm_tune_parm))) {
            ret = -EFAULT;
            goto out;
        }

		WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_TUNE_TX:1\n");
		break;
	}
	case FM_IOCTL_RDSTX_SUPPORT:
	{
		fm_s32 rds_tx_support = -1;
		WCN_DBG(FM_NTC | MAIN,"......FM_IOCTL_RDSTX_SUPPORT......\n");

		ret = fm_rdstx_support(fm,&rds_tx_support);
		if(ret)
		{
			WCN_DBG(FM_ERR | MAIN, "fm_rdstx_support err\n");
		}
		if (copy_to_user((void*)arg, &rds_tx_support, sizeof(fm_s32)))
		{
			WCN_DBG(FM_ERR | MAIN,"copy_to_user error\n");
			ret = -EFAULT;
			goto out;
		}
		break;
	}	
	
	case FM_IOCTL_RDSTX_ENABLE:
	{
		fm_s32 rds_tx_enable = -1;
		WCN_DBG(FM_NTC | MAIN,"......FM_IOCTL_RDSTX_ENABLE......\n");

		ret = fm_rdstx_enable(fm,&rds_tx_enable);
		if(ret)
		{
			WCN_DBG(FM_ERR | MAIN, "fm_rdstx_enable err\n");
		}
		if (copy_to_user((void*)arg, &rds_tx_enable, sizeof(fm_s32)))
		{
			WCN_DBG(FM_ERR | MAIN,"copy_to_user error\n");
			ret = -EFAULT;
			goto out;
		}
		break;
	}	
	
	case FM_IOCTL_RDS_TX:
	{
		struct fm_rds_tx_parm parm;
		WCN_DBG(FM_NTC | MAIN, "......FM_IOCTL_RDS_TX......\n");

		if (copy_from_user(&parm, (void*)arg, sizeof(struct fm_rds_tx_parm)))
		{
            WCN_DBG(FM_ALT | MAIN, "RDS Tx, copy_from_user err\n");
			ret = -EFAULT;
			goto out;
		}

		ret = fm_rds_tx(fm, &parm);
		if(ret)
		{
            WCN_DBG(FM_ALT | MAIN, "fm_rds_tx err\n");
		}

		if (copy_to_user((void*)arg, &parm, sizeof(struct fm_rds_tx_parm))){
            WCN_DBG(FM_ALT | MAIN, "RDS Tx, copy_to_user err\n");
			ret = -EFAULT;
			goto out;
		}
		break;
	}
	
	case FM_IOCTL_TX_SCAN:
	{
		struct fm_tx_scan_parm parm;
		WCN_DBG(FM_NTC | MAIN, "......FM_IOCTL_TX_SCAN......\n");

		if (copy_from_user(&parm, (void*)arg, sizeof(struct fm_tx_scan_parm))){
			WCN_DBG(FM_ALT | MAIN,"copy_from_user error\n");
			ret = -EFAULT;
			goto out;
		}
		ret = fm_tx_scan(fm, &parm);
		if(ret < 0){
			WCN_DBG(FM_ERR | MAIN,"FM_IOCTL_TX_SCAN failed\n");
		}
		if (copy_to_user((void*)arg, &parm, sizeof(struct fm_tx_scan_parm))){
			WCN_DBG(FM_ALT | MAIN,"copy_to_user error\n");
			ret = -EFAULT;
			goto out;
		}
		break;
	}

    default:
        ret = -EPERM;
    }

out:
    if (ret == -FM_EFW) {
        // subsystem reset
        fm_subsys_reset(fm);
    }

    return ret;
}