Esempio n. 1
0
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;
}
/*----------------------------------------------------------------------------*/
void fm_rev( void )
{
    u8 key;
	
    sys_mute_flag =0;

    PT2313_Config(sys_main_vol,VOL_ADJ);

    amp_mute(0);

    while (1)
    {
#if ECHO_ENABLE
        dac_out_select(DAC_AMUX1 | ADD_DEC, 0);  
#else
        dac_out_select(DAC_AMUX1, 0);  
#endif
        key = app_get_msg();

        switch (key)
        {
        case MSG_CHANGE_WORK_MODE:
            return;

        case MSG_MUSIC_NEW_DEVICE_IN:							//有新设备接入
            work_mode = MUSIC_MODE;
            return;
      	 case MSG_FM_SCAN_STOP:
		if(key_ir_detect){
			key_ir_detect =0;
			ir_remote_set_snooze();
		}
		break;
        case MSG_FM_SCAN_ALL:								//play 长按自动全频搜索

            key = fm_scan(0);
            if (key == 3)
            {
                put_msg_lifo(MSG_ALM_ON);
                break;
            }
            if (key == 2)
                return;
            put_msg_lifo(MSG_FM_NEXT_STATION);
            break;

        case  MSG_FM_SCAN_ALL_DOWN :
            key = fm_scan(1);
            if (key == 3)
            {
                put_msg_lifo(MSG_ALM_ON);
                break;
            }
            if (key == 2)
                return;
            break;

        case  MSG_FM_SCAN_ALL_UP:
            key =fm_scan(2);
            if (key == 3)
            {
                put_msg_lifo(MSG_ALM_ON);
                break;
            }
            if (key == 2)
                return;
            break;
#if 0
        case MSG_MUSIC_PP:									//play 短按静音
            put_msg_lifo(MSG_MUTE);
            break;

        case MSG_MUTE:
            break;
#endif
        case MSG_MUSIC_FR:									//搜索上一个台
            flush_all_msg();
            break;

        case MSG_MUSIC_FF:									//搜索下一个台
            flush_all_msg();
            break;

        case MSG_FM_PREV_STEP:
            set_fre(FM_FRE_DEC);
            fre_channel = get_channel_via_fre(frequency - MIN_FRE);						//查找该频点是否有记忆过
            disp_port(MENU_FM_MAIN);
            write_info(MEM_FRE, frequency-MIN_FRE);
            write_info(MEM_CHAN, fre_channel);
            break;

        case MSG_FM_NEXT_STEP:
            set_fre(FM_FRE_INC);
            fre_channel = get_channel_via_fre(frequency - MIN_FRE);						//查找该频点是否有记忆过
            disp_port(MENU_FM_MAIN);
            write_info(MEM_FRE, frequency-MIN_FRE);
            write_info(MEM_CHAN, fre_channel);
            break;

        case MSG_FM_PREV_STATION:
            if (total_channel == 0)
                break;
            fre_channel -= 2;
        case MSG_FM_NEXT_STATION:
            if (total_channel == 0)
                break;
            fre_channel++;

            if ((fre_channel == 0) || (fre_channel == 0xff))
            {
                fre_channel = total_channel;
            }
            else if (fre_channel > total_channel)
            {
                fre_channel = 1;
            }
            frequency = get_fre_via_channle(fre_channel) + MIN_FRE;				//根据台号找频点
            main_vol_set(0, CHANGE_VOL_NO_MEM);
            set_fre(FM_CUR_FRE);
            main_vol_set(0, SET_USE_CURRENT_VOL);
            disp_port(MENU_FM_CHANNEL);
            write_info(MEM_FRE, frequency-MIN_FRE);
            write_info(MEM_CHAN, fre_channel);
            break;

        case MSG_CH_SET:
            if (cur_menu == MENU_INPUT_NUMBER)			//数字输入模式
            {
                if (input_number <= MAX_CHANNL)							//输入的是台号
                {
                    key = get_fre_via_channle(input_number);
                    if (key != 0xff)
                    {
                        frequency = key + MIN_FRE;
                        fre_channel = input_number;
                        main_vol_set(0, CHANGE_VOL_NO_MEM);
                        set_fre(FM_CUR_FRE);
                        main_vol_set(0, SET_USE_CURRENT_VOL);
                        disp_port(MENU_FM_CHANNEL);
                        //break;
                    }

                }
                else if ((input_number >= MIN_FRE) && (input_number <= MAX_FRE)) //输入的是频点
                {
                    frequency = input_number;
                    fre_channel = get_channel_via_fre(frequency - MIN_FRE);
                    main_vol_set(0, CHANGE_VOL_NO_MEM);
                    set_fre(FM_CUR_FRE);
                    main_vol_set(0, SET_USE_CURRENT_VOL);
                }
            }
            write_info(MEM_FRE, frequency-MIN_FRE);
            write_info(MEM_CHAN, fre_channel);
            disp_port(main_menu);

            break;

        case MSG_CH_SAVE:
            ch_save();
            break;


        case MSG_HALF_SECOND:

		alarm_setting_vol_hdlr();

		//bmt_hdlr();			 
	     timer_pwr_off_hdlr();
		
            set_brightness_fade_out();
            if (main_menu_conter < SUB_MENU_TIME)
            {
                main_menu_conter++;
            }
            else if (cur_menu != main_menu)
            {
                put_msg_lifo(MSG_CH_SET);				//跳转至CH_SET消息处理
            }

	     if(cur_menu == main_menu)
	     {
		   disp_port(main_menu);
	     }
            break;


#if 0//RTC_ENABLE
        case MSG_ALM_ON:
            write_next_alm_sec();
            work_mode = RTC_MODE;
            put_msg_lifo(MSG_CHANGE_WORK_MODE);
            break;
#endif

#if KALAOK_FUNCTION
		case MSG_KALAOK:
			work_mode = REC_MIC_MODE;
			return ;
#endif

        default :
            ap_handle_hotkey(key);
            break;
        }
    }
}