import PULS_20160108
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / drivers / misc / mediatek / fmradio / core / fm_module.c
1 /* fm_module.c
2 *
3 * (C) Copyright 2011
4 * MediaTek <www.MediaTek.com>
5 * Hongcheng <hongcheng.xia@MediaTek.com>
6 *
7 * FM Radio Driver -- main functions
8 *
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.
13 *
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.
18 *
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
22 */
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>
28 #include <linux/fs.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() */
35
36 #include "fm_config.h"
37 #include "fm_main.h"
38 #include "fm_ioctl.h"
39
40 #define FM_PROC_FILE "fm"
41
42 fm_u32 g_dbg_level = 0xfffffff5; /* Debug level of FM */
43
44 /* fm main data structure */
45 static struct fm *g_fm;
46 /* proc file entry */
47 static struct proc_dir_entry *g_fm_proc;
48
49 /* char device interface */
50 static fm_s32 fm_cdev_setup(struct fm *fm);
51 static fm_s32 fm_cdev_destroy(struct fm *fm);
52
53 static long fm_ops_ioctl(struct file *filp, fm_u32 cmd, unsigned long arg);
54 #ifdef CONFIG_COMPAT
55 static long fm_ops_compat_ioctl(struct file * filp,fm_u32 cmd,unsigned long arg);
56 #endif
57 static loff_t fm_ops_lseek(struct file *filp, loff_t off, fm_s32 whence);
58 static ssize_t fm_ops_read(struct file *filp, char *buf, size_t len, loff_t *off);
59 static fm_s32 fm_ops_open(struct inode *inode, struct file *filp);
60 static fm_s32 fm_ops_release(struct inode *inode, struct file *filp);
61 static fm_s32 fm_ops_flush(struct file *filp, fl_owner_t Id);
62 static struct file_operations fm_ops = {
63 .owner = THIS_MODULE,
64 .unlocked_ioctl = fm_ops_ioctl,
65 #ifdef CONFIG_COMPAT
66 .compat_ioctl = fm_ops_compat_ioctl,
67 #endif
68 .llseek = fm_ops_lseek,
69 .read = fm_ops_read,
70 .open = fm_ops_open,
71 .release = fm_ops_release,
72 .flush = fm_ops_flush,
73 };
74
75 /* static fm_s32 fm_proc_read(char *page, char **start, off_t off, fm_s32 count, fm_s32 *eof, void *data); */
76 static ssize_t fm_proc_read(struct file *file, char __user *buf, size_t count, loff_t *ppos);
77 static ssize_t fm_proc_write(struct file *file, const char *buffer, size_t count, loff_t *ppos);
78
79 static struct file_operations fm_proc_ops = {
80 .owner = THIS_MODULE,
81 .read = fm_proc_read,
82 .write = fm_proc_write,
83 };
84
85 static struct fm_scan_t parm = {
86 .sr_size = 0,
87 .sr.ch_rssi_buf = NULL,
88 };
89
90 #ifdef CONFIG_COMPAT
91 static long fm_ops_compat_ioctl(struct file *filp, fm_u32 cmd, unsigned long arg)
92 {
93 long ret;
94
95 WCN_DBG(FM_NTC | MAIN, "COMPAT %s---pid(%d)---cmd(0x%08x)---arg(0x%08x)\n", current->comm,
96 current->pid, cmd, (fm_u32) arg);
97
98 if(!filp->f_op || !filp->f_op->unlocked_ioctl)
99 return -ENOTTY;
100
101 ret = filp->f_op->unlocked_ioctl(filp, cmd, arg);
102
103 return ret;
104 }
105 #endif
106
107 static long fm_ops_ioctl(struct file *filp, fm_u32 cmd, unsigned long arg)
108 {
109 fm_s32 ret = 0;
110 struct fm_platform *plat =
111 container_of(filp->f_dentry->d_inode->i_cdev, struct fm_platform, cdev);
112 struct fm *fm = container_of(plat, struct fm, platform);
113
114 WCN_DBG(FM_NTC | MAIN, "%s---pid(%d)---cmd(0x%08x)---arg(0x%08x)\n", current->comm,
115 current->pid, cmd, (fm_u32) arg);
116
117 if (fm_sys_state_get(fm) != FM_SUBSYS_RST_OFF) {
118 WCN_DBG(FM_ALT | MAIN, "FM subsys is resetting, retry later\n");
119 ret = -FM_ESRST;
120 return ret;
121 }
122
123 switch (cmd) {
124 case FM_IOCTL_POWERUP:{
125 struct fm_tune_parm parm;
126 WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_POWERUP:0\n");
127
128 if (copy_from_user(&parm, (void *)arg, sizeof(struct fm_tune_parm))) {
129 ret = -EFAULT;
130 goto out;
131 }
132
133 ret = fm_powerup(fm, &parm);
134 if (ret < 0)
135 goto out;
136 ret = fm_tune(fm, &parm);
137 if (ret < 0)
138 goto out;
139
140 if (copy_to_user((void *)arg, &parm, sizeof(struct fm_tune_parm))) {
141 ret = -EFAULT;
142 goto out;
143 }
144 WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_POWERUP:1\n");
145
146 break;
147 }
148
149 case FM_IOCTL_POWERDOWN:{
150 int powerdwn_type = 0;
151 WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_POWERDOWN:0\n");
152
153 if (copy_from_user(&powerdwn_type, (void *)arg, sizeof(int))) {
154 ret = -EFAULT;
155 goto out;
156 }
157
158 ret = fm_powerdown(fm, powerdwn_type); /* 0: RX 1: TX */
159 WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_POWERDOWN:1\n");
160 break;
161 }
162
163 case FM_IOCTL_TUNE:{
164 struct fm_tune_parm parm;
165 WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_TUNE:0\n");
166
167 if (copy_from_user(&parm, (void *)arg, sizeof(struct fm_tune_parm))) {
168 ret = -EFAULT;
169 goto out;
170 }
171
172 ret = fm_tune(fm, &parm);
173 if (ret < 0) {
174 goto out;
175 }
176
177 if (copy_to_user((void *)arg, &parm, sizeof(struct fm_tune_parm))) {
178 ret = -EFAULT;
179 goto out;
180 }
181
182 WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_TUNE:1\n");
183 break;
184 }
185
186 case FM_IOCTL_SOFT_MUTE_TUNE:
187 {
188 struct fm_softmute_tune_t parm;
189 fm_cqi_log(); /* cqi log tool */
190 WCN_DBG(FM_NTC | MAIN, "......FM_IOCTL_SOFT_MUTE_TUNE......\n");
191 if (copy_from_user(&parm, (void *)arg, sizeof(struct fm_softmute_tune_t))) {
192 ret = -EFAULT;
193 goto out;
194 }
195
196 ret = fm_soft_mute_tune(fm, &parm);
197 if (ret < 0) {
198 goto out;
199 }
200
201 if (copy_to_user((void *)arg, &parm, sizeof(struct fm_softmute_tune_t))) {
202 ret = -EFAULT;
203 goto out;
204 }
205 break;
206 }
207 case FM_IOCTL_PRE_SEARCH:
208 {
209 ret = fm_pre_search(fm);
210 break;
211 }
212 case FM_IOCTL_RESTORE_SEARCH:
213 {
214 ret = fm_restore_search(fm);
215 break;
216 }
217 case FM_IOCTL_SEEK:{
218 struct fm_seek_parm parm;
219 WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_SEEK:0\n");
220
221 if (copy_from_user(&parm, (void *)arg, sizeof(struct fm_seek_parm))) {
222 ret = -EFAULT;
223 goto out;
224 }
225
226 ret = fm_seek(fm, &parm);
227 if (ret < 0) {
228 goto out;
229 }
230
231 if (copy_to_user((void *)arg, &parm, sizeof(struct fm_seek_parm))) {
232 ret = -EFAULT;
233 goto out;
234 }
235 WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_SEEK:1\n");
236 break;
237 }
238
239 case FM_IOCTL_SCAN:{
240 struct fm_scan_parm parm;
241 WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_SCAN start\n");
242
243 if (copy_from_user(&parm, (void *)arg, sizeof(struct fm_scan_parm))) {
244 WCN_DBG(FM_ALT | MAIN, "copy_from_user failed\n");
245 ret = -EFAULT;
246 goto out;
247 }
248
249 ret = fm_scan(fm, &parm);
250 if (ret < 0)
251 goto out;
252
253 if (copy_to_user((void *)arg, &parm, sizeof(struct fm_scan_parm))) {
254 ret = -EFAULT;
255 goto out;
256 }
257
258 WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_SCAN end\n");
259 break;
260 }
261
262 case FM_IOCTL_TUNE_NEW:{
263 struct fm_tune_t tune_tmp;
264
265 WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_TUNE_NEW\n");
266
267 if (copy_from_user(&tune_tmp, (void *)arg, sizeof(struct fm_tune_t))) {
268 WCN_DBG(FM_ERR | MAIN, "tune new copy_from_user error\n");
269 ret = -EFAULT;
270 goto out;
271 }
272
273 ret = fm_tune_new(fm, &tune_tmp);
274 if (ret < 0) {
275 goto out;
276 }
277
278 if (copy_to_user((void *)arg, &tune_tmp, sizeof(struct fm_tune_t))) {
279 WCN_DBG(FM_ERR | MAIN, "tune new copy_to_user error\n");
280 ret = -EFAULT;
281 goto out;
282 }
283
284 break;
285 }
286
287 case FM_IOCTL_SEEK_NEW:{
288 struct fm_seek_t seek_tmp;
289
290 WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_SEEK_NEW\n");
291
292 if (copy_from_user(&seek_tmp, (void *)arg, sizeof(struct fm_seek_t))) {
293 WCN_DBG(FM_ERR | MAIN, "seek new copy_from_user error\n");
294 ret = -EFAULT;
295 goto out;
296 }
297
298 ret = fm_seek_new(fm, &seek_tmp);
299 if (ret < 0) {
300 goto out;
301 }
302
303 if (copy_to_user((void *)arg, &seek_tmp, sizeof(struct fm_seek_t))) {
304 WCN_DBG(FM_ERR | MAIN, "seek new copy_to_user error\n");
305 ret = -EFAULT;
306 goto out;
307 }
308
309 break;
310 }
311
312 case FM_IOCTL_SCAN_NEW:{
313 struct fm_scan_t tmp;
314
315 WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_SCAN_NEW\n");
316
317 if (copy_from_user(&tmp, (void *)arg, sizeof(struct fm_scan_t))) {
318 WCN_DBG(FM_ERR | MAIN, "copy_from_user error\n");
319 ret = -EFAULT;
320 goto out;
321 }
322
323 switch (tmp.cmd) {
324 case FM_SCAN_CMD_START:
325 if ((tmp.upper > 10800) || (tmp.lower < 7600) || (tmp.space < 5)
326 || (tmp.space > 20)) {
327 WCN_DBG(FM_ERR | MAIN, "scan para error\n");
328 ret = -EFAULT;
329 goto out;
330 }
331 parm.cmd = tmp.cmd;
332 parm.lower = tmp.lower;
333 parm.upper = tmp.upper;
334 parm.space = tmp.space;
335
336 ret = fm_scan_new(fm, &parm);
337 if (ret < 0) {
338 goto out;
339 }
340 break;
341
342 case FM_SCAN_CMD_GET_CH_RSSI:
343 if (parm.sr_size && parm.sr.ch_rssi_buf) {
344 if (copy_to_user
345 (tmp.sr.ch_rssi_buf, parm.sr.ch_rssi_buf,
346 parm.num * sizeof(struct fm_ch_rssi))) {
347 WCN_DBG(FM_ERR | MAIN, "scan copy_to_user err\n");
348 ret = -EFAULT;
349 goto out;
350 }
351 }
352 break;
353
354 default:
355 break;
356 }
357
358 tmp.num = parm.num;
359 if (copy_to_user((void *)arg, &tmp, sizeof(struct fm_scan_t))) {
360 WCN_DBG(FM_ERR | MAIN, "copy_to_user error\n");
361 ret = -EFAULT;
362 goto out;
363 }
364 break;
365 }
366
367 case FM_IOCTL_CQI_GET:{
368 struct fm_cqi_req cqi_req;
369 fm_s8 *buf = NULL;
370 fm_s32 tmp;
371
372 WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_CQI_GET\n");
373
374 if (copy_from_user(&cqi_req, (void *)arg, sizeof(struct fm_cqi_req))) {
375 WCN_DBG(FM_ALT | MAIN, "copy_from_user failed\n");
376 ret = -EFAULT;
377 goto out;
378 }
379
380 if ((cqi_req.ch_num * sizeof(struct fm_cqi) > cqi_req.buf_size)
381 || !cqi_req.cqi_buf) {
382 ret = -FM_EPARA;
383 goto out;
384 }
385
386 tmp = cqi_req.ch_num / 16 + ((cqi_req.ch_num % 16) ? 1 : 0);
387 tmp = tmp * 16 * sizeof(struct fm_cqi);
388 buf = fm_zalloc(tmp);
389
390 if (!buf) {
391 ret = -FM_ENOMEM;
392 goto out;
393 }
394
395 ret = fm_cqi_get(fm, cqi_req.ch_num, buf, tmp);
396
397 if (ret) {
398 fm_free(buf);
399 WCN_DBG(FM_ALT | MAIN, "get cqi failed\n");
400 goto out;
401 }
402
403 if (copy_to_user
404 ((void *)cqi_req.cqi_buf, buf,
405 cqi_req.ch_num * sizeof(struct fm_cqi))) {
406 fm_free(buf);
407 ret = -EFAULT;
408 goto out;
409 }
410
411 fm_free(buf);
412 break;
413 }
414
415 case FM_IOCTL_GET_HW_INFO:{
416 struct fm_hw_info info;
417
418 WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_GET_HW_INFO\n");
419
420 ret = fm_get_hw_info(fm, &info);
421
422 if (ret) {
423 WCN_DBG(FM_ALT | MAIN, "get hw info failed\n");
424 goto out;
425 }
426
427 if (copy_to_user((void *)arg, &info, sizeof(struct fm_hw_info))) {
428 ret = -EFAULT;
429 goto out;
430 }
431
432 break;
433 }
434
435 case FM_IOCTL_GET_I2S_INFO:{
436 struct fm_i2s_info i2sinfo;
437
438 WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_GET_I2S_INFO\n");
439
440 ret = fm_get_i2s_info(fm, &i2sinfo);
441
442 if (ret) {
443 WCN_DBG(FM_ALT | MAIN, "get i2s info failed\n");
444 goto out;
445 }
446
447 if (copy_to_user((void *)arg, &i2sinfo, sizeof(struct fm_i2s_info))) {
448 ret = -EFAULT;
449 goto out;
450 }
451
452 break;
453 }
454
455 case FM_IOCTL_SETVOL:{
456 fm_u32 vol;
457
458 WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_SETVOL start\n");
459 if (copy_from_user(&vol, (void *)arg, sizeof(fm_u32))) {
460 WCN_DBG(FM_ALT | MAIN, "copy_from_user failed\n");
461 ret = -EFAULT;
462 goto out;
463 }
464
465 ret = fm_setvol(fm, vol);
466 WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_SETVOL end:%d\n", vol);
467 break;
468 }
469 case FM_IOCTL_GETVOL:{
470 fm_u32 vol;
471 WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_GETVOL start\n");
472 ret = fm_getvol(fm, &vol);
473 if (ret < 0)
474 goto out;
475
476 if (copy_to_user((void *)arg, &vol, sizeof(fm_u32))) {
477 ret = -EFAULT;
478 goto out;
479 }
480
481 WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_GETVOL end=%d\n", vol);
482 break;
483 }
484
485 case FM_IOCTL_MUTE:{
486 fm_u32 bmute;
487
488 WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_MUTE start\n");
489 if (copy_from_user(&bmute, (void *)arg, sizeof(fm_u32))) {
490 ret = -EFAULT;
491 goto out;
492 }
493
494 ret = fm_mute(fm, bmute);
495 WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_MUTE end-%d\n", bmute);
496 break;
497 }
498
499 case FM_IOCTL_GETRSSI:{
500 fm_s32 rssi = 0;
501
502 ret = fm_getrssi(fm, &rssi);
503 if (ret < 0)
504 goto out;
505
506 if (copy_to_user((void *)arg, &rssi, sizeof(fm_s32))) {
507 ret = -EFAULT;
508 goto out;
509 }
510
511 WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_GETRSSI:%d\n", rssi);
512 break;
513 }
514
515 case FM_IOCTL_RW_REG:{
516 struct fm_ctl_parm parm_ctl;
517 WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_RW_REG\n");
518
519 if (copy_from_user(&parm_ctl, (void *)arg, sizeof(struct fm_ctl_parm))) {
520 ret = -EFAULT;
521 goto out;
522 }
523
524 if (parm_ctl.rw_flag == 0) {
525 ret = fm_reg_write(fm, parm_ctl.addr, parm_ctl.val);
526 } else {
527 ret = fm_reg_read(fm, parm_ctl.addr, &parm_ctl.val);
528 }
529 if (ret < 0)
530 goto out;
531
532 if ((parm_ctl.rw_flag == 0x01) && (!ret)) {
533 if (copy_to_user
534 ((void *)arg, &parm_ctl, sizeof(struct fm_ctl_parm))) {
535 ret = -EFAULT;
536 goto out;
537 }
538 }
539
540 break;
541 }
542 case FM_IOCTL_TOP_RDWR:
543 {
544 struct fm_top_rw_parm parm_ctl;
545 WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_TOP_RDWR\n");
546
547 if (copy_from_user(&parm_ctl, (void *)arg, sizeof(struct fm_top_rw_parm))) {
548 ret = -EFAULT;
549 goto out;
550 }
551
552 if (parm_ctl.rw_flag == 0) {
553 ret = fm_top_write(fm, parm_ctl.addr, parm_ctl.val);
554 } else {
555 ret = fm_top_read(fm, parm_ctl.addr, &parm_ctl.val);
556 }
557 if (ret < 0)
558 goto out;
559
560 if ((parm_ctl.rw_flag == 0x01) && (!ret)) {
561 if (copy_to_user
562 ((void *)arg, &parm_ctl, sizeof(struct fm_top_rw_parm))) {
563 ret = -EFAULT;
564 goto out;
565 }
566 }
567
568 break;
569 }
570 case FM_IOCTL_HOST_RDWR:
571 {
572 struct fm_host_rw_parm parm_ctl;
573 WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_TOP_RDWR\n");
574
575 if (copy_from_user(&parm_ctl, (void *)arg, sizeof(struct fm_host_rw_parm))) {
576 ret = -EFAULT;
577 goto out;
578 }
579
580 if (parm_ctl.rw_flag == 0) {
581 ret = fm_host_write(fm, parm_ctl.addr, parm_ctl.val);
582 } else {
583 ret = fm_host_read(fm, parm_ctl.addr, &parm_ctl.val);
584 }
585 if (ret < 0)
586 goto out;
587
588 if ((parm_ctl.rw_flag == 0x01) && (!ret)) {
589 if (copy_to_user
590 ((void *)arg, &parm_ctl, sizeof(struct fm_host_rw_parm))) {
591 ret = -EFAULT;
592 goto out;
593 }
594 }
595
596 break;
597 }
598
599 case FM_IOCTL_GETCHIPID:{
600 fm_u16 chipid;
601
602 ret = fm_chipid_get(fm, &chipid);
603 WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_GETCHIPID:%04x\n", chipid);
604 if (ret < 0)
605 goto out;
606
607 if (copy_to_user((void *)arg, &chipid, sizeof(fm_u16))) {
608 ret = -EFAULT;
609 goto out;
610 }
611 break;
612 }
613
614 case FM_IOCTL_GETMONOSTERO:{
615 fm_u16 usStereoMono;
616
617 ret = fm_monostereo_get(fm, &usStereoMono);
618 WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_GETMONOSTERO:%04x\n", usStereoMono);
619 if (ret < 0)
620 goto out;
621
622 if (copy_to_user((void *)arg, &usStereoMono, sizeof(fm_u16))) {
623 ret = -EFAULT;
624 goto out;
625 }
626 break;
627 }
628
629 case FM_IOCTL_SETMONOSTERO:{
630 WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_SETMONOSTERO, %d\n", (fm_s32) arg);
631 ret = fm_monostereo_set(fm, (fm_s32) arg);
632 break;
633 }
634
635 case FM_IOCTL_GETCURPAMD:{
636 fm_u16 PamdLevl;
637
638 ret = fm_pamd_get(fm, &PamdLevl);
639 WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_GETCURPAMD:%d\n", PamdLevl);
640 if (ret < 0)
641 goto out;
642
643 if (copy_to_user((void *)arg, &PamdLevl, sizeof(fm_u16)))
644 ret = -EFAULT;
645 goto out;
646
647 break;
648 }
649
650 case FM_IOCTL_GETCAPARRAY:{
651 fm_s32 ca;
652
653 ret = fm_caparray_get(fm, &ca);
654 WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_GETCAPARRAY:%d\n", ca);
655 if (ret < 0)
656 goto out;
657
658 if (copy_to_user((void *)arg, &ca, sizeof(fm_s32))) {
659 ret = -EFAULT;
660 goto out;
661 }
662 break;
663 }
664
665 case FM_IOCTL_EM_TEST:{
666 struct fm_em_parm parm_em;
667
668 WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_EM_TEST\n");
669
670 if (copy_from_user(&parm_em, (void *)arg, sizeof(struct fm_em_parm))) {
671 ret = -EFAULT;
672 goto out;
673 }
674 ret =
675 fm_em_test(fm, parm_em.group_idx, parm_em.item_idx, parm_em.item_value);
676 break;
677 }
678
679 case FM_IOCTL_RDS_SUPPORT:{
680 fm_s32 support = FM_RDS_ENABLE;
681 WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_RDS_SUPPORT\n");
682
683 if (copy_to_user((void *)arg, &support, sizeof(fm_s32))) {
684 ret = -EFAULT;
685 goto out;
686 }
687 break;
688 }
689
690 case FM_IOCTL_IS_FM_POWERED_UP:{
691 fm_u32 powerup;
692 WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_IS_FM_POWERED_UP");
693
694 if (fm->chipon && fm_pwr_state_get(fm)) {
695 powerup = 1;
696 } else {
697 powerup = 0;
698 }
699
700 if (copy_to_user((void *)arg, &powerup, sizeof(fm_u32))) {
701 ret = -EFAULT;
702 goto out;
703 }
704 break;
705 }
706
707 case FM_IOCTL_FM_SET_STATUS: {
708 fm_status_t fm_stat;
709 WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_FM_SET_STATUS");
710
711 if (copy_from_user(&fm_stat, (void*)arg, sizeof(fm_status_t))) {
712 ret = -EFAULT;
713 goto out;
714 }
715
716 fm_set_stat(fm, fm_stat.which, fm_stat.stat);
717
718 break;
719 }
720
721 case FM_IOCTL_FM_GET_STATUS: {
722 fm_status_t fm_stat;
723 WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_FM_GET_STATUS");
724
725 if (copy_from_user(&fm_stat, (void*)arg, sizeof(fm_status_t))) {
726 ret = -EFAULT;
727 goto out;
728 }
729
730 fm_get_stat(fm, fm_stat.which, &fm_stat.stat);
731
732 if (copy_to_user((void*)arg, &fm_stat, sizeof(fm_status_t))) {
733 ret = -EFAULT;
734 goto out;
735 }
736
737 break;
738 }
739
740 case FM_IOCTL_RDS_ONOFF:{
741 fm_u16 rdson_off = 0;
742 WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_RDS_ONOFF start\n");
743
744 if (copy_from_user(&rdson_off, (void *)arg, sizeof(fm_u16))) {
745 ret = -EFAULT;
746 goto out;
747 }
748 ret = fm_rds_onoff(fm, rdson_off);
749 WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_RDS_ONOFF end:%d\n", rdson_off);
750 break;
751 }
752
753 case FM_IOCTL_GETGOODBCNT:{
754 fm_u16 uGBLCNT = 0;
755
756 ret = fm_rds_good_bc_get(fm, &uGBLCNT);
757 WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_GETGOODBCNT:%d\n", uGBLCNT);
758 if (ret < 0)
759 goto out;
760
761 if (copy_to_user((void *)arg, &uGBLCNT, sizeof(fm_u16))) {
762 ret = -EFAULT;
763 goto out;
764 }
765 break;
766 }
767
768 case FM_IOCTL_GETBADBNT:{
769 fm_u16 uBadBLCNT = 0;
770
771 ret = fm_rds_bad_bc_get(fm, &uBadBLCNT);
772 WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_GETBADBNT:%d\n", uBadBLCNT);
773 if (ret < 0)
774 goto out;
775
776 if (copy_to_user((void *)arg, &uBadBLCNT, sizeof(fm_u16))) {
777 ret = -EFAULT;
778 goto out;
779 }
780 break;
781 }
782
783 case FM_IOCTL_GETBLERRATIO:{
784 fm_u16 uBlerRatio = 0;
785
786 ret = fm_rds_bler_ratio_get(fm, &uBlerRatio);
787 WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_GETBLERRATIO:%d\n", uBlerRatio);
788 if (ret < 0)
789 goto out;
790
791 if (copy_to_user((void *)arg, &uBlerRatio, sizeof(fm_u16))) {
792 ret = -EFAULT;
793 goto out;
794 }
795 break;
796 }
797
798 case FM_IOCTL_ANA_SWITCH:{
799 fm_s32 antenna = -1;
800 WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_ANA_SWITCH\n");
801
802 if (copy_from_user(&antenna, (void *)arg, sizeof(fm_s32))) {
803 WCN_DBG(FM_ALT | MAIN, "copy from user error\n");
804 ret = -EFAULT;
805 goto out;
806 }
807
808 ret = fm_ana_switch(fm, antenna);
809 break;
810 }
811
812 case FM_IOCTL_RDS_GROUPCNT:{
813 struct rds_group_cnt_req_t gc_req;
814 WCN_DBG(FM_DBG | MAIN, "......FM_IOCTL_RDS_GROUPCNT......\n");
815
816 if (copy_from_user
817 (&gc_req, (void *)arg, sizeof(struct rds_group_cnt_req_t))) {
818 WCN_DBG(FM_ALT | MAIN, "copy_from_user error\n");
819 ret = -EFAULT;
820 goto out;
821 }
822 /* handle group counter request */
823 switch (gc_req.op) {
824 case RDS_GROUP_CNT_READ:
825 ret = fm_rds_group_cnt_get(fm, &gc_req.gc);
826 break;
827 case RDS_GROUP_CNT_WRITE:
828 break;
829 case RDS_GROUP_CNT_RESET:
830 ret = fm_rds_group_cnt_reset(fm);
831 break;
832 default:
833 break;
834 }
835
836 if (copy_to_user((void *)arg, &gc_req, sizeof(struct rds_group_cnt_req_t))) {
837 WCN_DBG(FM_ALT | MAIN, "copy_to_user error\n");
838 ret = -EFAULT;
839 goto out;
840 }
841
842 break;
843 }
844
845 case FM_IOCTL_RDS_GET_LOG:{
846 struct rds_raw_t rds_log;
847 fm_s32 len;
848 WCN_DBG(FM_DBG | MAIN, "......FM_IOCTL_RDS_GET_LOG......\n");
849 /* fetch a record form RDS log buffer */
850 ret = fm_rds_log_get(fm, (struct rds_rx_t *)&(rds_log.data), &len);
851 rds_log.dirty = TRUE;
852 rds_log.len = (len < sizeof(rds_log.data)) ? len : sizeof(rds_log.data);
853 if (ret < 0)
854 goto out;
855
856 if (copy_to_user((void *)arg, &rds_log, rds_log.len + 2 * sizeof(fm_s32))) {
857 WCN_DBG(FM_ALT | MAIN, "copy_to_user error\n");
858 ret = -EFAULT;
859 goto out;
860 }
861
862 break;
863 }
864
865 case FM_IOCTL_RDS_BC_RST:{
866 WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_RDS_BC_RST\n");
867 ret = fm_rds_block_cnt_reset(fm);
868 break;
869 }
870
871 case FM_IOCTL_I2S_SETTING:{
872 struct fm_i2s_setting i2s_cfg;
873 WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_I2S_SETTING\n");
874
875 if (copy_from_user(&i2s_cfg, (void *)arg, sizeof(struct fm_i2s_setting))) {
876 WCN_DBG(FM_ALT | MAIN, "i2s set, copy_from_user err\n");
877 ret = -EFAULT;
878 goto out;
879 }
880
881 ret = fm_i2s_set(fm, i2s_cfg.onoff, i2s_cfg.mode, i2s_cfg.sample);
882
883 if (ret) {
884 WCN_DBG(FM_ALT | MAIN, "Set i2s err\n");
885 goto out;
886 }
887
888 break;
889 }
890
891 case FM_IOCTL_IS_DESE_CHAN:{
892 fm_s32 tmp;
893 WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_IS_DESE_CHAN\n");
894
895 if (copy_from_user(&tmp, (void *)arg, sizeof(fm_s32))) {
896 WCN_DBG(FM_ALT | MAIN, "is dese chan, copy_from_user err\n");
897 ret = -EFAULT;
898 goto out;
899 }
900
901 tmp = fm_is_dese_chan(fm, (fm_u16) tmp);
902
903 if (copy_to_user((void *)arg, &tmp, sizeof(fm_s32))) {
904 WCN_DBG(FM_ALT | MAIN, "is dese chan, copy_to_user err\n");
905 ret = -EFAULT;
906 goto out;
907 }
908
909 break;
910 }
911 case FM_IOCTL_DESENSE_CHECK:
912 {
913 fm_desense_check_t tmp;
914 WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_IS_DESE_CHAN\n");
915
916 if (copy_from_user(&tmp, (void *)arg, sizeof(fm_desense_check_t))) {
917 WCN_DBG(FM_ALT | MAIN, "desene check, copy_from_user err\n");
918 ret = -EFAULT;
919 goto out;
920 }
921 ret = fm_desense_check(fm, (fm_u16) tmp.freq, tmp.rssi);
922
923 /*if (copy_to_user((void*)arg, &tmp, sizeof(fm_desense_check_t))) {
924 WCN_DBG(FM_ALT | MAIN, "desene check, copy_to_user err\n");
925 ret = -EFAULT;
926 goto out;
927 } */
928
929 break;
930 }
931 case FM_IOCTL_SCAN_GETRSSI:
932 {
933 /*struct fm_rssi_req *req;
934 WCN_DBG(FM_DBG | MAIN, "FM_IOCTL_SCAN_GETRSSI\n");
935 if(!(req = fm_vmalloc(sizeof(struct fm_rssi_req))))
936 {
937 WCN_DBG(FM_ALT | MAIN, "fm_vmalloc err\n");
938 ret = -EFAULT;
939 goto out;
940 }
941 if(copy_from_user(req, (void*)arg, sizeof(struct fm_rssi_req)))
942 {
943 WCN_DBG(FM_ALT | MAIN, "copy_from_user err\n");
944 ret = -EFAULT;
945 fm_vfree(req);
946 goto out;
947 }
948 ret = fm_get_rssi_after_scan(fm, req);
949 if(-ERR_FW_NORES == ret){
950 WCN_DBG(FM_ALT | MAIN, "fm_get_rssi_after_scan err\n");
951 }
952 if(copy_to_user((void*)arg, req, sizeof(struct fm_rssi_req)))
953 {
954 WCN_DBG(FM_ALT | MAIN, "copy_to_user err\n");
955 ret = -EFAULT;
956 fm_vfree(req);
957 goto out;
958 }
959 */
960 WCN_DBG(FM_ALT | MAIN, "FM_IOCTL_SCAN_GETRSSI:not support\n");
961 break;
962 }
963
964 case FM_IOCTL_DUMP_REG:
965 {
966 WCN_DBG(FM_NTC | MAIN, "......FM_IOCTL_DUMP_REG......\n");
967
968 ret = fm_dump_reg();
969 if (ret) {
970 WCN_DBG(FM_ALT | MAIN, "fm_dump_reg err\n");
971 }
972 break;
973 }
974 case FM_IOCTL_GPS_RTC_DRIFT:{
975 struct fm_gps_rtc_info rtc_info;
976 WCN_DBG(FM_NTC | MAIN, "......FM_IOCTL_GPS_RTC_DRIFT......\n");
977
978 if (fm_false == fm->chipon) {
979 WCN_DBG(FM_ERR | MAIN, "ERROR, FM chip is OFF\n");
980 ret = -EFAULT;
981 goto out;
982 }
983 if (copy_from_user(&rtc_info, (void *)arg, sizeof(struct fm_gps_rtc_info))) {
984 WCN_DBG(FM_ERR | MAIN, "copy_from_user error\n");
985 ret = -EFAULT;
986 goto out;
987 }
988
989 ret = fm_get_gps_rtc_info(&rtc_info);
990 if (ret) {
991 WCN_DBG(FM_ERR | MAIN, "fm_get_gps_rtc_info error\n");
992 goto out;
993 }
994 break;
995 }
996 case FM_IOCTL_OVER_BT_ENABLE:
997 {
998 fm_s32 fm_via_bt = -1;
999 WCN_DBG(FM_NTC | MAIN, "......FM_IOCTL_OVER_BT_ENABLE......\n");
1000
1001 if (copy_from_user(&fm_via_bt, (void *)arg, sizeof(int32_t))) {
1002 WCN_DBG(FM_ERR | MAIN, "copy_from_user error\n");
1003 ret = -EFAULT;
1004 goto out;
1005 }
1006
1007 ret = fm_over_bt(fm, fm_via_bt);
1008 if (ret) {
1009 WCN_DBG(FM_ERR | MAIN, "fm_over_bt err\n");
1010 }
1011 break;
1012 }
1013
1014 case FM_IOCTL_SET_SEARCH_THRESHOLD:
1015 {
1016 struct fm_search_threshold_t parm;
1017 WCN_DBG(FM_NTC | MAIN, "......FM_IOCTL_SET_SEARCH_THRESHOLD......\n");
1018
1019 if (copy_from_user
1020 (&parm, (void *)arg, sizeof(struct fm_search_threshold_t))) {
1021 WCN_DBG(FM_ALT | MAIN, "copy_from_user error\n");
1022 ret = -EFAULT;
1023 goto out;
1024 }
1025 ret = fm_set_search_th(fm, parm);
1026 if (ret < 0) {
1027 WCN_DBG(FM_ERR | MAIN,
1028 "FM_IOCTL_SET_SEARCH_THRESHOLD not supported\n");
1029 }
1030 break;
1031 }
1032 case FM_IOCTL_GET_AUDIO_INFO:
1033 {
1034 fm_audio_info_t aud_data;
1035
1036 ret = fm_get_aud_info(&aud_data);
1037 if (ret) {
1038 WCN_DBG(FM_ERR | MAIN, "fm_get_aud_info err\n");
1039 }
1040 if (copy_to_user((void *)arg, &aud_data, sizeof(fm_audio_info_t))) {
1041 WCN_DBG(FM_ERR | MAIN, "copy_to_user error\n");
1042 ret = -EFAULT;
1043 goto out;
1044 }
1045
1046 WCN_DBG(FM_DBG | MAIN, "fm_get_aud_info ret=%d\n", ret);
1047 break;
1048 }
1049 /***************************FM Tx function************************************/
1050 case FM_IOCTL_TX_SUPPORT:
1051 {
1052 fm_s32 tx_support = -1;
1053 WCN_DBG(FM_NTC | MAIN, "......FM_IOCTL_TX_SUPPORT......\n");
1054
1055 ret = fm_tx_support(fm, &tx_support);
1056 if (ret) {
1057 WCN_DBG(FM_ERR | MAIN, "fm_tx_support err\n");
1058 }
1059 if (copy_to_user((void *)arg, &tx_support, sizeof(fm_s32))) {
1060 WCN_DBG(FM_ERR | MAIN, "copy_to_user error\n");
1061 ret = -EFAULT;
1062 goto out;
1063 }
1064 break;
1065 }
1066 case FM_IOCTL_POWERUP_TX:
1067 {
1068 struct fm_tune_parm parm;
1069 WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_POWERUP_TX:0\n");
1070 if (copy_from_user(&parm, (void *)arg, sizeof(struct fm_tune_parm))) {
1071 ret = -EFAULT;
1072 goto out;
1073 }
1074
1075 ret = fm_powerup_tx(fm, &parm);
1076 if (ret < 0) {
1077 goto out;
1078 }
1079 ret = fm_tune_tx(fm, &parm);
1080 if (ret < 0)
1081 goto out;
1082
1083 if (copy_to_user((void *)arg, &parm, sizeof(struct fm_tune_parm))) {
1084 ret = -EFAULT;
1085 goto out;
1086 }
1087 WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_POWERUP_TX:1\n");
1088 break;
1089 }
1090
1091 case FM_IOCTL_TUNE_TX:
1092 {
1093 struct fm_tune_parm parm;
1094 WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_TUNE_TX:0\n");
1095
1096 if (copy_from_user(&parm, (void *)arg, sizeof(struct fm_tune_parm))) {
1097 ret = -EFAULT;
1098 goto out;
1099 }
1100
1101 ret = fm_tune_tx(fm, &parm);
1102 if (ret < 0) {
1103 goto out;
1104 }
1105
1106 if (copy_to_user((void *)arg, &parm, sizeof(struct fm_tune_parm))) {
1107 ret = -EFAULT;
1108 goto out;
1109 }
1110
1111 WCN_DBG(FM_NTC | MAIN, "FM_IOCTL_TUNE_TX:1\n");
1112 break;
1113 }
1114 case FM_IOCTL_RDSTX_SUPPORT:
1115 {
1116 fm_s32 rds_tx_support = -1;
1117 WCN_DBG(FM_NTC | MAIN, "......FM_IOCTL_RDSTX_SUPPORT......\n");
1118
1119 ret = fm_rdstx_support(fm, &rds_tx_support);
1120 if (ret) {
1121 WCN_DBG(FM_ERR | MAIN, "fm_rdstx_support err\n");
1122 }
1123 if (copy_to_user((void *)arg, &rds_tx_support, sizeof(fm_s32))) {
1124 WCN_DBG(FM_ERR | MAIN, "copy_to_user error\n");
1125 ret = -EFAULT;
1126 goto out;
1127 }
1128 break;
1129 }
1130
1131 case FM_IOCTL_RDSTX_ENABLE:
1132 {
1133 fm_s32 onoff = -1;
1134 WCN_DBG(FM_NTC | MAIN, "......FM_IOCTL_RDSTX_ENABLE......\n");
1135
1136 if (copy_from_user(&onoff, (void *)arg, sizeof(fm_s32))) {
1137 WCN_DBG(FM_ALT | MAIN,
1138 "FM_IOCTL_RDSTX_ENABLE, copy_from_user err\n");
1139 ret = -EFAULT;
1140 goto out;
1141 }
1142
1143 ret = fm_rdstx_enable(fm, onoff);
1144 if (ret) {
1145 WCN_DBG(FM_ERR | MAIN, "fm_rdstx_enable err\n");
1146 }
1147
1148 break;
1149 }
1150
1151 case FM_IOCTL_RDS_TX:
1152 {
1153 struct fm_rds_tx_parm parm;
1154 WCN_DBG(FM_NTC | MAIN, "......FM_IOCTL_RDS_TX......\n");
1155
1156 if (copy_from_user(&parm, (void *)arg, sizeof(struct fm_rds_tx_parm))) {
1157 WCN_DBG(FM_ALT | MAIN, "RDS Tx, copy_from_user err\n");
1158 ret = -EFAULT;
1159 goto out;
1160 }
1161
1162 ret = fm_rds_tx(fm, &parm);
1163 if (ret) {
1164 WCN_DBG(FM_ALT | MAIN, "fm_rds_tx err\n");
1165 }
1166
1167 if (copy_to_user((void *)arg, &parm, sizeof(struct fm_rds_tx_parm))) {
1168 WCN_DBG(FM_ALT | MAIN, "RDS Tx, copy_to_user err\n");
1169 ret = -EFAULT;
1170 goto out;
1171 }
1172 break;
1173 }
1174
1175 case FM_IOCTL_TX_SCAN:
1176 {
1177 struct fm_tx_scan_parm parm;
1178 WCN_DBG(FM_NTC | MAIN, "......FM_IOCTL_TX_SCAN......\n");
1179
1180 if (copy_from_user(&parm, (void *)arg, sizeof(struct fm_tx_scan_parm))) {
1181 WCN_DBG(FM_ALT | MAIN, "copy_from_user error\n");
1182 ret = -EFAULT;
1183 goto out;
1184 }
1185 ret = fm_tx_scan(fm, &parm);
1186 if (ret < 0) {
1187 WCN_DBG(FM_ERR | MAIN, "FM_IOCTL_TX_SCAN failed\n");
1188 }
1189 if (copy_to_user((void *)arg, &parm, sizeof(struct fm_tx_scan_parm))) {
1190 WCN_DBG(FM_ALT | MAIN, "copy_to_user error\n");
1191 ret = -EFAULT;
1192 goto out;
1193 }
1194 break;
1195 }
1196
1197 default:
1198 ret = -EPERM;
1199 }
1200
1201 out:
1202 if (ret == -FM_EFW) {
1203 if (fm_sys_state_get(fm) == FM_SUBSYS_RST_OFF) {
1204 fm->wholechiprst = fm_false;
1205 /* subsystem reset */
1206 fm_subsys_reset(fm);
1207 }
1208 }
1209
1210 return ret;
1211 }
1212
1213 static loff_t fm_ops_lseek(struct file *filp, loff_t off, fm_s32 whence)
1214 {
1215 struct fm *fm = filp->private_data;
1216
1217 if (whence == SEEK_END) {
1218 fm_hwscan_stop(fm);
1219 } else if (whence == SEEK_SET) {
1220 FM_EVENT_SEND(fm->rds_event, FM_RDS_DATA_READY);
1221 }
1222
1223 return off;
1224 }
1225
1226 static ssize_t fm_ops_read(struct file *filp, char *buf, size_t len, loff_t *off)
1227 {
1228 struct fm *fm = filp->private_data;
1229 fm_s32 copy_len = 0;
1230
1231 if (!fm) {
1232 WCN_DBG(FM_ALT | MAIN, "fm_read invalid fm pointer\n");
1233 return 0;
1234 }
1235
1236 WCN_DBG(FM_DBG | MAIN, "rds buf len=%zu\n", len);
1237 WCN_DBG(FM_DBG | MAIN, "sizeof(rds_t)=%zu\n", sizeof(rds_t));
1238
1239 if (!buf || len < sizeof(rds_t)) {
1240 WCN_DBG(FM_DBG | MAIN, "fm_read invliad buf\n");
1241 return 0;
1242 }
1243 /* return if FM is resetting */
1244 if (fm_sys_state_get(fm) != FM_SUBSYS_RST_OFF) {
1245 WCN_DBG(FM_ALT | MAIN, "fm subsys underring reset\n");
1246 return 0;
1247 }
1248
1249 copy_len = sizeof(rds_t);
1250
1251 return fm_rds_read(fm, buf, copy_len);
1252 }
1253
1254 static fm_s32 fm_ops_open(struct inode *inode, struct file *filp)
1255 {
1256 fm_s32 ret = 0;
1257 struct fm_platform *plat = container_of(inode->i_cdev, struct fm_platform, cdev);
1258 struct fm *fm = container_of(plat, struct fm, platform);
1259
1260 WCN_DBG(FM_NTC | MAIN, "fm_ops_open:0\n");
1261 if (fm_sys_state_get(fm) != FM_SUBSYS_RST_OFF) {
1262 WCN_DBG(FM_ALT | MAIN, "FM subsys is resetting, retry later\n");
1263 ret = -FM_ESRST;
1264 return ret;
1265 }
1266
1267 ret = fm_open(fm);
1268 filp->private_data = fm;
1269
1270 WCN_DBG(FM_NTC | MAIN, "fm_ops_open:1\n");
1271 return ret;
1272 }
1273
1274 static fm_s32 fm_ops_release(struct inode *inode, struct file *filp)
1275 {
1276 /* fm_s32 ret = 0; */
1277 /* struct fm_platform *plat = container_of(inode->i_cdev, struct fm_platform, cdev); */
1278 /* struct fm *fm = container_of(plat, struct fm, platform); */
1279
1280 /* WCN_DBG(FM_NTC | MAIN, "fm_ops_release:0\n"); */
1281 /* fm_close(fm); */
1282 filp->private_data = NULL;
1283
1284 WCN_DBG(FM_NTC | MAIN, "fm_ops_release\n");
1285 return 0;
1286 }
1287
1288 static fm_s32 fm_ops_flush(struct file *filp, fl_owner_t Id)
1289 {
1290 fm_s32 ret = 0;
1291 struct fm *fm = filp->private_data;
1292
1293 WCN_DBG(FM_NTC | MAIN, "fm_ops_flush:0\n");
1294 fm_close(fm);
1295 filp->private_data = fm;
1296
1297 WCN_DBG(FM_NTC | MAIN, "fm_ops_flush:1\n");
1298 return ret;
1299 }
1300
1301 static ssize_t fm_proc_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
1302 {
1303 struct fm *fm = g_fm;
1304 ssize_t length = 0;
1305 char tmpbuf[3];
1306 unsigned long pos = *ppos;
1307
1308 WCN_DBG(FM_NTC | MAIN, "Enter fm_proc_read.\n");
1309 /* WCN_DBG(FM_NTC | MAIN, "count = %d\n", count); */
1310 /* WCN_DBG(FM_NTC | MAIN, "ppos = %d\n", pos); */
1311
1312 if (pos != 0)
1313 return 0;
1314
1315 if (!fm) {
1316 WCN_DBG(FM_ALT | MAIN, "para err\n");
1317 return 0;
1318 }
1319
1320 if (fm->chipon && (fm_pwr_state_get(fm) == FM_PWR_RX_ON)) {
1321 length = sprintf(tmpbuf, "1\n");
1322 WCN_DBG(FM_NTC | MAIN, " FM_PWR_RX_ON\n");
1323 } else if (fm->chipon && (fm_pwr_state_get(fm) == FM_PWR_TX_ON)) {
1324 length = sprintf(tmpbuf, "2\n");
1325 WCN_DBG(FM_NTC | MAIN, " FM_PWR_TX_ON\n");
1326 } else {
1327 length = sprintf(tmpbuf, "0\n");
1328 WCN_DBG(FM_NTC | MAIN, " FM POWER OFF\n");
1329 }
1330
1331 if (copy_to_user(buf, tmpbuf, length)) {
1332 WCN_DBG(FM_NTC | MAIN, " Read FM status fail!\n");
1333 return 0;
1334 }
1335
1336 pos += length;
1337 *ppos = pos;
1338 WCN_DBG(FM_NTC | MAIN, "Leave fm_proc_read. length = %zu\n", length);
1339
1340 return length;
1341 }
1342
1343 /*
1344 static fm_s32 fm_proc_read(char *page, char **start, off_t off, fm_s32 count, fm_s32 *eof, void *data)
1345 {
1346 fm_s32 cnt = 0;
1347 struct fm *fm = g_fm;
1348
1349 WCN_DBG(FM_NTC | MAIN, "Enter fm_proc_read.\n");
1350
1351 if (off != 0)
1352 return 0;
1353
1354 if (!fm) {
1355 WCN_DBG(FM_ALT | MAIN, "para err\n");
1356 return 0;
1357 }
1358
1359 if (fm->chipon && (fm_pwr_state_get(fm) == FM_PWR_RX_ON))
1360 {
1361 cnt = sprintf(page, "1\n");
1362 WCN_DBG(FM_NTC | MAIN, " FM_PWR_RX_ON\n");
1363 }
1364 else if (fm->chipon && (fm_pwr_state_get(fm) == FM_PWR_TX_ON))
1365 {
1366 WCN_DBG(FM_NTC | MAIN, " FM_PWR_TX_ON\n");
1367 cnt = sprintf(page, "2\n");
1368 }
1369 else
1370 {
1371 cnt = sprintf(page, "0\n");
1372 }
1373
1374 *eof = 1;
1375 WCN_DBG(FM_NTC | MAIN, "Leave fm_proc_read. cnt = %d\n", cnt);
1376 return cnt;
1377 }
1378 */
1379 static ssize_t fm_proc_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
1380 {
1381 fm_s8 tmp_buf[50] = { 0 };
1382 fm_u32 copysize;
1383
1384 copysize = (count < (sizeof(tmp_buf) - 1)) ? count : (sizeof(tmp_buf) - 1);
1385
1386 WCN_DBG(FM_NTC | MAIN, "fm_proc_write:0\n");
1387 if (copy_from_user(tmp_buf, buffer, copysize)) {
1388 WCN_DBG(FM_ERR | MAIN, "failed copy_from_user\n");
1389 return -EFAULT;
1390 }
1391
1392 if (strncmp(tmp_buf, "subsys reset", strlen("subsys reset")) == 0) {
1393 fm_subsys_reset(g_fm);
1394 return count;
1395 }
1396
1397 if (!fm_cust_config_setup(tmp_buf)) {
1398 WCN_DBG(FM_NTC | MAIN, "get config form %s ok\n", tmp_buf);
1399 return count;
1400 }
1401
1402 if (sscanf(tmp_buf, "%x", &g_dbg_level) != 1) {
1403 WCN_DBG(FM_ERR | MAIN, "failed g_dbg_level = 0x%x\n", g_dbg_level);
1404 return -EFAULT;
1405 }
1406
1407 WCN_DBG(FM_NTC | MAIN, "fm_proc_write:1 g_dbg_level = 0x%x\n", g_dbg_level);
1408 return count;
1409 }
1410
1411 //#define FM_DEV_STATIC_ALLOC
1412 /* #define FM_DEV_MAJOR 193 */
1413 /*static int FM_major = FM_DEV_MAJOR; */ /* dynamic allocation */
1414
1415 static fm_s32 fm_cdev_setup(struct fm *fm)
1416 {
1417 fm_s32 ret = 0;
1418 struct fm_platform *plat = &fm->platform;
1419
1420 #ifdef FM_DEV_STATIC_ALLOC
1421 /*static allocate chrdev */
1422 plat->dev_t = MKDEV(FM_major, 0);
1423 ret = register_chrdev_region(plat->dev_t, 1, FM_NAME);
1424
1425 if (ret) {
1426 WCN_DBG(FM_ERR | MAIN, "%s():fail to register chrdev\n", __func__);
1427 return ret;
1428 }
1429 #endif
1430
1431 #ifndef FM_DEV_STATIC_ALLOC
1432 ret = alloc_chrdev_region(&plat->dev_t, 0, 1, FM_NAME);
1433
1434 if (ret) {
1435 WCN_DBG(FM_ALT | MAIN, "alloc dev_t failed\n");
1436 return ret;
1437 }
1438 #endif
1439
1440 WCN_DBG(FM_NTC | MAIN, "alloc %s:%d:%d\n", FM_NAME, MAJOR(plat->dev_t), MINOR(plat->dev_t));
1441
1442 cdev_init(&plat->cdev, &fm_ops);
1443
1444 plat->cdev.owner = THIS_MODULE;
1445 plat->cdev.ops = &fm_ops;
1446
1447 ret = cdev_add(&plat->cdev, plat->dev_t, 1);
1448
1449 if (ret) {
1450 WCN_DBG(FM_ALT | MAIN, "add dev_t failed\n");
1451 return ret;
1452 }
1453 #ifndef FM_DEV_STATIC_ALLOC
1454 plat->cls = class_create(THIS_MODULE, FM_NAME);
1455
1456 if (IS_ERR(plat->cls)) {
1457 ret = PTR_ERR(plat->cls);
1458 WCN_DBG(FM_ALT | MAIN, "class_create err:%d\n", ret);
1459 return ret;
1460 }
1461
1462 plat->dev = device_create(plat->cls, NULL, plat->dev_t, NULL, FM_NAME);
1463 #endif
1464
1465 return ret;
1466 }
1467
1468 static fm_s32 fm_cdev_destroy(struct fm *fm)
1469 {
1470 FMR_ASSERT(fm);
1471
1472 device_destroy(fm->platform.cls, fm->platform.dev_t);
1473 class_destroy(fm->platform.cls);
1474 cdev_del(&fm->platform.cdev);
1475 unregister_chrdev_region(fm->platform.dev_t, 1);
1476
1477 return 0;
1478 }
1479
1480 static fm_s32 fm_mod_init(fm_u32 arg)
1481 {
1482 fm_s32 ret = 0;
1483 struct fm *fm = NULL;
1484
1485 fm = fm_dev_init(0);
1486
1487 if (!fm) {
1488 ret = -ENOMEM;
1489 goto ERR_EXIT;
1490 }
1491
1492 if ((ret = fm_cdev_setup(fm))) {
1493 goto ERR_EXIT;
1494 }
1495 /* fm proc file create "/proc/fm" */
1496 g_fm_proc = proc_create(FM_PROC_FILE, 0444, NULL, &fm_proc_ops);
1497
1498 if (g_fm_proc == NULL) {
1499 WCN_DBG(FM_ALT | MAIN, "create_proc_entry failed\n");
1500 ret = -ENOMEM;
1501 goto ERR_EXIT;
1502 } else {
1503 WCN_DBG(FM_NTC | MAIN, "create_proc_entry success\n");
1504 }
1505
1506 g_fm = fm;
1507 return 0;
1508
1509 ERR_EXIT:
1510
1511 if (fm) {
1512 fm_cdev_destroy(fm);
1513 fm_dev_destroy(fm);
1514 }
1515
1516 remove_proc_entry(FM_PROC_FILE, NULL);
1517 return ret;
1518 }
1519
1520 static fm_s32 fm_mod_destroy(struct fm *fm)
1521 {
1522 fm_s32 ret = 0;
1523
1524 FMR_ASSERT(fm);
1525
1526 WCN_DBG(FM_NTC | MAIN, "%s\n", __func__);
1527 remove_proc_entry(FM_PROC_FILE, NULL);
1528 fm_cdev_destroy(fm);
1529 fm_dev_destroy(fm);
1530
1531 return ret;
1532 }
1533
1534 static fm_s32 mt_fm_probe(struct platform_device *pdev)
1535 {
1536 fm_s32 ret = 0;
1537
1538 WCN_DBG(FM_NTC | MAIN, "%s\n", __func__);
1539
1540 ret = fm_mod_init(0);
1541
1542 if (ret) {
1543 WCN_DBG(FM_ERR | MAIN, "fm mod init err\n");
1544 return -ENOMEM;
1545 }
1546
1547 return ret;
1548 }
1549
1550 static fm_s32 mt_fm_remove(struct platform_device *pdev)
1551 {
1552 WCN_DBG(FM_NTC | MAIN, "%s\n", __func__);
1553
1554 fm_mod_destroy(g_fm);
1555 g_fm = NULL;
1556 return 0;
1557 }
1558
1559 static struct platform_device mt_fm_device = {
1560 .name = FM_NAME,
1561 .id = -1,
1562 };
1563
1564 /* platform driver entry */
1565 static struct platform_driver mt_fm_dev_drv = {
1566 .probe = mt_fm_probe,
1567 .remove = mt_fm_remove,
1568 .driver = {
1569 .name = FM_NAME,
1570 .owner = THIS_MODULE,
1571 }
1572 };
1573
1574 static fm_s32 mt_fm_init(void)
1575 {
1576 fm_s32 ret = 0;
1577
1578 ret = fm_env_setup();
1579
1580 if (ret) {
1581 fm_env_destroy();
1582 return ret;
1583 }
1584 /* register fm device to platform bus */
1585 ret = platform_device_register(&mt_fm_device);
1586
1587 if (ret) {
1588 return ret;
1589 }
1590 /* register fm driver to platform bus */
1591 ret = platform_driver_register(&mt_fm_dev_drv);
1592
1593 if (ret) {
1594 return ret;
1595 }
1596
1597 WCN_DBG(FM_NTC | MAIN, "6. fm platform driver registered\n");
1598 return ret;
1599 }
1600
1601 static void mt_fm_exit(void)
1602 {
1603 platform_driver_unregister(&mt_fm_dev_drv);
1604 fm_env_destroy();
1605 }
1606
1607 #ifdef MTK_WCN_REMOVE_KERNEL_MODULE
1608 int mtk_wcn_fm_init(void)
1609 {
1610 return mt_fm_init();
1611 }
1612
1613 void mtk_wcn_fm_exit(void)
1614 {
1615 mt_fm_exit();
1616 }
1617 EXPORT_SYMBOL(mtk_wcn_fm_init);
1618 EXPORT_SYMBOL(mtk_wcn_fm_exit);
1619 #else
1620 module_init(mt_fm_init);
1621 module_exit(mt_fm_exit);
1622 #endif
1623 EXPORT_SYMBOL(g_dbg_level);
1624 MODULE_LICENSE("GPL");
1625 MODULE_DESCRIPTION("MediaTek FM Driver");
1626 MODULE_AUTHOR("Hongcheng <hongcheng.xia@MediaTek.com>");