blob: b5aa6687e0078fef09a26aa5cc51aed40dcd783e [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* Copyright (c) 2011, Code Aurora Forum. All rights reserved
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13#define DRIVER_AUTHOR "Archana Ramchandran <archanar@codeaurora.org>"
14#define DRIVER_NAME "radio-iris"
15#define DRIVER_CARD "Qualcomm FM Radio Transceiver"
16#define DRIVER_DESC "Driver for Qualcomm FM Radio Transceiver "
17
18#include <linux/version.h>
19#include <linux/init.h>
20#include <linux/delay.h>
21#include <linux/uaccess.h>
22#include <linux/kfifo.h>
23#include <linux/param.h>
24#include <linux/interrupt.h>
25#include <linux/kernel.h>
26#include <linux/module.h>
27#include <linux/version.h>
28#include <linux/videodev2.h>
29#include <linux/mutex.h>
30#include <linux/unistd.h>
31#include <linux/atomic.h>
32#include <linux/platform_device.h>
33#include <linux/workqueue.h>
34#include <linux/slab.h>
35#include <media/v4l2-common.h>
36#include <media/v4l2-ioctl.h>
37#include <media/radio-iris.h>
38#include <asm/unaligned.h>
39
40static unsigned int rds_buf = 100;
41module_param(rds_buf, uint, 0);
42MODULE_PARM_DESC(rds_buf, "RDS buffer entries: *100*");
43
44static void radio_hci_cmd_task(unsigned long arg);
45static void radio_hci_rx_task(unsigned long arg);
46static struct video_device *video_get_dev(void);
47static DEFINE_RWLOCK(hci_task_lock);
48
49struct iris_device {
50 struct device *dev;
51 struct kfifo data_buf[IRIS_BUF_MAX];
52
53 int pending_xfrs[IRIS_XFR_MAX];
54 int xfr_bytes_left;
55 int xfr_in_progress;
56 struct completion sync_xfr_start;
57 int tune_req;
Ankur Nandwanid928d542011-08-11 13:15:41 -070058 unsigned int mode;
59
60 __u16 pi;
61 __u8 pty;
62 __u8 ps_repeatcount;
Ankur Nandwani8f972e52011-08-24 11:48:32 -070063 __u8 prev_trans_rds;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070064
65 struct video_device *videodev;
66
67 struct mutex lock;
68 spinlock_t buf_lock[IRIS_BUF_MAX];
69 wait_queue_head_t event_queue;
70 wait_queue_head_t read_queue;
71
72 struct radio_hci_dev *fm_hdev;
73
74 struct v4l2_capability *g_cap;
75 struct v4l2_control *g_ctl;
76
77 struct hci_fm_mute_mode_req mute_mode;
78 struct hci_fm_stereo_mode_req stereo_mode;
79 struct hci_fm_station_rsp fm_st_rsp;
80 struct hci_fm_search_station_req srch_st;
81 struct hci_fm_search_rds_station_req srch_rds;
82 struct hci_fm_search_station_list_req srch_st_list;
83 struct hci_fm_recv_conf_req recv_conf;
Ankur Nandwanid928d542011-08-11 13:15:41 -070084 struct hci_fm_trans_conf_req_struct trans_conf;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070085 struct hci_fm_rds_grp_req rds_grp;
86 unsigned char g_search_mode;
Ankur Nandwanid928d542011-08-11 13:15:41 -070087 unsigned int tone_freq;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070088 unsigned char g_scan_time;
89 unsigned int g_antenna;
90 unsigned int g_rds_grp_proc_ps;
91 enum iris_region_t region;
92 struct hci_fm_dbg_param_rsp st_dbg_param;
93 struct hci_ev_srch_list_compl srch_st_result;
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -070094 struct hci_fm_riva_poke riva_data_req;
95 struct hci_fm_ssbi_req ssbi_data_accs;
96 struct hci_fm_ssbi_peek ssbi_peek_reg;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070097};
98
99static struct video_device *priv_videodev;
100
101static struct v4l2_queryctrl iris_v4l2_queryctrl[] = {
102 {
103 .id = V4L2_CID_AUDIO_VOLUME,
104 .type = V4L2_CTRL_TYPE_INTEGER,
105 .name = "Volume",
106 .minimum = 0,
107 .maximum = 15,
108 .step = 1,
109 .default_value = 15,
110 },
111 {
112 .id = V4L2_CID_AUDIO_BALANCE,
113 .flags = V4L2_CTRL_FLAG_DISABLED,
114 },
115 {
116 .id = V4L2_CID_AUDIO_BASS,
117 .flags = V4L2_CTRL_FLAG_DISABLED,
118 },
119 {
120 .id = V4L2_CID_AUDIO_TREBLE,
121 .flags = V4L2_CTRL_FLAG_DISABLED,
122 },
123 {
124 .id = V4L2_CID_AUDIO_MUTE,
125 .type = V4L2_CTRL_TYPE_BOOLEAN,
126 .name = "Mute",
127 .minimum = 0,
128 .maximum = 1,
129 .step = 1,
130 .default_value = 1,
131 },
132 {
133 .id = V4L2_CID_AUDIO_LOUDNESS,
134 .flags = V4L2_CTRL_FLAG_DISABLED,
135 },
136 {
137 .id = V4L2_CID_PRIVATE_IRIS_SRCHMODE,
138 .type = V4L2_CTRL_TYPE_INTEGER,
139 .name = "Search mode",
140 .minimum = 0,
141 .maximum = 7,
142 .step = 1,
143 .default_value = 0,
144 },
145 {
146 .id = V4L2_CID_PRIVATE_IRIS_SCANDWELL,
147 .type = V4L2_CTRL_TYPE_INTEGER,
148 .name = "Search dwell time",
149 .minimum = 0,
150 .maximum = 7,
151 .step = 1,
152 .default_value = 0,
153 },
154 {
155 .id = V4L2_CID_PRIVATE_IRIS_SRCHON,
156 .type = V4L2_CTRL_TYPE_BOOLEAN,
157 .name = "Search on/off",
158 .minimum = 0,
159 .maximum = 1,
160 .step = 1,
161 .default_value = 1,
162
163 },
164 {
165 .id = V4L2_CID_PRIVATE_IRIS_STATE,
166 .type = V4L2_CTRL_TYPE_INTEGER,
167 .name = "radio 0ff/rx/tx/reset",
168 .minimum = 0,
169 .maximum = 3,
170 .step = 1,
171 .default_value = 1,
172
173 },
174 {
175 .id = V4L2_CID_PRIVATE_IRIS_REGION,
176 .type = V4L2_CTRL_TYPE_INTEGER,
177 .name = "radio standard",
178 .minimum = 0,
179 .maximum = 2,
180 .step = 1,
181 .default_value = 0,
182 },
183 {
184 .id = V4L2_CID_PRIVATE_IRIS_SIGNAL_TH,
185 .type = V4L2_CTRL_TYPE_INTEGER,
186 .name = "Signal Threshold",
187 .minimum = 0x80,
188 .maximum = 0x7F,
189 .step = 1,
190 .default_value = 0,
191 },
192 {
193 .id = V4L2_CID_PRIVATE_IRIS_SRCH_PTY,
194 .type = V4L2_CTRL_TYPE_INTEGER,
195 .name = "Search PTY",
196 .minimum = 0,
197 .maximum = 31,
198 .default_value = 0,
199 },
200 {
201 .id = V4L2_CID_PRIVATE_IRIS_SRCH_PI,
202 .type = V4L2_CTRL_TYPE_INTEGER,
203 .name = "Search PI",
204 .minimum = 0,
205 .maximum = 0xFF,
206 .default_value = 0,
207 },
208 {
209 .id = V4L2_CID_PRIVATE_IRIS_SRCH_CNT,
210 .type = V4L2_CTRL_TYPE_INTEGER,
211 .name = "Preset num",
212 .minimum = 0,
213 .maximum = 12,
214 .default_value = 0,
215 },
216 {
217 .id = V4L2_CID_PRIVATE_IRIS_EMPHASIS,
218 .type = V4L2_CTRL_TYPE_BOOLEAN,
219 .name = "Emphasis",
220 .minimum = 0,
221 .maximum = 1,
222 .default_value = 0,
223 },
224 {
225 .id = V4L2_CID_PRIVATE_IRIS_RDS_STD,
226 .type = V4L2_CTRL_TYPE_BOOLEAN,
227 .name = "RDS standard",
228 .minimum = 0,
229 .maximum = 1,
230 .default_value = 0,
231 },
232 {
233 .id = V4L2_CID_PRIVATE_IRIS_SPACING,
234 .type = V4L2_CTRL_TYPE_INTEGER,
235 .name = "Channel spacing",
236 .minimum = 0,
237 .maximum = 2,
238 .default_value = 0,
239 },
240 {
241 .id = V4L2_CID_PRIVATE_IRIS_RDSON,
242 .type = V4L2_CTRL_TYPE_BOOLEAN,
243 .name = "RDS on/off",
244 .minimum = 0,
245 .maximum = 1,
246 .default_value = 0,
247 },
248 {
249 .id = V4L2_CID_PRIVATE_IRIS_RDSGROUP_MASK,
250 .type = V4L2_CTRL_TYPE_INTEGER,
251 .name = "RDS group mask",
252 .minimum = 0,
253 .maximum = 0xFFFFFFFF,
254 .default_value = 0,
255 },
256 {
257 .id = V4L2_CID_PRIVATE_IRIS_RDSGROUP_PROC,
258 .type = V4L2_CTRL_TYPE_INTEGER,
259 .name = "RDS processing",
260 .minimum = 0,
261 .maximum = 0xFF,
262 .default_value = 0,
263 },
264 {
265 .id = V4L2_CID_PRIVATE_IRIS_RDSD_BUF,
266 .type = V4L2_CTRL_TYPE_INTEGER,
267 .name = "RDS data groups to buffer",
268 .minimum = 1,
269 .maximum = 21,
270 .default_value = 0,
271 },
272 {
273 .id = V4L2_CID_PRIVATE_IRIS_PSALL,
274 .type = V4L2_CTRL_TYPE_BOOLEAN,
275 .name = "pass all ps strings",
276 .minimum = 0,
277 .maximum = 1,
278 .default_value = 0,
279 },
280 {
281 .id = V4L2_CID_PRIVATE_IRIS_LP_MODE,
282 .type = V4L2_CTRL_TYPE_BOOLEAN,
283 .name = "Low power mode",
284 .minimum = 0,
285 .maximum = 1,
286 .default_value = 0,
287 },
288 {
289 .id = V4L2_CID_PRIVATE_IRIS_ANTENNA,
290 .type = V4L2_CTRL_TYPE_BOOLEAN,
291 .name = "headset/internal",
292 .minimum = 0,
293 .maximum = 1,
294 .default_value = 0,
295 },
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700296 {
297 .id = V4L2_CID_PRIVATE_IRIS_TX_SETPSREPEATCOUNT,
298 .type = V4L2_CTRL_TYPE_INTEGER,
299 .name = "Set PS REPEATCOUNT",
300 .minimum = 0,
301 .maximum = 15,
302 },
303 {
304 .id = V4L2_CID_PRIVATE_IRIS_STOP_RDS_TX_PS_NAME,
305 .type = V4L2_CTRL_TYPE_BOOLEAN,
306 .name = "Stop PS NAME",
307 .minimum = 0,
308 .maximum = 1,
309 },
310 {
311 .id = V4L2_CID_PRIVATE_IRIS_STOP_RDS_TX_RT,
312 .type = V4L2_CTRL_TYPE_BOOLEAN,
313 .name = "Stop RT",
314 .minimum = 0,
315 .maximum = 1,
316 },
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -0700317 {
318 .id = V4L2_CID_PRIVATE_IRIS_SOFT_MUTE,
319 .type = V4L2_CTRL_TYPE_BOOLEAN,
320 .name = "Soft Mute",
321 .minimum = 0,
322 .maximum = 1,
323 },
324 {
325 .id = V4L2_CID_PRIVATE_IRIS_RIVA_ACCS_ADDR,
326 .type = V4L2_CTRL_TYPE_BOOLEAN,
327 .name = "Riva addr",
Srinivasa Rao Uppala0f4098f2011-09-06 16:46:28 +0530328 .minimum = 0x3180000,
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -0700329 .maximum = 0x31E0004,
330 },
331 {
332 .id = V4L2_CID_PRIVATE_IRIS_RIVA_ACCS_LEN,
333 .type = V4L2_CTRL_TYPE_INTEGER,
334 .name = "Data len",
335 .minimum = 0,
336 .maximum = 0xFF,
337 },
338 {
339 .id = V4L2_CID_PRIVATE_IRIS_RIVA_PEEK,
340 .type = V4L2_CTRL_TYPE_BOOLEAN,
341 .name = "Riva peek",
342 .minimum = 0,
343 .maximum = 1,
344 },
345 {
346 .id = V4L2_CID_PRIVATE_IRIS_RIVA_POKE,
347 .type = V4L2_CTRL_TYPE_INTEGER,
348 .name = "Riva poke",
Srinivasa Rao Uppala0f4098f2011-09-06 16:46:28 +0530349 .minimum = 0x3180000,
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -0700350 .maximum = 0x31E0004,
351 },
352 {
353 .id = V4L2_CID_PRIVATE_IRIS_SSBI_ACCS_ADDR,
354 .type = V4L2_CTRL_TYPE_INTEGER,
355 .name = "Ssbi addr",
Srinivasa Rao Uppala0f4098f2011-09-06 16:46:28 +0530356 .minimum = 0x280,
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -0700357 .maximum = 0x37F,
358 },
359 {
360 .id = V4L2_CID_PRIVATE_IRIS_SSBI_PEEK,
361 .type = V4L2_CTRL_TYPE_INTEGER,
362 .name = "Ssbi peek",
363 .minimum = 0,
364 .maximum = 0x37F,
365 },
366 {
367 .id = V4L2_CID_PRIVATE_IRIS_SSBI_POKE,
368 .type = V4L2_CTRL_TYPE_INTEGER,
369 .name = "ssbi poke",
370 .minimum = 0x01,
371 .maximum = 0xFF,
372 },
373 {
374 .id = V4L2_CID_PRIVATE_IRIS_HLSI,
375 .type = V4L2_CTRL_TYPE_INTEGER,
376 .name = "set hlsi",
377 .minimum = 0,
378 .maximum = 2,
379 },
Srinivasa Rao Uppala0f4098f2011-09-06 16:46:28 +0530380 {
381 .id = V4L2_CID_PRIVATE_IRIS_RDS_GRP_COUNTERS,
382 .type = V4L2_CTRL_TYPE_BOOLEAN,
383 .name = "RDS grp",
384 .minimum = 0,
385 .maximum = 1,
386 },
387 {
388 .id = V4L2_CID_PRIVATE_IRIS_SET_NOTCH_FILTER,
389 .type = V4L2_CTRL_TYPE_INTEGER,
390 .name = "Notch filter",
391 .minimum = 0,
392 .maximum = 2,
393 },
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700394
395};
396
397static void iris_q_event(struct iris_device *radio,
398 enum iris_evt_t event)
399{
400 struct kfifo *data_b = &radio->data_buf[IRIS_BUF_EVENTS];
401 unsigned char evt = event;
402 if (kfifo_in_locked(data_b, &evt, 1, &radio->buf_lock[IRIS_BUF_EVENTS]))
403 wake_up_interruptible(&radio->event_queue);
404}
405
406static int hci_send_frame(struct sk_buff *skb)
407{
408 struct radio_hci_dev *hdev = (struct radio_hci_dev *) skb->dev;
409
410 if (!hdev) {
411 kfree_skb(skb);
412 return -ENODEV;
413 }
414
415 __net_timestamp(skb);
416
417 skb_orphan(skb);
418 return hdev->send(skb);
419}
420
421static void radio_hci_cmd_task(unsigned long arg)
422{
423 struct radio_hci_dev *hdev = (struct radio_hci_dev *) arg;
424 struct sk_buff *skb;
425 if (!(atomic_read(&hdev->cmd_cnt))
426 && time_after(jiffies, hdev->cmd_last_tx + HZ)) {
427 FMDERR("%s command tx timeout", hdev->name);
428 atomic_set(&hdev->cmd_cnt, 1);
429 }
430
431 skb = skb_dequeue(&hdev->cmd_q);
432 if (atomic_read(&hdev->cmd_cnt) && skb) {
433 kfree_skb(hdev->sent_cmd);
434 hdev->sent_cmd = skb_clone(skb, GFP_ATOMIC);
435 if (hdev->sent_cmd) {
436 atomic_dec(&hdev->cmd_cnt);
437 hci_send_frame(skb);
438 hdev->cmd_last_tx = jiffies;
439 } else {
440 skb_queue_head(&hdev->cmd_q, skb);
441 tasklet_schedule(&hdev->cmd_task);
442 }
443 }
444
445}
446
447static void radio_hci_rx_task(unsigned long arg)
448{
449 struct radio_hci_dev *hdev = (struct radio_hci_dev *) arg;
450 struct sk_buff *skb;
451
452 read_lock(&hci_task_lock);
453
454 skb = skb_dequeue(&hdev->rx_q);
455 radio_hci_event_packet(hdev, skb);
456
457 read_unlock(&hci_task_lock);
458}
459
460int radio_hci_register_dev(struct radio_hci_dev *hdev)
461{
462 struct iris_device *radio = video_get_drvdata(video_get_dev());
463 if (!radio) {
464 FMDERR(":radio is null");
465 return -EINVAL;
466 }
467
468 if (!hdev) {
469 FMDERR("hdev is null");
470 return -EINVAL;
471 }
472
473 hdev->flags = 0;
474
475 tasklet_init(&hdev->cmd_task, radio_hci_cmd_task, (unsigned long)
476 hdev);
477 tasklet_init(&hdev->rx_task, radio_hci_rx_task, (unsigned long)
478 hdev);
479
480 init_waitqueue_head(&hdev->req_wait_q);
481
482 skb_queue_head_init(&hdev->rx_q);
483 skb_queue_head_init(&hdev->cmd_q);
484 skb_queue_head_init(&hdev->raw_q);
485
486 if (!radio)
487 FMDERR(":radio is null");
488
489 radio->fm_hdev = hdev;
490
491 return 0;
492}
493EXPORT_SYMBOL(radio_hci_register_dev);
494
495int radio_hci_unregister_dev(struct radio_hci_dev *hdev)
496{
497 struct iris_device *radio = video_get_drvdata(video_get_dev());
498 if (!radio) {
499 FMDERR(":radio is null");
500 return -EINVAL;
501 }
502
503 tasklet_kill(&hdev->rx_task);
504 tasklet_kill(&hdev->cmd_task);
505 skb_queue_purge(&hdev->rx_q);
506 skb_queue_purge(&hdev->cmd_q);
507 skb_queue_purge(&hdev->raw_q);
508 kfree(radio->fm_hdev);
509 kfree(radio->videodev);
510
511 return 0;
512}
513EXPORT_SYMBOL(radio_hci_unregister_dev);
514
515int radio_hci_recv_frame(struct sk_buff *skb)
516{
517 struct radio_hci_dev *hdev = (struct radio_hci_dev *) skb->dev;
518 if (!hdev) {
519 FMDERR("%s hdev is null while receiving frame", hdev->name);
520 kfree_skb(skb);
521 return -ENXIO;
522 }
523
524 __net_timestamp(skb);
525
526 radio_hci_event_packet(hdev, skb);
527
528 return 0;
529}
530EXPORT_SYMBOL(radio_hci_recv_frame);
531
532int radio_hci_send_cmd(struct radio_hci_dev *hdev, __u16 opcode, __u32 plen,
533 void *param)
534{
535 int len = RADIO_HCI_COMMAND_HDR_SIZE + plen;
536 struct radio_hci_command_hdr *hdr;
537 struct sk_buff *skb;
538 int ret = 0;
539
540 skb = alloc_skb(len, GFP_ATOMIC);
541 if (!skb) {
542 FMDERR("%s no memory for command", hdev->name);
543 return -ENOMEM;
544 }
545
546 hdr = (struct radio_hci_command_hdr *) skb_put(skb,
547 RADIO_HCI_COMMAND_HDR_SIZE);
548 hdr->opcode = cpu_to_le16(opcode);
549 hdr->plen = plen;
550
551 if (plen)
552 memcpy(skb_put(skb, plen), param, plen);
553
554 skb->dev = (void *) hdev;
555
556 ret = hci_send_frame(skb);
557
558 return ret;
559}
560EXPORT_SYMBOL(radio_hci_send_cmd);
561
562static int hci_fm_enable_recv_req(struct radio_hci_dev *hdev,
563 unsigned long param)
564{
565 __u16 opcode = 0;
566
567 opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
568 HCI_OCF_FM_ENABLE_RECV_REQ);
569 return radio_hci_send_cmd(hdev, opcode, 0, NULL);
570}
571
Ankur Nandwanid928d542011-08-11 13:15:41 -0700572static int hci_fm_tone_generator(struct radio_hci_dev *hdev,
573 unsigned long param)
574{
575 struct iris_device *radio = video_get_drvdata(video_get_dev());
576 __u16 opcode = 0;
577
578 opcode = hci_opcode_pack(HCI_OGF_FM_DIAGNOSTIC_CMD_REQ,
579 HCI_FM_SET_INTERNAL_TONE_GENRATOR);
580 return radio_hci_send_cmd(hdev, opcode,
581 sizeof(radio->tone_freq), &radio->tone_freq);
582}
583
584static int hci_fm_enable_trans_req(struct radio_hci_dev *hdev,
585 unsigned long param)
586{
587 __u16 opcode = 0;
588
589 opcode = hci_opcode_pack(HCI_OGF_FM_TRANS_CTRL_CMD_REQ,
590 HCI_OCF_FM_ENABLE_TRANS_REQ);
591 return radio_hci_send_cmd(hdev, opcode, 0, NULL);
592}
593
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700594static int hci_fm_disable_recv_req(struct radio_hci_dev *hdev,
595 unsigned long param)
596{
597 __u16 opcode = 0;
598
599 opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
600 HCI_OCF_FM_DISABLE_RECV_REQ);
601 return radio_hci_send_cmd(hdev, opcode, 0, NULL);
602}
603
Ankur Nandwanid928d542011-08-11 13:15:41 -0700604static int hci_fm_disable_trans_req(struct radio_hci_dev *hdev,
605 unsigned long param)
606{
607 __u16 opcode = 0;
608
609 opcode = hci_opcode_pack(HCI_OGF_FM_TRANS_CTRL_CMD_REQ,
610 HCI_OCF_FM_DISABLE_TRANS_REQ);
611 return radio_hci_send_cmd(hdev, opcode, 0, NULL);
612}
613
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700614static int hci_get_fm_recv_conf_req(struct radio_hci_dev *hdev,
615 unsigned long param)
616{
617 __u16 opcode = 0;
618
619 opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
620 HCI_OCF_FM_GET_RECV_CONF_REQ);
621 return radio_hci_send_cmd(hdev, opcode, 0, NULL);
622}
623
624static int hci_set_fm_recv_conf_req(struct radio_hci_dev *hdev,
625 unsigned long param)
626{
627 __u16 opcode = 0;
628
629 struct hci_fm_recv_conf_req *recv_conf_req =
630 (struct hci_fm_recv_conf_req *) param;
631
632 opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
633 HCI_OCF_FM_SET_RECV_CONF_REQ);
634 return radio_hci_send_cmd(hdev, opcode, sizeof((*recv_conf_req)),
635 recv_conf_req);
636}
637
Ankur Nandwanid928d542011-08-11 13:15:41 -0700638static int hci_set_fm_trans_conf_req(struct radio_hci_dev *hdev,
639 unsigned long param)
640{
641 __u16 opcode = 0;
642
643 struct hci_fm_trans_conf_req_struct *trans_conf_req =
644 (struct hci_fm_trans_conf_req_struct *) param;
645
646 opcode = hci_opcode_pack(HCI_OGF_FM_TRANS_CTRL_CMD_REQ,
647 HCI_OCF_FM_SET_TRANS_CONF_REQ);
648 return radio_hci_send_cmd(hdev, opcode, sizeof((*trans_conf_req)),
649 trans_conf_req);
650}
651
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700652static int hci_fm_get_station_param_req(struct radio_hci_dev *hdev,
653 unsigned long param)
654{
655 __u16 opcode = 0;
656
657 opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
658 HCI_OCF_FM_GET_STATION_PARAM_REQ);
659 return radio_hci_send_cmd(hdev, opcode, 0, NULL);
660}
661
662static int hci_set_fm_mute_mode_req(struct radio_hci_dev *hdev,
663 unsigned long param)
664{
665 __u16 opcode = 0;
666 struct hci_fm_mute_mode_req *mute_mode_req =
667 (struct hci_fm_mute_mode_req *) param;
668
669 opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
670 HCI_OCF_FM_SET_MUTE_MODE_REQ);
671 return radio_hci_send_cmd(hdev, opcode, sizeof((*mute_mode_req)),
672 mute_mode_req);
673}
674
Ankur Nandwanid928d542011-08-11 13:15:41 -0700675
676static int hci_trans_ps_req(struct radio_hci_dev *hdev,
677 unsigned long param)
678{
679 __u16 opcode = 0;
680 struct hci_fm_tx_ps *tx_ps_req =
681 (struct hci_fm_tx_ps *) param;
682
683 opcode = hci_opcode_pack(HCI_OGF_FM_TRANS_CTRL_CMD_REQ,
684 HCI_OCF_FM_RDS_PS_REQ);
685
686 return radio_hci_send_cmd(hdev, opcode, sizeof((*tx_ps_req)),
687 tx_ps_req);
688}
689
690static int hci_trans_rt_req(struct radio_hci_dev *hdev,
691 unsigned long param)
692{
693 __u16 opcode = 0;
694 struct hci_fm_tx_rt *tx_rt_req =
695 (struct hci_fm_tx_rt *) param;
696
697 opcode = hci_opcode_pack(HCI_OGF_FM_TRANS_CTRL_CMD_REQ,
698 HCI_OCF_FM_RDS_RT_REQ);
699
700 return radio_hci_send_cmd(hdev, opcode, sizeof((*tx_rt_req)),
701 tx_rt_req);
702}
703
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700704static int hci_set_fm_stereo_mode_req(struct radio_hci_dev *hdev,
705 unsigned long param)
706{
707 __u16 opcode = 0;
708 struct hci_fm_stereo_mode_req *stereo_mode_req =
709 (struct hci_fm_stereo_mode_req *) param;
710 opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
711 HCI_OCF_FM_SET_STEREO_MODE_REQ);
712 return radio_hci_send_cmd(hdev, opcode, sizeof((*stereo_mode_req)),
713 stereo_mode_req);
714}
715
716static int hci_fm_set_antenna_req(struct radio_hci_dev *hdev,
717 unsigned long param)
718{
719 __u16 opcode = 0;
720
721 __u8 antenna = param;
722
723 opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
724 HCI_OCF_FM_SET_ANTENNA);
725 return radio_hci_send_cmd(hdev, opcode, sizeof(antenna), &antenna);
726}
727
728static int hci_fm_set_sig_threshold_req(struct radio_hci_dev *hdev,
729 unsigned long param)
730{
731 __u16 opcode = 0;
732
733 __u8 sig_threshold = param;
734
735 opcode = hci_opcode_pack(HCI_OGF_FM_COMMON_CTRL_CMD_REQ,
736 HCI_OCF_FM_SET_SIGNAL_THRESHOLD);
737 return radio_hci_send_cmd(hdev, opcode, sizeof(sig_threshold),
738 &sig_threshold);
739}
740
741static int hci_fm_get_sig_threshold_req(struct radio_hci_dev *hdev,
742 unsigned long param)
743{
744 __u16 opcode = 0;
745
746 opcode = hci_opcode_pack(HCI_OGF_FM_COMMON_CTRL_CMD_REQ,
747 HCI_OCF_FM_GET_SIGNAL_THRESHOLD);
748 return radio_hci_send_cmd(hdev, opcode, 0, NULL);
749}
750
751static int hci_fm_get_program_service_req(struct radio_hci_dev *hdev,
752 unsigned long param)
753{
754 __u16 opcode = 0;
755
756 opcode = hci_opcode_pack(HCI_OGF_FM_COMMON_CTRL_CMD_REQ,
757 HCI_OCF_FM_GET_PROGRAM_SERVICE_REQ);
758 return radio_hci_send_cmd(hdev, opcode, 0, NULL);
759}
760
761static int hci_fm_get_radio_text_req(struct radio_hci_dev *hdev,
762 unsigned long param)
763{
764 __u16 opcode = 0;
765
766 opcode = hci_opcode_pack(HCI_OGF_FM_COMMON_CTRL_CMD_REQ,
767 HCI_OCF_FM_GET_RADIO_TEXT_REQ);
768 return radio_hci_send_cmd(hdev, opcode, 0, NULL);
769}
770
771static int hci_fm_get_af_list_req(struct radio_hci_dev *hdev,
772 unsigned long param)
773{
774 __u16 opcode = 0;
775
776 opcode = hci_opcode_pack(HCI_OGF_FM_COMMON_CTRL_CMD_REQ,
777 HCI_OCF_FM_GET_AF_LIST_REQ);
778 return radio_hci_send_cmd(hdev, opcode, 0, NULL);
779}
780
781static int hci_fm_search_stations_req(struct radio_hci_dev *hdev,
782 unsigned long param)
783{
784 __u16 opcode = 0;
785 struct hci_fm_search_station_req *srch_stations =
786 (struct hci_fm_search_station_req *) param;
787
788 opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
789 HCI_OCF_FM_SEARCH_STATIONS);
790 return radio_hci_send_cmd(hdev, opcode, sizeof((*srch_stations)),
791 srch_stations);
792}
793
794static int hci_fm_srch_rds_stations_req(struct radio_hci_dev *hdev,
795 unsigned long param)
796{
797 __u16 opcode = 0;
798 struct hci_fm_search_rds_station_req *srch_stations =
799 (struct hci_fm_search_rds_station_req *) param;
800
801 opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
802 HCI_OCF_FM_SEARCH_RDS_STATIONS);
803 return radio_hci_send_cmd(hdev, opcode, sizeof((*srch_stations)),
804 srch_stations);
805}
806
807static int hci_fm_srch_station_list_req(struct radio_hci_dev *hdev,
808 unsigned long param)
809{
810 __u16 opcode = 0;
811 struct hci_fm_search_station_list_req *srch_list =
812 (struct hci_fm_search_station_list_req *) param;
813
814 opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
815 HCI_OCF_FM_SEARCH_STATIONS_LIST);
816 return radio_hci_send_cmd(hdev, opcode, sizeof((*srch_list)),
817 srch_list);
818}
819
820static int hci_fm_cancel_search_req(struct radio_hci_dev *hdev,
821 unsigned long param)
822{
823 __u16 opcode = 0;
824
825 opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
826 HCI_OCF_FM_CANCEL_SEARCH);
827 return radio_hci_send_cmd(hdev, opcode, 0, NULL);
828}
829
830static int hci_fm_rds_grp_process_req(struct radio_hci_dev *hdev,
831 unsigned long param)
832{
833 __u16 opcode = 0;
834
835 __u32 fm_grps_process = param;
836
837 opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
838 HCI_OCF_FM_RDS_GRP_PROCESS);
839 return radio_hci_send_cmd(hdev, opcode, sizeof(fm_grps_process),
840 &fm_grps_process);
841}
842
843static int hci_fm_tune_station_req(struct radio_hci_dev *hdev,
844 unsigned long param)
845{
846 __u16 opcode = 0;
847
848 __u32 tune_freq = param;
849
850 opcode = hci_opcode_pack(HCI_OGF_FM_COMMON_CTRL_CMD_REQ,
851 HCI_OCF_FM_TUNE_STATION_REQ);
852 return radio_hci_send_cmd(hdev, opcode, sizeof(tune_freq), &tune_freq);
853}
854
855static int hci_def_data_read_req(struct radio_hci_dev *hdev,
856 unsigned long param)
857{
858 __u16 opcode = 0;
859 struct hci_fm_def_data_rd_req *def_data_rd =
860 (struct hci_fm_def_data_rd_req *) param;
861
862 opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
863 HCI_OCF_FM_DEFAULT_DATA_READ);
864 return radio_hci_send_cmd(hdev, opcode, sizeof((*def_data_rd)),
865 def_data_rd);
866}
867
868static int hci_def_data_write_req(struct radio_hci_dev *hdev,
869 unsigned long param)
870{
871 __u16 opcode = 0;
872 struct hci_fm_def_data_wr_req *def_data_wr =
873 (struct hci_fm_def_data_wr_req *) param;
874
875 opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
876 HCI_OCF_FM_DEFAULT_DATA_WRITE);
877 return radio_hci_send_cmd(hdev, opcode, sizeof((*def_data_wr)),
878 def_data_wr);
879}
880
Srinivasa Rao Uppala0f4098f2011-09-06 16:46:28 +0530881static int hci_set_notch_filter_req(struct radio_hci_dev *hdev,
882 unsigned long param)
883{
884 __u16 opcode = 0;
885 __u8 notch_filter_val = param;
886
887 opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
888 HCI_OCF_FM_EN_NOTCH_CTRL);
889 return radio_hci_send_cmd(hdev, opcode, sizeof(notch_filter_val),
890 &notch_filter_val);
891}
892
893
894
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700895static int hci_fm_reset_req(struct radio_hci_dev *hdev, unsigned long param)
896{
897 __u16 opcode = 0;
898
899 opcode = hci_opcode_pack(HCI_OGF_FM_COMMON_CTRL_CMD_REQ,
900 HCI_OCF_FM_RESET);
901 return radio_hci_send_cmd(hdev, opcode, 0, NULL);
902}
903
904static int hci_fm_get_feature_lists_req(struct radio_hci_dev *hdev,
905 unsigned long param)
906{
907 __u16 opcode = 0;
908
909 opcode = hci_opcode_pack(HCI_OGF_FM_COMMON_CTRL_CMD_REQ,
910 HCI_OCF_FM_GET_FEATURE_LIST);
911 return radio_hci_send_cmd(hdev, opcode, 0, NULL);
912}
913
914static int hci_fm_do_calibration_req(struct radio_hci_dev *hdev,
915 unsigned long param)
916{
917 __u16 opcode = 0;
918
919 __u8 mode = param;
920
921 opcode = hci_opcode_pack(HCI_OGF_FM_COMMON_CTRL_CMD_REQ,
922 HCI_OCF_FM_DO_CALIBRATION);
923 return radio_hci_send_cmd(hdev, opcode, sizeof(mode), &mode);
924}
925
926static int hci_read_grp_counters_req(struct radio_hci_dev *hdev,
927 unsigned long param)
928{
929 __u16 opcode = 0;
930
931 __u8 reset_counters = param;
Srinivasa Rao Uppala0f4098f2011-09-06 16:46:28 +0530932 opcode = hci_opcode_pack(HCI_OGF_FM_STATUS_PARAMETERS_CMD_REQ,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700933 HCI_OCF_FM_READ_GRP_COUNTERS);
934 return radio_hci_send_cmd(hdev, opcode, sizeof(reset_counters),
935 &reset_counters);
936}
937
938static int hci_peek_data_req(struct radio_hci_dev *hdev, unsigned long param)
939{
940 __u16 opcode = 0;
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -0700941 struct hci_fm_riva_data *peek_data = (struct hci_fm_riva_data *)param;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700942
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -0700943 opcode = hci_opcode_pack(HCI_OGF_FM_DIAGNOSTIC_CMD_REQ,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700944 HCI_OCF_FM_PEEK_DATA);
945 return radio_hci_send_cmd(hdev, opcode, sizeof((*peek_data)),
946 peek_data);
947}
948
949static int hci_poke_data_req(struct radio_hci_dev *hdev, unsigned long param)
950{
951 __u16 opcode = 0;
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -0700952 struct hci_fm_riva_poke *poke_data = (struct hci_fm_riva_poke *) param;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700953
Srinivasa Rao Uppala6cc0e322011-08-12 10:54:48 -0700954 opcode = hci_opcode_pack(HCI_OGF_FM_DIAGNOSTIC_CMD_REQ,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700955 HCI_OCF_FM_POKE_DATA);
956 return radio_hci_send_cmd(hdev, opcode, sizeof((*poke_data)),
957 poke_data);
958}
959
960static int hci_ssbi_peek_reg_req(struct radio_hci_dev *hdev,
961 unsigned long param)
962{
963 __u16 opcode = 0;
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -0700964 struct hci_fm_ssbi_peek *ssbi_peek = (struct hci_fm_ssbi_peek *) param;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700965
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -0700966 opcode = hci_opcode_pack(HCI_OGF_FM_DIAGNOSTIC_CMD_REQ,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700967 HCI_OCF_FM_SSBI_PEEK_REG);
968 return radio_hci_send_cmd(hdev, opcode, sizeof((*ssbi_peek)),
969 ssbi_peek);
970}
971
972static int hci_ssbi_poke_reg_req(struct radio_hci_dev *hdev,
973 unsigned long param)
974{
975 __u16 opcode = 0;
976 struct hci_fm_ssbi_req *ssbi_poke = (struct hci_fm_ssbi_req *) param;
977
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -0700978 opcode = hci_opcode_pack(HCI_OGF_FM_DIAGNOSTIC_CMD_REQ,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700979 HCI_OCF_FM_SSBI_POKE_REG);
980 return radio_hci_send_cmd(hdev, opcode, sizeof((*ssbi_poke)),
981 ssbi_poke);
982}
983
984static int hci_fm_get_station_dbg_param_req(struct radio_hci_dev *hdev,
985 unsigned long param)
986{
987 __u16 opcode = 0;
988
989 opcode = hci_opcode_pack(HCI_OGF_FM_COMMON_CTRL_CMD_REQ,
990 HCI_OCF_FM_STATION_DBG_PARAM);
991 return radio_hci_send_cmd(hdev, opcode, 0, NULL);
992}
993
994static int radio_hci_err(__u16 code)
995{
996 switch (code) {
997 case 0:
998 return 0;
999 case 0x01:
1000 return -EBADRQC;
1001 case 0x02:
1002 return -ENOTCONN;
1003 case 0x03:
1004 return -EIO;
1005 case 0x07:
1006 return -ENOMEM;
1007 case 0x0c:
1008 return -EBUSY;
1009 case 0x11:
1010 return -EOPNOTSUPP;
1011 case 0x12:
1012 return -EINVAL;
1013 default:
1014 return -ENOSYS;
1015 }
1016}
1017
1018static int __radio_hci_request(struct radio_hci_dev *hdev,
1019 int (*req)(struct radio_hci_dev *hdev,
1020 unsigned long param),
1021 unsigned long param, __u32 timeout)
1022{
1023 int err = 0;
1024
1025 DECLARE_WAITQUEUE(wait, current);
1026
1027 hdev->req_status = HCI_REQ_PEND;
1028
1029 add_wait_queue(&hdev->req_wait_q, &wait);
1030 set_current_state(TASK_INTERRUPTIBLE);
1031
1032 err = req(hdev, param);
1033
1034 schedule_timeout(timeout);
1035
1036 remove_wait_queue(&hdev->req_wait_q, &wait);
1037
1038 if (signal_pending(current))
1039 return -EINTR;
1040
1041 switch (hdev->req_status) {
1042 case HCI_REQ_DONE:
1043 case HCI_REQ_STATUS:
1044 err = radio_hci_err(hdev->req_result);
1045 break;
1046
1047 case HCI_REQ_CANCELED:
1048 err = -hdev->req_result;
1049 break;
1050
1051 default:
1052 err = -ETIMEDOUT;
1053 break;
1054 }
1055
1056 hdev->req_status = hdev->req_result = 0;
1057
1058 return err;
1059}
1060
1061static inline int radio_hci_request(struct radio_hci_dev *hdev,
1062 int (*req)(struct
1063 radio_hci_dev * hdev, unsigned long param),
1064 unsigned long param, __u32 timeout)
1065{
1066 int ret = 0;
1067
1068 ret = __radio_hci_request(hdev, req, param, timeout);
1069
1070 return ret;
1071}
1072
1073static int hci_set_fm_recv_conf(struct hci_fm_recv_conf_req *arg,
1074 struct radio_hci_dev *hdev)
1075{
1076 int ret = 0;
1077 struct hci_fm_recv_conf_req *set_recv_conf = arg;
1078
1079 ret = radio_hci_request(hdev, hci_set_fm_recv_conf_req, (unsigned
1080 long)set_recv_conf, RADIO_HCI_TIMEOUT);
1081
1082 return ret;
1083}
1084
Ankur Nandwanid928d542011-08-11 13:15:41 -07001085static int hci_set_fm_trans_conf(struct hci_fm_trans_conf_req_struct *arg,
1086 struct radio_hci_dev *hdev)
1087{
1088 int ret = 0;
1089 struct hci_fm_trans_conf_req_struct *set_trans_conf = arg;
1090
1091 ret = radio_hci_request(hdev, hci_set_fm_trans_conf_req, (unsigned
1092 long)set_trans_conf, RADIO_HCI_TIMEOUT);
1093
1094 return ret;
1095}
1096
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001097static int hci_fm_tune_station(__u32 *arg, struct radio_hci_dev *hdev)
1098{
1099 int ret = 0;
1100 __u32 tune_freq = *arg;
1101
1102 ret = radio_hci_request(hdev, hci_fm_tune_station_req, tune_freq,
1103 RADIO_HCI_TIMEOUT);
1104
1105 return ret;
1106}
1107
1108static int hci_set_fm_mute_mode(struct hci_fm_mute_mode_req *arg,
1109 struct radio_hci_dev *hdev)
1110{
1111 int ret = 0;
1112 struct hci_fm_mute_mode_req *set_mute_conf = arg;
1113
1114 ret = radio_hci_request(hdev, hci_set_fm_mute_mode_req, (unsigned
1115 long)set_mute_conf, RADIO_HCI_TIMEOUT);
1116
1117 return ret;
1118}
1119
1120static int hci_set_fm_stereo_mode(struct hci_fm_stereo_mode_req *arg,
1121 struct radio_hci_dev *hdev)
1122{
1123 int ret = 0;
1124 struct hci_fm_stereo_mode_req *set_stereo_conf = arg;
1125
1126 ret = radio_hci_request(hdev, hci_set_fm_stereo_mode_req, (unsigned
1127 long)set_stereo_conf, RADIO_HCI_TIMEOUT);
1128
1129 return ret;
1130}
1131
1132static int hci_fm_set_antenna(__u8 *arg, struct radio_hci_dev *hdev)
1133{
1134 int ret = 0;
1135 __u8 antenna = *arg;
1136
1137 ret = radio_hci_request(hdev, hci_fm_set_antenna_req, antenna,
1138 RADIO_HCI_TIMEOUT);
1139
1140 return ret;
1141}
1142
1143static int hci_fm_set_signal_threshold(__u8 *arg,
1144 struct radio_hci_dev *hdev)
1145{
1146 int ret = 0;
1147 __u8 sig_threshold = *arg;
1148
1149 ret = radio_hci_request(hdev, hci_fm_set_sig_threshold_req,
1150 sig_threshold, RADIO_HCI_TIMEOUT);
1151
1152 return ret;
1153}
1154
1155static int hci_fm_search_stations(struct hci_fm_search_station_req *arg,
1156 struct radio_hci_dev *hdev)
1157{
1158 int ret = 0;
1159 struct hci_fm_search_station_req *srch_stations = arg;
1160
1161 ret = radio_hci_request(hdev, hci_fm_search_stations_req, (unsigned
1162 long)srch_stations, RADIO_HCI_TIMEOUT);
1163
1164 return ret;
1165}
1166
1167static int hci_fm_search_rds_stations(struct hci_fm_search_rds_station_req *arg,
1168 struct radio_hci_dev *hdev)
1169{
1170 int ret = 0;
1171 struct hci_fm_search_rds_station_req *srch_stations = arg;
1172
1173 ret = radio_hci_request(hdev, hci_fm_srch_rds_stations_req, (unsigned
1174 long)srch_stations, RADIO_HCI_TIMEOUT);
1175
1176 return ret;
1177}
1178
1179static int hci_fm_search_station_list
1180 (struct hci_fm_search_station_list_req *arg,
1181 struct radio_hci_dev *hdev)
1182{
1183 int ret = 0;
1184 struct hci_fm_search_station_list_req *srch_list = arg;
1185
1186 ret = radio_hci_request(hdev, hci_fm_srch_station_list_req, (unsigned
1187 long)srch_list, RADIO_HCI_TIMEOUT);
1188
1189 return ret;
1190}
1191
1192static int hci_fm_rds_grp(struct hci_fm_rds_grp_req *arg,
1193 struct radio_hci_dev *hdev)
1194{
1195 return 0;
1196}
1197
1198static int hci_fm_rds_grps_process(__u32 *arg, struct radio_hci_dev *hdev)
1199{
1200 int ret = 0;
1201 __u32 fm_grps_process = *arg;
1202
1203 ret = radio_hci_request(hdev, hci_fm_rds_grp_process_req,
1204 fm_grps_process, RADIO_HCI_TIMEOUT);
1205
1206 return ret;
1207}
1208
1209int hci_def_data_read(struct hci_fm_def_data_rd_req *arg,
1210 struct radio_hci_dev *hdev)
1211{
1212 int ret = 0;
1213 struct hci_fm_def_data_rd_req *def_data_rd = arg;
1214
1215 ret = radio_hci_request(hdev, hci_def_data_read_req, (unsigned
1216 long)def_data_rd, RADIO_HCI_TIMEOUT);
1217
1218 return ret;
1219}
1220
1221int hci_def_data_write(struct hci_fm_def_data_wr_req *arg,
1222 struct radio_hci_dev *hdev)
1223{
1224 int ret = 0;
1225 struct hci_fm_def_data_wr_req *def_data_wr = arg;
1226
1227 ret = radio_hci_request(hdev, hci_def_data_write_req, (unsigned
1228 long)def_data_wr, RADIO_HCI_TIMEOUT);
1229
1230 return ret;
1231}
1232
1233int hci_fm_do_calibration(__u8 *arg, struct radio_hci_dev *hdev)
1234{
1235 int ret = 0;
1236 __u8 mode = *arg;
1237
1238 ret = radio_hci_request(hdev, hci_fm_do_calibration_req, mode,
1239 RADIO_HCI_TIMEOUT);
1240
1241 return ret;
1242}
1243
Srinivasa Rao Uppala0f4098f2011-09-06 16:46:28 +05301244static int hci_read_grp_counters(__u8 *arg, struct radio_hci_dev *hdev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001245{
1246 int ret = 0;
1247 __u8 reset_counters = *arg;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001248 ret = radio_hci_request(hdev, hci_read_grp_counters_req,
1249 reset_counters, RADIO_HCI_TIMEOUT);
1250
1251 return ret;
1252}
1253
Srinivasa Rao Uppala0f4098f2011-09-06 16:46:28 +05301254static int hci_set_notch_filter(__u8 *arg, struct radio_hci_dev *hdev)
1255{
1256 int ret = 0;
1257 __u8 notch_filter = *arg;
1258 ret = radio_hci_request(hdev, hci_set_notch_filter_req,
1259 notch_filter, RADIO_HCI_TIMEOUT);
1260
1261 return ret;
1262}
1263
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -07001264static int hci_peek_data(struct hci_fm_riva_data *arg,
1265 struct radio_hci_dev *hdev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001266{
1267 int ret = 0;
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -07001268 struct hci_fm_riva_data *peek_data = arg;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001269
1270 ret = radio_hci_request(hdev, hci_peek_data_req, (unsigned
1271 long)peek_data, RADIO_HCI_TIMEOUT);
1272
1273 return ret;
1274}
1275
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -07001276static int hci_poke_data(struct hci_fm_riva_poke *arg,
1277 struct radio_hci_dev *hdev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001278{
1279 int ret = 0;
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -07001280 struct hci_fm_riva_poke *poke_data = arg;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001281
1282 ret = radio_hci_request(hdev, hci_poke_data_req, (unsigned
1283 long)poke_data, RADIO_HCI_TIMEOUT);
1284
1285 return ret;
1286}
1287
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -07001288static int hci_ssbi_peek_reg(struct hci_fm_ssbi_peek *arg,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001289 struct radio_hci_dev *hdev)
1290{
1291 int ret = 0;
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -07001292 struct hci_fm_ssbi_peek *ssbi_peek_reg = arg;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001293
1294 ret = radio_hci_request(hdev, hci_ssbi_peek_reg_req, (unsigned
1295 long)ssbi_peek_reg, RADIO_HCI_TIMEOUT);
1296
1297 return ret;
1298}
1299
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -07001300static int hci_ssbi_poke_reg(struct hci_fm_ssbi_req *arg,
1301 struct radio_hci_dev *hdev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001302{
1303 int ret = 0;
1304 struct hci_fm_ssbi_req *ssbi_poke_reg = arg;
1305
1306 ret = radio_hci_request(hdev, hci_ssbi_poke_reg_req, (unsigned
1307 long)ssbi_poke_reg, RADIO_HCI_TIMEOUT);
1308
1309 return ret;
1310}
1311
1312static int hci_cmd(unsigned int cmd, struct radio_hci_dev *hdev)
1313{
1314 int ret = 0;
1315 unsigned long arg = 0;
1316
Ankur Nandwanid928d542011-08-11 13:15:41 -07001317 if (!hdev)
1318 return -ENODEV;
1319
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001320 switch (cmd) {
1321 case HCI_FM_ENABLE_RECV_CMD:
1322 ret = radio_hci_request(hdev, hci_fm_enable_recv_req, arg,
1323 msecs_to_jiffies(RADIO_HCI_TIMEOUT));
1324 break;
1325
1326 case HCI_FM_DISABLE_RECV_CMD:
1327 ret = radio_hci_request(hdev, hci_fm_disable_recv_req, arg,
1328 msecs_to_jiffies(RADIO_HCI_TIMEOUT));
1329 break;
1330
1331 case HCI_FM_GET_RECV_CONF_CMD:
1332 ret = radio_hci_request(hdev, hci_get_fm_recv_conf_req, arg,
1333 msecs_to_jiffies(RADIO_HCI_TIMEOUT));
1334 break;
1335
1336 case HCI_FM_GET_STATION_PARAM_CMD:
1337 ret = radio_hci_request(hdev,
1338 hci_fm_get_station_param_req, arg,
1339 msecs_to_jiffies(RADIO_HCI_TIMEOUT));
1340 break;
1341
1342 case HCI_FM_GET_SIGNAL_TH_CMD:
1343 ret = radio_hci_request(hdev,
1344 hci_fm_get_sig_threshold_req, arg,
1345 msecs_to_jiffies(RADIO_HCI_TIMEOUT));
1346 break;
1347
1348 case HCI_FM_GET_PROGRAM_SERVICE_CMD:
1349 ret = radio_hci_request(hdev,
1350 hci_fm_get_program_service_req, arg,
1351 msecs_to_jiffies(RADIO_HCI_TIMEOUT));
1352 break;
1353
1354 case HCI_FM_GET_RADIO_TEXT_CMD:
1355 ret = radio_hci_request(hdev, hci_fm_get_radio_text_req, arg,
1356 msecs_to_jiffies(RADIO_HCI_TIMEOUT));
1357 break;
1358
1359 case HCI_FM_GET_AF_LIST_CMD:
1360 ret = radio_hci_request(hdev, hci_fm_get_af_list_req, arg,
1361 msecs_to_jiffies(RADIO_HCI_TIMEOUT));
1362 break;
1363
1364 case HCI_FM_CANCEL_SEARCH_CMD:
1365 ret = radio_hci_request(hdev, hci_fm_cancel_search_req, arg,
1366 msecs_to_jiffies(RADIO_HCI_TIMEOUT));
1367 break;
1368
1369 case HCI_FM_RESET_CMD:
1370 ret = radio_hci_request(hdev, hci_fm_reset_req, arg,
1371 msecs_to_jiffies(RADIO_HCI_TIMEOUT));
1372 break;
1373
1374 case HCI_FM_GET_FEATURES_CMD:
1375 ret = radio_hci_request(hdev,
1376 hci_fm_get_feature_lists_req, arg,
1377 msecs_to_jiffies(RADIO_HCI_TIMEOUT));
1378 break;
1379
1380 case HCI_FM_STATION_DBG_PARAM_CMD:
1381 ret = radio_hci_request(hdev,
1382 hci_fm_get_station_dbg_param_req, arg,
1383 msecs_to_jiffies(RADIO_HCI_TIMEOUT));
1384 break;
1385
Ankur Nandwanid928d542011-08-11 13:15:41 -07001386 case HCI_FM_ENABLE_TRANS_CMD:
1387 ret = radio_hci_request(hdev, hci_fm_enable_trans_req, arg,
1388 msecs_to_jiffies(RADIO_HCI_TIMEOUT));
1389 break;
1390
1391 case HCI_FM_DISABLE_TRANS_CMD:
1392 ret = radio_hci_request(hdev, hci_fm_disable_trans_req, arg,
1393 msecs_to_jiffies(RADIO_HCI_TIMEOUT));
1394 break;
1395
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001396 default:
1397 ret = -EINVAL;
1398 break;
1399 }
1400
1401 return ret;
1402}
1403
1404static void radio_hci_req_complete(struct radio_hci_dev *hdev, int result)
1405{
1406 hdev->req_result = result;
1407 hdev->req_status = HCI_REQ_DONE;
1408 wake_up_interruptible(&hdev->req_wait_q);
1409}
1410
1411static void radio_hci_status_complete(struct radio_hci_dev *hdev, int result)
1412{
1413 hdev->req_result = result;
1414 hdev->req_status = HCI_REQ_STATUS;
1415 wake_up_interruptible(&hdev->req_wait_q);
1416}
1417
1418static void hci_cc_rsp(struct radio_hci_dev *hdev, struct sk_buff *skb)
1419{
1420 __u8 status = *((__u8 *) skb->data);
1421
1422 if (status)
1423 return;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001424 radio_hci_req_complete(hdev, status);
1425}
1426
1427static void hci_cc_fm_disable_rsp(struct radio_hci_dev *hdev,
1428 struct sk_buff *skb)
1429{
1430 __u8 status = *((__u8 *) skb->data);
1431 struct iris_device *radio = video_get_drvdata(video_get_dev());
1432
1433 if (status)
1434 return;
1435
1436 iris_q_event(radio, IRIS_EVT_RADIO_READY);
1437
1438 radio_hci_req_complete(hdev, status);
1439}
1440
1441static void hci_cc_conf_rsp(struct radio_hci_dev *hdev, struct sk_buff *skb)
1442{
1443 struct hci_fm_conf_rsp *rsp = (void *)skb->data;
1444 struct iris_device *radio = video_get_drvdata(video_get_dev());
1445
1446 if (rsp->status)
1447 return;
1448
1449 radio->recv_conf = rsp->recv_conf_rsp;
1450 radio_hci_req_complete(hdev, rsp->status);
1451}
1452
1453static void hci_cc_fm_enable_rsp(struct radio_hci_dev *hdev,
1454 struct sk_buff *skb)
1455{
1456 struct hci_fm_conf_rsp *rsp = (void *)skb->data;
1457 struct iris_device *radio = video_get_drvdata(video_get_dev());
1458
1459 if (rsp->status)
1460 return;
1461
1462 iris_q_event(radio, IRIS_EVT_RADIO_READY);
1463
1464 radio_hci_req_complete(hdev, rsp->status);
1465}
1466
Ankur Nandwanid928d542011-08-11 13:15:41 -07001467
1468static void hci_cc_fm_trans_set_conf_rsp(struct radio_hci_dev *hdev,
1469 struct sk_buff *skb)
1470{
1471 struct hci_fm_conf_rsp *rsp = (void *)skb->data;
1472 struct iris_device *radio = video_get_drvdata(video_get_dev());
1473
1474 if (rsp->status)
1475 return;
1476
1477 iris_q_event(radio, HCI_EV_CMD_COMPLETE);
1478
1479 radio_hci_req_complete(hdev, rsp->status);
1480}
1481
1482
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001483static void hci_cc_sig_threshold_rsp(struct radio_hci_dev *hdev,
1484 struct sk_buff *skb)
1485{
1486 struct hci_fm_sig_threshold_rsp *rsp = (void *)skb->data;
1487 struct iris_device *radio = video_get_drvdata(video_get_dev());
1488 struct v4l2_control *v4l_ctl = radio->g_ctl;
1489
1490 if (rsp->status)
1491 return;
1492
1493 v4l_ctl->value = rsp->sig_threshold;
1494
1495 radio_hci_req_complete(hdev, rsp->status);
1496}
1497
1498static void hci_cc_station_rsp(struct radio_hci_dev *hdev, struct sk_buff *skb)
1499{
1500 struct iris_device *radio = video_get_drvdata(video_get_dev());
1501 struct hci_fm_station_rsp *rsp = (void *)skb->data;
1502 radio->fm_st_rsp = *(rsp);
1503
1504 /* Tune is always succesful */
1505 radio_hci_req_complete(hdev, 0);
1506}
1507
1508static void hci_cc_prg_srv_rsp(struct radio_hci_dev *hdev, struct sk_buff *skb)
1509{
1510 struct hci_fm_prgm_srv_rsp *rsp = (void *)skb->data;
1511
1512 if (rsp->status)
1513 return;
1514
1515 radio_hci_req_complete(hdev, rsp->status);
1516}
1517
1518static void hci_cc_rd_txt_rsp(struct radio_hci_dev *hdev, struct sk_buff *skb)
1519{
1520 struct hci_fm_radio_txt_rsp *rsp = (void *)skb->data;
1521
1522 if (rsp->status)
1523 return;
1524
1525 radio_hci_req_complete(hdev, rsp->status);
1526}
1527
1528static void hci_cc_af_list_rsp(struct radio_hci_dev *hdev, struct sk_buff *skb)
1529{
1530 struct hci_fm_af_list_rsp *rsp = (void *)skb->data;
1531
1532 if (rsp->status)
1533 return;
1534
1535 radio_hci_req_complete(hdev, rsp->status);
1536}
1537
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001538static void hci_cc_feature_list_rsp(struct radio_hci_dev *hdev,
1539 struct sk_buff *skb)
1540{
1541 struct hci_fm_feature_list_rsp *rsp = (void *)skb->data;
1542 struct iris_device *radio = video_get_drvdata(video_get_dev());
1543 struct v4l2_capability *v4l_cap = radio->g_cap;
1544
1545 if (rsp->status)
1546 return;
1547 v4l_cap->capabilities = (rsp->feature_mask & 0x000002) |
1548 (rsp->feature_mask & 0x000001);
1549
1550 radio_hci_req_complete(hdev, rsp->status);
1551}
1552
1553static void hci_cc_dbg_param_rsp(struct radio_hci_dev *hdev,
1554 struct sk_buff *skb)
1555{
1556 struct iris_device *radio = video_get_drvdata(video_get_dev());
1557 struct hci_fm_dbg_param_rsp *rsp = (void *)skb->data;
1558 radio->st_dbg_param = *(rsp);
1559
1560 if (radio->st_dbg_param.status)
1561 return;
1562
1563 radio_hci_req_complete(hdev, radio->st_dbg_param.status);
1564}
1565
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -07001566static void iris_q_evt_data(struct iris_device *radio,
1567 char *data, int len, int event)
1568{
1569 struct kfifo *data_b = &radio->data_buf[event];
1570 if (kfifo_in_locked(data_b, data, len, &radio->buf_lock[event]))
1571 wake_up_interruptible(&radio->event_queue);
1572}
1573
1574static void hci_cc_riva_peek_rsp(struct radio_hci_dev *hdev,
1575 struct sk_buff *skb)
1576{
1577 struct iris_device *radio = video_get_drvdata(video_get_dev());
Srinivasa Rao Uppala0f4098f2011-09-06 16:46:28 +05301578 __u8 status = *((__u8 *) skb->data);
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -07001579 int len;
1580 char *data;
1581
Srinivasa Rao Uppala0f4098f2011-09-06 16:46:28 +05301582 if (status)
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -07001583 return;
1584 len = skb->data[RIVA_PEEK_LEN_OFSET] + RIVA_PEEK_PARAM;
1585 data = kmalloc(len, GFP_ATOMIC);
1586
1587 if (!data) {
1588 FMDERR("Memory allocation failed");
1589 return;
1590 }
1591
1592 memcpy(data, &skb->data[PEEK_DATA_OFSET], len);
1593 iris_q_evt_data(radio, data, len, IRIS_BUF_PEEK);
Srinivasa Rao Uppala0f4098f2011-09-06 16:46:28 +05301594 radio_hci_req_complete(hdev, status);
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -07001595
1596
1597}
1598static void hci_cc_ssbi_peek_rsp(struct radio_hci_dev *hdev,
1599 struct sk_buff *skb)
1600{
1601 struct iris_device *radio = video_get_drvdata(video_get_dev());
Srinivasa Rao Uppala0f4098f2011-09-06 16:46:28 +05301602 __u8 status = *((__u8 *) skb->data);
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -07001603 char *data;
1604
Srinivasa Rao Uppala0f4098f2011-09-06 16:46:28 +05301605 if (status)
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -07001606 return;
1607 data = kmalloc(SSBI_PEEK_LEN, GFP_ATOMIC);
1608 if (!data) {
1609 FMDERR("Memory allocation failed");
1610 return;
1611 }
1612
1613 data[0] = skb->data[PEEK_DATA_OFSET];
1614 iris_q_evt_data(radio, data, SSBI_PEEK_LEN, IRIS_BUF_SSBI_PEEK);
Srinivasa Rao Uppala0f4098f2011-09-06 16:46:28 +05301615 radio_hci_req_complete(hdev, status);
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -07001616 kfree(data);
1617}
1618
Srinivasa Rao Uppala0f4098f2011-09-06 16:46:28 +05301619static void hci_cc_rds_grp_cntrs_rsp(struct radio_hci_dev *hdev,
1620 struct sk_buff *skb)
1621{
1622 struct iris_device *radio = video_get_drvdata(video_get_dev());
1623 __u8 status = *((__u8 *) skb->data);
1624 char *data;
1625 if (status)
1626 return;
1627 data = kmalloc(RDS_GRP_CNTR_LEN, GFP_ATOMIC);
1628 if (!data) {
1629 FMDERR("memory allocation failed");
1630 return;
1631 }
1632 memcpy(data, &skb->data[1], RDS_GRP_CNTR_LEN);
1633 iris_q_evt_data(radio, data, RDS_GRP_CNTR_LEN, IRIS_BUF_RDS_CNTRS);
1634 radio_hci_req_complete(hdev, status);
1635 kfree(data);
1636
1637}
1638
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001639static inline void hci_cmd_complete_event(struct radio_hci_dev *hdev,
1640 struct sk_buff *skb)
1641{
1642 struct hci_ev_cmd_complete *cmd_compl_ev = (void *) skb->data;
1643 __u16 opcode;
1644
1645 skb_pull(skb, sizeof(*cmd_compl_ev));
1646
1647 opcode = __le16_to_cpu(cmd_compl_ev->cmd_opcode);
1648
1649 switch (opcode) {
1650 case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_ENABLE_RECV_REQ):
1651 hci_cc_fm_enable_rsp(hdev, skb);
1652 break;
1653 case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_GET_RECV_CONF_REQ):
1654 hci_cc_conf_rsp(hdev, skb);
1655 break;
1656
1657 case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_DISABLE_RECV_REQ):
1658 hci_cc_fm_disable_rsp(hdev, skb);
1659 break;
1660
1661 case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_SET_RECV_CONF_REQ):
1662 case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_SET_MUTE_MODE_REQ):
1663 case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_SET_STEREO_MODE_REQ):
1664 case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_SET_ANTENNA):
1665 case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_SET_SIGNAL_THRESHOLD):
1666 case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_CANCEL_SEARCH):
1667 case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_RDS_GRP):
1668 case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_RDS_GRP_PROCESS):
1669 case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_EN_WAN_AVD_CTRL):
1670 case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_EN_NOTCH_CTRL):
Ankur Nandwanid928d542011-08-11 13:15:41 -07001671 case hci_trans_ctrl_cmd_op_pack(HCI_OCF_FM_ENABLE_TRANS_REQ):
1672 case hci_trans_ctrl_cmd_op_pack(HCI_OCF_FM_DISABLE_TRANS_REQ):
1673 case hci_trans_ctrl_cmd_op_pack(HCI_OCF_FM_RDS_RT_REQ):
1674 case hci_trans_ctrl_cmd_op_pack(HCI_OCF_FM_RDS_PS_REQ):
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001675 case hci_common_cmd_op_pack(HCI_OCF_FM_DEFAULT_DATA_WRITE):
1676 case hci_common_cmd_op_pack(HCI_OCF_FM_RESET):
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001677 case hci_diagnostic_cmd_op_pack(HCI_OCF_FM_SSBI_POKE_REG):
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -07001678 case hci_diagnostic_cmd_op_pack(HCI_OCF_FM_POKE_DATA):
Ankur Nandwanid928d542011-08-11 13:15:41 -07001679 case hci_diagnostic_cmd_op_pack(HCI_FM_SET_INTERNAL_TONE_GENRATOR):
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001680 hci_cc_rsp(hdev, skb);
1681 break;
1682
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -07001683 case hci_diagnostic_cmd_op_pack(HCI_OCF_FM_SSBI_PEEK_REG):
1684 hci_cc_ssbi_peek_rsp(hdev, skb);
1685 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001686 case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_GET_SIGNAL_THRESHOLD):
1687 hci_cc_sig_threshold_rsp(hdev, skb);
1688 break;
1689
1690 case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_GET_STATION_PARAM_REQ):
1691 hci_cc_station_rsp(hdev, skb);
1692 break;
1693
1694 case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_GET_PROGRAM_SERVICE_REQ):
1695 hci_cc_prg_srv_rsp(hdev, skb);
1696 break;
1697
1698 case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_GET_RADIO_TEXT_REQ):
1699 hci_cc_rd_txt_rsp(hdev, skb);
1700 break;
1701
1702 case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_GET_AF_LIST_REQ):
1703 hci_cc_af_list_rsp(hdev, skb);
1704 break;
1705
1706 case hci_common_cmd_op_pack(HCI_OCF_FM_DEFAULT_DATA_READ):
1707 case hci_diagnostic_cmd_op_pack(HCI_OCF_FM_PEEK_DATA):
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -07001708 hci_cc_riva_peek_rsp(hdev, skb);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001709 break;
1710
1711 case hci_common_cmd_op_pack(HCI_OCF_FM_GET_FEATURE_LIST):
1712 hci_cc_feature_list_rsp(hdev, skb);
1713 break;
1714
1715 case hci_diagnostic_cmd_op_pack(HCI_OCF_FM_STATION_DBG_PARAM):
1716 hci_cc_dbg_param_rsp(hdev, skb);
1717 break;
Ankur Nandwanid928d542011-08-11 13:15:41 -07001718 case hci_trans_ctrl_cmd_op_pack(HCI_OCF_FM_SET_TRANS_CONF_REQ):
1719 hci_cc_fm_trans_set_conf_rsp(hdev, skb);
1720 break;
1721
Srinivasa Rao Uppala0f4098f2011-09-06 16:46:28 +05301722 case hci_status_param_op_pack(HCI_OCF_FM_READ_GRP_COUNTERS):
1723 hci_cc_rds_grp_cntrs_rsp(hdev, skb);
1724 break;
1725
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001726 default:
1727 FMDERR("%s opcode 0x%x", hdev->name, opcode);
1728 break;
1729 }
1730
1731}
1732
1733static inline void hci_cmd_status_event(struct radio_hci_dev *hdev,
1734 struct sk_buff *skb)
1735{
1736 struct hci_ev_cmd_status *ev = (void *) skb->data;
1737 radio_hci_status_complete(hdev, ev->status);
1738}
1739
1740static inline void hci_ev_tune_status(struct radio_hci_dev *hdev,
1741 struct sk_buff *skb)
1742{
1743 int i;
1744 int len;
1745
1746 struct iris_device *radio = video_get_drvdata(video_get_dev());
1747
1748 len = sizeof(struct hci_fm_station_rsp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001749 memcpy(&radio->fm_st_rsp.station_rsp, skb_pull(skb, len), len);
1750
1751 iris_q_event(radio, IRIS_EVT_TUNE_SUCC);
1752
1753 for (i = 0; i < IRIS_BUF_MAX; i++) {
1754 if (i >= IRIS_BUF_RT_RDS)
1755 kfifo_reset(&radio->data_buf[i]);
1756 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001757 if (radio->fm_st_rsp.station_rsp.rssi)
1758 iris_q_event(radio, IRIS_EVT_ABOVE_TH);
1759 else
1760 iris_q_event(radio, IRIS_EVT_BELOW_TH);
1761
1762 if (radio->fm_st_rsp.station_rsp.stereo_prg)
1763 iris_q_event(radio, IRIS_EVT_STEREO);
1764
1765 if (radio->fm_st_rsp.station_rsp.mute_mode)
1766 iris_q_event(radio, IRIS_EVT_MONO);
1767
1768 if (radio->fm_st_rsp.station_rsp.rds_sync_status)
1769 iris_q_event(radio, IRIS_EVT_RDS_AVAIL);
1770 else
1771 iris_q_event(radio, IRIS_EVT_RDS_NOT_AVAIL);
1772}
1773
1774static inline void hci_ev_search_compl(struct radio_hci_dev *hdev,
1775 struct sk_buff *skb)
1776{
1777 struct iris_device *radio = video_get_drvdata(video_get_dev());
1778 iris_q_event(radio, IRIS_EVT_SEEK_COMPLETE);
1779}
1780
1781static inline void hci_ev_srch_st_list_compl(struct radio_hci_dev *hdev,
1782 struct sk_buff *skb)
1783{
1784 struct iris_device *radio = video_get_drvdata(video_get_dev());
Srinivasa Rao Uppala18fb80e2011-07-17 17:33:00 -07001785 struct hci_ev_srch_list_compl *ev ;
1786 int cnt;
1787 int stn_num;
1788 int rel_freq;
1789 int abs_freq;
1790 int len;
1791
1792 ev = kmalloc(sizeof(*ev), GFP_ATOMIC);
1793 if (!ev) {
1794 FMDERR("Memory allocation failed");
1795 return ;
1796 }
1797
1798 ev->num_stations_found = skb->data[STN_NUM_OFFSET];
1799 len = ev->num_stations_found * PARAMS_PER_STATION + STN_FREQ_OFFSET;
1800
1801 for (cnt = STN_FREQ_OFFSET, stn_num = 0;
1802 (cnt < len) && (stn_num < ev->num_stations_found);
1803 cnt += PARAMS_PER_STATION, stn_num++) {
1804 abs_freq = *((int *)&skb->data[cnt]);
1805 rel_freq = abs_freq - radio->recv_conf.band_low_limit;
1806 rel_freq = (rel_freq * 20) / KHZ_TO_MHZ;
1807
1808 ev->rel_freq[stn_num].rel_freq_lsb = GET_LSB(rel_freq);
1809 ev->rel_freq[stn_num].rel_freq_msb = GET_MSB(rel_freq);
1810 }
1811
1812 len = ev->num_stations_found * 2 + sizeof(ev->num_stations_found);
1813 iris_q_event(radio, IRIS_EVT_NEW_SRCH_LIST);
1814 iris_q_evt_data(radio, (char *)ev, len, IRIS_BUF_SRCH_LIST);
1815 kfree(ev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001816}
1817
1818static inline void hci_ev_search_next(struct radio_hci_dev *hdev,
1819 struct sk_buff *skb)
1820{
1821 struct iris_device *radio = video_get_drvdata(video_get_dev());
1822 iris_q_event(radio, IRIS_EVT_SCAN_NEXT);
1823}
1824
1825static inline void hci_ev_stereo_status(struct radio_hci_dev *hdev,
1826 struct sk_buff *skb)
1827{
1828 struct iris_device *radio = video_get_drvdata(video_get_dev());
1829 __u8 st_status = *((__u8 *) skb->data);
1830 if (st_status)
1831 iris_q_event(radio, IRIS_EVT_STEREO);
1832 else
1833 iris_q_event(radio, IRIS_EVT_MONO);
1834}
1835
Ankur Nandwani78a782b2011-07-07 21:11:21 -07001836
Ankur Nandwani78a782b2011-07-07 21:11:21 -07001837static inline void hci_ev_program_service(struct radio_hci_dev *hdev,
1838 struct sk_buff *skb)
1839{
1840 struct iris_device *radio = video_get_drvdata(video_get_dev());
1841 int len;
1842 char *data;
1843
1844 len = (skb->data[RDS_PS_LENGTH_OFFSET] * RDS_STRING) + RDS_OFFSET;
1845 iris_q_event(radio, IRIS_EVT_NEW_PS_RDS);
1846 data = kmalloc(len, GFP_ATOMIC);
1847 if (!data) {
1848 FMDERR("Failed to allocate memory");
1849 return;
1850 }
1851
1852 data[0] = skb->data[RDS_PS_LENGTH_OFFSET];
1853 data[1] = skb->data[RDS_PTYPE];
1854 data[2] = skb->data[RDS_PID_LOWER];
1855 data[3] = skb->data[RDS_PID_HIGHER];
1856 data[4] = 0;
1857
1858 memcpy(data+RDS_OFFSET, &skb->data[RDS_PS_DATA_OFFSET], len-RDS_OFFSET);
1859
Srinivasa Rao Uppala18fb80e2011-07-17 17:33:00 -07001860 iris_q_evt_data(radio, data, len, IRIS_BUF_PS_RDS);
Ankur Nandwani78a782b2011-07-07 21:11:21 -07001861
1862 kfree(data);
1863}
1864
1865
1866static inline void hci_ev_radio_text(struct radio_hci_dev *hdev,
1867 struct sk_buff *skb)
1868{
1869 struct iris_device *radio = video_get_drvdata(video_get_dev());
1870 int len = 0;
1871 char *data;
1872
1873 iris_q_event(radio, IRIS_EVT_NEW_RT_RDS);
1874
1875 while (skb->data[len+RDS_OFFSET] != 0x0d)
1876 len++;
1877 len++;
1878
1879 data = kmalloc(len+RDS_OFFSET, GFP_ATOMIC);
1880 if (!data) {
1881 FMDERR("Failed to allocate memory");
1882 return;
1883 }
1884
1885 data[0] = len;
1886 data[1] = skb->data[RDS_PTYPE];
1887 data[2] = skb->data[RDS_PID_LOWER];
1888 data[3] = skb->data[RDS_PID_HIGHER];
1889 data[4] = 0;
1890
1891 memcpy(data+RDS_OFFSET, &skb->data[RDS_OFFSET], len);
1892 data[len+RDS_OFFSET] = 0x00;
1893
Srinivasa Rao Uppala18fb80e2011-07-17 17:33:00 -07001894 iris_q_evt_data(radio, data, len+RDS_OFFSET, IRIS_BUF_RT_RDS);
Ankur Nandwani78a782b2011-07-07 21:11:21 -07001895
1896 kfree(data);
1897}
1898
1899
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001900void radio_hci_event_packet(struct radio_hci_dev *hdev, struct sk_buff *skb)
1901{
1902 struct radio_hci_event_hdr *hdr = (void *) skb->data;
1903 __u8 event = hdr->evt;
1904
1905 skb_pull(skb, RADIO_HCI_EVENT_HDR_SIZE);
1906
1907 switch (event) {
1908 case HCI_EV_TUNE_STATUS:
1909 hci_ev_tune_status(hdev, skb);
1910 break;
1911 case HCI_EV_SEARCH_PROGRESS:
1912 case HCI_EV_SEARCH_RDS_PROGRESS:
1913 case HCI_EV_SEARCH_LIST_PROGRESS:
1914 hci_ev_search_next(hdev, skb);
1915 break;
1916 case HCI_EV_STEREO_STATUS:
1917 hci_ev_stereo_status(hdev, skb);
1918 break;
1919 case HCI_EV_RDS_LOCK_STATUS:
1920 case HCI_EV_SERVICE_AVAILABLE:
1921 case HCI_EV_RDS_RX_DATA:
Ankur Nandwani78a782b2011-07-07 21:11:21 -07001922 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001923 case HCI_EV_PROGRAM_SERVICE:
Ankur Nandwani78a782b2011-07-07 21:11:21 -07001924 hci_ev_program_service(hdev, skb);
1925 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001926 case HCI_EV_RADIO_TEXT:
Ankur Nandwani78a782b2011-07-07 21:11:21 -07001927 hci_ev_radio_text(hdev, skb);
1928 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001929 case HCI_EV_FM_AF_LIST:
1930 case HCI_EV_TX_RDS_GRP_COMPL:
1931 case HCI_EV_TX_RDS_CONT_GRP_COMPL:
1932 break;
1933
1934 case HCI_EV_CMD_COMPLETE:
1935 hci_cmd_complete_event(hdev, skb);
1936 break;
1937
1938 case HCI_EV_CMD_STATUS:
1939 hci_cmd_status_event(hdev, skb);
1940 break;
1941
1942 case HCI_EV_SEARCH_COMPLETE:
1943 case HCI_EV_SEARCH_RDS_COMPLETE:
1944 hci_ev_search_compl(hdev, skb);
1945 break;
1946
1947 case HCI_EV_SEARCH_LIST_COMPLETE:
Srinivasa Rao Uppala18fb80e2011-07-17 17:33:00 -07001948 hci_ev_srch_st_list_compl(hdev, skb);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001949 break;
1950
1951 default:
1952 break;
1953 }
1954}
1955
1956/*
1957 * fops/IOCTL helper functions
1958 */
1959
1960static int iris_search(struct iris_device *radio, int on, int dir)
1961{
1962 int retval = 0;
1963 enum search_t srch = radio->g_search_mode & SRCH_MODE;
1964
1965 if (on) {
1966 switch (srch) {
1967 case SCAN_FOR_STRONG:
1968 case SCAN_FOR_WEAK:
1969 radio->srch_st_list.srch_list_dir = dir;
1970 radio->srch_st_list.srch_list_mode = srch;
1971 radio->srch_st_list.srch_list_max = 0;
1972 retval = hci_fm_search_station_list(
1973 &radio->srch_st_list, radio->fm_hdev);
1974 break;
1975 case RDS_SEEK_PTY:
1976 case RDS_SCAN_PTY:
1977 case RDS_SEEK_PI:
Srinivasa Rao Uppala7bb22102011-07-14 11:27:30 -07001978 srch = srch - SEARCH_RDS_STNS_MODE_OFFSET;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001979 radio->srch_rds.srch_station.srch_mode = srch;
1980 radio->srch_rds.srch_station.srch_dir = dir;
1981 radio->srch_rds.srch_station.scan_time =
1982 radio->g_scan_time;
1983 retval = hci_fm_search_rds_stations(&radio->srch_rds,
1984 radio->fm_hdev);
1985 break;
1986 default:
1987 radio->srch_st.srch_mode = srch;
1988 radio->srch_st.scan_time = radio->g_scan_time;
1989 radio->srch_st.srch_dir = dir;
1990 retval = hci_fm_search_stations(
1991 &radio->srch_st, radio->fm_hdev);
1992 break;
1993 }
1994
1995 } else {
1996 retval = hci_cmd(HCI_FM_CANCEL_SEARCH_CMD, radio->fm_hdev);
1997 }
1998
1999 return retval;
2000}
2001
Ankur Nandwanid928d542011-08-11 13:15:41 -07002002static int iris_recv_set_region(struct iris_device *radio, int req_region)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002003{
2004 int retval;
2005 radio->region = req_region;
2006
2007 switch (radio->region) {
2008 case IRIS_REGION_US:
Ankur Nandwanid928d542011-08-11 13:15:41 -07002009 radio->recv_conf.band_low_limit =
2010 REGION_US_EU_BAND_LOW;
2011 radio->recv_conf.band_high_limit =
2012 REGION_US_EU_BAND_HIGH;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002013 break;
2014 case IRIS_REGION_EU:
Ankur Nandwanid928d542011-08-11 13:15:41 -07002015 radio->recv_conf.band_low_limit =
2016 REGION_US_EU_BAND_LOW;
2017 radio->recv_conf.band_high_limit =
2018 REGION_US_EU_BAND_HIGH;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002019 break;
2020 case IRIS_REGION_JAPAN:
Ankur Nandwanid928d542011-08-11 13:15:41 -07002021 radio->recv_conf.band_low_limit =
Ankur Nandwanid928d542011-08-11 13:15:41 -07002022 REGION_JAPAN_STANDARD_BAND_LOW;
Srinivasa Rao Uppalacc62b1c2011-08-22 19:15:29 +05302023 radio->recv_conf.band_high_limit =
2024 REGION_JAPAN_STANDARD_BAND_HIGH;
Ankur Nandwanid928d542011-08-11 13:15:41 -07002025 break;
2026 case IRIS_REGION_JAPAN_WIDE:
2027 radio->recv_conf.band_low_limit =
2028 REGION_JAPAN_WIDE_BAND_LOW;
2029 radio->recv_conf.band_high_limit =
2030 REGION_JAPAN_WIDE_BAND_HIGH;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002031 break;
2032 default:
Ankur Nandwanid928d542011-08-11 13:15:41 -07002033 /* The user specifies the value.
2034 So nothing needs to be done */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002035 break;
2036 }
2037
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002038 retval = hci_set_fm_recv_conf(
2039 &radio->recv_conf,
2040 radio->fm_hdev);
2041
2042 return retval;
2043}
2044
Ankur Nandwanid928d542011-08-11 13:15:41 -07002045
2046static int iris_trans_set_region(struct iris_device *radio, int req_region)
2047{
2048 int retval;
2049 radio->region = req_region;
2050
2051 switch (radio->region) {
2052 case IRIS_REGION_US:
2053 radio->trans_conf.band_low_limit =
2054 REGION_US_EU_BAND_LOW;
2055 radio->trans_conf.band_high_limit =
2056 REGION_US_EU_BAND_HIGH;
2057 break;
2058 case IRIS_REGION_EU:
2059 radio->trans_conf.band_low_limit =
2060 REGION_US_EU_BAND_LOW;
2061 radio->trans_conf.band_high_limit =
2062 REGION_US_EU_BAND_HIGH;
2063 break;
2064 case IRIS_REGION_JAPAN:
2065 radio->trans_conf.band_low_limit =
Ankur Nandwanid928d542011-08-11 13:15:41 -07002066 REGION_JAPAN_STANDARD_BAND_LOW;
Srinivasa Rao Uppalacc62b1c2011-08-22 19:15:29 +05302067 radio->trans_conf.band_high_limit =
2068 REGION_JAPAN_STANDARD_BAND_HIGH;
Ankur Nandwanid928d542011-08-11 13:15:41 -07002069 break;
2070 case IRIS_REGION_JAPAN_WIDE:
2071 radio->recv_conf.band_low_limit =
2072 REGION_JAPAN_WIDE_BAND_LOW;
2073 radio->recv_conf.band_high_limit =
2074 REGION_JAPAN_WIDE_BAND_HIGH;
2075 default:
2076 break;
2077 }
2078
2079 retval = hci_set_fm_trans_conf(
2080 &radio->trans_conf,
2081 radio->fm_hdev);
2082 return retval;
2083}
2084
2085
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002086static int iris_set_freq(struct iris_device *radio, unsigned int freq)
2087{
2088
2089 int retval;
2090 retval = hci_fm_tune_station(&freq, radio->fm_hdev);
2091 if (retval < 0)
2092 FMDERR("Error while setting the frequency : %d\n", retval);
2093 return retval;
2094}
2095
2096
2097static int iris_vidioc_queryctrl(struct file *file, void *priv,
2098 struct v4l2_queryctrl *qc)
2099{
2100 unsigned char i;
2101 int retval = -EINVAL;
2102
2103 for (i = 0; i < ARRAY_SIZE(iris_v4l2_queryctrl); i++) {
2104 if (qc->id && qc->id == iris_v4l2_queryctrl[i].id) {
2105 memcpy(qc, &(iris_v4l2_queryctrl[i]), sizeof(*qc));
2106 retval = 0;
2107 break;
2108 }
2109 }
2110
2111 return retval;
2112}
2113
2114static int iris_vidioc_g_ctrl(struct file *file, void *priv,
2115 struct v4l2_control *ctrl)
2116{
2117 struct iris_device *radio = video_get_drvdata(video_devdata(file));
2118 int retval = 0;
2119
2120 switch (ctrl->id) {
2121 case V4L2_CID_AUDIO_VOLUME:
2122 break;
2123 case V4L2_CID_AUDIO_MUTE:
2124 ctrl->value = radio->mute_mode.hard_mute;
2125 break;
2126 case V4L2_CID_PRIVATE_IRIS_SRCHMODE:
2127 ctrl->value = radio->g_search_mode;
2128 break;
2129 case V4L2_CID_PRIVATE_IRIS_SCANDWELL:
2130 ctrl->value = radio->g_scan_time;
2131 break;
2132 case V4L2_CID_PRIVATE_IRIS_SRCHON:
2133 break;
2134 case V4L2_CID_PRIVATE_IRIS_STATE:
2135 break;
2136 case V4L2_CID_PRIVATE_IRIS_IOVERC:
2137 retval = hci_cmd(HCI_FM_STATION_DBG_PARAM_CMD, radio->fm_hdev);
2138 if (retval < 0)
2139 return retval;
2140 ctrl->value = radio->st_dbg_param.io_verc;
2141 break;
2142 case V4L2_CID_PRIVATE_IRIS_INTDET:
2143 retval = hci_cmd(HCI_FM_STATION_DBG_PARAM_CMD, radio->fm_hdev);
2144 if (retval < 0)
2145 return retval;
2146 ctrl->value = radio->st_dbg_param.in_det_out;
2147 break;
2148 case V4L2_CID_PRIVATE_IRIS_REGION:
2149 ctrl->value = radio->region;
2150 break;
2151 case V4L2_CID_PRIVATE_IRIS_SIGNAL_TH:
2152 retval = hci_cmd(HCI_FM_GET_SIGNAL_TH_CMD, radio->fm_hdev);
2153 break;
2154 case V4L2_CID_PRIVATE_IRIS_SRCH_PTY:
2155 break;
2156 case V4L2_CID_PRIVATE_IRIS_SRCH_PI:
2157 break;
2158 case V4L2_CID_PRIVATE_IRIS_SRCH_CNT:
2159 break;
2160 case V4L2_CID_PRIVATE_IRIS_EMPHASIS:
Ankur Nandwanid928d542011-08-11 13:15:41 -07002161 if (radio->mode == FM_RECV) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002162 ctrl->value = radio->recv_conf.emphasis;
Ankur Nandwanid928d542011-08-11 13:15:41 -07002163 } else if (radio->mode == FM_TRANS) {
2164 ctrl->value = radio->trans_conf.emphasis;
2165 } else {
2166 FMDERR("Error in radio mode"
2167 " %d\n", retval);
2168 return -EINVAL;
2169 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002170 break;
2171 case V4L2_CID_PRIVATE_IRIS_RDS_STD:
Ankur Nandwanid928d542011-08-11 13:15:41 -07002172 if (radio->mode == FM_RECV) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002173 ctrl->value = radio->recv_conf.rds_std;
Ankur Nandwanid928d542011-08-11 13:15:41 -07002174 } else if (radio->mode == FM_TRANS) {
2175 ctrl->value = radio->trans_conf.rds_std;
2176 } else {
2177 FMDERR("Error in radio mode"
2178 " %d\n", retval);
2179 return -EINVAL;
2180 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002181 break;
2182 case V4L2_CID_PRIVATE_IRIS_SPACING:
Ankur Nandwanid928d542011-08-11 13:15:41 -07002183 if (radio->mode == FM_RECV) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002184 ctrl->value = radio->recv_conf.ch_spacing;
Ankur Nandwanid928d542011-08-11 13:15:41 -07002185 } else {
2186 FMDERR("Error in radio mode"
2187 " %d\n", retval);
2188 return -EINVAL;
2189 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002190 break;
2191 case V4L2_CID_PRIVATE_IRIS_RDSON:
Ankur Nandwanid928d542011-08-11 13:15:41 -07002192 if (radio->mode == FM_RECV) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002193 ctrl->value = radio->recv_conf.rds_std;
Ankur Nandwanid928d542011-08-11 13:15:41 -07002194 } else {
2195 FMDERR("Error in radio mode"
2196 " %d\n", retval);
2197 return -EINVAL;
2198 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002199 break;
2200 case V4L2_CID_PRIVATE_IRIS_RDSGROUP_MASK:
2201 ctrl->value = radio->rds_grp.rds_grp_enable_mask;
2202 break;
2203 case V4L2_CID_PRIVATE_IRIS_RDSGROUP_PROC:
Srinivasa Rao Uppala58273f82011-08-10 19:07:45 -07002204 case V4L2_CID_PRIVATE_IRIS_PSALL:
2205 ctrl->value = (radio->g_rds_grp_proc_ps << RDS_CONFIG_OFFSET);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002206 break;
2207 case V4L2_CID_PRIVATE_IRIS_RDSD_BUF:
2208 ctrl->value = radio->rds_grp.rds_buf_size;
2209 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002210 case V4L2_CID_PRIVATE_IRIS_LP_MODE:
2211 break;
2212 case V4L2_CID_PRIVATE_IRIS_ANTENNA:
2213 ctrl->value = radio->g_antenna;
2214 break;
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -07002215 case V4L2_CID_PRIVATE_IRIS_SOFT_MUTE:
2216 ctrl->value = radio->mute_mode.soft_mute;
2217 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002218 default:
2219 retval = -EINVAL;
2220 }
2221 if (retval < 0)
2222 FMDERR("get control failed with %d, id: %d\n",
2223 retval, ctrl->id);
2224 return retval;
2225}
2226
2227static int iris_vidioc_s_ext_ctrls(struct file *file, void *priv,
2228 struct v4l2_ext_controls *ctrl)
2229{
Ankur Nandwanid928d542011-08-11 13:15:41 -07002230 int retval = 0;
2231 int bytes_to_copy;
2232 struct hci_fm_tx_ps tx_ps;
2233 struct hci_fm_tx_rt tx_rt;
2234
2235 struct iris_device *radio = video_get_drvdata(video_devdata(file));
2236 char *data = NULL;
2237
2238 switch ((ctrl->controls[0]).id) {
2239 case V4L2_CID_RDS_TX_PS_NAME:
2240 FMDBG("In V4L2_CID_RDS_TX_PS_NAME\n");
2241 /*Pass a sample PS string */
2242
2243 memset(tx_ps.ps_data, 0, MAX_PS_LENGTH);
2244 bytes_to_copy = min((int)(ctrl->controls[0]).size,
2245 MAX_PS_LENGTH);
2246 data = (ctrl->controls[0]).string;
2247
2248 if (copy_from_user(tx_ps.ps_data,
2249 data, bytes_to_copy))
2250 return -EFAULT;
2251 tx_ps.ps_control = 0x01;
2252 tx_ps.pi = radio->pi;
2253 tx_ps.pty = radio->pty;
2254 tx_ps.ps_repeatcount = radio->ps_repeatcount;
2255 tx_ps.ps_len = bytes_to_copy;
2256
2257 retval = radio_hci_request(radio->fm_hdev, hci_trans_ps_req,
2258 (unsigned long)&tx_ps, RADIO_HCI_TIMEOUT);
2259 break;
2260 case V4L2_CID_RDS_TX_RADIO_TEXT:
2261 bytes_to_copy =
2262 min((int)(ctrl->controls[0]).size, MAX_RT_LENGTH);
2263 data = (ctrl->controls[0]).string;
2264
2265 memset(tx_rt.rt_data, 0, MAX_RT_LENGTH);
2266
2267 if (copy_from_user(tx_rt.rt_data,
2268 data, bytes_to_copy))
2269 return -EFAULT;
2270
2271 tx_rt.rt_control = 0x01;
2272 tx_rt.pi = radio->pi;
2273 tx_rt.pty = radio->pty;
2274 tx_rt.ps_len = bytes_to_copy;
2275
2276 retval = radio_hci_request(radio->fm_hdev, hci_trans_rt_req,
2277 (unsigned long)&tx_rt, RADIO_HCI_TIMEOUT);
2278 break;
2279 default:
2280 FMDBG("Shouldn't reach here\n");
2281 retval = -1;
2282 }
2283 return retval;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002284}
2285
2286static int iris_vidioc_s_ctrl(struct file *file, void *priv,
2287 struct v4l2_control *ctrl)
2288{
2289 struct iris_device *radio = video_get_drvdata(video_devdata(file));
2290 int retval = 0;
2291 unsigned int rds_grps_proc = 0;
2292 __u8 temp_val = 0;
Ankur Nandwanid928d542011-08-11 13:15:41 -07002293 unsigned long arg = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002294
2295 switch (ctrl->id) {
Srinivasa Rao Uppala0f4098f2011-09-06 16:46:28 +05302296 case V4L2_CID_PRIVATE_IRIS_TX_TONE:
Ankur Nandwanid928d542011-08-11 13:15:41 -07002297 radio->tone_freq = ctrl->value;
2298 retval = radio_hci_request(radio->fm_hdev,
2299 hci_fm_tone_generator, arg,
2300 msecs_to_jiffies(RADIO_HCI_TIMEOUT));
2301 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002302 case V4L2_CID_AUDIO_VOLUME:
2303 break;
2304 case V4L2_CID_AUDIO_MUTE:
2305 radio->mute_mode.hard_mute = ctrl->value;
2306 radio->mute_mode.soft_mute = IOC_SFT_MUTE;
2307 retval = hci_set_fm_mute_mode(
2308 &radio->mute_mode,
2309 radio->fm_hdev);
2310 if (retval < 0)
2311 FMDERR("Error while set FM hard mute"" %d\n",
2312 retval);
2313 break;
2314 case V4L2_CID_PRIVATE_IRIS_SRCHMODE:
2315 radio->g_search_mode = ctrl->value;
2316 break;
2317 case V4L2_CID_PRIVATE_IRIS_SCANDWELL:
2318 radio->g_scan_time = ctrl->value;
2319 break;
2320 case V4L2_CID_PRIVATE_IRIS_SRCHON:
2321 iris_search(radio, ctrl->value, SRCH_DIR_UP);
2322 break;
2323 case V4L2_CID_PRIVATE_IRIS_STATE:
Srinivasa Rao Uppalacc62b1c2011-08-22 19:15:29 +05302324 radio->recv_conf.emphasis = 0;
2325 radio->recv_conf.ch_spacing = 0;
2326 radio->recv_conf.hlsi = 0;
2327 radio->recv_conf.band_low_limit =
2328 REGION_US_EU_BAND_LOW;
2329 radio->recv_conf.band_high_limit =
2330 REGION_US_EU_BAND_HIGH;
2331 radio->recv_conf.rds_std = 0;
Ankur Nandwanid928d542011-08-11 13:15:41 -07002332 switch (ctrl->value) {
2333 case FM_RECV:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002334 retval = hci_cmd(HCI_FM_ENABLE_RECV_CMD,
2335 radio->fm_hdev);
Ankur Nandwanid928d542011-08-11 13:15:41 -07002336
2337 radio->mode = FM_RECV;
2338
2339 if (retval < 0)
2340 FMDERR("Error while enabling RECV FM"
Srinivasa Rao Uppala0ffb5d62011-08-02 17:54:13 -07002341 " %d\n", retval);
Ankur Nandwanid928d542011-08-11 13:15:41 -07002342 radio->mute_mode.soft_mute = CTRL_ON;
2343 retval = hci_set_fm_mute_mode(
2344 &radio->mute_mode,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002345 radio->fm_hdev);
Ankur Nandwanid928d542011-08-11 13:15:41 -07002346 if (retval < 0)
2347 FMDERR("Failed to enable Smute\n");
2348 radio->stereo_mode.stereo_mode = CTRL_OFF;
2349 radio->stereo_mode.sig_blend = CTRL_ON;
2350 radio->stereo_mode.intf_blend = CTRL_ON;
2351 radio->stereo_mode.most_switch = CTRL_ON;
2352 retval = hci_set_fm_stereo_mode(
2353 &radio->stereo_mode,
2354 radio->fm_hdev);
2355 if (retval < 0)
2356 FMDERR("Failed to set stereo mode\n");
Srinivasa Rao Uppala07522d92011-08-16 05:09:30 -07002357 break;
Ankur Nandwanid928d542011-08-11 13:15:41 -07002358 case FM_TRANS:
2359 retval = hci_cmd(HCI_FM_ENABLE_TRANS_CMD,
2360 radio->fm_hdev);
2361 radio->mode = FM_TRANS;
2362
2363 if (retval < 0)
2364 FMDERR("Error while enabling TRANS FM"
2365 " %d\n", retval);
Srinivasa Rao Uppala07522d92011-08-16 05:09:30 -07002366 break;
Ankur Nandwanid928d542011-08-11 13:15:41 -07002367 case FM_OFF:
2368 switch (radio->mode) {
2369 case FM_RECV:
2370 retval = hci_cmd(HCI_FM_DISABLE_RECV_CMD,
2371 radio->fm_hdev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002372 if (retval < 0)
Ankur Nandwanid928d542011-08-11 13:15:41 -07002373 FMDERR("Err on disable recv FM"
2374 " %d\n", retval);
2375 break;
2376 case FM_TRANS:
2377 retval = hci_cmd(HCI_FM_DISABLE_TRANS_CMD,
2378 radio->fm_hdev);
2379
Srinivasa Rao Uppala0ffb5d62011-08-02 17:54:13 -07002380 if (retval < 0)
Ankur Nandwanid928d542011-08-11 13:15:41 -07002381 FMDERR("Err disabling trans FM"
Srinivasa Rao Uppala0ffb5d62011-08-02 17:54:13 -07002382 " %d\n", retval);
Ankur Nandwanid928d542011-08-11 13:15:41 -07002383 break;
2384 default:
2385 retval = -EINVAL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002386 }
Srinivasa Rao Uppalacc62b1c2011-08-22 19:15:29 +05302387 break;
Ankur Nandwanid928d542011-08-11 13:15:41 -07002388 default:
2389 retval = -EINVAL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002390 }
2391 break;
2392 case V4L2_CID_PRIVATE_IRIS_REGION:
Ankur Nandwanid928d542011-08-11 13:15:41 -07002393 if (radio->mode == FM_RECV) {
2394 retval = iris_recv_set_region(radio, ctrl->value);
2395 } else {
2396 if (radio->mode == FM_TRANS)
2397 retval = iris_trans_set_region(radio,
2398 ctrl->value);
2399 else
2400 retval = -EINVAL;
2401 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002402 break;
2403 case V4L2_CID_PRIVATE_IRIS_SIGNAL_TH:
2404 temp_val = ctrl->value;
2405 retval = hci_fm_set_signal_threshold(
2406 &temp_val,
2407 radio->fm_hdev);
2408 if (retval < 0) {
2409 FMDERR("Error while setting signal threshold\n");
2410 break;
2411 }
2412 break;
2413 case V4L2_CID_PRIVATE_IRIS_SRCH_PTY:
2414 radio->srch_rds.srch_pty = ctrl->value;
2415 radio->srch_st_list.srch_pty = ctrl->value;
2416 break;
2417 case V4L2_CID_PRIVATE_IRIS_SRCH_PI:
2418 radio->srch_rds.srch_pi = ctrl->value;
2419 break;
2420 case V4L2_CID_PRIVATE_IRIS_SRCH_CNT:
2421 break;
2422 case V4L2_CID_PRIVATE_IRIS_SPACING:
2423 radio->recv_conf.ch_spacing = ctrl->value;
2424 break;
2425 case V4L2_CID_PRIVATE_IRIS_EMPHASIS:
Ankur Nandwani8f972e52011-08-24 11:48:32 -07002426 switch (radio->mode) {
2427 case FM_RECV:
2428 radio->recv_conf.emphasis = ctrl->value;
2429 retval = hci_set_fm_recv_conf(
2430 &radio->recv_conf,
2431 radio->fm_hdev);
2432 if (retval < 0)
2433 FMDERR("Error in setting emphasis");
Ankur Nandwanid928d542011-08-11 13:15:41 -07002434 break;
Ankur Nandwani8f972e52011-08-24 11:48:32 -07002435 case FM_TRANS:
2436 radio->trans_conf.emphasis = ctrl->value;
2437 retval = hci_set_fm_trans_conf(
2438 &radio->trans_conf,
2439 radio->fm_hdev);
2440 if (retval < 0)
2441 FMDERR("Error in setting emphasis");
2442 break;
2443 default:
2444 retval = -EINVAL;
Ankur Nandwanid928d542011-08-11 13:15:41 -07002445 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002446 break;
2447 case V4L2_CID_PRIVATE_IRIS_RDS_STD:
Ankur Nandwani8f972e52011-08-24 11:48:32 -07002448 switch (radio->mode) {
2449 case FM_RECV:
2450 radio->recv_conf.rds_std = ctrl->value;
2451 retval = hci_set_fm_recv_conf(
2452 &radio->recv_conf,
2453 radio->fm_hdev);
2454 if (retval < 0)
2455 FMDERR("Error in rds_std");
Ankur Nandwanid928d542011-08-11 13:15:41 -07002456 break;
Ankur Nandwani8f972e52011-08-24 11:48:32 -07002457 case FM_TRANS:
2458 radio->trans_conf.rds_std = ctrl->value;
2459 retval = hci_set_fm_trans_conf(
2460 &radio->trans_conf,
2461 radio->fm_hdev);
2462 if (retval < 0)
2463 FMDERR("Error in rds_Std");
2464 break;
2465 default:
2466 retval = -EINVAL;
Ankur Nandwanid928d542011-08-11 13:15:41 -07002467 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002468 break;
2469 case V4L2_CID_PRIVATE_IRIS_RDSON:
Ankur Nandwani8f972e52011-08-24 11:48:32 -07002470 switch (radio->mode) {
2471 case FM_RECV:
2472 radio->recv_conf.rds_std = ctrl->value;
2473 retval = hci_set_fm_recv_conf(
2474 &radio->recv_conf,
2475 radio->fm_hdev);
2476 if (retval < 0)
2477 FMDERR("Error in rds_std");
2478 break;
2479 case FM_TRANS:
2480 radio->trans_conf.rds_std = ctrl->value;
2481 retval = hci_set_fm_trans_conf(
2482 &radio->trans_conf,
2483 radio->fm_hdev);
2484 if (retval < 0)
2485 FMDERR("Error in rds_Std");
2486 break;
2487 default:
2488 retval = -EINVAL;
2489 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002490 break;
2491 case V4L2_CID_PRIVATE_IRIS_RDSGROUP_MASK:
2492 radio->rds_grp.rds_grp_enable_mask = ctrl->value;
2493 retval = hci_fm_rds_grp(&radio->rds_grp, radio->fm_hdev);
2494 break;
2495 case V4L2_CID_PRIVATE_IRIS_RDSGROUP_PROC:
2496 rds_grps_proc = radio->g_rds_grp_proc_ps | ctrl->value;
Srinivasa Rao Uppala58273f82011-08-10 19:07:45 -07002497 radio->g_rds_grp_proc_ps = (rds_grps_proc >> RDS_CONFIG_OFFSET);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002498 retval = hci_fm_rds_grps_process(
Srinivasa Rao Uppala58273f82011-08-10 19:07:45 -07002499 &radio->g_rds_grp_proc_ps,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002500 radio->fm_hdev);
2501 break;
2502 case V4L2_CID_PRIVATE_IRIS_RDSD_BUF:
2503 radio->rds_grp.rds_buf_size = ctrl->value;
2504 break;
2505 case V4L2_CID_PRIVATE_IRIS_PSALL:
Srinivasa Rao Uppala58273f82011-08-10 19:07:45 -07002506 rds_grps_proc = (ctrl->value << RDS_CONFIG_OFFSET);
2507 radio->g_rds_grp_proc_ps |= rds_grps_proc;
2508 retval = hci_fm_rds_grps_process(
2509 &radio->g_rds_grp_proc_ps,
2510 radio->fm_hdev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002511 break;
2512 case V4L2_CID_PRIVATE_IRIS_LP_MODE:
2513 break;
2514 case V4L2_CID_PRIVATE_IRIS_ANTENNA:
2515 temp_val = ctrl->value;
2516 retval = hci_fm_set_antenna(&temp_val, radio->fm_hdev);
2517 break;
2518 case V4L2_CID_RDS_TX_PTY:
Ankur Nandwanid928d542011-08-11 13:15:41 -07002519 radio->pty = ctrl->value;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002520 break;
2521 case V4L2_CID_RDS_TX_PI:
Ankur Nandwanid928d542011-08-11 13:15:41 -07002522 radio->pi = ctrl->value;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002523 break;
2524 case V4L2_CID_PRIVATE_IRIS_STOP_RDS_TX_PS_NAME:
2525 break;
2526 case V4L2_CID_PRIVATE_IRIS_STOP_RDS_TX_RT:
2527 break;
2528 case V4L2_CID_PRIVATE_IRIS_TX_SETPSREPEATCOUNT:
Ankur Nandwanid928d542011-08-11 13:15:41 -07002529 radio->ps_repeatcount = ctrl->value;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002530 break;
2531 case V4L2_CID_TUNE_POWER_LEVEL:
2532 break;
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -07002533 case V4L2_CID_PRIVATE_IRIS_SOFT_MUTE:
2534 radio->mute_mode.soft_mute = ctrl->value;
2535 retval = hci_set_fm_mute_mode(
2536 &radio->mute_mode,
2537 radio->fm_hdev);
2538 if (retval < 0)
2539 FMDERR("Error while setting FM soft mute"" %d\n",
2540 retval);
2541 break;
2542 case V4L2_CID_PRIVATE_IRIS_RIVA_ACCS_ADDR:
2543 radio->riva_data_req.cmd_params.start_addr = ctrl->value;
2544 break;
2545 case V4L2_CID_PRIVATE_IRIS_RIVA_ACCS_LEN:
2546 radio->riva_data_req.cmd_params.length = ctrl->value;
2547 break;
2548 case V4L2_CID_PRIVATE_IRIS_RIVA_POKE:
2549 memcpy(radio->riva_data_req.data, (void *)ctrl->value,
2550 radio->riva_data_req.cmd_params.length);
2551 radio->riva_data_req.cmd_params.subopcode = RIVA_POKE_OPCODE;
2552 retval = hci_poke_data(&radio->riva_data_req , radio->fm_hdev);
2553 break;
2554 case V4L2_CID_PRIVATE_IRIS_SSBI_ACCS_ADDR:
2555 radio->ssbi_data_accs.start_addr = ctrl->value;
2556 break;
2557 case V4L2_CID_PRIVATE_IRIS_SSBI_POKE:
2558 radio->ssbi_data_accs.data = ctrl->value;
2559 retval = hci_ssbi_poke_reg(&radio->ssbi_data_accs ,
2560 radio->fm_hdev);
2561 break;
2562 case V4L2_CID_PRIVATE_IRIS_RIVA_PEEK:
2563 radio->riva_data_req.cmd_params.subopcode = RIVA_PEEK_OPCODE;
2564 ctrl->value = hci_peek_data(&radio->riva_data_req.cmd_params ,
2565 radio->fm_hdev);
2566 break;
2567 case V4L2_CID_PRIVATE_IRIS_SSBI_PEEK:
2568 radio->ssbi_peek_reg.start_address = ctrl->value;
2569 hci_ssbi_peek_reg(&radio->ssbi_peek_reg, radio->fm_hdev);
2570 break;
Srinivasa Rao Uppala0f4098f2011-09-06 16:46:28 +05302571 case V4L2_CID_PRIVATE_IRIS_RDS_GRP_COUNTERS:
2572 temp_val = ctrl->value;
2573 hci_read_grp_counters(&temp_val, radio->fm_hdev);
2574 break;
2575 case V4L2_CID_PRIVATE_IRIS_HLSI:
2576 retval = hci_cmd(HCI_FM_GET_RECV_CONF_CMD,
2577 radio->fm_hdev);
2578 if (retval)
2579 break;
2580 radio->recv_conf.hlsi = ctrl->value;
2581 retval = hci_set_fm_recv_conf(
2582 &radio->recv_conf,
2583 radio->fm_hdev);
2584 break;
2585 case V4L2_CID_PRIVATE_IRIS_SET_NOTCH_FILTER:
2586 temp_val = ctrl->value;
2587 retval = hci_set_notch_filter(&temp_val, radio->fm_hdev);
2588 break;
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -07002589
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002590 default:
2591 retval = -EINVAL;
2592 }
2593 return retval;
2594}
2595
2596static int iris_vidioc_g_tuner(struct file *file, void *priv,
2597 struct v4l2_tuner *tuner)
2598{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002599 int retval;
Ankur Nandwanid928d542011-08-11 13:15:41 -07002600 struct iris_device *radio = video_get_drvdata(video_devdata(file));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002601 if (tuner->index > 0)
2602 return -EINVAL;
2603
2604 retval = hci_cmd(HCI_FM_GET_STATION_PARAM_CMD, radio->fm_hdev);
2605 if (retval < 0)
2606 return retval;
2607
2608 tuner->type = V4L2_TUNER_RADIO;
2609 tuner->rangelow = radio->recv_conf.band_low_limit * TUNE_PARAM;
2610 tuner->rangehigh = radio->recv_conf.band_high_limit * TUNE_PARAM;
2611 tuner->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
2612 tuner->capability = V4L2_TUNER_CAP_LOW;
2613 tuner->signal = radio->fm_st_rsp.station_rsp.rssi;
2614 tuner->audmode = radio->fm_st_rsp.station_rsp.stereo_prg;
2615 tuner->afc = 0;
2616
2617 return 0;
2618}
2619
2620static int iris_vidioc_s_tuner(struct file *file, void *priv,
2621 struct v4l2_tuner *tuner)
2622{
2623 struct iris_device *radio = video_get_drvdata(video_devdata(file));
Ankur Nandwanid928d542011-08-11 13:15:41 -07002624 int retval = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002625 if (tuner->index > 0)
2626 return -EINVAL;
2627
Ankur Nandwanid928d542011-08-11 13:15:41 -07002628 if (radio->mode == FM_RECV) {
2629 radio->recv_conf.band_low_limit = tuner->rangelow / TUNE_PARAM;
2630 radio->recv_conf.band_high_limit =
2631 tuner->rangehigh / TUNE_PARAM;
2632 if (tuner->audmode == V4L2_TUNER_MODE_MONO) {
2633 radio->stereo_mode.stereo_mode = 0x01;
2634 retval = hci_set_fm_stereo_mode(
2635 &radio->stereo_mode,
2636 radio->fm_hdev);
2637 } else {
2638 radio->stereo_mode.stereo_mode = 0x00;
2639 retval = hci_set_fm_stereo_mode(
2640 &radio->stereo_mode,
2641 radio->fm_hdev);
2642 }
2643 if (retval < 0)
2644 FMDERR(": set tuner failed with %d\n", retval);
2645 return retval;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002646 } else {
Ankur Nandwanid928d542011-08-11 13:15:41 -07002647 if (radio->mode == FM_TRANS) {
2648 radio->trans_conf.band_low_limit =
2649 tuner->rangelow / TUNE_PARAM;
2650 radio->trans_conf.band_high_limit =
2651 tuner->rangehigh / TUNE_PARAM;
2652 } else {
2653 return -EINVAL;
2654 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002655 }
Ankur Nandwanid928d542011-08-11 13:15:41 -07002656
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002657 return retval;
2658}
2659
2660static int iris_vidioc_g_frequency(struct file *file, void *priv,
2661 struct v4l2_frequency *freq)
2662{
2663 struct iris_device *radio = video_get_drvdata(video_devdata(file));
2664 int retval;
2665
2666 freq->type = V4L2_TUNER_RADIO;
2667 retval = hci_cmd(HCI_FM_GET_STATION_PARAM_CMD, radio->fm_hdev);
2668 if (retval < 0)
2669 FMDERR("get frequency failed %d\n", retval);
2670 else
2671 freq->frequency =
2672 radio->fm_st_rsp.station_rsp.station_freq * TUNE_PARAM;
2673 return retval;
2674}
2675
2676static int iris_vidioc_s_frequency(struct file *file, void *priv,
2677 struct v4l2_frequency *freq)
2678{
2679 struct iris_device *radio = video_get_drvdata(video_devdata(file));
2680 int retval = -1;
2681 freq->frequency = freq->frequency / TUNE_PARAM;
2682
2683 if (freq->type != V4L2_TUNER_RADIO)
2684 return -EINVAL;
2685
Ankur Nandwani8f972e52011-08-24 11:48:32 -07002686 /* We turn off RDS prior to tuning to a new station.
2687 because of a bug in SoC which prevents tuning
2688 during RDS transmission.
2689 */
2690 if (radio->mode == FM_TRANS
2691 && (radio->trans_conf.rds_std == 0 ||
2692 radio->trans_conf.rds_std == 1)) {
2693 radio->prev_trans_rds = radio->trans_conf.rds_std;
2694 radio->trans_conf.rds_std = 2;
2695 hci_set_fm_trans_conf(&radio->trans_conf,
2696 radio->fm_hdev);
2697 }
2698
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002699 retval = iris_set_freq(radio, freq->frequency);
Ankur Nandwani8f972e52011-08-24 11:48:32 -07002700
2701 if (radio->mode == FM_TRANS
2702 && radio->trans_conf.rds_std == 2
2703 && (radio->prev_trans_rds == 1
2704 || radio->prev_trans_rds == 0)) {
2705 radio->trans_conf.rds_std = radio->prev_trans_rds;
2706 hci_set_fm_trans_conf(&radio->trans_conf,
2707 radio->fm_hdev);
2708 }
2709
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002710 if (retval < 0)
2711 FMDERR(" set frequency failed with %d\n", retval);
2712 return retval;
2713}
2714
2715static int iris_vidioc_dqbuf(struct file *file, void *priv,
2716 struct v4l2_buffer *buffer)
2717{
2718 struct iris_device *radio = video_get_drvdata(video_devdata(file));
2719 enum iris_buf_t buf_type = buffer->index;
2720 struct kfifo *data_fifo;
2721 unsigned char *buf = (unsigned char *)buffer->m.userptr;
2722 unsigned int len = buffer->length;
2723 if (!access_ok(VERIFY_WRITE, buf, len))
2724 return -EFAULT;
2725 if ((buf_type < IRIS_BUF_MAX) && (buf_type >= 0)) {
2726 data_fifo = &radio->data_buf[buf_type];
2727 if (buf_type == IRIS_BUF_EVENTS)
2728 if (wait_event_interruptible(radio->event_queue,
2729 kfifo_len(data_fifo)) < 0)
2730 return -EINTR;
2731 } else {
2732 FMDERR("invalid buffer type\n");
2733 return -EINVAL;
2734 }
2735 buffer->bytesused = kfifo_out_locked(data_fifo, buf, len,
2736 &radio->buf_lock[buf_type]);
2737
2738 return 0;
2739}
2740
2741static int iris_vidioc_g_fmt_type_private(struct file *file, void *priv,
2742 struct v4l2_format *f)
2743{
2744 return 0;
2745
2746}
2747
2748static int iris_vidioc_s_hw_freq_seek(struct file *file, void *priv,
2749 struct v4l2_hw_freq_seek *seek)
2750{
2751 struct iris_device *radio = video_get_drvdata(video_devdata(file));
2752 int dir;
2753 if (seek->seek_upward)
2754 dir = SRCH_DIR_UP;
2755 else
2756 dir = SRCH_DIR_DOWN;
2757 return iris_search(radio, CTRL_ON, dir);
2758}
2759
2760static int iris_vidioc_querycap(struct file *file, void *priv,
2761 struct v4l2_capability *capability)
2762{
2763 struct iris_device *radio;
2764 radio = video_get_drvdata(video_devdata(file));
2765 strlcpy(capability->driver, DRIVER_NAME, sizeof(capability->driver));
2766 strlcpy(capability->card, DRIVER_CARD, sizeof(capability->card));
2767 radio->g_cap = capability;
2768 return 0;
2769}
2770
2771
2772static const struct v4l2_ioctl_ops iris_ioctl_ops = {
2773 .vidioc_querycap = iris_vidioc_querycap,
2774 .vidioc_queryctrl = iris_vidioc_queryctrl,
2775 .vidioc_g_ctrl = iris_vidioc_g_ctrl,
2776 .vidioc_s_ctrl = iris_vidioc_s_ctrl,
2777 .vidioc_g_tuner = iris_vidioc_g_tuner,
2778 .vidioc_s_tuner = iris_vidioc_s_tuner,
2779 .vidioc_g_frequency = iris_vidioc_g_frequency,
2780 .vidioc_s_frequency = iris_vidioc_s_frequency,
2781 .vidioc_s_hw_freq_seek = iris_vidioc_s_hw_freq_seek,
2782 .vidioc_dqbuf = iris_vidioc_dqbuf,
2783 .vidioc_g_fmt_type_private = iris_vidioc_g_fmt_type_private,
2784 .vidioc_s_ext_ctrls = iris_vidioc_s_ext_ctrls,
2785};
2786
2787static const struct v4l2_file_operations iris_fops = {
2788 .owner = THIS_MODULE,
2789 .unlocked_ioctl = video_ioctl2,
2790};
2791
2792static struct video_device iris_viddev_template = {
2793 .fops = &iris_fops,
2794 .ioctl_ops = &iris_ioctl_ops,
2795 .name = DRIVER_NAME,
2796 .release = video_device_release,
2797};
2798
2799static struct video_device *video_get_dev(void)
2800{
2801 return priv_videodev;
2802}
2803
2804static int __init iris_probe(struct platform_device *pdev)
2805{
2806 struct iris_device *radio;
2807 int retval;
2808 int radio_nr = -1;
2809 int i;
2810
2811 if (!pdev) {
2812 FMDERR(": pdev is null\n");
2813 return -ENOMEM;
2814 }
2815
2816 radio = kzalloc(sizeof(struct iris_device), GFP_KERNEL);
2817 if (!radio) {
2818 FMDERR(": Could not allocate radio device\n");
2819 return -ENOMEM;
2820 }
2821
2822 radio->dev = &pdev->dev;
2823 platform_set_drvdata(pdev, radio);
2824
2825 radio->videodev = video_device_alloc();
2826 if (!radio->videodev) {
2827 FMDERR(": Could not allocate V4L device\n");
2828 kfree(radio);
2829 return -ENOMEM;
2830 }
2831
2832 memcpy(radio->videodev, &iris_viddev_template,
2833 sizeof(iris_viddev_template));
2834
2835 for (i = 0; i < IRIS_BUF_MAX; i++) {
2836 int kfifo_alloc_rc = 0;
2837 spin_lock_init(&radio->buf_lock[i]);
2838
Srinivasa Rao Uppala6cc0e322011-08-12 10:54:48 -07002839 if ((i == IRIS_BUF_RAW_RDS) || (i == IRIS_BUF_PEEK))
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002840 kfifo_alloc_rc = kfifo_alloc(&radio->data_buf[i],
2841 rds_buf*3, GFP_KERNEL);
2842 else
2843 kfifo_alloc_rc = kfifo_alloc(&radio->data_buf[i],
2844 STD_BUF_SIZE, GFP_KERNEL);
2845
2846 if (kfifo_alloc_rc != 0) {
2847 FMDERR("failed allocating buffers %d\n",
2848 kfifo_alloc_rc);
2849 for (; i > -1; i--) {
2850 kfifo_free(&radio->data_buf[i]);
2851 kfree(radio);
2852 return -ENOMEM;
2853 }
2854 }
2855 }
2856
2857 mutex_init(&radio->lock);
2858 init_completion(&radio->sync_xfr_start);
2859 radio->tune_req = 0;
Ankur Nandwani8f972e52011-08-24 11:48:32 -07002860 radio->prev_trans_rds = 2;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002861 init_waitqueue_head(&radio->event_queue);
2862 init_waitqueue_head(&radio->read_queue);
2863
2864 video_set_drvdata(radio->videodev, radio);
2865
2866 if (NULL == video_get_drvdata(radio->videodev))
2867 FMDERR(": video_get_drvdata failed\n");
2868
2869 retval = video_register_device(radio->videodev, VFL_TYPE_RADIO,
2870 radio_nr);
2871 if (retval) {
2872 FMDERR(": Could not register video device\n");
2873 video_device_release(radio->videodev);
2874 for (; i > -1; i--)
2875 kfifo_free(&radio->data_buf[i]);
2876 kfree(radio);
2877 return retval;
2878 } else {
2879 priv_videodev = kzalloc(sizeof(struct video_device),
2880 GFP_KERNEL);
2881 memcpy(priv_videodev, radio->videodev,
2882 sizeof(struct video_device));
2883 }
2884 return 0;
2885}
2886
2887
2888static int __devexit iris_remove(struct platform_device *pdev)
2889{
2890 int i;
2891 struct iris_device *radio = platform_get_drvdata(pdev);
2892
2893 video_unregister_device(radio->videodev);
2894
2895 for (i = 0; i < IRIS_BUF_MAX; i++)
2896 kfifo_free(&radio->data_buf[i]);
2897
2898 kfree(radio);
2899
2900 platform_set_drvdata(pdev, NULL);
2901
2902 return 0;
2903}
2904
2905static struct platform_driver iris_driver = {
2906 .driver = {
2907 .owner = THIS_MODULE,
2908 .name = "iris_fm",
2909 },
2910 .remove = __devexit_p(iris_remove),
2911};
2912
2913static int __init iris_radio_init(void)
2914{
2915 return platform_driver_probe(&iris_driver, iris_probe);
2916}
2917module_init(iris_radio_init);
2918
2919static void __exit iris_radio_exit(void)
2920{
2921 platform_driver_unregister(&iris_driver);
2922}
2923module_exit(iris_radio_exit);
2924
2925MODULE_LICENSE("GPL v2");
2926MODULE_AUTHOR(DRIVER_AUTHOR);
2927MODULE_DESCRIPTION(DRIVER_DESC);