Esempio n. 1
0
struct fm_fifo* fm_fifo_init(struct fm_fifo* fifo, void *buf, const fm_s8 *name, fm_s32 item_size, fm_s32 item_num) 
{
    fm_memcpy(fifo->obj.name, name, 20);
    fifo->size = item_num;
    fifo->in = 0;
    fifo->out = 0;
    fifo->len = 0;
    fifo->item_size = item_size;
    fifo->obj.priv = buf;

    fifo->input = fm_fifo_in;
    fifo->output = fm_fifo_out;
    fifo->is_full = fm_fifo_is_full;
    fifo->is_empty = fm_fifo_is_empty;
    fifo->get_total_len = fm_fifo_get_total_len;
    fifo->get_valid_len = fm_fifo_get_valid_len;
    fifo->reset = fm_fifo_reset;
    
    WCN_DBG(FM_NTC | LINK, "%s inited\n", fifo->obj.name);

    return fifo;
}
fm_s32 mt6627_host_set_reg(fm_u8 *buf, fm_s32 buf_size, fm_u32 addr, fm_u32 value)
{
    if (buf_size < TX_BUF_SIZE) {
        return (-1);
    }

    buf[0] = FM_TASK_COMMAND_PKT_TYPE;
    buf[1] = FM_HOST_WRITE_OPCODE;
    buf[2] = 0x08;
    buf[3] = 0x00;
    buf[4] = (fm_u8)((addr) & 0x00FF);
    buf[5] = (fm_u8)((addr >> 8) & 0x00FF);
    buf[6] = (fm_u8)((addr >> 16) & 0x00FF);
    buf[7] = (fm_u8)((addr >> 24) & 0x00FF);
    buf[8] = (fm_u8)((value) & 0x00FF);
    buf[9] = (fm_u8)((value >> 8) & 0x00FF);
    buf[10] = (fm_u8)((value >> 16) & 0x00FF);
    buf[11] = (fm_u8)((value >> 24) & 0x00FF);

    WCN_DBG(FM_DBG | CHIP, "%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], buf[8], buf[9], buf[10], buf[11]);
    return 12;
}
static fm_s32 fm_bop_modify(fm_u8 addr, fm_u16 mask_and, fm_u16 mask_or, fm_u8 *buf, fm_s32 size)
{
    if (size < (FM_MODIFY_BASIC_OP_SIZE + 2)) {
        return (-1);
    }

    if (buf == NULL) {
        return (-2);
    }

    buf[0] = FM_MODIFY_BASIC_OP;
    buf[1] = FM_MODIFY_BASIC_OP_SIZE;
    buf[2] = addr;
    buf[3] = (fm_u8)((mask_and) & 0x00FF);
    buf[4] = (fm_u8)((mask_and >> 8) & 0x00FF);
    buf[5] = (fm_u8)((mask_or) & 0x00FF);
    buf[6] = (fm_u8)((mask_or >> 8) & 0x00FF);

    WCN_DBG(FM_DBG | CHIP, "%02x %02x %02x %02x %02x %02x %02x \n", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]);

    return (FM_MODIFY_BASIC_OP_SIZE + 2);
}
static fm_s32 fm_bop_rd_until(fm_u8 addr, fm_u16 mask, fm_u16 value, fm_u8 *buf, fm_s32 size)
{
    if (size < (FM_RD_UNTIL_BASIC_OP_SIZE + 2)) {
        return (-1);
    }

    if (buf == NULL) {
        return (-2);
    }

    buf[0] = FM_RD_UNTIL_BASIC_OP;
    buf[1] = FM_RD_UNTIL_BASIC_OP_SIZE;
    buf[2] = addr;
    buf[3] = (fm_u8)((mask) & 0x00FF);
    buf[4] = (fm_u8)((mask >> 8) & 0x00FF);
    buf[5] = (fm_u8)((value) & 0x00FF);
    buf[6] = (fm_u8)((value >> 8) & 0x00FF);

    WCN_DBG(FM_DBG | CHIP, "%02x %02x %02x %02x %02x %02x %02x \n", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]);

    return (FM_RD_UNTIL_BASIC_OP_SIZE + 2);
}
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;
}
/*
 * mt6628_rom_download - Wholechip FM Power Up: step 3,download rom to f/w,
 * @buf - target buf
 * @buf_size - buffer size
 * @seg_num - total segments that this patch divided into
 * @seg_id - No. of Segments: segment that will now be sent
 * @src - patch source buffer
 * @seg_len - segment size: segment that will now be sent
 * return package size
 */
fm_s32 mt6628_rom_download(fm_u8 *buf, fm_s32 buf_size, fm_u8 seg_num, fm_u8 seg_id, const fm_u8 *src, fm_s32 seg_len)
{
    fm_s32 pkt_size = 0;
    fm_u8 *dst = NULL;

    if (buf_size < TX_BUF_SIZE) {
        return (-1);
    }

    buf[0] = FM_TASK_COMMAND_PKT_TYPE;
    buf[1] = FM_ROM_DOWNLOAD_OPCODE;
    pkt_size = 4;

    buf[pkt_size++] = seg_num;
    buf[pkt_size++] = seg_id;

    if (seg_len > (buf_size - pkt_size)) {
        return -1;
    }

    dst = &buf[pkt_size];
    pkt_size += seg_len;

    //copy patch to tx buffer
    while (seg_len--) {
        *dst = *src;
        //printk(KERN_ALERT "%02x ", *dst);
        src++;
        dst++;
    }

    buf[2] = (fm_u8)((pkt_size - 4) & 0x00FF);
    buf[3] = (fm_u8)(((pkt_size - 4) >> 8) & 0x00FF);
    WCN_DBG(FM_DBG | CHIP, "%02x %02x %02x %02x %02x %02x %02x \n", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]);

    return pkt_size;
}
fm_s32 fm_eint_pin_cfg(fm_s32 mode)
{
    int ret = 0;

    WCN_DBG(FM_NTC | EINT, "%s\n", __func__);

    switch (mode) {
    case FM_EINT_PIN_EINT_MODE:
        mt_set_gpio_mode(GPIO_FM_RDS_PIN, GPIO_FM_RDS_PIN_M_GPIO);
        mt_set_gpio_pull_enable(GPIO_FM_RDS_PIN, GPIO_PULL_ENABLE);
        mt_set_gpio_pull_select(GPIO_FM_RDS_PIN, GPIO_PULL_UP);
        mt_set_gpio_mode(GPIO_FM_RDS_PIN, GPIO_FM_RDS_PIN_M_EINT);
        break;
    case FM_EINT_PIN_GPIO_MODE:
        mt_set_gpio_mode(GPIO_FM_RDS_PIN, GPIO_FM_RDS_PIN_M_GPIO);
        mt_set_gpio_dir(GPIO_FM_RDS_PIN, GPIO_DIR_IN);
        break;
    default:
        ret = -1;
        break;
    }

    return ret;
}
static fm_s32 fm_bop_top_write(fm_u16 addr, fm_u32 value, fm_u8 *buf, fm_s32 size)
{
    if (size < (FM_TOP_WRITE_BOP_SIZE + 2)) {
        return (-1);
    }

    if (buf == NULL) {
        return (-2);
    }

    buf[0] = FM_TOP_WRITE_BASIC_OP;
    buf[1] = FM_TOP_WRITE_BOP_SIZE;
    buf[2] = 04;
    buf[3] = (fm_u8)((addr) & 0x00FF);
    buf[4] = (fm_u8)((addr >> 8) & 0x00FF);
    buf[5] = (fm_u8)((value) & 0x00FF);
    buf[6] = (fm_u8)((value >> 8) & 0x00FF);
    buf[7] = (fm_u8)((value >> 16) & 0x00FF);
    buf[8] = (fm_u8)((value >> 24) & 0x00FF);

    WCN_DBG(FM_DBG | CHIP, "%02x %02x %02x %02x %02x %02x %02x %02x %02x\n", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], buf[8]);

    return (FM_TOP_WRITE_BOP_SIZE + 2);
}
fm_s32 fm_file_write(const fm_s8 *filename, fm_u8* dst, fm_s32 len, fm_s32 *ppos)
{
    fm_s32 ret = 0;
    loff_t pos = *ppos;
    mm_segment_t old_fs;
    struct file *fp = NULL;

    old_fs = get_fs();
    set_fs(KERNEL_DS);
    fp = filp_open(filename, O_CREAT | O_RDWR, 0);

    if (IS_ERR(fp)) {
        WCN_DBG(FM_ERR | CHIP, "open \"%s\" failed\n", filename);
        set_fs(old_fs);
        return -FM_EPATCH;
    } else {
        WCN_DBG(FM_NTC | CHIP, "open \"%s\" ok\n", filename);
    }

    WCN_DBG(FM_NTC | CHIP, "\"%s\" old pos %d\n", filename, pos);
    ret = vfs_write(fp, (char __user *)dst, len, &pos);
    WCN_DBG(FM_NTC | CHIP, "\"%s\" new pos %d\n", filename, pos);
    *ppos = pos;
    if (ret < 0) {
        WCN_DBG(FM_ERR | CHIP, "write \"%s\" failed\n", filename);
    } else if (ret < len) {
        WCN_DBG(FM_NTC | CHIP, "write \"%s\" data\n", filename);
    } 

    if (fp) {
        filp_close(fp, NULL);
    }

    set_fs(old_fs);

    return ret;
}
static fm_s32 MT6628fm_cust_config_print(fm_cust_cfg *cfg)
{
    WCN_DBG(FM_NTC | MAIN, "MT6628 rssi_l:\t%d\n", cfg->rx_cfg.long_ana_rssi_th);
    WCN_DBG(FM_NTC | MAIN, "MT6628 rssi_s:\t%d\n", cfg->rx_cfg.short_ana_rssi_th);
    WCN_DBG(FM_NTC | MAIN, "MT6628 pamd_th:\t%d\n", cfg->rx_cfg.pamd_th);
    WCN_DBG(FM_NTC | MAIN, "MT6628 mr_th:\t%d\n", cfg->rx_cfg.mr_th);
    WCN_DBG(FM_NTC | MAIN, "MT6628 atdc_th:\t%d\n", cfg->rx_cfg.atdc_th);
    WCN_DBG(FM_NTC | MAIN, "MT6628 prx_th:\t%d\n", cfg->rx_cfg.prx_th);
    WCN_DBG(FM_NTC | MAIN, "MT6628 atdev_th:\t%d\n", cfg->rx_cfg.atdev_th);
    WCN_DBG(FM_NTC | MAIN, "MT6628 smg_th:\t%d\n", cfg->rx_cfg.smg_th);
    WCN_DBG(FM_NTC | MAIN, "de_emphasis:\t%d\n", cfg->rx_cfg.deemphasis);
    WCN_DBG(FM_NTC | MAIN, "osc_freq:\t%d\n", cfg->rx_cfg.osc_freq);

    WCN_DBG(FM_NTC | MAIN, "aud path[%d]I2S state[%d]mode[%d]rate[%d]\n", cfg->aud_cfg.aud_path,cfg->aud_cfg.i2s_info.status,cfg->aud_cfg.i2s_info.mode,cfg->aud_cfg.i2s_info.rate);
    return 0;
}
fm_u16 MT6628fm_cust_config_fetch(enum fm_cust_cfg_op op_code)
{
#if 0
    fm_u16 tmp = 0;
    fm_s32 i;
    static fm_s32 fake_ch_idx = 0;

    switch (op_code) {
        //For FM RX
    case FM_CFG_RX_RSSI_TH_LONG: {
        tmp = mt6628_fm_config.rx_cfg.long_ana_rssi_th;
        break;
    }
    case FM_CFG_RX_RSSI_TH_SHORT: {
        tmp = mt6628_fm_config.rx_cfg.short_ana_rssi_th;
        break;
    }
    case FM_CFG_RX_CQI_TH: {
        tmp = mt6628_fm_config.rx_cfg.cqi_th;
        break;
    }
    case FM_CFG_RX_MR_TH: {
        tmp = mt6628_fm_config.rx_cfg.mr_th;
        break;
    }
    case FM_CFG_RX_SMG_TH: {
        tmp = mt6628_fm_config.rx_cfg.smg_th;
        break;
    }
    case FM_CFG_RX_SCAN_CH_SIZE: {
        tmp = mt6628_fm_config.rx_cfg.scan_ch_size;
        break;
    }
    case FM_CFG_RX_SEEK_SPACE: {
        tmp = mt6628_fm_config.rx_cfg.seek_space;
        break;
    }
    case FM_CFG_RX_BAND: {
        tmp = mt6628_fm_config.rx_cfg.band;
        break;
    }
    case FM_CFG_RX_BAND_FREQ_L: {
        tmp = mt6628_fm_config.rx_cfg.band_freq_l;
        break;
    }
    case FM_CFG_RX_BAND_FREQ_H: {
        tmp = mt6628_fm_config.rx_cfg.band_freq_h;
        break;
    }
    case FM_CFG_RX_SCAN_SORT: {
        tmp = mt6628_fm_config.rx_cfg.scan_sort;
        break;
    }
    case FM_CFG_RX_FAKE_CH_NUM: {
        tmp = mt6628_fm_config.rx_cfg.fake_ch_num;
        break;
    }
    case FM_CFG_RX_FAKE_CH: {
        tmp = mt6628_fm_config.rx_cfg.fake_ch[fake_ch_idx];
        i = (mt6628_fm_config.rx_cfg.fake_ch_num > 0) ? mt6628_fm_config.rx_cfg.fake_ch_num : FAKE_CH_MAX;
        fake_ch_idx++;
        fake_ch_idx = fake_ch_idx % i;
        break;
    }
    case FM_CFG_RX_FAKE_CH_RSSI: {
        tmp = mt6628_fm_config.rx_cfg.fake_ch_rssi_th;
        break;
    }
    case FM_CFG_RX_DEEMPHASIS: {
        tmp = mt6628_fm_config.rx_cfg.deemphasis;
        break;
    }
    case FM_CFG_RX_OSC_FREQ: {
        tmp = mt6628_fm_config.rx_cfg.osc_freq;
        break;
    }
    //For FM TX
    case FM_CFG_TX_SCAN_HOLE_LOW: {
        tmp = mt6628_fm_config.tx_cfg.scan_hole_low;
        break;
    }
    case FM_CFG_TX_SCAN_HOLE_HIGH: {
        tmp = mt6628_fm_config.tx_cfg.scan_hole_high;
        break;
    }
    case FM_CFG_TX_PWR_LEVEL: {
        tmp = mt6628_fm_config.tx_cfg.power_level;
        break;
    }
    default:
        break;
    }

    WCN_DBG(FM_DBG | MAIN, "mt6628_cust cfg %d: 0x%04x\n", op_code, tmp);
#endif
    return 0;
}
Esempio n. 12
0
/*
 * fm_cmd_tx() - send cmd to FM firmware and wait event
 * @buf - send buffer
 * @len - the length of cmd
 * @mask - the event flag mask
 * @	cnt - the retry conter
 * @timeout - timeout per cmd
 * Return 0, if success; error code, if failed
 */
fm_s32 fm_cmd_tx(fm_u8 *buf, fm_u16 len, fm_s32 mask, fm_s32 cnt, fm_s32 timeout,
		 fm_s32(*callback) (struct fm_res_ctx *result))
{
	fm_s32 ret_time = 0;
	struct task_struct *task = current;
	struct fm_trace_t trace;

	if ((NULL == buf) || (len < 0) || (0 == mask)
	    || (cnt > SW_RETRY_CNT_MAX) || (timeout > SW_WAIT_TIMEOUT_MAX)) {
		WCN_DBG(FM_ERR | LINK, "cmd tx, invalid para\n");
		return -FM_EPARA;
	}

	FM_EVENT_CLR(link_event->ln_event, mask);

#ifdef FM_TRACE_ENABLE
	trace.type = buf[0];
	trace.opcode = buf[1];
	trace.len = len - 4;
	trace.tid = (fm_s32) task->pid;
	fm_memset(trace.pkt, 0, FM_TRACE_PKT_SIZE);
	fm_memcpy(trace.pkt, &buf[4],
		  (trace.len > FM_TRACE_PKT_SIZE) ? FM_TRACE_PKT_SIZE : trace.len);
#endif

 sw_retry:

#ifdef FM_TRACE_ENABLE
	if (fm_true == FM_TRACE_FULL(cmd_fifo)) {
		FM_TRACE_OUT(cmd_fifo, NULL);
	}
	FM_TRACE_IN(cmd_fifo, &trace);
#endif

	/* send cmd to FM firmware */
	if ((ret_time = mtk_wcn_stp_send_data(buf, len, FM_TASK_INDX)) <= 0) {
		WCN_DBG(FM_EMG | LINK, "send data over stp failed[%d]\n", ret_time);
		return -FM_ELINK;
	}
	/* wait the response form FM firmware */
	ret_time = FM_EVENT_WAIT_TIMEOUT(link_event->ln_event, mask, timeout);

	if (!ret_time) {
		if (0 < cnt--) {
			WCN_DBG(FM_WAR | LINK, "wait even timeout, [retry_cnt=%d], pid=%d\n", cnt,
				task->pid);
			fm_print_cmd_fifo();
			fm_print_evt_fifo();
			return -FM_EFW;
			goto sw_retry;	/* retry if timeout and retry cnt > 0 */
		} else {
			WCN_DBG(FM_ALT | LINK, "fatal error, SW retry failed, reset HW\n");
			return -FM_EFW;
		}
	}

	FM_EVENT_CLR(link_event->ln_event, mask);

	if (callback) {
		callback(&link_event->result);
	}

	return 0;
}
Esempio n. 13
0
fm_s32 fm_event_parser(fm_s32(*rds_parser) (struct rds_rx_t *, fm_s32))
{
	fm_s32 len;
	fm_s32 i = 0;
	fm_u8 opcode = 0;
	fm_u16 length = 0;
	fm_u8 ch;
	fm_u8 rx_buf[RX_BUF_SIZE + 10] = { 0 };	/* the 10 bytes are protect gaps */
	static volatile fm_task_parser_state state = FM_TASK_RX_PARSER_PKT_TYPE;
	struct fm_trace_t trace;
	struct task_struct *task = current;

	len = mtk_wcn_stp_receive_data(rx_buf, RX_BUF_SIZE, FM_TASK_INDX);
	WCN_DBG(FM_DBG | LINK, "[len=%d],[CMD=0x%02x 0x%02x 0x%02x 0x%02x]\n", len, rx_buf[0],
		rx_buf[1], rx_buf[2], rx_buf[3]);

	while (i < len) {
		ch = rx_buf[i];

		switch (state) {
		case FM_TASK_RX_PARSER_PKT_TYPE:

			if (ch == FM_TASK_EVENT_PKT_TYPE) {
				if ((i + 5) < RX_BUF_SIZE) {
					WCN_DBG(FM_DBG | LINK,
						"0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n",
						rx_buf[i], rx_buf[i + 1], rx_buf[i + 2],
						rx_buf[i + 3], rx_buf[i + 4], rx_buf[i + 5]);
				} else {
					WCN_DBG(FM_DBG | LINK, "0x%02x 0x%02x\n", rx_buf[i],
						rx_buf[i + 1]);
				}

				state = FM_TASK_RX_PARSER_OPCODE;
			} else {
				WCN_DBG(FM_ALT | LINK,
					"event pkt type error (rx_buf[%d] = 0x%02x)\n", i, ch);
			}

			i++;
			break;

		case FM_TASK_RX_PARSER_OPCODE:
			i++;
			opcode = ch;
			state = FM_TASK_RX_PARSER_PKT_LEN_1;
			break;

		case FM_TASK_RX_PARSER_PKT_LEN_1:
			i++;
			length = ch;
			state = FM_TASK_RX_PARSER_PKT_LEN_2;
			break;

		case FM_TASK_RX_PARSER_PKT_LEN_2:
			i++;
			length |= (fm_u16) (ch << 0x8);

#ifdef FM_TRACE_ENABLE
			trace.type = FM_TASK_EVENT_PKT_TYPE;
			trace.opcode = opcode;
			trace.len = length;
			trace.tid = (fm_s32) task->pid;
			fm_memset(trace.pkt, 0, FM_TRACE_PKT_SIZE);
			fm_memcpy(trace.pkt, &rx_buf[i],
				  (length > FM_TRACE_PKT_SIZE) ? FM_TRACE_PKT_SIZE : length);

			if (fm_true == FM_TRACE_FULL(cmd_fifo)) {
				FM_TRACE_OUT(cmd_fifo, NULL);
			}
			FM_TRACE_IN(cmd_fifo, &trace);
#endif
			if (length > 0) {
				state = FM_TASK_RX_PARSER_PKT_PAYLOAD;
			} else if (opcode == CSPI_WRITE_OPCODE) {
				state = FM_TASK_RX_PARSER_PKT_TYPE;
				FM_EVENT_SEND(link_event->ln_event, FLAG_CSPI_WRITE);
			} else {
				state = FM_TASK_RX_PARSER_PKT_TYPE;
				FM_EVENT_SEND(link_event->ln_event, (1 << opcode));
			}

			break;

		case FM_TASK_RX_PARSER_PKT_PAYLOAD:

			switch (opcode) {
			case FM_TUNE_OPCODE:

				if ((length == 1) && (rx_buf[i] == 1)) {
					FM_EVENT_SEND(link_event->ln_event, FLAG_TUNE_DONE);
				}

				break;

			case FM_SOFT_MUTE_TUNE_OPCODE:

				if (length >= 2) {
					fm_memcpy(link_event->result.cqi, &rx_buf[i],
						  (length >
						   FM_CQI_BUF_SIZE) ? FM_CQI_BUF_SIZE : length);
					FM_EVENT_SEND(link_event->ln_event, FLAG_SM_TUNE);
				}
				break;

			case FM_SEEK_OPCODE:

				if ((i + 1) < RX_BUF_SIZE) {
					link_event->result.seek_result = rx_buf[i] + (rx_buf[i + 1] << 8);	/* 8760 means 87.60Mhz */
				}

				FM_EVENT_SEND(link_event->ln_event, FLAG_SEEK_DONE);
				break;

			case FM_SCAN_OPCODE:

				/* check if the result data is long enough */
				if ((RX_BUF_SIZE - i) < (sizeof(fm_u16) * FM_SCANTBL_SIZE)) {
					WCN_DBG(FM_ALT | LINK,
						"FM_SCAN_OPCODE err, [tblsize=%d],[bufsize=%d]\n",
						(unsigned int)(sizeof(fm_u16) * FM_SCANTBL_SIZE),
						(unsigned int)(RX_BUF_SIZE - i));
					FM_EVENT_SEND(link_event->ln_event, FLAG_SCAN_DONE);
					return 0;
				} else if ((length >= FM_CQI_BUF_SIZE)
					   && ((RX_BUF_SIZE - i) >= FM_CQI_BUF_SIZE)) {
					fm_memcpy(link_event->result.cqi, &rx_buf[i],
						  FM_CQI_BUF_SIZE);
					FM_EVENT_SEND(link_event->ln_event, FLAG_CQI_DONE);
				} else {
					fm_memcpy(link_event->result.scan_result, &rx_buf[i],
						  sizeof(fm_u16) * FM_SCANTBL_SIZE);
					FM_EVENT_SEND(link_event->ln_event, FLAG_SCAN_DONE);
				}

				break;

			case FSPI_READ_OPCODE:

				if ((i + 1) < RX_BUF_SIZE) {
					link_event->result.fspi_rd =
					    (rx_buf[i] + (rx_buf[i + 1] << 8));
				}

				FM_EVENT_SEND(link_event->ln_event, (1 << opcode));
				break;
			case CSPI_READ_OPCODE:
				{
					if ((i + 1) < RX_BUF_SIZE) {
						link_event->result.cspi_rd =
						    (rx_buf[i] + (rx_buf[i + 1] << 8) +
						     (rx_buf[i + 2] << 16) + (rx_buf[i + 3] << 24));
					}

					FM_EVENT_SEND(link_event->ln_event, FLAG_CSPI_READ);
					break;
				}
			case FM_HOST_READ_OPCODE:
				{
					if ((i + 1) < RX_BUF_SIZE) {
						link_event->result.cspi_rd =
						    (rx_buf[i] + (rx_buf[i + 1] << 8) +
						     (rx_buf[i + 2] << 16) + (rx_buf[i + 3] << 24));
					}

					FM_EVENT_SEND(link_event->ln_event, (1 << opcode));
					break;
				}

			case RDS_RX_DATA_OPCODE:

				/* check if the rds data is long enough */
				if ((RX_BUF_SIZE - i) < length) {
					WCN_DBG(FM_ALT | LINK,
						"RDS RX err, [rxlen=%d],[bufsize=%d]\n",
						(fm_s32) length, (RX_BUF_SIZE - i));
					FM_EVENT_SEND(link_event->ln_event, (1 << opcode));
					break;
				}
				/* copy rds data to rds buf */
				fm_memcpy(&link_event->result.rds_rx_result, &rx_buf[i], length);

				/*Handle the RDS data that we get */
				if (rds_parser) {
					rds_parser(&link_event->result.rds_rx_result, length);
				} else {
					WCN_DBG(FM_WAR | LINK, "no method to parse RDS data\n");
				}

				FM_EVENT_SEND(link_event->ln_event, (1 << opcode));
				break;

			default:
				FM_EVENT_SEND(link_event->ln_event, (1 << opcode));
				break;
			}

			state = FM_TASK_RX_PARSER_PKT_TYPE;
			i += length;
			break;

		default:
			break;
		}
	}

	return 0;
}
fm_s32 fm_disable_eint(void)
{
    WCN_DBG(FM_INF | EINT, "%s\n", __func__);
    fm_eint_ops.mask(CUST_EINT_FM_RDS_NUM);
    return 0;
}
Esempio n. 15
0
static fm_s32 fm_cust_config_print(fm_cust_cfg *cfg)
{
	fm_s32 i;

	WCN_DBG(FM_NTC | MAIN, "rssi_l:\t0x%04x\n", cfg->rx_cfg.long_ana_rssi_th);
	WCN_DBG(FM_NTC | MAIN, "rssi_s:\t0x%04x\n", cfg->rx_cfg.short_ana_rssi_th);
	WCN_DBG(FM_NTC | MAIN, "mr_th:\t0x%04x\n", cfg->rx_cfg.mr_th);
	WCN_DBG(FM_NTC | MAIN, "cqi_th:\t0x%04x\n", cfg->rx_cfg.cqi_th);
	WCN_DBG(FM_NTC | MAIN, "smg_th:\t0x%04x\n", cfg->rx_cfg.smg_th);
	WCN_DBG(FM_NTC | MAIN, "scan_ch_size:\t%d\n", cfg->rx_cfg.scan_ch_size);
	WCN_DBG(FM_NTC | MAIN, "seek_space:\t%d\n", cfg->rx_cfg.seek_space);
	WCN_DBG(FM_NTC | MAIN, "band:\t%d\n", cfg->rx_cfg.band);
	WCN_DBG(FM_NTC | MAIN, "band_freq_l:\t%d\n", cfg->rx_cfg.band_freq_l);
	WCN_DBG(FM_NTC | MAIN, "band_freq_h:\t%d\n", cfg->rx_cfg.band_freq_h);
	WCN_DBG(FM_NTC | MAIN, "scan_sort:\t%d\n", cfg->rx_cfg.scan_sort);
	WCN_DBG(FM_NTC | MAIN, "fake_ch_num:\t%d\n", cfg->rx_cfg.fake_ch_num);
	WCN_DBG(FM_NTC | MAIN, "fake_ch_rssi_th:\t%d\n", cfg->rx_cfg.fake_ch_rssi_th);

	for (i = 0; i < cfg->rx_cfg.fake_ch_num; i++)
		WCN_DBG(FM_NTC | MAIN, "fake_ch:\t%d\n", cfg->rx_cfg.fake_ch[i]);

	WCN_DBG(FM_NTC | MAIN, "de_emphasis:\t%d\n", cfg->rx_cfg.deemphasis);
	WCN_DBG(FM_NTC | MAIN, "osc_freq:\t%d\n", cfg->rx_cfg.osc_freq);
	WCN_DBG(FM_NTC | MAIN, "scan_hole_low:\t%d\n", cfg->tx_cfg.scan_hole_low);
	WCN_DBG(FM_NTC | MAIN, "scan_hole_high:\t%d\n", cfg->tx_cfg.scan_hole_high);
	WCN_DBG(FM_NTC | MAIN, "power_level:\t%d\n", cfg->tx_cfg.power_level);

	return 0;
}
Esempio n. 16
0
static fm_s32 cfg_item_handler(fm_s8 *grp, fm_s8 *key, fm_s8 *val, fm_cust_cfg *cfg)
{
	fm_s32 ret = 0;
	struct fm_rx_cust_cfg *rx_cfg = &cfg->rx_cfg;
	struct fm_tx_cust_cfg *tx_cfg = &cfg->tx_cfg;

	ret = cfg_item_match(key, val, "FMR_RSSI_TH_L", &rx_cfg->long_ana_rssi_th);
	if (0 <= ret)
		return ret;

	ret = cfg_item_match(key, val, "FMR_RSSI_TH_S", &rx_cfg->short_ana_rssi_th);
	if (0 <= ret)
		return ret;

	ret = cfg_item_match(key, val, "FMR_CQI_TH", &rx_cfg->cqi_th);
	if (0 <= ret)
		return ret;

	ret = cfg_item_match(key, val, "FMR_MR_TH", &rx_cfg->mr_th);
	if (0 <= ret)
		return ret;

	ret = cfg_item_match(key, val, "FMR_SMG_TH", &rx_cfg->smg_th);
	if (0 <= ret)
		return ret;

	ret = cfg_item_match(key, val, "FMR_SCAN_CH_SIZE", &rx_cfg->scan_ch_size);
	if (0 <= ret)
		return ret;

	ret = cfg_item_match(key, val, "FMR_SCAN_SORT", &rx_cfg->scan_sort);
	if (0 <= ret)
		return ret;

	ret = cfg_item_match(key, val, "FMR_SEEK_SPACE", &rx_cfg->seek_space);
	if (0 <= ret)
		return ret;

	ret = cfg_item_match(key, val, "FMR_BAND", &rx_cfg->band);
	if (0 <= ret)
		return ret;

	ret = cfg_item_match(key, val, "FMR_BAND_FREQ_L", &rx_cfg->band_freq_l);
	if (0 <= ret)
		return ret;

	ret = cfg_item_match(key, val, "FMR_BAND_FREQ_H", &rx_cfg->band_freq_h);
	if (0 <= ret)
		return ret;

	ret = cfg_item_match(key, val, "FMR_FAKE_CH", &rx_cfg->fake_ch[fm_index]);
	if (0 <= ret) {
		fm_index += 1;
		rx_cfg->fake_ch_num = (rx_cfg->fake_ch_num < fm_index) ? fm_index : rx_cfg->fake_ch_num;
		return ret;
	}

	ret = cfg_item_match(key, val, "FMR_FAKE_CH_RSSI", &rx_cfg->fake_ch_rssi_th);
	if (0 <= ret)
		return ret;

	ret = cfg_item_match(key, val, "FMR_DEEMPHASIS", &rx_cfg->deemphasis);
	if (0 <= ret)
		return ret;

	ret = cfg_item_match(key, val, "FMR_OSC_FREQ", &rx_cfg->osc_freq);
	if (0 <= ret)
		return ret;

	ret = cfg_item_match(key, val, "FMT_SCAN_HOLE_L", &tx_cfg->scan_hole_low);
	if (0 <= ret)
		return ret;

	ret = cfg_item_match(key, val, "FMT_SCAN_HOLE_H", &tx_cfg->scan_hole_high);
	if (0 <= ret)
		return ret;

	ret = cfg_item_match(key, val, "FMT_PWR_LVL_MAX", &tx_cfg->power_level);
	if (0 <= ret)
		return ret;

	WCN_DBG(FM_WAR | MAIN, "invalid key\n");
	return -1;
}
static int fm_i2c_remove(struct i2c_client *client)
{
    WCN_DBG(FM_NTC | LINK, "%s\n", __func__);
    return 0;
}
Esempio n. 18
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;
}
static fm_s32 MT6620fm_cust_config_print(fm_cust_cfg *cfg)
{
	/* fm_s32 i; */

	WCN_DBG(FM_NTC | MAIN, "MT6620 rssi_l:\t%d\n", cfg->rx_cfg.long_ana_rssi_th);
	WCN_DBG(FM_NTC | MAIN, "MT6620 rssi_s:\t%d\n", cfg->rx_cfg.short_ana_rssi_th);
	WCN_DBG(FM_NTC | MAIN, "MT6620 pamd_th:\t%d\n", cfg->rx_cfg.pamd_th);
	WCN_DBG(FM_NTC | MAIN, "MT6620 mr_th:\t%d\n", cfg->rx_cfg.mr_th);
	WCN_DBG(FM_NTC | MAIN, "MT6620 atdc_th:\t%d\n", cfg->rx_cfg.atdc_th);
	WCN_DBG(FM_NTC | MAIN, "MT6620 prx_th:\t%d\n", cfg->rx_cfg.prx_th);
	WCN_DBG(FM_NTC | MAIN, "MT6620 atdev_th:\t%d\n", cfg->rx_cfg.atdev_th);
	WCN_DBG(FM_NTC | MAIN, "MT6620 smg_th:\t%d\n", cfg->rx_cfg.smg_th);
	WCN_DBG(FM_NTC | MAIN, "MT6620 de_emphasis:\t%d\n", cfg->rx_cfg.deemphasis);
	WCN_DBG(FM_NTC | MAIN, "MT6620 osc_freq:\t%d\n", cfg->rx_cfg.osc_freq);

	WCN_DBG(FM_NTC | MAIN, "MT6620 scan_hole_low:\t%d\n", cfg->tx_cfg.scan_hole_low);
	WCN_DBG(FM_NTC | MAIN, "MT6620 scan_hole_high:\t%d\n", cfg->tx_cfg.scan_hole_high);
	WCN_DBG(FM_NTC | MAIN, "MT6620 power_level:\t%d\n", cfg->tx_cfg.power_level);

	WCN_DBG(FM_NTC | MAIN, "aud path[%d]I2S state[%d]mode[%d]rate[%d]\n", cfg->aud_cfg.aud_path,
		cfg->aud_cfg.i2s_info.status, cfg->aud_cfg.i2s_info.mode,
		cfg->aud_cfg.i2s_info.rate);
	return 0;
}
static int fm_i2c_detect(struct i2c_client *client, int kind, struct i2c_board_info *info)
{
    WCN_DBG(FM_NTC | LINK, "%s\n", __func__);
    strcpy(info->type, MT6626_DEV);
    return 0;
}