4 * MediaTek <www.MediaTek.com>
5 * Hongcheng <hongcheng.xia@MediaTek.com>
7 * FM Radio Driver -- main functions
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 #include <linux/kernel.h>
24 #include <linux/init.h>
25 #include <linux/module.h>
26 #include <linux/platform_device.h>
27 #include <linux/device.h>
29 #include <linux/proc_fs.h>
30 #include <linux/cdev.h>
31 #include <linux/interrupt.h>
32 #include <asm/uaccess.h>
33 #include <linux/sched.h>
34 #include <linux/delay.h> // udelay()
36 #include "fm_config.h"
40 #define FM_PROC_FILE "fm"
42 fm_u32 g_dbg_level = 0xfffffff5; // Debug level of FM
44 //fm main data structure
45 static struct fm *g_fm = NULL;
47 static struct proc_dir_entry *g_fm_proc = NULL;
49 //char device interface
50 static fm_s32 fm_cdev_setup(struct fm *fm);
51 static fm_s32 fm_cdev_destroy(struct fm *fm);
53 static long fm_ops_ioctl(struct file *filp, fm_u32 cmd, unsigned long arg);
54 static loff_t fm_ops_lseek(struct file *filp, loff_t off, fm_s32 whence);
55 static ssize_t fm_ops_read(struct file *filp, char *buf, size_t len, loff_t *off);
56 static fm_s32 fm_ops_open(struct inode *inode, struct file *filp);
57 static fm_s32 fm_ops_release(struct inode *inode, struct file *filp);
58 static fm_s32 fm_ops_flush(struct file *filp,fl_owner_t Id);
59 static struct file_operations fm_ops = {
61 .unlocked_ioctl = fm_ops_ioctl,
62 .llseek = fm_ops_lseek,
65 .release = fm_ops_release,
66 .flush = fm_ops_flush,
69 static fm_s32 fm_proc_read(char *page, char **start, off_t off, fm_s32 count, fm_s32 *eof, void *data);
70 static fm_s32 fm_proc_write(struct file *file, const char *buffer, unsigned long count, void *data);
73 static struct fm_scan_t parm = {
75 .sr.ch_rssi_buf = NULL,
78 static long fm_ops_ioctl(struct file *filp, fm_u32 cmd, unsigned long arg)
81 struct fm_platform *plat = container_of(filp->f_dentry->d_inode->i_cdev, struct fm_platform, cdev);
82 struct fm *fm = container_of(plat, struct fm, platform);
84 WCN_DBG(FM_NTC | MAIN, "%s---pid(%d)---cmd(0x%08x)---arg(0x%08x)\n", current->comm, current->pid, cmd, (fm_u32)arg);
86 if (fm_sys_state_get(fm) != FM_SUBSYS_RST_OFF) {
87 WCN_DBG(FM_ALT | MAIN, "FM subsys is resetting, retry later\n");
93 case FM_IOCTL_POWERUP: {
94 struct fm_tune_parm parm;
95 WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_POWERUP:0\n");
97 if (copy_from_user(&parm, (void*)arg, sizeof(struct fm_tune_parm))) {
102 ret = fm_powerup(fm, &parm);
103 if (ret < 0) goto out;
104 ret = fm_tune(fm, &parm);
105 if (ret < 0) goto out;
107 if (copy_to_user((void*)arg, &parm, sizeof(struct fm_tune_parm))) {
111 WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_POWERUP:1\n");
116 case FM_IOCTL_POWERDOWN: {
117 WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_POWERDOWN:0\n");
118 ret = fm_powerdown(fm);
119 WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_POWERDOWN:1\n");
123 case FM_IOCTL_TUNE: {
124 struct fm_tune_parm parm;
125 WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_TUNE:0\n");
127 if (copy_from_user(&parm, (void*)arg, sizeof(struct fm_tune_parm))) {
132 ret = fm_tune(fm, &parm);
137 if (copy_to_user((void*)arg, &parm, sizeof(struct fm_tune_parm))) {
142 WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_TUNE:1\n");
146 case FM_IOCTL_SOFT_MUTE_TUNE:
148 struct fm_softmute_tune_t parm;
149 fm_cqi_log();//cqi log tool
150 WCN_DBG(FM_NTC | MAIN,"......FM_IOCTL_SOFT_MUTE_TUNE......\n");
151 if(copy_from_user(&parm, (void*)arg, sizeof(struct fm_softmute_tune_t)))
157 ret = fm_soft_mute_tune(fm, &parm);
162 if(copy_to_user((void*)arg, &parm, sizeof(struct fm_softmute_tune_t)))
169 case FM_IOCTL_SEEK: {
170 struct fm_seek_parm parm;
171 WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_SEEK:0\n");
173 if (copy_from_user(&parm, (void*)arg, sizeof(struct fm_seek_parm))) {
178 ret = fm_seek(fm, &parm);
183 if (copy_to_user((void*)arg, &parm, sizeof(struct fm_seek_parm))) {
187 WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_SEEK:1\n");
191 case FM_IOCTL_SCAN: {
192 struct fm_scan_parm parm;
193 WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_SCAN start\n");
195 if (copy_from_user(&parm, (void*)arg, sizeof(struct fm_scan_parm))) {
196 WCN_DBG(FM_ALT | MAIN, "copy_from_user failed\n");
201 ret = fm_scan(fm, &parm);
202 if (ret < 0) goto out;
204 if (copy_to_user((void*)arg, &parm, sizeof(struct fm_scan_parm))) {
209 WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_SCAN end\n");
213 case FM_IOCTL_TUNE_NEW: {
214 struct fm_tune_t tune_tmp;
216 WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_TUNE_NEW\n");
218 if (copy_from_user(&tune_tmp, (void*)arg, sizeof(struct fm_tune_t))) {
219 WCN_DBG(FM_ERR | MAIN, "tune new copy_from_user error\n");
224 ret = fm_tune_new(fm, &tune_tmp);
229 if (copy_to_user((void*)arg, &tune_tmp, sizeof(struct fm_tune_t))) {
230 WCN_DBG(FM_ERR | MAIN, "tune new copy_to_user error\n");
238 case FM_IOCTL_SEEK_NEW: {
239 struct fm_seek_t seek_tmp;
241 WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_SEEK_NEW\n");
243 if (copy_from_user(&seek_tmp, (void*)arg, sizeof(struct fm_seek_t))) {
244 WCN_DBG(FM_ERR | MAIN, "seek new copy_from_user error\n");
249 ret = fm_seek_new(fm, &seek_tmp);
254 if (copy_to_user((void*)arg, &seek_tmp, sizeof(struct fm_seek_t))) {
255 WCN_DBG(FM_ERR | MAIN, "seek new copy_to_user error\n");
263 case FM_IOCTL_SCAN_NEW: {
264 struct fm_scan_t tmp;
266 WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_SCAN_NEW\n");
268 if (copy_from_user(&tmp, (void*)arg, sizeof(struct fm_scan_t))) {
269 WCN_DBG(FM_ERR | MAIN, "copy_from_user error\n");
275 case FM_SCAN_CMD_START:
276 if ((tmp.upper > 10800) || (tmp.lower < 7600) || (tmp.space < 5) || (tmp.space > 20)) {
277 WCN_DBG(FM_ERR | MAIN, "scan para error\n");
282 parm.lower = tmp.lower;
283 parm.upper = tmp.upper;
284 parm.space = tmp.space;
286 ret = fm_scan_new(fm, &parm);
292 case FM_SCAN_CMD_GET_CH_RSSI:
293 if (parm.sr_size && parm.sr.ch_rssi_buf) {
294 if (copy_to_user(tmp.sr.ch_rssi_buf, parm.sr.ch_rssi_buf, parm.num * sizeof(struct fm_ch_rssi))) {
295 WCN_DBG(FM_ERR | MAIN, "scan copy_to_user err\n");
307 if (copy_to_user((void*)arg, &tmp, sizeof(struct fm_scan_t))) {
308 WCN_DBG(FM_ERR | MAIN, "copy_to_user error\n");
315 case FM_IOCTL_CQI_GET: {
316 struct fm_cqi_req cqi_req;
320 WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_CQI_GET\n");
322 if (copy_from_user(&cqi_req, (void*)arg, sizeof(struct fm_cqi_req))) {
323 WCN_DBG(FM_ALT | MAIN, "copy_from_user failed\n");
328 if ((cqi_req.ch_num*sizeof(struct fm_cqi) > cqi_req.buf_size) || !cqi_req.cqi_buf) {
333 tmp = cqi_req.ch_num / 16 + ((cqi_req.ch_num % 16) ? 1 : 0);
334 tmp = tmp * 16 * sizeof(struct fm_cqi);
335 buf = fm_zalloc(tmp);
342 ret = fm_cqi_get(fm, cqi_req.ch_num, buf, tmp);
346 WCN_DBG(FM_ALT | MAIN, "get cqi failed\n");
350 if (copy_to_user((void*)cqi_req.cqi_buf, buf, cqi_req.ch_num*sizeof(struct fm_cqi))) {
360 case FM_IOCTL_GET_HW_INFO: {
361 struct fm_hw_info info;
363 WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_GET_HW_INFO\n");
365 ret = fm_get_hw_info(fm, &info);
368 WCN_DBG(FM_ALT | MAIN, "get hw info failed\n");
372 if (copy_to_user((void*)arg, &info, sizeof(struct fm_hw_info))) {
380 case FM_IOCTL_GET_I2S_INFO: {
381 struct fm_i2s_info i2sinfo;
383 WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_GET_I2S_INFO\n");
385 ret = fm_get_i2s_info(fm, &i2sinfo);
388 WCN_DBG(FM_ALT | MAIN, "get i2s info failed\n");
392 if (copy_to_user((void*)arg, &i2sinfo, sizeof(struct fm_i2s_info))) {
400 case FM_IOCTL_SETVOL: {
403 WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_SETVOL start\n");
404 if (copy_from_user(&vol, (void*)arg, sizeof(fm_u32))) {
405 WCN_DBG(FM_ALT | MAIN, "copy_from_user failed\n");
410 ret = fm_setvol(fm, vol);
411 WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_SETVOL end:%d\n", vol);
414 case FM_IOCTL_GETVOL: {
416 WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_GETVOL start\n");
417 ret = fm_getvol(fm, &vol);
418 if (ret < 0) goto out;
420 if (copy_to_user((void*)arg, &vol, sizeof(fm_u32))) {
425 WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_GETVOL end=%d\n",vol);
429 case FM_IOCTL_MUTE: {
432 WCN_DBG(FM_NTC| MAIN, "FM_IOCTL_MUTE start\n");
433 if (copy_from_user(&bmute, (void*)arg, sizeof(fm_u32))) {
438 ret = fm_mute(fm, bmute);
439 WCN_DBG(FM_NTC| MAIN, "FM_IOCTL_MUTE end-%d\n", bmute);
443 case FM_IOCTL_GETRSSI: {
446 ret = fm_getrssi(fm, &rssi);
447 if (ret < 0) goto out;
449 if (copy_to_user((void*)arg, &rssi, sizeof(fm_s32))) {
454 WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_GETRSSI:%d\n",rssi);
458 case FM_IOCTL_RW_REG: {
459 struct fm_ctl_parm parm_ctl;
460 WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_RW_REG\n");
462 if (copy_from_user(&parm_ctl, (void*)arg, sizeof(struct fm_ctl_parm))) {
467 if (parm_ctl.rw_flag == 0) {
468 ret = fm_reg_write(fm, parm_ctl.addr, parm_ctl.val);
470 ret = fm_reg_read(fm, parm_ctl.addr, &parm_ctl.val);
472 if (ret < 0) goto out;
474 if ((parm_ctl.rw_flag == 0x01) && (!ret)) {
475 if (copy_to_user((void*)arg, &parm_ctl, sizeof(struct fm_ctl_parm))) {
484 case FM_IOCTL_GETCHIPID: {
487 ret = fm_chipid_get(fm, &chipid);
488 WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_GETCHIPID:%04x\n", chipid);
489 if (ret < 0) goto out;
491 if (copy_to_user((void*)arg, &chipid, sizeof(fm_u16))) {
498 case FM_IOCTL_GETMONOSTERO: {
501 ret = fm_monostereo_get(fm, &usStereoMono);
502 WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_GETMONOSTERO:%04x\n", usStereoMono);
503 if (ret < 0) goto out;
505 if (copy_to_user((void*)arg, &usStereoMono, sizeof(fm_u16))) {
512 case FM_IOCTL_SETMONOSTERO: {
513 WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_SETMONOSTERO, %d\n", (fm_s32)arg);
514 ret = fm_monostereo_set(fm, (fm_s32)arg);
518 case FM_IOCTL_GETCURPAMD: {
521 ret = fm_pamd_get(fm, &PamdLevl);
522 WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_GETCURPAMD:%d\n", PamdLevl);
523 if (ret < 0) goto out;
525 if (copy_to_user((void*)arg, &PamdLevl, sizeof(fm_u16)))
532 case FM_IOCTL_GETCAPARRAY: {
535 ret = fm_caparray_get(fm, &ca);
536 WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_GETCAPARRAY:%d\n", ca);
537 if (ret < 0) goto out;
539 if (copy_to_user((void*)arg, &ca, sizeof(fm_s32))) {
546 case FM_IOCTL_EM_TEST: {
547 struct fm_em_parm parm_em;
549 WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_EM_TEST\n");
551 if (copy_from_user(&parm_em, (void*)arg, sizeof(struct fm_em_parm))) {
555 ret = fm_em_test(fm, parm_em.group_idx, parm_em.item_idx, parm_em.item_value);
559 case FM_IOCTL_RDS_SUPPORT: {
560 fm_s32 support = FM_RDS_ENABLE;
561 WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_RDS_SUPPORT\n");
563 if (copy_to_user((void*)arg, &support, sizeof(fm_s32))) {
570 case FM_IOCTL_IS_FM_POWERED_UP: {
572 WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_IS_FM_POWERED_UP");
574 if (fm->chipon && fm_pwr_state_get(fm)) {
580 if (copy_to_user((void*)arg, &powerup, sizeof(fm_u32))) {
587 case FM_IOCTL_RDS_ONOFF: {
588 fm_u16 rdson_off = 0;
589 WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_RDS_ONOFF start\n");
591 if (copy_from_user(&rdson_off, (void*)arg, sizeof(fm_u16))) {
595 ret = fm_rds_onoff(fm, rdson_off);
596 WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_RDS_ONOFF end:%d\n",rdson_off);
600 case FM_IOCTL_GETGOODBCNT: {
603 ret = fm_rds_good_bc_get(fm, &uGBLCNT);
604 WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_GETGOODBCNT:%d\n", uGBLCNT);
605 if (ret < 0) goto out;
607 if (copy_to_user((void*)arg, &uGBLCNT, sizeof(fm_u16))) {
614 case FM_IOCTL_GETBADBNT: {
615 fm_u16 uBadBLCNT = 0;
617 ret = fm_rds_bad_bc_get(fm, &uBadBLCNT);
618 WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_GETBADBNT:%d\n", uBadBLCNT);
619 if (ret < 0) goto out;
621 if (copy_to_user((void*)arg, &uBadBLCNT, sizeof(fm_u16))) {
628 case FM_IOCTL_GETBLERRATIO: {
629 fm_u16 uBlerRatio = 0;
631 ret = fm_rds_bler_ratio_get(fm, &uBlerRatio);
632 WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_GETBLERRATIO:%d\n", uBlerRatio);
633 if (ret < 0) goto out;
635 if (copy_to_user((void*)arg, &uBlerRatio, sizeof(fm_u16))) {
642 case FM_IOCTL_ANA_SWITCH: {
644 WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_ANA_SWITCH\n");
646 if (copy_from_user(&antenna, (void*)arg, sizeof(fm_s32))) {
647 WCN_DBG(FM_ALT | MAIN, "copy from user error\n");
652 ret = fm_ana_switch(fm, antenna);
656 case FM_IOCTL_RDS_GROUPCNT: {
657 struct rds_group_cnt_req_t gc_req;
658 WCN_DBG(FM_DBG | MAIN, "......FM_IOCTL_RDS_GROUPCNT......\n");
660 if (copy_from_user(&gc_req, (void*)arg, sizeof(struct rds_group_cnt_req_t))) {
661 WCN_DBG(FM_ALT | MAIN, "copy_from_user error\n");
666 //handle group counter request
668 case RDS_GROUP_CNT_READ:
669 ret = fm_rds_group_cnt_get(fm, &gc_req.gc);
671 case RDS_GROUP_CNT_WRITE:
673 case RDS_GROUP_CNT_RESET:
674 ret = fm_rds_group_cnt_reset(fm);
680 if (copy_to_user((void*)arg, &gc_req, sizeof(struct rds_group_cnt_req_t))) {
681 WCN_DBG(FM_ALT | MAIN, "copy_to_user error\n");
689 case FM_IOCTL_RDS_GET_LOG: {
690 struct rds_raw_t rds_log;
692 WCN_DBG(FM_DBG | MAIN, "......FM_IOCTL_RDS_GET_LOG......\n");
693 //fetch a record form RDS log buffer
694 ret = fm_rds_log_get(fm, (struct rds_rx_t*) & (rds_log.data), &len);
695 rds_log.dirty = TRUE;
696 rds_log.len = (len < sizeof(rds_log.data)) ? len : sizeof(rds_log.data);
697 if (ret < 0) goto out;
699 if (copy_to_user((void*)arg, &rds_log, rds_log.len + 2*sizeof(fm_s32))) {
700 WCN_DBG(FM_ALT | MAIN, "copy_to_user error\n");
708 case FM_IOCTL_RDS_BC_RST: {
709 WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_RDS_BC_RST\n");
710 ret = fm_rds_block_cnt_reset(fm);
714 case FM_IOCTL_I2S_SETTING: {
715 struct fm_i2s_setting i2s_cfg;
716 WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_I2S_SETTING\n");
718 if (copy_from_user(&i2s_cfg, (void*)arg, sizeof(struct fm_i2s_setting))) {
719 WCN_DBG(FM_ALT | MAIN, "i2s set, copy_from_user err\n");
724 ret = fm_i2s_set(fm, i2s_cfg.onoff, i2s_cfg.mode, i2s_cfg.sample);
727 WCN_DBG(FM_ALT | MAIN, "Set i2s err\n");
734 case FM_IOCTL_IS_DESE_CHAN: {
736 WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_IS_DESE_CHAN\n");
738 if (copy_from_user(&tmp, (void*)arg, sizeof(fm_s32))) {
739 WCN_DBG(FM_ALT | MAIN, "is dese chan, copy_from_user err\n");
744 tmp = fm_is_dese_chan(fm, (fm_u16)tmp);
746 if (copy_to_user((void*)arg, &tmp, sizeof(fm_s32))) {
747 WCN_DBG(FM_ALT | MAIN, "is dese chan, copy_to_user err\n");
754 case FM_IOCTL_DESENSE_CHECK:
756 fm_desense_check_t tmp;
757 WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_IS_DESE_CHAN\n");
759 if (copy_from_user(&tmp, (void*)arg, sizeof(fm_desense_check_t)))
761 WCN_DBG(FM_ALT | MAIN, "desene check, copy_from_user err\n");
765 ret = fm_desense_check(fm, (fm_u16)tmp.freq,tmp.rssi);
767 /*if (copy_to_user((void*)arg, &tmp, sizeof(fm_desense_check_t))) {
768 WCN_DBG(FM_ALT | MAIN, "desene check, copy_to_user err\n");
775 case FM_IOCTL_SCAN_GETRSSI:
777 /*struct fm_rssi_req *req;
778 WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_SCAN_GETRSSI\n");
779 if(!(req = fm_vmalloc(sizeof(struct fm_rssi_req))))
781 WCN_DBG(FM_ALT | MAIN, "fm_vmalloc err\n");
785 if(copy_from_user(req, (void*)arg, sizeof(struct fm_rssi_req)))
787 WCN_DBG(FM_ALT | MAIN, "copy_from_user err\n");
792 ret = fm_get_rssi_after_scan(fm, req);
793 if(-ERR_FW_NORES == ret){
794 WCN_DBG(FM_ALT | MAIN, "fm_get_rssi_after_scan err\n");
796 if(copy_to_user((void*)arg, req, sizeof(struct fm_rssi_req)))
798 WCN_DBG(FM_ALT | MAIN, "copy_to_user err\n");
804 WCN_DBG(FM_ALT | MAIN, "FM_IOCTL_SCAN_GETRSSI:not support\n");
808 case FM_IOCTL_DUMP_REG:
810 WCN_DBG(FM_NTC | MAIN,"......FM_IOCTL_DUMP_REG......\n");
815 WCN_DBG(FM_ALT | MAIN, "fm_dump_reg err\n");
819 case FM_IOCTL_GPS_RTC_DRIFT:{
820 struct fm_gps_rtc_info rtc_info;
821 WCN_DBG(FM_NTC | MAIN,"......FM_IOCTL_GPS_RTC_DRIFT......\n");
823 if (fm_false == fm->chipon){
824 WCN_DBG(FM_ERR | MAIN,"ERROR, FM chip is OFF\n");
828 if(copy_from_user(&rtc_info, (void*)arg, sizeof(struct fm_gps_rtc_info))){
829 WCN_DBG(FM_ERR | MAIN,"copy_from_user error\n");
834 ret = fm_get_gps_rtc_info(&rtc_info);
836 WCN_DBG(FM_ERR | MAIN,"fm_get_gps_rtc_info error\n");
841 case FM_IOCTL_OVER_BT_ENABLE:
843 fm_s32 fm_via_bt = -1;
844 WCN_DBG(FM_NTC | MAIN,"......FM_IOCTL_OVER_BT_ENABLE......\n");
846 if(copy_from_user(&fm_via_bt, (void*)arg, sizeof(int32_t))){
847 WCN_DBG(FM_ERR | MAIN,"copy_from_user error\n");
852 ret = fm_over_bt(fm, fm_via_bt);
855 WCN_DBG(FM_ERR | MAIN, "fm_over_bt err\n");
860 /***************************FM Tx function************************************/
861 case FM_IOCTL_TX_SUPPORT:
863 fm_s32 tx_support = -1;
864 WCN_DBG(FM_NTC | MAIN,"......FM_IOCTL_TX_SUPPORT......\n");
866 ret = fm_tx_support(fm,&tx_support);
869 WCN_DBG(FM_ERR | MAIN, "fm_tx_support err\n");
871 if (copy_to_user((void*)arg, &tx_support, sizeof(fm_s32)))
873 WCN_DBG(FM_ERR | MAIN,"copy_to_user error\n");
879 case FM_IOCTL_POWERUP_TX:
881 struct fm_tune_parm parm;
882 WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_POWERUP_TX:0\n");
883 if (copy_from_user(&parm, (void*)arg, sizeof(struct fm_tune_parm))) {
888 ret = fm_powerup_tx(fm, &parm);
892 ret = fm_tune_tx(fm, &parm);
893 if (ret < 0) goto out;
895 if (copy_to_user((void*)arg, &parm, sizeof(struct fm_tune_parm))) {
899 WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_POWERUP_TX:1\n");
903 case FM_IOCTL_TUNE_TX:
905 struct fm_tune_parm parm;
906 WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_TUNE_TX:0\n");
908 if (copy_from_user(&parm, (void*)arg, sizeof(struct fm_tune_parm))) {
913 ret = fm_tune_tx(fm, &parm);
918 if (copy_to_user((void*)arg, &parm, sizeof(struct fm_tune_parm))) {
923 WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_TUNE_TX:1\n");
926 case FM_IOCTL_RDSTX_SUPPORT:
928 fm_s32 rds_tx_support = -1;
929 WCN_DBG(FM_NTC | MAIN,"......FM_IOCTL_RDSTX_SUPPORT......\n");
931 ret = fm_rdstx_support(fm,&rds_tx_support);
934 WCN_DBG(FM_ERR | MAIN, "fm_rdstx_support err\n");
936 if (copy_to_user((void*)arg, &rds_tx_support, sizeof(fm_s32)))
938 WCN_DBG(FM_ERR | MAIN,"copy_to_user error\n");
945 case FM_IOCTL_RDSTX_ENABLE:
947 fm_s32 rds_tx_enable = -1;
948 WCN_DBG(FM_NTC | MAIN,"......FM_IOCTL_RDSTX_ENABLE......\n");
950 ret = fm_rdstx_enable(fm,&rds_tx_enable);
953 WCN_DBG(FM_ERR | MAIN, "fm_rdstx_enable err\n");
955 if (copy_to_user((void*)arg, &rds_tx_enable, sizeof(fm_s32)))
957 WCN_DBG(FM_ERR | MAIN,"copy_to_user error\n");
964 case FM_IOCTL_RDS_TX:
966 struct fm_rds_tx_parm parm;
967 WCN_DBG(FM_NTC | MAIN, "......FM_IOCTL_RDS_TX......\n");
969 if (copy_from_user(&parm, (void*)arg, sizeof(struct fm_rds_tx_parm)))
971 WCN_DBG(FM_ALT | MAIN, "RDS Tx, copy_from_user err\n");
976 ret = fm_rds_tx(fm, &parm);
979 WCN_DBG(FM_ALT | MAIN, "fm_rds_tx err\n");
982 if (copy_to_user((void*)arg, &parm, sizeof(struct fm_rds_tx_parm))){
983 WCN_DBG(FM_ALT | MAIN, "RDS Tx, copy_to_user err\n");
990 case FM_IOCTL_TX_SCAN:
992 struct fm_tx_scan_parm parm;
993 WCN_DBG(FM_NTC | MAIN, "......FM_IOCTL_TX_SCAN......\n");
995 if (copy_from_user(&parm, (void*)arg, sizeof(struct fm_tx_scan_parm))){
996 WCN_DBG(FM_ALT | MAIN,"copy_from_user error\n");
1000 ret = fm_tx_scan(fm, &parm);
1002 WCN_DBG(FM_ERR | MAIN,"FM_IOCTL_TX_SCAN failed\n");
1004 if (copy_to_user((void*)arg, &parm, sizeof(struct fm_tx_scan_parm))){
1005 WCN_DBG(FM_ALT | MAIN,"copy_to_user error\n");
1017 if (ret == -FM_EFW) {
1019 fm_subsys_reset(fm);
1025 static loff_t fm_ops_lseek(struct file *filp, loff_t off, fm_s32 whence)
1027 struct fm *fm = filp->private_data;
1029 if (whence == SEEK_END) {
1031 } else if (whence == SEEK_SET) {
1032 FM_EVENT_SEND(fm->rds_event, FM_RDS_DATA_READY);
1038 static ssize_t fm_ops_read(struct file *filp, char *buf, size_t len, loff_t *off)
1040 struct fm *fm = filp->private_data;
1041 fm_s32 copy_len = 0;
1044 WCN_DBG(FM_ALT | MAIN, "fm_read invalid fm pointer\n");
1048 if (!buf || len < sizeof(rds_t)) {
1049 WCN_DBG(FM_DBG | MAIN, "fm_read invliad buf\n");
1053 //block when FM is resetting
1054 while (fm_sys_state_get(fm) != FM_SUBSYS_RST_OFF) {
1055 msleep_interruptible(100);
1058 copy_len = sizeof(rds_t);
1060 return fm_rds_read(fm, buf, copy_len);
1063 static fm_s32 fm_ops_open(struct inode *inode, struct file *filp)
1066 struct fm_platform *plat = container_of(inode->i_cdev, struct fm_platform, cdev);
1067 struct fm *fm = container_of(plat, struct fm, platform);
1069 WCN_DBG(FM_NTC | MAIN, "fm_ops_open:0\n");
1070 if (fm_sys_state_get(fm) != FM_SUBSYS_RST_OFF) {
1071 WCN_DBG(FM_ALT| MAIN, "FM subsys is resetting, retry later\n");
1077 filp->private_data = fm;
1079 WCN_DBG(FM_NTC | MAIN, "fm_ops_open:1\n");
1083 static fm_s32 fm_ops_release(struct inode *inode, struct file *filp)
1086 // struct fm_platform *plat = container_of(inode->i_cdev, struct fm_platform, cdev);
1087 // struct fm *fm = container_of(plat, struct fm, platform);
1089 // WCN_DBG(FM_NTC | MAIN, "fm_ops_release:0\n");
1091 filp->private_data = NULL;
1093 WCN_DBG(FM_NTC | MAIN, "fm_ops_release\n");
1097 static fm_s32 fm_ops_flush(struct file *filp,fl_owner_t Id)
1100 struct fm *fm = filp->private_data;
1102 WCN_DBG(FM_NTC | MAIN, "fm_ops_flush:0\n");
1104 filp->private_data = fm;
1106 WCN_DBG(FM_NTC | MAIN, "fm_ops_flush:1\n");
1110 static fm_s32 fm_proc_read(char *page, char **start, off_t off, fm_s32 count, fm_s32 *eof, void *data)
1113 struct fm *fm = g_fm;
1115 WCN_DBG(FM_NTC | MAIN, "Enter fm_proc_read.\n");
1121 WCN_DBG(FM_ALT | MAIN, "para err\n");
1125 if (fm->chipon && (fm_pwr_state_get(fm) == FM_PWR_RX_ON))
1127 cnt = sprintf(page, "1\n");
1128 WCN_DBG(FM_NTC | MAIN, " FM_PWR_RX_ON\n");
1130 else if (fm->chipon && (fm_pwr_state_get(fm) == FM_PWR_TX_ON))
1132 WCN_DBG(FM_NTC | MAIN, " FM_PWR_TX_ON\n");
1133 cnt = sprintf(page, "2\n");
1137 cnt = sprintf(page, "0\n");
1141 WCN_DBG(FM_NTC | MAIN, "Leave fm_proc_read. cnt = %d\n", cnt);
1145 static fm_s32 fm_proc_write(struct file *file, const char *buffer, unsigned long count, void *data)
1147 fm_s8 tmp_buf[50] = {0};
1150 copysize = (count < (sizeof(tmp_buf) - 1)) ? count : (sizeof(tmp_buf) - 1);
1152 WCN_DBG(FM_NTC | MAIN, "fm_proc_write:0\n");
1153 if (copy_from_user(tmp_buf, buffer, copysize)) {
1154 WCN_DBG(FM_ERR | MAIN, "failed copy_from_user\n");
1158 if (strncmp(tmp_buf, "subsys reset", strlen("subsys reset")) == 0) {
1159 fm_subsys_reset(g_fm);
1163 if (!fm_cust_config_setup(tmp_buf)) {
1164 WCN_DBG(FM_NTC | MAIN, "get config form %s ok\n", tmp_buf);
1168 if (sscanf(tmp_buf, "%x", &g_dbg_level) != 1) {
1169 WCN_DBG(FM_ERR | MAIN, "failed g_dbg_level = 0x%x\n", g_dbg_level);
1173 WCN_DBG(FM_NTC | MAIN, "fm_proc_write:1 g_dbg_level = 0x%x\n", g_dbg_level);
1177 #define FM_DEV_STATIC_ALLOC
1178 #define FM_DEV_MAJOR 193
1179 static int FM_major = FM_DEV_MAJOR; /* dynamic allocation */
1181 static fm_s32 fm_cdev_setup(struct fm *fm)
1184 struct fm_platform *plat = &fm->platform;
1186 #ifdef FM_DEV_STATIC_ALLOC
1187 /*static allocate chrdev*/
1188 plat->dev_t = MKDEV(FM_major, 0);
1189 ret = register_chrdev_region(plat->dev_t, 1, FM_NAME);
1192 WCN_DBG(FM_ERR | MAIN, "%s():fail to register chrdev\n", __func__);
1198 #ifndef FM_DEV_STATIC_ALLOC
1199 ret = alloc_chrdev_region(&plat->dev_t, 0, 1, FM_NAME);
1202 WCN_DBG(FM_ALT | MAIN, "alloc dev_t failed\n");
1208 WCN_DBG(FM_NTC | MAIN, "alloc %s:%d:%d\n", FM_NAME, MAJOR(plat->dev_t), MINOR(plat->dev_t));
1210 cdev_init(&plat->cdev, &fm_ops);
1212 plat->cdev.owner = THIS_MODULE;
1213 plat->cdev.ops = &fm_ops;
1215 ret = cdev_add(&plat->cdev, plat->dev_t, 1);
1218 WCN_DBG(FM_ALT | MAIN, "add dev_t failed\n");
1222 #ifndef FM_DEV_STATIC_ALLOC
1223 plat->cls = class_create(THIS_MODULE, FM_NAME);
1225 if (IS_ERR(plat->cls)) {
1226 ret = PTR_ERR(plat->cls);
1227 WCN_DBG(FM_ALT | MAIN, "class_create err:%d\n", ret);
1231 plat->dev = device_create(plat->cls, NULL, plat->dev_t, NULL, FM_NAME);
1237 static fm_s32 fm_cdev_destroy(struct fm *fm)
1241 device_destroy(fm->platform.cls, fm->platform.dev_t);
1242 class_destroy(fm->platform.cls);
1243 cdev_del(&fm->platform.cdev);
1244 unregister_chrdev_region(fm->platform.dev_t, 1);
1249 static fm_s32 fm_mod_init(fm_u32 arg)
1252 struct fm *fm = NULL;
1254 fm = fm_dev_init(0);
1261 if ((ret = fm_cdev_setup(fm))) {
1265 //fm proc file create "/proc/fm"
1266 g_fm_proc = create_proc_entry(FM_PROC_FILE, 0444, NULL);
1268 if (g_fm_proc == NULL) {
1269 WCN_DBG(FM_ALT | MAIN, "create_proc_entry failed\n");
1273 g_fm_proc->read_proc = fm_proc_read;
1274 g_fm_proc->write_proc = fm_proc_write;
1275 WCN_DBG(FM_NTC | MAIN, "create_proc_entry success\n");
1284 fm_cdev_destroy(fm);
1288 remove_proc_entry(FM_PROC_FILE, NULL);
1292 static fm_s32 fm_mod_destroy(struct fm *fm)
1298 WCN_DBG(FM_NTC | MAIN, "%s\n", __func__);
1299 remove_proc_entry(FM_PROC_FILE, NULL);
1300 fm_cdev_destroy(fm);
1306 static fm_s32 mt_fm_probe(struct platform_device *pdev)
1310 WCN_DBG(FM_NTC | MAIN, "%s\n", __func__);
1312 ret = fm_mod_init(0);
1315 WCN_DBG(FM_ERR | MAIN, "fm mod init err\n");
1322 static fm_s32 mt_fm_remove(struct platform_device *pdev)
1324 WCN_DBG(FM_NTC | MAIN, "%s\n", __func__);
1326 fm_mod_destroy(g_fm);
1331 static struct platform_device mt_fm_device = {
1336 //platform driver entry
1337 static struct platform_driver mt_fm_dev_drv = {
1338 .probe = mt_fm_probe,
1339 .remove = mt_fm_remove,
1342 .owner = THIS_MODULE,
1346 static fm_s32 __init mt_fm_init(void)
1350 ret = fm_env_setup();
1357 //register fm device to platform bus
1358 ret = platform_device_register(&mt_fm_device);
1364 //register fm driver to platform bus
1365 ret = platform_driver_register(&mt_fm_dev_drv);
1371 WCN_DBG(FM_NTC | MAIN, "6. fm platform driver registered\n");
1375 static void __exit mt_fm_exit(void)
1377 platform_driver_unregister(&mt_fm_dev_drv);
1382 EXPORT_SYMBOL(g_dbg_level);
1383 module_init(mt_fm_init);
1384 module_exit(mt_fm_exit);
1386 MODULE_LICENSE("GPL");
1387 MODULE_DESCRIPTION("MediaTek FM Driver");
1388 MODULE_AUTHOR("Hongcheng <hongcheng.xia@MediaTek.com>");