blob: 2a192db4031be85de1fcaa3f5c5d1696fe38ed21 [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;
Srinivasa Rao Uppala3c7a8eb2011-09-18 09:10:21 +053087 int search_on;
Ankur Nandwanid928d542011-08-11 13:15:41 -070088 unsigned int tone_freq;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070089 unsigned char g_scan_time;
90 unsigned int g_antenna;
91 unsigned int g_rds_grp_proc_ps;
92 enum iris_region_t region;
93 struct hci_fm_dbg_param_rsp st_dbg_param;
94 struct hci_ev_srch_list_compl srch_st_result;
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -070095 struct hci_fm_riva_poke riva_data_req;
96 struct hci_fm_ssbi_req ssbi_data_accs;
97 struct hci_fm_ssbi_peek ssbi_peek_reg;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070098};
99
100static struct video_device *priv_videodev;
101
102static struct v4l2_queryctrl iris_v4l2_queryctrl[] = {
103 {
104 .id = V4L2_CID_AUDIO_VOLUME,
105 .type = V4L2_CTRL_TYPE_INTEGER,
106 .name = "Volume",
107 .minimum = 0,
108 .maximum = 15,
109 .step = 1,
110 .default_value = 15,
111 },
112 {
113 .id = V4L2_CID_AUDIO_BALANCE,
114 .flags = V4L2_CTRL_FLAG_DISABLED,
115 },
116 {
117 .id = V4L2_CID_AUDIO_BASS,
118 .flags = V4L2_CTRL_FLAG_DISABLED,
119 },
120 {
121 .id = V4L2_CID_AUDIO_TREBLE,
122 .flags = V4L2_CTRL_FLAG_DISABLED,
123 },
124 {
125 .id = V4L2_CID_AUDIO_MUTE,
126 .type = V4L2_CTRL_TYPE_BOOLEAN,
127 .name = "Mute",
128 .minimum = 0,
129 .maximum = 1,
130 .step = 1,
131 .default_value = 1,
132 },
133 {
134 .id = V4L2_CID_AUDIO_LOUDNESS,
135 .flags = V4L2_CTRL_FLAG_DISABLED,
136 },
137 {
138 .id = V4L2_CID_PRIVATE_IRIS_SRCHMODE,
139 .type = V4L2_CTRL_TYPE_INTEGER,
140 .name = "Search mode",
141 .minimum = 0,
142 .maximum = 7,
143 .step = 1,
144 .default_value = 0,
145 },
146 {
147 .id = V4L2_CID_PRIVATE_IRIS_SCANDWELL,
148 .type = V4L2_CTRL_TYPE_INTEGER,
149 .name = "Search dwell time",
150 .minimum = 0,
151 .maximum = 7,
152 .step = 1,
153 .default_value = 0,
154 },
155 {
156 .id = V4L2_CID_PRIVATE_IRIS_SRCHON,
157 .type = V4L2_CTRL_TYPE_BOOLEAN,
158 .name = "Search on/off",
159 .minimum = 0,
160 .maximum = 1,
161 .step = 1,
162 .default_value = 1,
163
164 },
165 {
166 .id = V4L2_CID_PRIVATE_IRIS_STATE,
167 .type = V4L2_CTRL_TYPE_INTEGER,
168 .name = "radio 0ff/rx/tx/reset",
169 .minimum = 0,
170 .maximum = 3,
171 .step = 1,
172 .default_value = 1,
173
174 },
175 {
176 .id = V4L2_CID_PRIVATE_IRIS_REGION,
177 .type = V4L2_CTRL_TYPE_INTEGER,
178 .name = "radio standard",
179 .minimum = 0,
180 .maximum = 2,
181 .step = 1,
182 .default_value = 0,
183 },
184 {
185 .id = V4L2_CID_PRIVATE_IRIS_SIGNAL_TH,
186 .type = V4L2_CTRL_TYPE_INTEGER,
187 .name = "Signal Threshold",
188 .minimum = 0x80,
189 .maximum = 0x7F,
190 .step = 1,
191 .default_value = 0,
192 },
193 {
194 .id = V4L2_CID_PRIVATE_IRIS_SRCH_PTY,
195 .type = V4L2_CTRL_TYPE_INTEGER,
196 .name = "Search PTY",
197 .minimum = 0,
198 .maximum = 31,
199 .default_value = 0,
200 },
201 {
202 .id = V4L2_CID_PRIVATE_IRIS_SRCH_PI,
203 .type = V4L2_CTRL_TYPE_INTEGER,
204 .name = "Search PI",
205 .minimum = 0,
206 .maximum = 0xFF,
207 .default_value = 0,
208 },
209 {
210 .id = V4L2_CID_PRIVATE_IRIS_SRCH_CNT,
211 .type = V4L2_CTRL_TYPE_INTEGER,
212 .name = "Preset num",
213 .minimum = 0,
214 .maximum = 12,
215 .default_value = 0,
216 },
217 {
218 .id = V4L2_CID_PRIVATE_IRIS_EMPHASIS,
219 .type = V4L2_CTRL_TYPE_BOOLEAN,
220 .name = "Emphasis",
221 .minimum = 0,
222 .maximum = 1,
223 .default_value = 0,
224 },
225 {
226 .id = V4L2_CID_PRIVATE_IRIS_RDS_STD,
227 .type = V4L2_CTRL_TYPE_BOOLEAN,
228 .name = "RDS standard",
229 .minimum = 0,
230 .maximum = 1,
231 .default_value = 0,
232 },
233 {
234 .id = V4L2_CID_PRIVATE_IRIS_SPACING,
235 .type = V4L2_CTRL_TYPE_INTEGER,
236 .name = "Channel spacing",
237 .minimum = 0,
238 .maximum = 2,
239 .default_value = 0,
240 },
241 {
242 .id = V4L2_CID_PRIVATE_IRIS_RDSON,
243 .type = V4L2_CTRL_TYPE_BOOLEAN,
244 .name = "RDS on/off",
245 .minimum = 0,
246 .maximum = 1,
247 .default_value = 0,
248 },
249 {
250 .id = V4L2_CID_PRIVATE_IRIS_RDSGROUP_MASK,
251 .type = V4L2_CTRL_TYPE_INTEGER,
252 .name = "RDS group mask",
253 .minimum = 0,
254 .maximum = 0xFFFFFFFF,
255 .default_value = 0,
256 },
257 {
258 .id = V4L2_CID_PRIVATE_IRIS_RDSGROUP_PROC,
259 .type = V4L2_CTRL_TYPE_INTEGER,
260 .name = "RDS processing",
261 .minimum = 0,
262 .maximum = 0xFF,
263 .default_value = 0,
264 },
265 {
266 .id = V4L2_CID_PRIVATE_IRIS_RDSD_BUF,
267 .type = V4L2_CTRL_TYPE_INTEGER,
268 .name = "RDS data groups to buffer",
269 .minimum = 1,
270 .maximum = 21,
271 .default_value = 0,
272 },
273 {
274 .id = V4L2_CID_PRIVATE_IRIS_PSALL,
275 .type = V4L2_CTRL_TYPE_BOOLEAN,
276 .name = "pass all ps strings",
277 .minimum = 0,
278 .maximum = 1,
279 .default_value = 0,
280 },
281 {
282 .id = V4L2_CID_PRIVATE_IRIS_LP_MODE,
283 .type = V4L2_CTRL_TYPE_BOOLEAN,
284 .name = "Low power mode",
285 .minimum = 0,
286 .maximum = 1,
287 .default_value = 0,
288 },
289 {
290 .id = V4L2_CID_PRIVATE_IRIS_ANTENNA,
291 .type = V4L2_CTRL_TYPE_BOOLEAN,
292 .name = "headset/internal",
293 .minimum = 0,
294 .maximum = 1,
295 .default_value = 0,
296 },
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700297 {
298 .id = V4L2_CID_PRIVATE_IRIS_TX_SETPSREPEATCOUNT,
299 .type = V4L2_CTRL_TYPE_INTEGER,
300 .name = "Set PS REPEATCOUNT",
301 .minimum = 0,
302 .maximum = 15,
303 },
304 {
305 .id = V4L2_CID_PRIVATE_IRIS_STOP_RDS_TX_PS_NAME,
306 .type = V4L2_CTRL_TYPE_BOOLEAN,
307 .name = "Stop PS NAME",
308 .minimum = 0,
309 .maximum = 1,
310 },
311 {
312 .id = V4L2_CID_PRIVATE_IRIS_STOP_RDS_TX_RT,
313 .type = V4L2_CTRL_TYPE_BOOLEAN,
314 .name = "Stop RT",
315 .minimum = 0,
316 .maximum = 1,
317 },
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -0700318 {
319 .id = V4L2_CID_PRIVATE_IRIS_SOFT_MUTE,
320 .type = V4L2_CTRL_TYPE_BOOLEAN,
321 .name = "Soft Mute",
322 .minimum = 0,
323 .maximum = 1,
324 },
325 {
326 .id = V4L2_CID_PRIVATE_IRIS_RIVA_ACCS_ADDR,
327 .type = V4L2_CTRL_TYPE_BOOLEAN,
328 .name = "Riva addr",
Srinivasa Rao Uppala0f4098f2011-09-06 16:46:28 +0530329 .minimum = 0x3180000,
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -0700330 .maximum = 0x31E0004,
331 },
332 {
333 .id = V4L2_CID_PRIVATE_IRIS_RIVA_ACCS_LEN,
334 .type = V4L2_CTRL_TYPE_INTEGER,
335 .name = "Data len",
336 .minimum = 0,
337 .maximum = 0xFF,
338 },
339 {
340 .id = V4L2_CID_PRIVATE_IRIS_RIVA_PEEK,
341 .type = V4L2_CTRL_TYPE_BOOLEAN,
342 .name = "Riva peek",
343 .minimum = 0,
344 .maximum = 1,
345 },
346 {
347 .id = V4L2_CID_PRIVATE_IRIS_RIVA_POKE,
348 .type = V4L2_CTRL_TYPE_INTEGER,
349 .name = "Riva poke",
Srinivasa Rao Uppala0f4098f2011-09-06 16:46:28 +0530350 .minimum = 0x3180000,
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -0700351 .maximum = 0x31E0004,
352 },
353 {
354 .id = V4L2_CID_PRIVATE_IRIS_SSBI_ACCS_ADDR,
355 .type = V4L2_CTRL_TYPE_INTEGER,
356 .name = "Ssbi addr",
Srinivasa Rao Uppala0f4098f2011-09-06 16:46:28 +0530357 .minimum = 0x280,
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -0700358 .maximum = 0x37F,
359 },
360 {
361 .id = V4L2_CID_PRIVATE_IRIS_SSBI_PEEK,
362 .type = V4L2_CTRL_TYPE_INTEGER,
363 .name = "Ssbi peek",
364 .minimum = 0,
365 .maximum = 0x37F,
366 },
367 {
368 .id = V4L2_CID_PRIVATE_IRIS_SSBI_POKE,
369 .type = V4L2_CTRL_TYPE_INTEGER,
370 .name = "ssbi poke",
371 .minimum = 0x01,
372 .maximum = 0xFF,
373 },
374 {
375 .id = V4L2_CID_PRIVATE_IRIS_HLSI,
376 .type = V4L2_CTRL_TYPE_INTEGER,
377 .name = "set hlsi",
378 .minimum = 0,
379 .maximum = 2,
380 },
Srinivasa Rao Uppala0f4098f2011-09-06 16:46:28 +0530381 {
382 .id = V4L2_CID_PRIVATE_IRIS_RDS_GRP_COUNTERS,
383 .type = V4L2_CTRL_TYPE_BOOLEAN,
384 .name = "RDS grp",
385 .minimum = 0,
386 .maximum = 1,
387 },
388 {
389 .id = V4L2_CID_PRIVATE_IRIS_SET_NOTCH_FILTER,
390 .type = V4L2_CTRL_TYPE_INTEGER,
391 .name = "Notch filter",
392 .minimum = 0,
393 .maximum = 2,
394 },
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700395
396};
397
398static void iris_q_event(struct iris_device *radio,
399 enum iris_evt_t event)
400{
401 struct kfifo *data_b = &radio->data_buf[IRIS_BUF_EVENTS];
402 unsigned char evt = event;
403 if (kfifo_in_locked(data_b, &evt, 1, &radio->buf_lock[IRIS_BUF_EVENTS]))
404 wake_up_interruptible(&radio->event_queue);
405}
406
407static int hci_send_frame(struct sk_buff *skb)
408{
409 struct radio_hci_dev *hdev = (struct radio_hci_dev *) skb->dev;
410
411 if (!hdev) {
412 kfree_skb(skb);
413 return -ENODEV;
414 }
415
416 __net_timestamp(skb);
417
418 skb_orphan(skb);
419 return hdev->send(skb);
420}
421
422static void radio_hci_cmd_task(unsigned long arg)
423{
424 struct radio_hci_dev *hdev = (struct radio_hci_dev *) arg;
425 struct sk_buff *skb;
426 if (!(atomic_read(&hdev->cmd_cnt))
427 && time_after(jiffies, hdev->cmd_last_tx + HZ)) {
428 FMDERR("%s command tx timeout", hdev->name);
429 atomic_set(&hdev->cmd_cnt, 1);
430 }
431
432 skb = skb_dequeue(&hdev->cmd_q);
433 if (atomic_read(&hdev->cmd_cnt) && skb) {
434 kfree_skb(hdev->sent_cmd);
435 hdev->sent_cmd = skb_clone(skb, GFP_ATOMIC);
436 if (hdev->sent_cmd) {
437 atomic_dec(&hdev->cmd_cnt);
438 hci_send_frame(skb);
439 hdev->cmd_last_tx = jiffies;
440 } else {
441 skb_queue_head(&hdev->cmd_q, skb);
442 tasklet_schedule(&hdev->cmd_task);
443 }
444 }
445
446}
447
448static void radio_hci_rx_task(unsigned long arg)
449{
450 struct radio_hci_dev *hdev = (struct radio_hci_dev *) arg;
451 struct sk_buff *skb;
452
453 read_lock(&hci_task_lock);
454
455 skb = skb_dequeue(&hdev->rx_q);
456 radio_hci_event_packet(hdev, skb);
457
458 read_unlock(&hci_task_lock);
459}
460
461int radio_hci_register_dev(struct radio_hci_dev *hdev)
462{
463 struct iris_device *radio = video_get_drvdata(video_get_dev());
464 if (!radio) {
465 FMDERR(":radio is null");
466 return -EINVAL;
467 }
468
469 if (!hdev) {
470 FMDERR("hdev is null");
471 return -EINVAL;
472 }
473
474 hdev->flags = 0;
475
476 tasklet_init(&hdev->cmd_task, radio_hci_cmd_task, (unsigned long)
477 hdev);
478 tasklet_init(&hdev->rx_task, radio_hci_rx_task, (unsigned long)
479 hdev);
480
481 init_waitqueue_head(&hdev->req_wait_q);
482
483 skb_queue_head_init(&hdev->rx_q);
484 skb_queue_head_init(&hdev->cmd_q);
485 skb_queue_head_init(&hdev->raw_q);
486
487 if (!radio)
488 FMDERR(":radio is null");
489
490 radio->fm_hdev = hdev;
491
492 return 0;
493}
494EXPORT_SYMBOL(radio_hci_register_dev);
495
496int radio_hci_unregister_dev(struct radio_hci_dev *hdev)
497{
498 struct iris_device *radio = video_get_drvdata(video_get_dev());
499 if (!radio) {
500 FMDERR(":radio is null");
501 return -EINVAL;
502 }
503
504 tasklet_kill(&hdev->rx_task);
505 tasklet_kill(&hdev->cmd_task);
506 skb_queue_purge(&hdev->rx_q);
507 skb_queue_purge(&hdev->cmd_q);
508 skb_queue_purge(&hdev->raw_q);
509 kfree(radio->fm_hdev);
510 kfree(radio->videodev);
511
512 return 0;
513}
514EXPORT_SYMBOL(radio_hci_unregister_dev);
515
516int radio_hci_recv_frame(struct sk_buff *skb)
517{
518 struct radio_hci_dev *hdev = (struct radio_hci_dev *) skb->dev;
519 if (!hdev) {
520 FMDERR("%s hdev is null while receiving frame", hdev->name);
521 kfree_skb(skb);
522 return -ENXIO;
523 }
524
525 __net_timestamp(skb);
526
527 radio_hci_event_packet(hdev, skb);
Srinivasa Rao Uppalacf3a8112011-09-22 21:02:02 +0530528 kfree_skb(skb);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700529 return 0;
530}
531EXPORT_SYMBOL(radio_hci_recv_frame);
532
533int radio_hci_send_cmd(struct radio_hci_dev *hdev, __u16 opcode, __u32 plen,
534 void *param)
535{
536 int len = RADIO_HCI_COMMAND_HDR_SIZE + plen;
537 struct radio_hci_command_hdr *hdr;
538 struct sk_buff *skb;
539 int ret = 0;
540
541 skb = alloc_skb(len, GFP_ATOMIC);
542 if (!skb) {
543 FMDERR("%s no memory for command", hdev->name);
544 return -ENOMEM;
545 }
546
547 hdr = (struct radio_hci_command_hdr *) skb_put(skb,
548 RADIO_HCI_COMMAND_HDR_SIZE);
549 hdr->opcode = cpu_to_le16(opcode);
550 hdr->plen = plen;
551
552 if (plen)
553 memcpy(skb_put(skb, plen), param, plen);
554
555 skb->dev = (void *) hdev;
556
557 ret = hci_send_frame(skb);
558
559 return ret;
560}
561EXPORT_SYMBOL(radio_hci_send_cmd);
562
563static int hci_fm_enable_recv_req(struct radio_hci_dev *hdev,
564 unsigned long param)
565{
566 __u16 opcode = 0;
567
568 opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
569 HCI_OCF_FM_ENABLE_RECV_REQ);
570 return radio_hci_send_cmd(hdev, opcode, 0, NULL);
571}
572
Ankur Nandwanid928d542011-08-11 13:15:41 -0700573static int hci_fm_tone_generator(struct radio_hci_dev *hdev,
574 unsigned long param)
575{
576 struct iris_device *radio = video_get_drvdata(video_get_dev());
577 __u16 opcode = 0;
578
579 opcode = hci_opcode_pack(HCI_OGF_FM_DIAGNOSTIC_CMD_REQ,
580 HCI_FM_SET_INTERNAL_TONE_GENRATOR);
581 return radio_hci_send_cmd(hdev, opcode,
582 sizeof(radio->tone_freq), &radio->tone_freq);
583}
584
585static int hci_fm_enable_trans_req(struct radio_hci_dev *hdev,
586 unsigned long param)
587{
588 __u16 opcode = 0;
589
590 opcode = hci_opcode_pack(HCI_OGF_FM_TRANS_CTRL_CMD_REQ,
591 HCI_OCF_FM_ENABLE_TRANS_REQ);
592 return radio_hci_send_cmd(hdev, opcode, 0, NULL);
593}
594
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700595static int hci_fm_disable_recv_req(struct radio_hci_dev *hdev,
596 unsigned long param)
597{
598 __u16 opcode = 0;
599
600 opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
601 HCI_OCF_FM_DISABLE_RECV_REQ);
602 return radio_hci_send_cmd(hdev, opcode, 0, NULL);
603}
604
Ankur Nandwanid928d542011-08-11 13:15:41 -0700605static int hci_fm_disable_trans_req(struct radio_hci_dev *hdev,
606 unsigned long param)
607{
608 __u16 opcode = 0;
609
610 opcode = hci_opcode_pack(HCI_OGF_FM_TRANS_CTRL_CMD_REQ,
611 HCI_OCF_FM_DISABLE_TRANS_REQ);
612 return radio_hci_send_cmd(hdev, opcode, 0, NULL);
613}
614
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700615static int hci_get_fm_recv_conf_req(struct radio_hci_dev *hdev,
616 unsigned long param)
617{
618 __u16 opcode = 0;
619
620 opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
621 HCI_OCF_FM_GET_RECV_CONF_REQ);
622 return radio_hci_send_cmd(hdev, opcode, 0, NULL);
623}
624
625static int hci_set_fm_recv_conf_req(struct radio_hci_dev *hdev,
626 unsigned long param)
627{
628 __u16 opcode = 0;
629
630 struct hci_fm_recv_conf_req *recv_conf_req =
631 (struct hci_fm_recv_conf_req *) param;
632
633 opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
634 HCI_OCF_FM_SET_RECV_CONF_REQ);
635 return radio_hci_send_cmd(hdev, opcode, sizeof((*recv_conf_req)),
636 recv_conf_req);
637}
638
Ankur Nandwanid928d542011-08-11 13:15:41 -0700639static int hci_set_fm_trans_conf_req(struct radio_hci_dev *hdev,
640 unsigned long param)
641{
642 __u16 opcode = 0;
643
644 struct hci_fm_trans_conf_req_struct *trans_conf_req =
645 (struct hci_fm_trans_conf_req_struct *) param;
646
647 opcode = hci_opcode_pack(HCI_OGF_FM_TRANS_CTRL_CMD_REQ,
648 HCI_OCF_FM_SET_TRANS_CONF_REQ);
649 return radio_hci_send_cmd(hdev, opcode, sizeof((*trans_conf_req)),
650 trans_conf_req);
651}
652
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700653static int hci_fm_get_station_param_req(struct radio_hci_dev *hdev,
654 unsigned long param)
655{
656 __u16 opcode = 0;
657
658 opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
659 HCI_OCF_FM_GET_STATION_PARAM_REQ);
660 return radio_hci_send_cmd(hdev, opcode, 0, NULL);
661}
662
663static int hci_set_fm_mute_mode_req(struct radio_hci_dev *hdev,
664 unsigned long param)
665{
666 __u16 opcode = 0;
667 struct hci_fm_mute_mode_req *mute_mode_req =
668 (struct hci_fm_mute_mode_req *) param;
669
670 opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
671 HCI_OCF_FM_SET_MUTE_MODE_REQ);
672 return radio_hci_send_cmd(hdev, opcode, sizeof((*mute_mode_req)),
673 mute_mode_req);
674}
675
Ankur Nandwanid928d542011-08-11 13:15:41 -0700676
677static int hci_trans_ps_req(struct radio_hci_dev *hdev,
678 unsigned long param)
679{
680 __u16 opcode = 0;
681 struct hci_fm_tx_ps *tx_ps_req =
682 (struct hci_fm_tx_ps *) param;
683
684 opcode = hci_opcode_pack(HCI_OGF_FM_TRANS_CTRL_CMD_REQ,
685 HCI_OCF_FM_RDS_PS_REQ);
686
687 return radio_hci_send_cmd(hdev, opcode, sizeof((*tx_ps_req)),
688 tx_ps_req);
689}
690
691static int hci_trans_rt_req(struct radio_hci_dev *hdev,
692 unsigned long param)
693{
694 __u16 opcode = 0;
695 struct hci_fm_tx_rt *tx_rt_req =
696 (struct hci_fm_tx_rt *) param;
697
698 opcode = hci_opcode_pack(HCI_OGF_FM_TRANS_CTRL_CMD_REQ,
699 HCI_OCF_FM_RDS_RT_REQ);
700
701 return radio_hci_send_cmd(hdev, opcode, sizeof((*tx_rt_req)),
702 tx_rt_req);
703}
704
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700705static int hci_set_fm_stereo_mode_req(struct radio_hci_dev *hdev,
706 unsigned long param)
707{
708 __u16 opcode = 0;
709 struct hci_fm_stereo_mode_req *stereo_mode_req =
710 (struct hci_fm_stereo_mode_req *) param;
711 opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
712 HCI_OCF_FM_SET_STEREO_MODE_REQ);
713 return radio_hci_send_cmd(hdev, opcode, sizeof((*stereo_mode_req)),
714 stereo_mode_req);
715}
716
717static int hci_fm_set_antenna_req(struct radio_hci_dev *hdev,
718 unsigned long param)
719{
720 __u16 opcode = 0;
721
722 __u8 antenna = param;
723
724 opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
725 HCI_OCF_FM_SET_ANTENNA);
726 return radio_hci_send_cmd(hdev, opcode, sizeof(antenna), &antenna);
727}
728
729static int hci_fm_set_sig_threshold_req(struct radio_hci_dev *hdev,
730 unsigned long param)
731{
732 __u16 opcode = 0;
733
734 __u8 sig_threshold = param;
735
Venkateshwarlu Domakonda1c67b752011-10-17 13:05:48 +0530736 opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700737 HCI_OCF_FM_SET_SIGNAL_THRESHOLD);
738 return radio_hci_send_cmd(hdev, opcode, sizeof(sig_threshold),
739 &sig_threshold);
740}
741
742static int hci_fm_get_sig_threshold_req(struct radio_hci_dev *hdev,
743 unsigned long param)
744{
745 __u16 opcode = 0;
746
Venkateshwarlu Domakonda1c67b752011-10-17 13:05:48 +0530747 opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700748 HCI_OCF_FM_GET_SIGNAL_THRESHOLD);
749 return radio_hci_send_cmd(hdev, opcode, 0, NULL);
750}
751
752static int hci_fm_get_program_service_req(struct radio_hci_dev *hdev,
753 unsigned long param)
754{
755 __u16 opcode = 0;
756
Venkateshwarlu Domakonda1c67b752011-10-17 13:05:48 +0530757 opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700758 HCI_OCF_FM_GET_PROGRAM_SERVICE_REQ);
759 return radio_hci_send_cmd(hdev, opcode, 0, NULL);
760}
761
762static int hci_fm_get_radio_text_req(struct radio_hci_dev *hdev,
763 unsigned long param)
764{
765 __u16 opcode = 0;
766
Venkateshwarlu Domakonda1c67b752011-10-17 13:05:48 +0530767 opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700768 HCI_OCF_FM_GET_RADIO_TEXT_REQ);
769 return radio_hci_send_cmd(hdev, opcode, 0, NULL);
770}
771
772static int hci_fm_get_af_list_req(struct radio_hci_dev *hdev,
773 unsigned long param)
774{
775 __u16 opcode = 0;
776
Venkateshwarlu Domakonda1c67b752011-10-17 13:05:48 +0530777 opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700778 HCI_OCF_FM_GET_AF_LIST_REQ);
779 return radio_hci_send_cmd(hdev, opcode, 0, NULL);
780}
781
782static int hci_fm_search_stations_req(struct radio_hci_dev *hdev,
783 unsigned long param)
784{
785 __u16 opcode = 0;
786 struct hci_fm_search_station_req *srch_stations =
787 (struct hci_fm_search_station_req *) param;
788
789 opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
790 HCI_OCF_FM_SEARCH_STATIONS);
791 return radio_hci_send_cmd(hdev, opcode, sizeof((*srch_stations)),
792 srch_stations);
793}
794
795static int hci_fm_srch_rds_stations_req(struct radio_hci_dev *hdev,
796 unsigned long param)
797{
798 __u16 opcode = 0;
799 struct hci_fm_search_rds_station_req *srch_stations =
800 (struct hci_fm_search_rds_station_req *) param;
801
802 opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
803 HCI_OCF_FM_SEARCH_RDS_STATIONS);
804 return radio_hci_send_cmd(hdev, opcode, sizeof((*srch_stations)),
805 srch_stations);
806}
807
808static int hci_fm_srch_station_list_req(struct radio_hci_dev *hdev,
809 unsigned long param)
810{
811 __u16 opcode = 0;
812 struct hci_fm_search_station_list_req *srch_list =
813 (struct hci_fm_search_station_list_req *) param;
814
815 opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
816 HCI_OCF_FM_SEARCH_STATIONS_LIST);
817 return radio_hci_send_cmd(hdev, opcode, sizeof((*srch_list)),
818 srch_list);
819}
820
821static int hci_fm_cancel_search_req(struct radio_hci_dev *hdev,
822 unsigned long param)
823{
824 __u16 opcode = 0;
825
826 opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
827 HCI_OCF_FM_CANCEL_SEARCH);
828 return radio_hci_send_cmd(hdev, opcode, 0, NULL);
829}
830
831static int hci_fm_rds_grp_process_req(struct radio_hci_dev *hdev,
832 unsigned long param)
833{
834 __u16 opcode = 0;
835
836 __u32 fm_grps_process = param;
837
838 opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
839 HCI_OCF_FM_RDS_GRP_PROCESS);
840 return radio_hci_send_cmd(hdev, opcode, sizeof(fm_grps_process),
841 &fm_grps_process);
842}
843
844static int hci_fm_tune_station_req(struct radio_hci_dev *hdev,
845 unsigned long param)
846{
847 __u16 opcode = 0;
848
849 __u32 tune_freq = param;
850
851 opcode = hci_opcode_pack(HCI_OGF_FM_COMMON_CTRL_CMD_REQ,
852 HCI_OCF_FM_TUNE_STATION_REQ);
853 return radio_hci_send_cmd(hdev, opcode, sizeof(tune_freq), &tune_freq);
854}
855
856static int hci_def_data_read_req(struct radio_hci_dev *hdev,
857 unsigned long param)
858{
859 __u16 opcode = 0;
860 struct hci_fm_def_data_rd_req *def_data_rd =
861 (struct hci_fm_def_data_rd_req *) param;
862
863 opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
864 HCI_OCF_FM_DEFAULT_DATA_READ);
865 return radio_hci_send_cmd(hdev, opcode, sizeof((*def_data_rd)),
866 def_data_rd);
867}
868
869static int hci_def_data_write_req(struct radio_hci_dev *hdev,
870 unsigned long param)
871{
872 __u16 opcode = 0;
873 struct hci_fm_def_data_wr_req *def_data_wr =
874 (struct hci_fm_def_data_wr_req *) param;
875
876 opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
877 HCI_OCF_FM_DEFAULT_DATA_WRITE);
878 return radio_hci_send_cmd(hdev, opcode, sizeof((*def_data_wr)),
879 def_data_wr);
880}
881
Srinivasa Rao Uppala0f4098f2011-09-06 16:46:28 +0530882static int hci_set_notch_filter_req(struct radio_hci_dev *hdev,
883 unsigned long param)
884{
885 __u16 opcode = 0;
886 __u8 notch_filter_val = param;
887
888 opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
889 HCI_OCF_FM_EN_NOTCH_CTRL);
890 return radio_hci_send_cmd(hdev, opcode, sizeof(notch_filter_val),
891 &notch_filter_val);
892}
893
894
895
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700896static int hci_fm_reset_req(struct radio_hci_dev *hdev, unsigned long param)
897{
898 __u16 opcode = 0;
899
900 opcode = hci_opcode_pack(HCI_OGF_FM_COMMON_CTRL_CMD_REQ,
901 HCI_OCF_FM_RESET);
902 return radio_hci_send_cmd(hdev, opcode, 0, NULL);
903}
904
905static int hci_fm_get_feature_lists_req(struct radio_hci_dev *hdev,
906 unsigned long param)
907{
908 __u16 opcode = 0;
909
910 opcode = hci_opcode_pack(HCI_OGF_FM_COMMON_CTRL_CMD_REQ,
911 HCI_OCF_FM_GET_FEATURE_LIST);
912 return radio_hci_send_cmd(hdev, opcode, 0, NULL);
913}
914
915static int hci_fm_do_calibration_req(struct radio_hci_dev *hdev,
916 unsigned long param)
917{
918 __u16 opcode = 0;
919
920 __u8 mode = param;
921
922 opcode = hci_opcode_pack(HCI_OGF_FM_COMMON_CTRL_CMD_REQ,
923 HCI_OCF_FM_DO_CALIBRATION);
924 return radio_hci_send_cmd(hdev, opcode, sizeof(mode), &mode);
925}
926
927static int hci_read_grp_counters_req(struct radio_hci_dev *hdev,
928 unsigned long param)
929{
930 __u16 opcode = 0;
931
932 __u8 reset_counters = param;
Srinivasa Rao Uppala0f4098f2011-09-06 16:46:28 +0530933 opcode = hci_opcode_pack(HCI_OGF_FM_STATUS_PARAMETERS_CMD_REQ,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700934 HCI_OCF_FM_READ_GRP_COUNTERS);
935 return radio_hci_send_cmd(hdev, opcode, sizeof(reset_counters),
936 &reset_counters);
937}
938
939static int hci_peek_data_req(struct radio_hci_dev *hdev, unsigned long param)
940{
941 __u16 opcode = 0;
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -0700942 struct hci_fm_riva_data *peek_data = (struct hci_fm_riva_data *)param;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700943
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -0700944 opcode = hci_opcode_pack(HCI_OGF_FM_DIAGNOSTIC_CMD_REQ,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700945 HCI_OCF_FM_PEEK_DATA);
946 return radio_hci_send_cmd(hdev, opcode, sizeof((*peek_data)),
947 peek_data);
948}
949
950static int hci_poke_data_req(struct radio_hci_dev *hdev, unsigned long param)
951{
952 __u16 opcode = 0;
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -0700953 struct hci_fm_riva_poke *poke_data = (struct hci_fm_riva_poke *) param;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700954
Srinivasa Rao Uppala6cc0e322011-08-12 10:54:48 -0700955 opcode = hci_opcode_pack(HCI_OGF_FM_DIAGNOSTIC_CMD_REQ,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700956 HCI_OCF_FM_POKE_DATA);
957 return radio_hci_send_cmd(hdev, opcode, sizeof((*poke_data)),
958 poke_data);
959}
960
961static int hci_ssbi_peek_reg_req(struct radio_hci_dev *hdev,
962 unsigned long param)
963{
964 __u16 opcode = 0;
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -0700965 struct hci_fm_ssbi_peek *ssbi_peek = (struct hci_fm_ssbi_peek *) param;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700966
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -0700967 opcode = hci_opcode_pack(HCI_OGF_FM_DIAGNOSTIC_CMD_REQ,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700968 HCI_OCF_FM_SSBI_PEEK_REG);
969 return radio_hci_send_cmd(hdev, opcode, sizeof((*ssbi_peek)),
970 ssbi_peek);
971}
972
973static int hci_ssbi_poke_reg_req(struct radio_hci_dev *hdev,
974 unsigned long param)
975{
976 __u16 opcode = 0;
977 struct hci_fm_ssbi_req *ssbi_poke = (struct hci_fm_ssbi_req *) param;
978
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -0700979 opcode = hci_opcode_pack(HCI_OGF_FM_DIAGNOSTIC_CMD_REQ,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700980 HCI_OCF_FM_SSBI_POKE_REG);
981 return radio_hci_send_cmd(hdev, opcode, sizeof((*ssbi_poke)),
982 ssbi_poke);
983}
984
985static int hci_fm_get_station_dbg_param_req(struct radio_hci_dev *hdev,
986 unsigned long param)
987{
988 __u16 opcode = 0;
989
Venkateshwarlu Domakonda1c67b752011-10-17 13:05:48 +0530990 opcode = hci_opcode_pack(HCI_OGF_FM_DIAGNOSTIC_CMD_REQ,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700991 HCI_OCF_FM_STATION_DBG_PARAM);
992 return radio_hci_send_cmd(hdev, opcode, 0, NULL);
993}
994
995static int radio_hci_err(__u16 code)
996{
997 switch (code) {
998 case 0:
999 return 0;
1000 case 0x01:
1001 return -EBADRQC;
1002 case 0x02:
1003 return -ENOTCONN;
1004 case 0x03:
1005 return -EIO;
1006 case 0x07:
1007 return -ENOMEM;
1008 case 0x0c:
1009 return -EBUSY;
1010 case 0x11:
1011 return -EOPNOTSUPP;
1012 case 0x12:
1013 return -EINVAL;
1014 default:
1015 return -ENOSYS;
1016 }
1017}
1018
1019static int __radio_hci_request(struct radio_hci_dev *hdev,
1020 int (*req)(struct radio_hci_dev *hdev,
1021 unsigned long param),
1022 unsigned long param, __u32 timeout)
1023{
1024 int err = 0;
1025
1026 DECLARE_WAITQUEUE(wait, current);
1027
1028 hdev->req_status = HCI_REQ_PEND;
1029
1030 add_wait_queue(&hdev->req_wait_q, &wait);
1031 set_current_state(TASK_INTERRUPTIBLE);
1032
1033 err = req(hdev, param);
1034
1035 schedule_timeout(timeout);
1036
1037 remove_wait_queue(&hdev->req_wait_q, &wait);
1038
1039 if (signal_pending(current))
1040 return -EINTR;
1041
1042 switch (hdev->req_status) {
1043 case HCI_REQ_DONE:
1044 case HCI_REQ_STATUS:
1045 err = radio_hci_err(hdev->req_result);
1046 break;
1047
1048 case HCI_REQ_CANCELED:
1049 err = -hdev->req_result;
1050 break;
1051
1052 default:
1053 err = -ETIMEDOUT;
1054 break;
1055 }
1056
1057 hdev->req_status = hdev->req_result = 0;
1058
1059 return err;
1060}
1061
1062static inline int radio_hci_request(struct radio_hci_dev *hdev,
1063 int (*req)(struct
1064 radio_hci_dev * hdev, unsigned long param),
1065 unsigned long param, __u32 timeout)
1066{
1067 int ret = 0;
1068
1069 ret = __radio_hci_request(hdev, req, param, timeout);
1070
1071 return ret;
1072}
1073
1074static int hci_set_fm_recv_conf(struct hci_fm_recv_conf_req *arg,
1075 struct radio_hci_dev *hdev)
1076{
1077 int ret = 0;
1078 struct hci_fm_recv_conf_req *set_recv_conf = arg;
1079
1080 ret = radio_hci_request(hdev, hci_set_fm_recv_conf_req, (unsigned
1081 long)set_recv_conf, RADIO_HCI_TIMEOUT);
1082
1083 return ret;
1084}
1085
Ankur Nandwanid928d542011-08-11 13:15:41 -07001086static int hci_set_fm_trans_conf(struct hci_fm_trans_conf_req_struct *arg,
1087 struct radio_hci_dev *hdev)
1088{
1089 int ret = 0;
1090 struct hci_fm_trans_conf_req_struct *set_trans_conf = arg;
1091
1092 ret = radio_hci_request(hdev, hci_set_fm_trans_conf_req, (unsigned
1093 long)set_trans_conf, RADIO_HCI_TIMEOUT);
1094
1095 return ret;
1096}
1097
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001098static int hci_fm_tune_station(__u32 *arg, struct radio_hci_dev *hdev)
1099{
1100 int ret = 0;
1101 __u32 tune_freq = *arg;
1102
1103 ret = radio_hci_request(hdev, hci_fm_tune_station_req, tune_freq,
1104 RADIO_HCI_TIMEOUT);
1105
1106 return ret;
1107}
1108
1109static int hci_set_fm_mute_mode(struct hci_fm_mute_mode_req *arg,
1110 struct radio_hci_dev *hdev)
1111{
1112 int ret = 0;
1113 struct hci_fm_mute_mode_req *set_mute_conf = arg;
1114
1115 ret = radio_hci_request(hdev, hci_set_fm_mute_mode_req, (unsigned
1116 long)set_mute_conf, RADIO_HCI_TIMEOUT);
1117
1118 return ret;
1119}
1120
1121static int hci_set_fm_stereo_mode(struct hci_fm_stereo_mode_req *arg,
1122 struct radio_hci_dev *hdev)
1123{
1124 int ret = 0;
1125 struct hci_fm_stereo_mode_req *set_stereo_conf = arg;
1126
1127 ret = radio_hci_request(hdev, hci_set_fm_stereo_mode_req, (unsigned
1128 long)set_stereo_conf, RADIO_HCI_TIMEOUT);
1129
1130 return ret;
1131}
1132
1133static int hci_fm_set_antenna(__u8 *arg, struct radio_hci_dev *hdev)
1134{
1135 int ret = 0;
1136 __u8 antenna = *arg;
1137
1138 ret = radio_hci_request(hdev, hci_fm_set_antenna_req, antenna,
1139 RADIO_HCI_TIMEOUT);
1140
1141 return ret;
1142}
1143
1144static int hci_fm_set_signal_threshold(__u8 *arg,
1145 struct radio_hci_dev *hdev)
1146{
1147 int ret = 0;
1148 __u8 sig_threshold = *arg;
1149
1150 ret = radio_hci_request(hdev, hci_fm_set_sig_threshold_req,
1151 sig_threshold, RADIO_HCI_TIMEOUT);
1152
1153 return ret;
1154}
1155
1156static int hci_fm_search_stations(struct hci_fm_search_station_req *arg,
1157 struct radio_hci_dev *hdev)
1158{
1159 int ret = 0;
1160 struct hci_fm_search_station_req *srch_stations = arg;
1161
1162 ret = radio_hci_request(hdev, hci_fm_search_stations_req, (unsigned
1163 long)srch_stations, RADIO_HCI_TIMEOUT);
1164
1165 return ret;
1166}
1167
1168static int hci_fm_search_rds_stations(struct hci_fm_search_rds_station_req *arg,
1169 struct radio_hci_dev *hdev)
1170{
1171 int ret = 0;
1172 struct hci_fm_search_rds_station_req *srch_stations = arg;
1173
1174 ret = radio_hci_request(hdev, hci_fm_srch_rds_stations_req, (unsigned
1175 long)srch_stations, RADIO_HCI_TIMEOUT);
1176
1177 return ret;
1178}
1179
1180static int hci_fm_search_station_list
1181 (struct hci_fm_search_station_list_req *arg,
1182 struct radio_hci_dev *hdev)
1183{
1184 int ret = 0;
1185 struct hci_fm_search_station_list_req *srch_list = arg;
1186
1187 ret = radio_hci_request(hdev, hci_fm_srch_station_list_req, (unsigned
1188 long)srch_list, RADIO_HCI_TIMEOUT);
1189
1190 return ret;
1191}
1192
1193static int hci_fm_rds_grp(struct hci_fm_rds_grp_req *arg,
1194 struct radio_hci_dev *hdev)
1195{
1196 return 0;
1197}
1198
1199static int hci_fm_rds_grps_process(__u32 *arg, struct radio_hci_dev *hdev)
1200{
1201 int ret = 0;
1202 __u32 fm_grps_process = *arg;
1203
1204 ret = radio_hci_request(hdev, hci_fm_rds_grp_process_req,
1205 fm_grps_process, RADIO_HCI_TIMEOUT);
1206
1207 return ret;
1208}
1209
1210int hci_def_data_read(struct hci_fm_def_data_rd_req *arg,
1211 struct radio_hci_dev *hdev)
1212{
1213 int ret = 0;
1214 struct hci_fm_def_data_rd_req *def_data_rd = arg;
1215
1216 ret = radio_hci_request(hdev, hci_def_data_read_req, (unsigned
1217 long)def_data_rd, RADIO_HCI_TIMEOUT);
1218
1219 return ret;
1220}
1221
1222int hci_def_data_write(struct hci_fm_def_data_wr_req *arg,
1223 struct radio_hci_dev *hdev)
1224{
1225 int ret = 0;
1226 struct hci_fm_def_data_wr_req *def_data_wr = arg;
1227
1228 ret = radio_hci_request(hdev, hci_def_data_write_req, (unsigned
1229 long)def_data_wr, RADIO_HCI_TIMEOUT);
1230
1231 return ret;
1232}
1233
1234int hci_fm_do_calibration(__u8 *arg, struct radio_hci_dev *hdev)
1235{
1236 int ret = 0;
1237 __u8 mode = *arg;
1238
1239 ret = radio_hci_request(hdev, hci_fm_do_calibration_req, mode,
1240 RADIO_HCI_TIMEOUT);
1241
1242 return ret;
1243}
1244
Srinivasa Rao Uppala0f4098f2011-09-06 16:46:28 +05301245static int hci_read_grp_counters(__u8 *arg, struct radio_hci_dev *hdev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001246{
1247 int ret = 0;
1248 __u8 reset_counters = *arg;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001249 ret = radio_hci_request(hdev, hci_read_grp_counters_req,
1250 reset_counters, RADIO_HCI_TIMEOUT);
1251
1252 return ret;
1253}
1254
Srinivasa Rao Uppala0f4098f2011-09-06 16:46:28 +05301255static int hci_set_notch_filter(__u8 *arg, struct radio_hci_dev *hdev)
1256{
1257 int ret = 0;
1258 __u8 notch_filter = *arg;
1259 ret = radio_hci_request(hdev, hci_set_notch_filter_req,
1260 notch_filter, RADIO_HCI_TIMEOUT);
1261
1262 return ret;
1263}
1264
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -07001265static int hci_peek_data(struct hci_fm_riva_data *arg,
1266 struct radio_hci_dev *hdev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001267{
1268 int ret = 0;
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -07001269 struct hci_fm_riva_data *peek_data = arg;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001270
1271 ret = radio_hci_request(hdev, hci_peek_data_req, (unsigned
1272 long)peek_data, RADIO_HCI_TIMEOUT);
1273
1274 return ret;
1275}
1276
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -07001277static int hci_poke_data(struct hci_fm_riva_poke *arg,
1278 struct radio_hci_dev *hdev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001279{
1280 int ret = 0;
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -07001281 struct hci_fm_riva_poke *poke_data = arg;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001282
1283 ret = radio_hci_request(hdev, hci_poke_data_req, (unsigned
1284 long)poke_data, RADIO_HCI_TIMEOUT);
1285
1286 return ret;
1287}
1288
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -07001289static int hci_ssbi_peek_reg(struct hci_fm_ssbi_peek *arg,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001290 struct radio_hci_dev *hdev)
1291{
1292 int ret = 0;
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -07001293 struct hci_fm_ssbi_peek *ssbi_peek_reg = arg;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001294
1295 ret = radio_hci_request(hdev, hci_ssbi_peek_reg_req, (unsigned
1296 long)ssbi_peek_reg, RADIO_HCI_TIMEOUT);
1297
1298 return ret;
1299}
1300
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -07001301static int hci_ssbi_poke_reg(struct hci_fm_ssbi_req *arg,
1302 struct radio_hci_dev *hdev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001303{
1304 int ret = 0;
1305 struct hci_fm_ssbi_req *ssbi_poke_reg = arg;
1306
1307 ret = radio_hci_request(hdev, hci_ssbi_poke_reg_req, (unsigned
1308 long)ssbi_poke_reg, RADIO_HCI_TIMEOUT);
1309
1310 return ret;
1311}
1312
1313static int hci_cmd(unsigned int cmd, struct radio_hci_dev *hdev)
1314{
1315 int ret = 0;
1316 unsigned long arg = 0;
1317
Ankur Nandwanid928d542011-08-11 13:15:41 -07001318 if (!hdev)
1319 return -ENODEV;
1320
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001321 switch (cmd) {
1322 case HCI_FM_ENABLE_RECV_CMD:
1323 ret = radio_hci_request(hdev, hci_fm_enable_recv_req, arg,
1324 msecs_to_jiffies(RADIO_HCI_TIMEOUT));
1325 break;
1326
1327 case HCI_FM_DISABLE_RECV_CMD:
1328 ret = radio_hci_request(hdev, hci_fm_disable_recv_req, arg,
1329 msecs_to_jiffies(RADIO_HCI_TIMEOUT));
1330 break;
1331
1332 case HCI_FM_GET_RECV_CONF_CMD:
1333 ret = radio_hci_request(hdev, hci_get_fm_recv_conf_req, arg,
1334 msecs_to_jiffies(RADIO_HCI_TIMEOUT));
1335 break;
1336
1337 case HCI_FM_GET_STATION_PARAM_CMD:
1338 ret = radio_hci_request(hdev,
1339 hci_fm_get_station_param_req, arg,
1340 msecs_to_jiffies(RADIO_HCI_TIMEOUT));
1341 break;
1342
1343 case HCI_FM_GET_SIGNAL_TH_CMD:
1344 ret = radio_hci_request(hdev,
1345 hci_fm_get_sig_threshold_req, arg,
1346 msecs_to_jiffies(RADIO_HCI_TIMEOUT));
1347 break;
1348
1349 case HCI_FM_GET_PROGRAM_SERVICE_CMD:
1350 ret = radio_hci_request(hdev,
1351 hci_fm_get_program_service_req, arg,
1352 msecs_to_jiffies(RADIO_HCI_TIMEOUT));
1353 break;
1354
1355 case HCI_FM_GET_RADIO_TEXT_CMD:
1356 ret = radio_hci_request(hdev, hci_fm_get_radio_text_req, arg,
1357 msecs_to_jiffies(RADIO_HCI_TIMEOUT));
1358 break;
1359
1360 case HCI_FM_GET_AF_LIST_CMD:
1361 ret = radio_hci_request(hdev, hci_fm_get_af_list_req, arg,
1362 msecs_to_jiffies(RADIO_HCI_TIMEOUT));
1363 break;
1364
1365 case HCI_FM_CANCEL_SEARCH_CMD:
1366 ret = radio_hci_request(hdev, hci_fm_cancel_search_req, arg,
1367 msecs_to_jiffies(RADIO_HCI_TIMEOUT));
1368 break;
1369
1370 case HCI_FM_RESET_CMD:
1371 ret = radio_hci_request(hdev, hci_fm_reset_req, arg,
1372 msecs_to_jiffies(RADIO_HCI_TIMEOUT));
1373 break;
1374
1375 case HCI_FM_GET_FEATURES_CMD:
1376 ret = radio_hci_request(hdev,
1377 hci_fm_get_feature_lists_req, arg,
1378 msecs_to_jiffies(RADIO_HCI_TIMEOUT));
1379 break;
1380
1381 case HCI_FM_STATION_DBG_PARAM_CMD:
1382 ret = radio_hci_request(hdev,
1383 hci_fm_get_station_dbg_param_req, arg,
1384 msecs_to_jiffies(RADIO_HCI_TIMEOUT));
1385 break;
1386
Ankur Nandwanid928d542011-08-11 13:15:41 -07001387 case HCI_FM_ENABLE_TRANS_CMD:
1388 ret = radio_hci_request(hdev, hci_fm_enable_trans_req, arg,
1389 msecs_to_jiffies(RADIO_HCI_TIMEOUT));
1390 break;
1391
1392 case HCI_FM_DISABLE_TRANS_CMD:
1393 ret = radio_hci_request(hdev, hci_fm_disable_trans_req, arg,
1394 msecs_to_jiffies(RADIO_HCI_TIMEOUT));
1395 break;
1396
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001397 default:
1398 ret = -EINVAL;
1399 break;
1400 }
1401
1402 return ret;
1403}
1404
1405static void radio_hci_req_complete(struct radio_hci_dev *hdev, int result)
1406{
1407 hdev->req_result = result;
1408 hdev->req_status = HCI_REQ_DONE;
1409 wake_up_interruptible(&hdev->req_wait_q);
1410}
1411
1412static void radio_hci_status_complete(struct radio_hci_dev *hdev, int result)
1413{
1414 hdev->req_result = result;
1415 hdev->req_status = HCI_REQ_STATUS;
1416 wake_up_interruptible(&hdev->req_wait_q);
1417}
1418
1419static void hci_cc_rsp(struct radio_hci_dev *hdev, struct sk_buff *skb)
1420{
1421 __u8 status = *((__u8 *) skb->data);
1422
1423 if (status)
1424 return;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001425 radio_hci_req_complete(hdev, status);
1426}
1427
1428static void hci_cc_fm_disable_rsp(struct radio_hci_dev *hdev,
1429 struct sk_buff *skb)
1430{
1431 __u8 status = *((__u8 *) skb->data);
1432 struct iris_device *radio = video_get_drvdata(video_get_dev());
1433
1434 if (status)
1435 return;
1436
1437 iris_q_event(radio, IRIS_EVT_RADIO_READY);
1438
1439 radio_hci_req_complete(hdev, status);
1440}
1441
1442static void hci_cc_conf_rsp(struct radio_hci_dev *hdev, struct sk_buff *skb)
1443{
1444 struct hci_fm_conf_rsp *rsp = (void *)skb->data;
1445 struct iris_device *radio = video_get_drvdata(video_get_dev());
1446
1447 if (rsp->status)
1448 return;
1449
1450 radio->recv_conf = rsp->recv_conf_rsp;
1451 radio_hci_req_complete(hdev, rsp->status);
1452}
1453
1454static void hci_cc_fm_enable_rsp(struct radio_hci_dev *hdev,
1455 struct sk_buff *skb)
1456{
1457 struct hci_fm_conf_rsp *rsp = (void *)skb->data;
1458 struct iris_device *radio = video_get_drvdata(video_get_dev());
1459
1460 if (rsp->status)
1461 return;
1462
1463 iris_q_event(radio, IRIS_EVT_RADIO_READY);
1464
1465 radio_hci_req_complete(hdev, rsp->status);
1466}
1467
Ankur Nandwanid928d542011-08-11 13:15:41 -07001468
1469static void hci_cc_fm_trans_set_conf_rsp(struct radio_hci_dev *hdev,
1470 struct sk_buff *skb)
1471{
1472 struct hci_fm_conf_rsp *rsp = (void *)skb->data;
1473 struct iris_device *radio = video_get_drvdata(video_get_dev());
1474
1475 if (rsp->status)
1476 return;
1477
1478 iris_q_event(radio, HCI_EV_CMD_COMPLETE);
1479
1480 radio_hci_req_complete(hdev, rsp->status);
1481}
1482
1483
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001484static void hci_cc_sig_threshold_rsp(struct radio_hci_dev *hdev,
1485 struct sk_buff *skb)
1486{
1487 struct hci_fm_sig_threshold_rsp *rsp = (void *)skb->data;
1488 struct iris_device *radio = video_get_drvdata(video_get_dev());
1489 struct v4l2_control *v4l_ctl = radio->g_ctl;
1490
1491 if (rsp->status)
1492 return;
1493
1494 v4l_ctl->value = rsp->sig_threshold;
1495
1496 radio_hci_req_complete(hdev, rsp->status);
1497}
1498
1499static void hci_cc_station_rsp(struct radio_hci_dev *hdev, struct sk_buff *skb)
1500{
1501 struct iris_device *radio = video_get_drvdata(video_get_dev());
1502 struct hci_fm_station_rsp *rsp = (void *)skb->data;
1503 radio->fm_st_rsp = *(rsp);
1504
1505 /* Tune is always succesful */
1506 radio_hci_req_complete(hdev, 0);
1507}
1508
1509static void hci_cc_prg_srv_rsp(struct radio_hci_dev *hdev, struct sk_buff *skb)
1510{
1511 struct hci_fm_prgm_srv_rsp *rsp = (void *)skb->data;
1512
1513 if (rsp->status)
1514 return;
1515
1516 radio_hci_req_complete(hdev, rsp->status);
1517}
1518
1519static void hci_cc_rd_txt_rsp(struct radio_hci_dev *hdev, struct sk_buff *skb)
1520{
1521 struct hci_fm_radio_txt_rsp *rsp = (void *)skb->data;
1522
1523 if (rsp->status)
1524 return;
1525
1526 radio_hci_req_complete(hdev, rsp->status);
1527}
1528
1529static void hci_cc_af_list_rsp(struct radio_hci_dev *hdev, struct sk_buff *skb)
1530{
1531 struct hci_fm_af_list_rsp *rsp = (void *)skb->data;
1532
1533 if (rsp->status)
1534 return;
1535
1536 radio_hci_req_complete(hdev, rsp->status);
1537}
1538
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001539static void hci_cc_feature_list_rsp(struct radio_hci_dev *hdev,
1540 struct sk_buff *skb)
1541{
1542 struct hci_fm_feature_list_rsp *rsp = (void *)skb->data;
1543 struct iris_device *radio = video_get_drvdata(video_get_dev());
1544 struct v4l2_capability *v4l_cap = radio->g_cap;
1545
1546 if (rsp->status)
1547 return;
1548 v4l_cap->capabilities = (rsp->feature_mask & 0x000002) |
1549 (rsp->feature_mask & 0x000001);
1550
1551 radio_hci_req_complete(hdev, rsp->status);
1552}
1553
1554static void hci_cc_dbg_param_rsp(struct radio_hci_dev *hdev,
1555 struct sk_buff *skb)
1556{
1557 struct iris_device *radio = video_get_drvdata(video_get_dev());
1558 struct hci_fm_dbg_param_rsp *rsp = (void *)skb->data;
1559 radio->st_dbg_param = *(rsp);
1560
1561 if (radio->st_dbg_param.status)
1562 return;
1563
1564 radio_hci_req_complete(hdev, radio->st_dbg_param.status);
1565}
1566
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -07001567static void iris_q_evt_data(struct iris_device *radio,
1568 char *data, int len, int event)
1569{
1570 struct kfifo *data_b = &radio->data_buf[event];
1571 if (kfifo_in_locked(data_b, data, len, &radio->buf_lock[event]))
1572 wake_up_interruptible(&radio->event_queue);
1573}
1574
1575static void hci_cc_riva_peek_rsp(struct radio_hci_dev *hdev,
1576 struct sk_buff *skb)
1577{
1578 struct iris_device *radio = video_get_drvdata(video_get_dev());
Srinivasa Rao Uppala0f4098f2011-09-06 16:46:28 +05301579 __u8 status = *((__u8 *) skb->data);
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -07001580 int len;
1581 char *data;
1582
Srinivasa Rao Uppala0f4098f2011-09-06 16:46:28 +05301583 if (status)
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -07001584 return;
1585 len = skb->data[RIVA_PEEK_LEN_OFSET] + RIVA_PEEK_PARAM;
1586 data = kmalloc(len, GFP_ATOMIC);
1587
1588 if (!data) {
1589 FMDERR("Memory allocation failed");
1590 return;
1591 }
1592
1593 memcpy(data, &skb->data[PEEK_DATA_OFSET], len);
1594 iris_q_evt_data(radio, data, len, IRIS_BUF_PEEK);
Srinivasa Rao Uppala0f4098f2011-09-06 16:46:28 +05301595 radio_hci_req_complete(hdev, status);
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -07001596
1597
1598}
1599static void hci_cc_ssbi_peek_rsp(struct radio_hci_dev *hdev,
1600 struct sk_buff *skb)
1601{
1602 struct iris_device *radio = video_get_drvdata(video_get_dev());
Srinivasa Rao Uppala0f4098f2011-09-06 16:46:28 +05301603 __u8 status = *((__u8 *) skb->data);
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -07001604 char *data;
1605
Srinivasa Rao Uppala0f4098f2011-09-06 16:46:28 +05301606 if (status)
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -07001607 return;
1608 data = kmalloc(SSBI_PEEK_LEN, GFP_ATOMIC);
1609 if (!data) {
1610 FMDERR("Memory allocation failed");
1611 return;
1612 }
1613
1614 data[0] = skb->data[PEEK_DATA_OFSET];
1615 iris_q_evt_data(radio, data, SSBI_PEEK_LEN, IRIS_BUF_SSBI_PEEK);
Srinivasa Rao Uppala0f4098f2011-09-06 16:46:28 +05301616 radio_hci_req_complete(hdev, status);
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -07001617 kfree(data);
1618}
1619
Srinivasa Rao Uppala0f4098f2011-09-06 16:46:28 +05301620static void hci_cc_rds_grp_cntrs_rsp(struct radio_hci_dev *hdev,
1621 struct sk_buff *skb)
1622{
1623 struct iris_device *radio = video_get_drvdata(video_get_dev());
1624 __u8 status = *((__u8 *) skb->data);
1625 char *data;
1626 if (status)
1627 return;
1628 data = kmalloc(RDS_GRP_CNTR_LEN, GFP_ATOMIC);
1629 if (!data) {
1630 FMDERR("memory allocation failed");
1631 return;
1632 }
1633 memcpy(data, &skb->data[1], RDS_GRP_CNTR_LEN);
1634 iris_q_evt_data(radio, data, RDS_GRP_CNTR_LEN, IRIS_BUF_RDS_CNTRS);
1635 radio_hci_req_complete(hdev, status);
1636 kfree(data);
1637
1638}
1639
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001640static inline void hci_cmd_complete_event(struct radio_hci_dev *hdev,
1641 struct sk_buff *skb)
1642{
1643 struct hci_ev_cmd_complete *cmd_compl_ev = (void *) skb->data;
1644 __u16 opcode;
1645
1646 skb_pull(skb, sizeof(*cmd_compl_ev));
1647
1648 opcode = __le16_to_cpu(cmd_compl_ev->cmd_opcode);
1649
1650 switch (opcode) {
1651 case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_ENABLE_RECV_REQ):
1652 hci_cc_fm_enable_rsp(hdev, skb);
1653 break;
1654 case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_GET_RECV_CONF_REQ):
1655 hci_cc_conf_rsp(hdev, skb);
1656 break;
1657
1658 case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_DISABLE_RECV_REQ):
1659 hci_cc_fm_disable_rsp(hdev, skb);
1660 break;
1661
1662 case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_SET_RECV_CONF_REQ):
1663 case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_SET_MUTE_MODE_REQ):
1664 case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_SET_STEREO_MODE_REQ):
1665 case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_SET_ANTENNA):
1666 case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_SET_SIGNAL_THRESHOLD):
1667 case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_CANCEL_SEARCH):
1668 case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_RDS_GRP):
1669 case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_RDS_GRP_PROCESS):
1670 case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_EN_WAN_AVD_CTRL):
1671 case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_EN_NOTCH_CTRL):
Ankur Nandwanid928d542011-08-11 13:15:41 -07001672 case hci_trans_ctrl_cmd_op_pack(HCI_OCF_FM_ENABLE_TRANS_REQ):
1673 case hci_trans_ctrl_cmd_op_pack(HCI_OCF_FM_DISABLE_TRANS_REQ):
1674 case hci_trans_ctrl_cmd_op_pack(HCI_OCF_FM_RDS_RT_REQ):
1675 case hci_trans_ctrl_cmd_op_pack(HCI_OCF_FM_RDS_PS_REQ):
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001676 case hci_common_cmd_op_pack(HCI_OCF_FM_DEFAULT_DATA_WRITE):
1677 case hci_common_cmd_op_pack(HCI_OCF_FM_RESET):
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001678 case hci_diagnostic_cmd_op_pack(HCI_OCF_FM_SSBI_POKE_REG):
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -07001679 case hci_diagnostic_cmd_op_pack(HCI_OCF_FM_POKE_DATA):
Ankur Nandwanid928d542011-08-11 13:15:41 -07001680 case hci_diagnostic_cmd_op_pack(HCI_FM_SET_INTERNAL_TONE_GENRATOR):
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001681 hci_cc_rsp(hdev, skb);
1682 break;
1683
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -07001684 case hci_diagnostic_cmd_op_pack(HCI_OCF_FM_SSBI_PEEK_REG):
1685 hci_cc_ssbi_peek_rsp(hdev, skb);
1686 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001687 case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_GET_SIGNAL_THRESHOLD):
1688 hci_cc_sig_threshold_rsp(hdev, skb);
1689 break;
1690
1691 case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_GET_STATION_PARAM_REQ):
1692 hci_cc_station_rsp(hdev, skb);
1693 break;
1694
1695 case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_GET_PROGRAM_SERVICE_REQ):
1696 hci_cc_prg_srv_rsp(hdev, skb);
1697 break;
1698
1699 case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_GET_RADIO_TEXT_REQ):
1700 hci_cc_rd_txt_rsp(hdev, skb);
1701 break;
1702
1703 case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_GET_AF_LIST_REQ):
1704 hci_cc_af_list_rsp(hdev, skb);
1705 break;
1706
1707 case hci_common_cmd_op_pack(HCI_OCF_FM_DEFAULT_DATA_READ):
1708 case hci_diagnostic_cmd_op_pack(HCI_OCF_FM_PEEK_DATA):
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -07001709 hci_cc_riva_peek_rsp(hdev, skb);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001710 break;
1711
1712 case hci_common_cmd_op_pack(HCI_OCF_FM_GET_FEATURE_LIST):
1713 hci_cc_feature_list_rsp(hdev, skb);
1714 break;
1715
1716 case hci_diagnostic_cmd_op_pack(HCI_OCF_FM_STATION_DBG_PARAM):
1717 hci_cc_dbg_param_rsp(hdev, skb);
1718 break;
Ankur Nandwanid928d542011-08-11 13:15:41 -07001719 case hci_trans_ctrl_cmd_op_pack(HCI_OCF_FM_SET_TRANS_CONF_REQ):
1720 hci_cc_fm_trans_set_conf_rsp(hdev, skb);
1721 break;
1722
Srinivasa Rao Uppala0f4098f2011-09-06 16:46:28 +05301723 case hci_status_param_op_pack(HCI_OCF_FM_READ_GRP_COUNTERS):
1724 hci_cc_rds_grp_cntrs_rsp(hdev, skb);
1725 break;
1726
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001727 default:
1728 FMDERR("%s opcode 0x%x", hdev->name, opcode);
1729 break;
1730 }
1731
1732}
1733
1734static inline void hci_cmd_status_event(struct radio_hci_dev *hdev,
1735 struct sk_buff *skb)
1736{
1737 struct hci_ev_cmd_status *ev = (void *) skb->data;
1738 radio_hci_status_complete(hdev, ev->status);
1739}
1740
1741static inline void hci_ev_tune_status(struct radio_hci_dev *hdev,
1742 struct sk_buff *skb)
1743{
1744 int i;
1745 int len;
1746
1747 struct iris_device *radio = video_get_drvdata(video_get_dev());
1748
1749 len = sizeof(struct hci_fm_station_rsp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001750 memcpy(&radio->fm_st_rsp.station_rsp, skb_pull(skb, len), len);
1751
1752 iris_q_event(radio, IRIS_EVT_TUNE_SUCC);
1753
1754 for (i = 0; i < IRIS_BUF_MAX; i++) {
1755 if (i >= IRIS_BUF_RT_RDS)
1756 kfifo_reset(&radio->data_buf[i]);
1757 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001758 if (radio->fm_st_rsp.station_rsp.rssi)
1759 iris_q_event(radio, IRIS_EVT_ABOVE_TH);
1760 else
1761 iris_q_event(radio, IRIS_EVT_BELOW_TH);
1762
1763 if (radio->fm_st_rsp.station_rsp.stereo_prg)
1764 iris_q_event(radio, IRIS_EVT_STEREO);
1765
1766 if (radio->fm_st_rsp.station_rsp.mute_mode)
1767 iris_q_event(radio, IRIS_EVT_MONO);
1768
1769 if (radio->fm_st_rsp.station_rsp.rds_sync_status)
1770 iris_q_event(radio, IRIS_EVT_RDS_AVAIL);
1771 else
1772 iris_q_event(radio, IRIS_EVT_RDS_NOT_AVAIL);
1773}
1774
1775static inline void hci_ev_search_compl(struct radio_hci_dev *hdev,
1776 struct sk_buff *skb)
1777{
1778 struct iris_device *radio = video_get_drvdata(video_get_dev());
1779 iris_q_event(radio, IRIS_EVT_SEEK_COMPLETE);
1780}
1781
1782static inline void hci_ev_srch_st_list_compl(struct radio_hci_dev *hdev,
1783 struct sk_buff *skb)
1784{
1785 struct iris_device *radio = video_get_drvdata(video_get_dev());
Srinivasa Rao Uppala18fb80e2011-07-17 17:33:00 -07001786 struct hci_ev_srch_list_compl *ev ;
1787 int cnt;
1788 int stn_num;
1789 int rel_freq;
1790 int abs_freq;
1791 int len;
1792
1793 ev = kmalloc(sizeof(*ev), GFP_ATOMIC);
1794 if (!ev) {
1795 FMDERR("Memory allocation failed");
1796 return ;
1797 }
1798
1799 ev->num_stations_found = skb->data[STN_NUM_OFFSET];
1800 len = ev->num_stations_found * PARAMS_PER_STATION + STN_FREQ_OFFSET;
1801
1802 for (cnt = STN_FREQ_OFFSET, stn_num = 0;
1803 (cnt < len) && (stn_num < ev->num_stations_found);
1804 cnt += PARAMS_PER_STATION, stn_num++) {
1805 abs_freq = *((int *)&skb->data[cnt]);
1806 rel_freq = abs_freq - radio->recv_conf.band_low_limit;
1807 rel_freq = (rel_freq * 20) / KHZ_TO_MHZ;
1808
1809 ev->rel_freq[stn_num].rel_freq_lsb = GET_LSB(rel_freq);
1810 ev->rel_freq[stn_num].rel_freq_msb = GET_MSB(rel_freq);
1811 }
1812
1813 len = ev->num_stations_found * 2 + sizeof(ev->num_stations_found);
1814 iris_q_event(radio, IRIS_EVT_NEW_SRCH_LIST);
1815 iris_q_evt_data(radio, (char *)ev, len, IRIS_BUF_SRCH_LIST);
1816 kfree(ev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001817}
1818
1819static inline void hci_ev_search_next(struct radio_hci_dev *hdev,
1820 struct sk_buff *skb)
1821{
1822 struct iris_device *radio = video_get_drvdata(video_get_dev());
1823 iris_q_event(radio, IRIS_EVT_SCAN_NEXT);
1824}
1825
1826static inline void hci_ev_stereo_status(struct radio_hci_dev *hdev,
1827 struct sk_buff *skb)
1828{
1829 struct iris_device *radio = video_get_drvdata(video_get_dev());
1830 __u8 st_status = *((__u8 *) skb->data);
1831 if (st_status)
1832 iris_q_event(radio, IRIS_EVT_STEREO);
1833 else
1834 iris_q_event(radio, IRIS_EVT_MONO);
1835}
1836
Ankur Nandwani78a782b2011-07-07 21:11:21 -07001837
Ankur Nandwani78a782b2011-07-07 21:11:21 -07001838static inline void hci_ev_program_service(struct radio_hci_dev *hdev,
1839 struct sk_buff *skb)
1840{
1841 struct iris_device *radio = video_get_drvdata(video_get_dev());
1842 int len;
1843 char *data;
1844
1845 len = (skb->data[RDS_PS_LENGTH_OFFSET] * RDS_STRING) + RDS_OFFSET;
1846 iris_q_event(radio, IRIS_EVT_NEW_PS_RDS);
1847 data = kmalloc(len, GFP_ATOMIC);
1848 if (!data) {
1849 FMDERR("Failed to allocate memory");
1850 return;
1851 }
1852
1853 data[0] = skb->data[RDS_PS_LENGTH_OFFSET];
1854 data[1] = skb->data[RDS_PTYPE];
1855 data[2] = skb->data[RDS_PID_LOWER];
1856 data[3] = skb->data[RDS_PID_HIGHER];
1857 data[4] = 0;
1858
1859 memcpy(data+RDS_OFFSET, &skb->data[RDS_PS_DATA_OFFSET], len-RDS_OFFSET);
1860
Srinivasa Rao Uppala18fb80e2011-07-17 17:33:00 -07001861 iris_q_evt_data(radio, data, len, IRIS_BUF_PS_RDS);
Ankur Nandwani78a782b2011-07-07 21:11:21 -07001862
1863 kfree(data);
1864}
1865
1866
1867static inline void hci_ev_radio_text(struct radio_hci_dev *hdev,
1868 struct sk_buff *skb)
1869{
1870 struct iris_device *radio = video_get_drvdata(video_get_dev());
1871 int len = 0;
1872 char *data;
1873
1874 iris_q_event(radio, IRIS_EVT_NEW_RT_RDS);
1875
1876 while (skb->data[len+RDS_OFFSET] != 0x0d)
1877 len++;
1878 len++;
1879
1880 data = kmalloc(len+RDS_OFFSET, GFP_ATOMIC);
1881 if (!data) {
1882 FMDERR("Failed to allocate memory");
1883 return;
1884 }
1885
1886 data[0] = len;
1887 data[1] = skb->data[RDS_PTYPE];
1888 data[2] = skb->data[RDS_PID_LOWER];
1889 data[3] = skb->data[RDS_PID_HIGHER];
1890 data[4] = 0;
1891
1892 memcpy(data+RDS_OFFSET, &skb->data[RDS_OFFSET], len);
1893 data[len+RDS_OFFSET] = 0x00;
1894
Srinivasa Rao Uppala18fb80e2011-07-17 17:33:00 -07001895 iris_q_evt_data(radio, data, len+RDS_OFFSET, IRIS_BUF_RT_RDS);
Ankur Nandwani78a782b2011-07-07 21:11:21 -07001896
1897 kfree(data);
1898}
1899
Srinivasa Rao Uppala3c7a8eb2011-09-18 09:10:21 +05301900static void hci_ev_af_list(struct radio_hci_dev *hdev,
1901 struct sk_buff *skb)
1902{
1903 struct iris_device *radio = video_get_drvdata(video_get_dev());
1904 struct hci_ev_af_list ev;
1905
1906 ev.tune_freq = *((int *) &skb->data[0]);
1907 ev.pi_code = *((__le16 *) &skb->data[PI_CODE_OFFSET]);
1908 ev.af_size = skb->data[AF_SIZE_OFFSET];
1909 memcpy(&ev.af_list[0], &skb->data[AF_LIST_OFFSET], ev.af_size);
1910 iris_q_event(radio, IRIS_EVT_NEW_AF_LIST);
1911 iris_q_evt_data(radio, (char *)&ev, sizeof(ev), IRIS_BUF_AF_LIST);
1912}
1913
1914static void hci_ev_rds_lock_status(struct radio_hci_dev *hdev,
1915 struct sk_buff *skb)
1916{
1917 struct iris_device *radio = video_get_drvdata(video_get_dev());
1918 __u8 rds_status = skb->data[0];
1919
1920 if (rds_status)
1921 iris_q_event(radio, IRIS_EVT_RDS_AVAIL);
1922 else
1923 iris_q_event(radio, IRIS_EVT_RDS_NOT_AVAIL);
1924}
1925
1926static void hci_ev_service_available(struct radio_hci_dev *hdev,
1927 struct sk_buff *skb)
1928{
1929 struct iris_device *radio = video_get_drvdata(video_get_dev());
1930 if (radio->fm_st_rsp.station_rsp.serv_avble)
1931 iris_q_event(radio, IRIS_EVT_ABOVE_TH);
1932 else
1933 iris_q_event(radio, IRIS_EVT_BELOW_TH);
1934}
1935
1936static void hci_ev_rds_grp_complete(struct radio_hci_dev *hdev,
1937 struct sk_buff *skb)
1938{
1939 struct iris_device *radio = video_get_drvdata(video_get_dev());
1940 iris_q_event(radio, IRIS_EVT_TXRDSDONE);
1941}
Ankur Nandwani78a782b2011-07-07 21:11:21 -07001942
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001943void radio_hci_event_packet(struct radio_hci_dev *hdev, struct sk_buff *skb)
1944{
1945 struct radio_hci_event_hdr *hdr = (void *) skb->data;
1946 __u8 event = hdr->evt;
1947
1948 skb_pull(skb, RADIO_HCI_EVENT_HDR_SIZE);
1949
1950 switch (event) {
1951 case HCI_EV_TUNE_STATUS:
1952 hci_ev_tune_status(hdev, skb);
1953 break;
1954 case HCI_EV_SEARCH_PROGRESS:
1955 case HCI_EV_SEARCH_RDS_PROGRESS:
1956 case HCI_EV_SEARCH_LIST_PROGRESS:
1957 hci_ev_search_next(hdev, skb);
1958 break;
1959 case HCI_EV_STEREO_STATUS:
1960 hci_ev_stereo_status(hdev, skb);
1961 break;
1962 case HCI_EV_RDS_LOCK_STATUS:
Srinivasa Rao Uppala3c7a8eb2011-09-18 09:10:21 +05301963 hci_ev_rds_lock_status(hdev, skb);
1964 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001965 case HCI_EV_SERVICE_AVAILABLE:
Srinivasa Rao Uppala3c7a8eb2011-09-18 09:10:21 +05301966 hci_ev_service_available(hdev, skb);
1967 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001968 case HCI_EV_RDS_RX_DATA:
Ankur Nandwani78a782b2011-07-07 21:11:21 -07001969 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001970 case HCI_EV_PROGRAM_SERVICE:
Ankur Nandwani78a782b2011-07-07 21:11:21 -07001971 hci_ev_program_service(hdev, skb);
1972 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001973 case HCI_EV_RADIO_TEXT:
Ankur Nandwani78a782b2011-07-07 21:11:21 -07001974 hci_ev_radio_text(hdev, skb);
1975 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001976 case HCI_EV_FM_AF_LIST:
Srinivasa Rao Uppala3c7a8eb2011-09-18 09:10:21 +05301977 hci_ev_af_list(hdev, skb);
1978 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001979 case HCI_EV_TX_RDS_GRP_COMPL:
Srinivasa Rao Uppala3c7a8eb2011-09-18 09:10:21 +05301980 hci_ev_rds_grp_complete(hdev, skb);
1981 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001982 case HCI_EV_TX_RDS_CONT_GRP_COMPL:
1983 break;
1984
1985 case HCI_EV_CMD_COMPLETE:
1986 hci_cmd_complete_event(hdev, skb);
1987 break;
1988
1989 case HCI_EV_CMD_STATUS:
1990 hci_cmd_status_event(hdev, skb);
1991 break;
1992
1993 case HCI_EV_SEARCH_COMPLETE:
1994 case HCI_EV_SEARCH_RDS_COMPLETE:
1995 hci_ev_search_compl(hdev, skb);
1996 break;
1997
1998 case HCI_EV_SEARCH_LIST_COMPLETE:
Srinivasa Rao Uppala18fb80e2011-07-17 17:33:00 -07001999 hci_ev_srch_st_list_compl(hdev, skb);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002000 break;
2001
2002 default:
2003 break;
2004 }
2005}
2006
2007/*
2008 * fops/IOCTL helper functions
2009 */
2010
2011static int iris_search(struct iris_device *radio, int on, int dir)
2012{
2013 int retval = 0;
2014 enum search_t srch = radio->g_search_mode & SRCH_MODE;
Srinivasa Rao Uppala3c7a8eb2011-09-18 09:10:21 +05302015 radio->search_on = on;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002016
2017 if (on) {
2018 switch (srch) {
2019 case SCAN_FOR_STRONG:
2020 case SCAN_FOR_WEAK:
2021 radio->srch_st_list.srch_list_dir = dir;
2022 radio->srch_st_list.srch_list_mode = srch;
2023 radio->srch_st_list.srch_list_max = 0;
2024 retval = hci_fm_search_station_list(
2025 &radio->srch_st_list, radio->fm_hdev);
2026 break;
2027 case RDS_SEEK_PTY:
2028 case RDS_SCAN_PTY:
2029 case RDS_SEEK_PI:
Srinivasa Rao Uppala7bb22102011-07-14 11:27:30 -07002030 srch = srch - SEARCH_RDS_STNS_MODE_OFFSET;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002031 radio->srch_rds.srch_station.srch_mode = srch;
2032 radio->srch_rds.srch_station.srch_dir = dir;
2033 radio->srch_rds.srch_station.scan_time =
2034 radio->g_scan_time;
2035 retval = hci_fm_search_rds_stations(&radio->srch_rds,
2036 radio->fm_hdev);
2037 break;
2038 default:
2039 radio->srch_st.srch_mode = srch;
2040 radio->srch_st.scan_time = radio->g_scan_time;
2041 radio->srch_st.srch_dir = dir;
2042 retval = hci_fm_search_stations(
2043 &radio->srch_st, radio->fm_hdev);
2044 break;
2045 }
2046
2047 } else {
2048 retval = hci_cmd(HCI_FM_CANCEL_SEARCH_CMD, radio->fm_hdev);
2049 }
2050
2051 return retval;
2052}
2053
Ankur Nandwanid928d542011-08-11 13:15:41 -07002054static int iris_recv_set_region(struct iris_device *radio, int req_region)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002055{
2056 int retval;
2057 radio->region = req_region;
2058
2059 switch (radio->region) {
2060 case IRIS_REGION_US:
Ankur Nandwanid928d542011-08-11 13:15:41 -07002061 radio->recv_conf.band_low_limit =
2062 REGION_US_EU_BAND_LOW;
2063 radio->recv_conf.band_high_limit =
2064 REGION_US_EU_BAND_HIGH;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002065 break;
2066 case IRIS_REGION_EU:
Ankur Nandwanid928d542011-08-11 13:15:41 -07002067 radio->recv_conf.band_low_limit =
2068 REGION_US_EU_BAND_LOW;
2069 radio->recv_conf.band_high_limit =
2070 REGION_US_EU_BAND_HIGH;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002071 break;
2072 case IRIS_REGION_JAPAN:
Ankur Nandwanid928d542011-08-11 13:15:41 -07002073 radio->recv_conf.band_low_limit =
Ankur Nandwanid928d542011-08-11 13:15:41 -07002074 REGION_JAPAN_STANDARD_BAND_LOW;
Srinivasa Rao Uppalacc62b1c2011-08-22 19:15:29 +05302075 radio->recv_conf.band_high_limit =
2076 REGION_JAPAN_STANDARD_BAND_HIGH;
Ankur Nandwanid928d542011-08-11 13:15:41 -07002077 break;
2078 case IRIS_REGION_JAPAN_WIDE:
2079 radio->recv_conf.band_low_limit =
2080 REGION_JAPAN_WIDE_BAND_LOW;
2081 radio->recv_conf.band_high_limit =
2082 REGION_JAPAN_WIDE_BAND_HIGH;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002083 break;
2084 default:
Ankur Nandwanid928d542011-08-11 13:15:41 -07002085 /* The user specifies the value.
2086 So nothing needs to be done */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002087 break;
2088 }
2089
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002090 retval = hci_set_fm_recv_conf(
2091 &radio->recv_conf,
2092 radio->fm_hdev);
2093
2094 return retval;
2095}
2096
Ankur Nandwanid928d542011-08-11 13:15:41 -07002097
2098static int iris_trans_set_region(struct iris_device *radio, int req_region)
2099{
2100 int retval;
2101 radio->region = req_region;
2102
2103 switch (radio->region) {
2104 case IRIS_REGION_US:
2105 radio->trans_conf.band_low_limit =
2106 REGION_US_EU_BAND_LOW;
2107 radio->trans_conf.band_high_limit =
2108 REGION_US_EU_BAND_HIGH;
2109 break;
2110 case IRIS_REGION_EU:
2111 radio->trans_conf.band_low_limit =
2112 REGION_US_EU_BAND_LOW;
2113 radio->trans_conf.band_high_limit =
2114 REGION_US_EU_BAND_HIGH;
2115 break;
2116 case IRIS_REGION_JAPAN:
2117 radio->trans_conf.band_low_limit =
Ankur Nandwanid928d542011-08-11 13:15:41 -07002118 REGION_JAPAN_STANDARD_BAND_LOW;
Srinivasa Rao Uppalacc62b1c2011-08-22 19:15:29 +05302119 radio->trans_conf.band_high_limit =
2120 REGION_JAPAN_STANDARD_BAND_HIGH;
Ankur Nandwanid928d542011-08-11 13:15:41 -07002121 break;
2122 case IRIS_REGION_JAPAN_WIDE:
2123 radio->recv_conf.band_low_limit =
2124 REGION_JAPAN_WIDE_BAND_LOW;
2125 radio->recv_conf.band_high_limit =
2126 REGION_JAPAN_WIDE_BAND_HIGH;
2127 default:
2128 break;
2129 }
2130
2131 retval = hci_set_fm_trans_conf(
2132 &radio->trans_conf,
2133 radio->fm_hdev);
2134 return retval;
2135}
2136
2137
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002138static int iris_set_freq(struct iris_device *radio, unsigned int freq)
2139{
2140
2141 int retval;
2142 retval = hci_fm_tune_station(&freq, radio->fm_hdev);
2143 if (retval < 0)
2144 FMDERR("Error while setting the frequency : %d\n", retval);
2145 return retval;
2146}
2147
2148
2149static int iris_vidioc_queryctrl(struct file *file, void *priv,
2150 struct v4l2_queryctrl *qc)
2151{
2152 unsigned char i;
2153 int retval = -EINVAL;
2154
2155 for (i = 0; i < ARRAY_SIZE(iris_v4l2_queryctrl); i++) {
2156 if (qc->id && qc->id == iris_v4l2_queryctrl[i].id) {
2157 memcpy(qc, &(iris_v4l2_queryctrl[i]), sizeof(*qc));
2158 retval = 0;
2159 break;
2160 }
2161 }
2162
2163 return retval;
2164}
2165
2166static int iris_vidioc_g_ctrl(struct file *file, void *priv,
2167 struct v4l2_control *ctrl)
2168{
2169 struct iris_device *radio = video_get_drvdata(video_devdata(file));
2170 int retval = 0;
2171
2172 switch (ctrl->id) {
2173 case V4L2_CID_AUDIO_VOLUME:
2174 break;
2175 case V4L2_CID_AUDIO_MUTE:
2176 ctrl->value = radio->mute_mode.hard_mute;
2177 break;
2178 case V4L2_CID_PRIVATE_IRIS_SRCHMODE:
2179 ctrl->value = radio->g_search_mode;
2180 break;
2181 case V4L2_CID_PRIVATE_IRIS_SCANDWELL:
2182 ctrl->value = radio->g_scan_time;
2183 break;
2184 case V4L2_CID_PRIVATE_IRIS_SRCHON:
Srinivasa Rao Uppala3c7a8eb2011-09-18 09:10:21 +05302185 ctrl->value = radio->search_on;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002186 break;
2187 case V4L2_CID_PRIVATE_IRIS_STATE:
Srinivasa Rao Uppala3c7a8eb2011-09-18 09:10:21 +05302188 ctrl->value = radio->mode;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002189 break;
2190 case V4L2_CID_PRIVATE_IRIS_IOVERC:
2191 retval = hci_cmd(HCI_FM_STATION_DBG_PARAM_CMD, radio->fm_hdev);
2192 if (retval < 0)
2193 return retval;
2194 ctrl->value = radio->st_dbg_param.io_verc;
2195 break;
2196 case V4L2_CID_PRIVATE_IRIS_INTDET:
2197 retval = hci_cmd(HCI_FM_STATION_DBG_PARAM_CMD, radio->fm_hdev);
2198 if (retval < 0)
2199 return retval;
2200 ctrl->value = radio->st_dbg_param.in_det_out;
2201 break;
2202 case V4L2_CID_PRIVATE_IRIS_REGION:
2203 ctrl->value = radio->region;
2204 break;
2205 case V4L2_CID_PRIVATE_IRIS_SIGNAL_TH:
2206 retval = hci_cmd(HCI_FM_GET_SIGNAL_TH_CMD, radio->fm_hdev);
2207 break;
2208 case V4L2_CID_PRIVATE_IRIS_SRCH_PTY:
Srinivasa Rao Uppala3c7a8eb2011-09-18 09:10:21 +05302209 ctrl->value = radio->srch_rds.srch_pty;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002210 break;
2211 case V4L2_CID_PRIVATE_IRIS_SRCH_PI:
Srinivasa Rao Uppala3c7a8eb2011-09-18 09:10:21 +05302212 ctrl->value = radio->srch_rds.srch_pi;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002213 break;
2214 case V4L2_CID_PRIVATE_IRIS_SRCH_CNT:
Srinivasa Rao Uppala3c7a8eb2011-09-18 09:10:21 +05302215 ctrl->value = radio->srch_st_result.num_stations_found;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002216 break;
2217 case V4L2_CID_PRIVATE_IRIS_EMPHASIS:
Ankur Nandwanid928d542011-08-11 13:15:41 -07002218 if (radio->mode == FM_RECV) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002219 ctrl->value = radio->recv_conf.emphasis;
Ankur Nandwanid928d542011-08-11 13:15:41 -07002220 } else if (radio->mode == FM_TRANS) {
2221 ctrl->value = radio->trans_conf.emphasis;
2222 } else {
2223 FMDERR("Error in radio mode"
2224 " %d\n", retval);
2225 return -EINVAL;
2226 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002227 break;
2228 case V4L2_CID_PRIVATE_IRIS_RDS_STD:
Ankur Nandwanid928d542011-08-11 13:15:41 -07002229 if (radio->mode == FM_RECV) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002230 ctrl->value = radio->recv_conf.rds_std;
Ankur Nandwanid928d542011-08-11 13:15:41 -07002231 } else if (radio->mode == FM_TRANS) {
2232 ctrl->value = radio->trans_conf.rds_std;
2233 } else {
2234 FMDERR("Error in radio mode"
2235 " %d\n", retval);
2236 return -EINVAL;
2237 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002238 break;
2239 case V4L2_CID_PRIVATE_IRIS_SPACING:
Ankur Nandwanid928d542011-08-11 13:15:41 -07002240 if (radio->mode == FM_RECV) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002241 ctrl->value = radio->recv_conf.ch_spacing;
Ankur Nandwanid928d542011-08-11 13:15:41 -07002242 } else {
2243 FMDERR("Error in radio mode"
2244 " %d\n", retval);
2245 return -EINVAL;
2246 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002247 break;
2248 case V4L2_CID_PRIVATE_IRIS_RDSON:
Ankur Nandwanid928d542011-08-11 13:15:41 -07002249 if (radio->mode == FM_RECV) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002250 ctrl->value = radio->recv_conf.rds_std;
Ankur Nandwanid928d542011-08-11 13:15:41 -07002251 } else {
2252 FMDERR("Error in radio mode"
2253 " %d\n", retval);
2254 return -EINVAL;
2255 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002256 break;
2257 case V4L2_CID_PRIVATE_IRIS_RDSGROUP_MASK:
2258 ctrl->value = radio->rds_grp.rds_grp_enable_mask;
2259 break;
2260 case V4L2_CID_PRIVATE_IRIS_RDSGROUP_PROC:
Srinivasa Rao Uppala58273f82011-08-10 19:07:45 -07002261 case V4L2_CID_PRIVATE_IRIS_PSALL:
2262 ctrl->value = (radio->g_rds_grp_proc_ps << RDS_CONFIG_OFFSET);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002263 break;
2264 case V4L2_CID_PRIVATE_IRIS_RDSD_BUF:
2265 ctrl->value = radio->rds_grp.rds_buf_size;
2266 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002267 case V4L2_CID_PRIVATE_IRIS_LP_MODE:
2268 break;
2269 case V4L2_CID_PRIVATE_IRIS_ANTENNA:
2270 ctrl->value = radio->g_antenna;
2271 break;
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -07002272 case V4L2_CID_PRIVATE_IRIS_SOFT_MUTE:
2273 ctrl->value = radio->mute_mode.soft_mute;
2274 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002275 default:
2276 retval = -EINVAL;
2277 }
2278 if (retval < 0)
2279 FMDERR("get control failed with %d, id: %d\n",
2280 retval, ctrl->id);
2281 return retval;
2282}
2283
2284static int iris_vidioc_s_ext_ctrls(struct file *file, void *priv,
2285 struct v4l2_ext_controls *ctrl)
2286{
Ankur Nandwanid928d542011-08-11 13:15:41 -07002287 int retval = 0;
2288 int bytes_to_copy;
2289 struct hci_fm_tx_ps tx_ps;
2290 struct hci_fm_tx_rt tx_rt;
2291
2292 struct iris_device *radio = video_get_drvdata(video_devdata(file));
2293 char *data = NULL;
2294
2295 switch ((ctrl->controls[0]).id) {
2296 case V4L2_CID_RDS_TX_PS_NAME:
2297 FMDBG("In V4L2_CID_RDS_TX_PS_NAME\n");
2298 /*Pass a sample PS string */
2299
2300 memset(tx_ps.ps_data, 0, MAX_PS_LENGTH);
2301 bytes_to_copy = min((int)(ctrl->controls[0]).size,
2302 MAX_PS_LENGTH);
2303 data = (ctrl->controls[0]).string;
2304
2305 if (copy_from_user(tx_ps.ps_data,
2306 data, bytes_to_copy))
2307 return -EFAULT;
2308 tx_ps.ps_control = 0x01;
2309 tx_ps.pi = radio->pi;
2310 tx_ps.pty = radio->pty;
2311 tx_ps.ps_repeatcount = radio->ps_repeatcount;
2312 tx_ps.ps_len = bytes_to_copy;
2313
2314 retval = radio_hci_request(radio->fm_hdev, hci_trans_ps_req,
2315 (unsigned long)&tx_ps, RADIO_HCI_TIMEOUT);
2316 break;
2317 case V4L2_CID_RDS_TX_RADIO_TEXT:
2318 bytes_to_copy =
2319 min((int)(ctrl->controls[0]).size, MAX_RT_LENGTH);
2320 data = (ctrl->controls[0]).string;
2321
2322 memset(tx_rt.rt_data, 0, MAX_RT_LENGTH);
2323
2324 if (copy_from_user(tx_rt.rt_data,
2325 data, bytes_to_copy))
2326 return -EFAULT;
2327
2328 tx_rt.rt_control = 0x01;
2329 tx_rt.pi = radio->pi;
2330 tx_rt.pty = radio->pty;
2331 tx_rt.ps_len = bytes_to_copy;
2332
2333 retval = radio_hci_request(radio->fm_hdev, hci_trans_rt_req,
2334 (unsigned long)&tx_rt, RADIO_HCI_TIMEOUT);
2335 break;
2336 default:
2337 FMDBG("Shouldn't reach here\n");
2338 retval = -1;
2339 }
2340 return retval;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002341}
2342
2343static int iris_vidioc_s_ctrl(struct file *file, void *priv,
2344 struct v4l2_control *ctrl)
2345{
2346 struct iris_device *radio = video_get_drvdata(video_devdata(file));
2347 int retval = 0;
2348 unsigned int rds_grps_proc = 0;
2349 __u8 temp_val = 0;
Ankur Nandwanid928d542011-08-11 13:15:41 -07002350 unsigned long arg = 0;
Srinivasa Rao Uppala3c7a8eb2011-09-18 09:10:21 +05302351 struct hci_fm_tx_ps tx_ps = {0};
2352 struct hci_fm_tx_rt tx_rt = {0};
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002353
2354 switch (ctrl->id) {
Srinivasa Rao Uppala0f4098f2011-09-06 16:46:28 +05302355 case V4L2_CID_PRIVATE_IRIS_TX_TONE:
Ankur Nandwanid928d542011-08-11 13:15:41 -07002356 radio->tone_freq = ctrl->value;
2357 retval = radio_hci_request(radio->fm_hdev,
2358 hci_fm_tone_generator, arg,
2359 msecs_to_jiffies(RADIO_HCI_TIMEOUT));
2360 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002361 case V4L2_CID_AUDIO_VOLUME:
2362 break;
2363 case V4L2_CID_AUDIO_MUTE:
2364 radio->mute_mode.hard_mute = ctrl->value;
2365 radio->mute_mode.soft_mute = IOC_SFT_MUTE;
2366 retval = hci_set_fm_mute_mode(
2367 &radio->mute_mode,
2368 radio->fm_hdev);
2369 if (retval < 0)
2370 FMDERR("Error while set FM hard mute"" %d\n",
2371 retval);
2372 break;
2373 case V4L2_CID_PRIVATE_IRIS_SRCHMODE:
2374 radio->g_search_mode = ctrl->value;
2375 break;
2376 case V4L2_CID_PRIVATE_IRIS_SCANDWELL:
2377 radio->g_scan_time = ctrl->value;
2378 break;
2379 case V4L2_CID_PRIVATE_IRIS_SRCHON:
2380 iris_search(radio, ctrl->value, SRCH_DIR_UP);
2381 break;
2382 case V4L2_CID_PRIVATE_IRIS_STATE:
Ankur Nandwanid928d542011-08-11 13:15:41 -07002383 switch (ctrl->value) {
2384 case FM_RECV:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002385 retval = hci_cmd(HCI_FM_ENABLE_RECV_CMD,
2386 radio->fm_hdev);
Ankur Nandwanid928d542011-08-11 13:15:41 -07002387
2388 radio->mode = FM_RECV;
2389
2390 if (retval < 0)
2391 FMDERR("Error while enabling RECV FM"
Srinivasa Rao Uppala0ffb5d62011-08-02 17:54:13 -07002392 " %d\n", retval);
Ankur Nandwanid928d542011-08-11 13:15:41 -07002393 radio->mute_mode.soft_mute = CTRL_ON;
2394 retval = hci_set_fm_mute_mode(
2395 &radio->mute_mode,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002396 radio->fm_hdev);
Ankur Nandwanid928d542011-08-11 13:15:41 -07002397 if (retval < 0)
2398 FMDERR("Failed to enable Smute\n");
2399 radio->stereo_mode.stereo_mode = CTRL_OFF;
2400 radio->stereo_mode.sig_blend = CTRL_ON;
2401 radio->stereo_mode.intf_blend = CTRL_ON;
2402 radio->stereo_mode.most_switch = CTRL_ON;
2403 retval = hci_set_fm_stereo_mode(
2404 &radio->stereo_mode,
2405 radio->fm_hdev);
2406 if (retval < 0)
2407 FMDERR("Failed to set stereo mode\n");
Srinivasa Rao Uppalaf0d13742011-09-08 10:13:13 +05302408 retval = hci_cmd(HCI_FM_GET_RECV_CONF_CMD,
2409 radio->fm_hdev);
2410 if (retval < 0)
2411 FMDERR("Failed to get the Recv Config\n");
Srinivasa Rao Uppala07522d92011-08-16 05:09:30 -07002412 break;
Ankur Nandwanid928d542011-08-11 13:15:41 -07002413 case FM_TRANS:
2414 retval = hci_cmd(HCI_FM_ENABLE_TRANS_CMD,
2415 radio->fm_hdev);
2416 radio->mode = FM_TRANS;
2417
2418 if (retval < 0)
2419 FMDERR("Error while enabling TRANS FM"
2420 " %d\n", retval);
Srinivasa Rao Uppala07522d92011-08-16 05:09:30 -07002421 break;
Ankur Nandwanid928d542011-08-11 13:15:41 -07002422 case FM_OFF:
2423 switch (radio->mode) {
2424 case FM_RECV:
2425 retval = hci_cmd(HCI_FM_DISABLE_RECV_CMD,
2426 radio->fm_hdev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002427 if (retval < 0)
Ankur Nandwanid928d542011-08-11 13:15:41 -07002428 FMDERR("Err on disable recv FM"
2429 " %d\n", retval);
2430 break;
2431 case FM_TRANS:
2432 retval = hci_cmd(HCI_FM_DISABLE_TRANS_CMD,
2433 radio->fm_hdev);
2434
Srinivasa Rao Uppala0ffb5d62011-08-02 17:54:13 -07002435 if (retval < 0)
Ankur Nandwanid928d542011-08-11 13:15:41 -07002436 FMDERR("Err disabling trans FM"
Srinivasa Rao Uppala0ffb5d62011-08-02 17:54:13 -07002437 " %d\n", retval);
Ankur Nandwanid928d542011-08-11 13:15:41 -07002438 break;
2439 default:
2440 retval = -EINVAL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002441 }
Srinivasa Rao Uppalacc62b1c2011-08-22 19:15:29 +05302442 break;
Ankur Nandwanid928d542011-08-11 13:15:41 -07002443 default:
2444 retval = -EINVAL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002445 }
2446 break;
2447 case V4L2_CID_PRIVATE_IRIS_REGION:
Ankur Nandwanid928d542011-08-11 13:15:41 -07002448 if (radio->mode == FM_RECV) {
2449 retval = iris_recv_set_region(radio, ctrl->value);
2450 } else {
2451 if (radio->mode == FM_TRANS)
2452 retval = iris_trans_set_region(radio,
2453 ctrl->value);
2454 else
2455 retval = -EINVAL;
2456 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002457 break;
2458 case V4L2_CID_PRIVATE_IRIS_SIGNAL_TH:
2459 temp_val = ctrl->value;
2460 retval = hci_fm_set_signal_threshold(
2461 &temp_val,
2462 radio->fm_hdev);
2463 if (retval < 0) {
2464 FMDERR("Error while setting signal threshold\n");
2465 break;
2466 }
2467 break;
2468 case V4L2_CID_PRIVATE_IRIS_SRCH_PTY:
2469 radio->srch_rds.srch_pty = ctrl->value;
2470 radio->srch_st_list.srch_pty = ctrl->value;
2471 break;
2472 case V4L2_CID_PRIVATE_IRIS_SRCH_PI:
2473 radio->srch_rds.srch_pi = ctrl->value;
2474 break;
2475 case V4L2_CID_PRIVATE_IRIS_SRCH_CNT:
2476 break;
2477 case V4L2_CID_PRIVATE_IRIS_SPACING:
2478 radio->recv_conf.ch_spacing = ctrl->value;
2479 break;
2480 case V4L2_CID_PRIVATE_IRIS_EMPHASIS:
Ankur Nandwani8f972e52011-08-24 11:48:32 -07002481 switch (radio->mode) {
2482 case FM_RECV:
2483 radio->recv_conf.emphasis = ctrl->value;
2484 retval = hci_set_fm_recv_conf(
2485 &radio->recv_conf,
2486 radio->fm_hdev);
2487 if (retval < 0)
2488 FMDERR("Error in setting emphasis");
Ankur Nandwanid928d542011-08-11 13:15:41 -07002489 break;
Ankur Nandwani8f972e52011-08-24 11:48:32 -07002490 case FM_TRANS:
2491 radio->trans_conf.emphasis = ctrl->value;
2492 retval = hci_set_fm_trans_conf(
2493 &radio->trans_conf,
2494 radio->fm_hdev);
2495 if (retval < 0)
2496 FMDERR("Error in setting emphasis");
2497 break;
2498 default:
2499 retval = -EINVAL;
Ankur Nandwanid928d542011-08-11 13:15:41 -07002500 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002501 break;
2502 case V4L2_CID_PRIVATE_IRIS_RDS_STD:
Ankur Nandwani8f972e52011-08-24 11:48:32 -07002503 switch (radio->mode) {
2504 case FM_RECV:
2505 radio->recv_conf.rds_std = ctrl->value;
2506 retval = hci_set_fm_recv_conf(
2507 &radio->recv_conf,
2508 radio->fm_hdev);
2509 if (retval < 0)
2510 FMDERR("Error in rds_std");
Ankur Nandwanid928d542011-08-11 13:15:41 -07002511 break;
Ankur Nandwani8f972e52011-08-24 11:48:32 -07002512 case FM_TRANS:
2513 radio->trans_conf.rds_std = ctrl->value;
2514 retval = hci_set_fm_trans_conf(
2515 &radio->trans_conf,
2516 radio->fm_hdev);
2517 if (retval < 0)
2518 FMDERR("Error in rds_Std");
2519 break;
2520 default:
2521 retval = -EINVAL;
Ankur Nandwanid928d542011-08-11 13:15:41 -07002522 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002523 break;
2524 case V4L2_CID_PRIVATE_IRIS_RDSON:
Ankur Nandwani8f972e52011-08-24 11:48:32 -07002525 switch (radio->mode) {
2526 case FM_RECV:
2527 radio->recv_conf.rds_std = ctrl->value;
2528 retval = hci_set_fm_recv_conf(
2529 &radio->recv_conf,
2530 radio->fm_hdev);
2531 if (retval < 0)
2532 FMDERR("Error in rds_std");
2533 break;
2534 case FM_TRANS:
2535 radio->trans_conf.rds_std = ctrl->value;
2536 retval = hci_set_fm_trans_conf(
2537 &radio->trans_conf,
2538 radio->fm_hdev);
2539 if (retval < 0)
2540 FMDERR("Error in rds_Std");
2541 break;
2542 default:
2543 retval = -EINVAL;
2544 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002545 break;
2546 case V4L2_CID_PRIVATE_IRIS_RDSGROUP_MASK:
2547 radio->rds_grp.rds_grp_enable_mask = ctrl->value;
2548 retval = hci_fm_rds_grp(&radio->rds_grp, radio->fm_hdev);
2549 break;
2550 case V4L2_CID_PRIVATE_IRIS_RDSGROUP_PROC:
2551 rds_grps_proc = radio->g_rds_grp_proc_ps | ctrl->value;
Srinivasa Rao Uppala58273f82011-08-10 19:07:45 -07002552 radio->g_rds_grp_proc_ps = (rds_grps_proc >> RDS_CONFIG_OFFSET);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002553 retval = hci_fm_rds_grps_process(
Srinivasa Rao Uppala58273f82011-08-10 19:07:45 -07002554 &radio->g_rds_grp_proc_ps,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002555 radio->fm_hdev);
2556 break;
2557 case V4L2_CID_PRIVATE_IRIS_RDSD_BUF:
2558 radio->rds_grp.rds_buf_size = ctrl->value;
2559 break;
2560 case V4L2_CID_PRIVATE_IRIS_PSALL:
Srinivasa Rao Uppala58273f82011-08-10 19:07:45 -07002561 rds_grps_proc = (ctrl->value << RDS_CONFIG_OFFSET);
2562 radio->g_rds_grp_proc_ps |= rds_grps_proc;
2563 retval = hci_fm_rds_grps_process(
2564 &radio->g_rds_grp_proc_ps,
2565 radio->fm_hdev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002566 break;
Srinivasa Rao Uppala3c7a8eb2011-09-18 09:10:21 +05302567 case V4L2_CID_PRIVATE_IRIS_AF_JUMP:
2568 rds_grps_proc = (ctrl->value << RDS_AF_JUMP_OFFSET);
2569 radio->g_rds_grp_proc_ps |= rds_grps_proc;
2570 retval = hci_fm_rds_grps_process(
2571 &radio->g_rds_grp_proc_ps,
2572 radio->fm_hdev);
2573 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002574 case V4L2_CID_PRIVATE_IRIS_LP_MODE:
2575 break;
2576 case V4L2_CID_PRIVATE_IRIS_ANTENNA:
2577 temp_val = ctrl->value;
2578 retval = hci_fm_set_antenna(&temp_val, radio->fm_hdev);
2579 break;
2580 case V4L2_CID_RDS_TX_PTY:
Ankur Nandwanid928d542011-08-11 13:15:41 -07002581 radio->pty = ctrl->value;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002582 break;
2583 case V4L2_CID_RDS_TX_PI:
Ankur Nandwanid928d542011-08-11 13:15:41 -07002584 radio->pi = ctrl->value;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002585 break;
2586 case V4L2_CID_PRIVATE_IRIS_STOP_RDS_TX_PS_NAME:
Srinivasa Rao Uppala3c7a8eb2011-09-18 09:10:21 +05302587 tx_ps.ps_control = 0x00;
2588 retval = radio_hci_request(radio->fm_hdev, hci_trans_ps_req,
2589 (unsigned long)&tx_ps, RADIO_HCI_TIMEOUT);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002590 break;
2591 case V4L2_CID_PRIVATE_IRIS_STOP_RDS_TX_RT:
Srinivasa Rao Uppala3c7a8eb2011-09-18 09:10:21 +05302592 tx_rt.rt_control = 0x00;
2593 retval = radio_hci_request(radio->fm_hdev, hci_trans_rt_req,
2594 (unsigned long)&tx_rt, RADIO_HCI_TIMEOUT);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002595 break;
2596 case V4L2_CID_PRIVATE_IRIS_TX_SETPSREPEATCOUNT:
Ankur Nandwanid928d542011-08-11 13:15:41 -07002597 radio->ps_repeatcount = ctrl->value;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002598 break;
2599 case V4L2_CID_TUNE_POWER_LEVEL:
2600 break;
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -07002601 case V4L2_CID_PRIVATE_IRIS_SOFT_MUTE:
2602 radio->mute_mode.soft_mute = ctrl->value;
2603 retval = hci_set_fm_mute_mode(
2604 &radio->mute_mode,
2605 radio->fm_hdev);
2606 if (retval < 0)
2607 FMDERR("Error while setting FM soft mute"" %d\n",
2608 retval);
2609 break;
2610 case V4L2_CID_PRIVATE_IRIS_RIVA_ACCS_ADDR:
2611 radio->riva_data_req.cmd_params.start_addr = ctrl->value;
2612 break;
2613 case V4L2_CID_PRIVATE_IRIS_RIVA_ACCS_LEN:
2614 radio->riva_data_req.cmd_params.length = ctrl->value;
2615 break;
2616 case V4L2_CID_PRIVATE_IRIS_RIVA_POKE:
2617 memcpy(radio->riva_data_req.data, (void *)ctrl->value,
2618 radio->riva_data_req.cmd_params.length);
2619 radio->riva_data_req.cmd_params.subopcode = RIVA_POKE_OPCODE;
2620 retval = hci_poke_data(&radio->riva_data_req , radio->fm_hdev);
2621 break;
2622 case V4L2_CID_PRIVATE_IRIS_SSBI_ACCS_ADDR:
2623 radio->ssbi_data_accs.start_addr = ctrl->value;
2624 break;
2625 case V4L2_CID_PRIVATE_IRIS_SSBI_POKE:
2626 radio->ssbi_data_accs.data = ctrl->value;
2627 retval = hci_ssbi_poke_reg(&radio->ssbi_data_accs ,
2628 radio->fm_hdev);
2629 break;
2630 case V4L2_CID_PRIVATE_IRIS_RIVA_PEEK:
2631 radio->riva_data_req.cmd_params.subopcode = RIVA_PEEK_OPCODE;
2632 ctrl->value = hci_peek_data(&radio->riva_data_req.cmd_params ,
2633 radio->fm_hdev);
2634 break;
2635 case V4L2_CID_PRIVATE_IRIS_SSBI_PEEK:
2636 radio->ssbi_peek_reg.start_address = ctrl->value;
2637 hci_ssbi_peek_reg(&radio->ssbi_peek_reg, radio->fm_hdev);
2638 break;
Srinivasa Rao Uppala0f4098f2011-09-06 16:46:28 +05302639 case V4L2_CID_PRIVATE_IRIS_RDS_GRP_COUNTERS:
2640 temp_val = ctrl->value;
2641 hci_read_grp_counters(&temp_val, radio->fm_hdev);
2642 break;
2643 case V4L2_CID_PRIVATE_IRIS_HLSI:
2644 retval = hci_cmd(HCI_FM_GET_RECV_CONF_CMD,
2645 radio->fm_hdev);
2646 if (retval)
2647 break;
2648 radio->recv_conf.hlsi = ctrl->value;
2649 retval = hci_set_fm_recv_conf(
2650 &radio->recv_conf,
2651 radio->fm_hdev);
2652 break;
2653 case V4L2_CID_PRIVATE_IRIS_SET_NOTCH_FILTER:
2654 temp_val = ctrl->value;
2655 retval = hci_set_notch_filter(&temp_val, radio->fm_hdev);
2656 break;
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -07002657
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002658 default:
2659 retval = -EINVAL;
2660 }
2661 return retval;
2662}
2663
2664static int iris_vidioc_g_tuner(struct file *file, void *priv,
2665 struct v4l2_tuner *tuner)
2666{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002667 int retval;
Ankur Nandwanid928d542011-08-11 13:15:41 -07002668 struct iris_device *radio = video_get_drvdata(video_devdata(file));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002669 if (tuner->index > 0)
2670 return -EINVAL;
2671
2672 retval = hci_cmd(HCI_FM_GET_STATION_PARAM_CMD, radio->fm_hdev);
2673 if (retval < 0)
2674 return retval;
2675
2676 tuner->type = V4L2_TUNER_RADIO;
2677 tuner->rangelow = radio->recv_conf.band_low_limit * TUNE_PARAM;
2678 tuner->rangehigh = radio->recv_conf.band_high_limit * TUNE_PARAM;
2679 tuner->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
2680 tuner->capability = V4L2_TUNER_CAP_LOW;
2681 tuner->signal = radio->fm_st_rsp.station_rsp.rssi;
2682 tuner->audmode = radio->fm_st_rsp.station_rsp.stereo_prg;
2683 tuner->afc = 0;
2684
2685 return 0;
2686}
2687
2688static int iris_vidioc_s_tuner(struct file *file, void *priv,
2689 struct v4l2_tuner *tuner)
2690{
2691 struct iris_device *radio = video_get_drvdata(video_devdata(file));
Ankur Nandwanid928d542011-08-11 13:15:41 -07002692 int retval = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002693 if (tuner->index > 0)
2694 return -EINVAL;
2695
Ankur Nandwanid928d542011-08-11 13:15:41 -07002696 if (radio->mode == FM_RECV) {
2697 radio->recv_conf.band_low_limit = tuner->rangelow / TUNE_PARAM;
2698 radio->recv_conf.band_high_limit =
2699 tuner->rangehigh / TUNE_PARAM;
2700 if (tuner->audmode == V4L2_TUNER_MODE_MONO) {
2701 radio->stereo_mode.stereo_mode = 0x01;
2702 retval = hci_set_fm_stereo_mode(
2703 &radio->stereo_mode,
2704 radio->fm_hdev);
2705 } else {
2706 radio->stereo_mode.stereo_mode = 0x00;
2707 retval = hci_set_fm_stereo_mode(
2708 &radio->stereo_mode,
2709 radio->fm_hdev);
2710 }
2711 if (retval < 0)
2712 FMDERR(": set tuner failed with %d\n", retval);
2713 return retval;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002714 } else {
Ankur Nandwanid928d542011-08-11 13:15:41 -07002715 if (radio->mode == FM_TRANS) {
2716 radio->trans_conf.band_low_limit =
2717 tuner->rangelow / TUNE_PARAM;
2718 radio->trans_conf.band_high_limit =
2719 tuner->rangehigh / TUNE_PARAM;
2720 } else {
2721 return -EINVAL;
2722 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002723 }
Ankur Nandwanid928d542011-08-11 13:15:41 -07002724
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002725 return retval;
2726}
2727
2728static int iris_vidioc_g_frequency(struct file *file, void *priv,
2729 struct v4l2_frequency *freq)
2730{
2731 struct iris_device *radio = video_get_drvdata(video_devdata(file));
2732 int retval;
2733
2734 freq->type = V4L2_TUNER_RADIO;
2735 retval = hci_cmd(HCI_FM_GET_STATION_PARAM_CMD, radio->fm_hdev);
2736 if (retval < 0)
2737 FMDERR("get frequency failed %d\n", retval);
2738 else
2739 freq->frequency =
2740 radio->fm_st_rsp.station_rsp.station_freq * TUNE_PARAM;
2741 return retval;
2742}
2743
2744static int iris_vidioc_s_frequency(struct file *file, void *priv,
2745 struct v4l2_frequency *freq)
2746{
2747 struct iris_device *radio = video_get_drvdata(video_devdata(file));
2748 int retval = -1;
2749 freq->frequency = freq->frequency / TUNE_PARAM;
2750
2751 if (freq->type != V4L2_TUNER_RADIO)
2752 return -EINVAL;
2753
Ankur Nandwani8f972e52011-08-24 11:48:32 -07002754 /* We turn off RDS prior to tuning to a new station.
2755 because of a bug in SoC which prevents tuning
2756 during RDS transmission.
2757 */
2758 if (radio->mode == FM_TRANS
2759 && (radio->trans_conf.rds_std == 0 ||
2760 radio->trans_conf.rds_std == 1)) {
2761 radio->prev_trans_rds = radio->trans_conf.rds_std;
2762 radio->trans_conf.rds_std = 2;
2763 hci_set_fm_trans_conf(&radio->trans_conf,
2764 radio->fm_hdev);
2765 }
2766
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002767 retval = iris_set_freq(radio, freq->frequency);
Ankur Nandwani8f972e52011-08-24 11:48:32 -07002768
2769 if (radio->mode == FM_TRANS
2770 && radio->trans_conf.rds_std == 2
2771 && (radio->prev_trans_rds == 1
2772 || radio->prev_trans_rds == 0)) {
2773 radio->trans_conf.rds_std = radio->prev_trans_rds;
2774 hci_set_fm_trans_conf(&radio->trans_conf,
2775 radio->fm_hdev);
2776 }
2777
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002778 if (retval < 0)
2779 FMDERR(" set frequency failed with %d\n", retval);
2780 return retval;
2781}
2782
2783static int iris_vidioc_dqbuf(struct file *file, void *priv,
2784 struct v4l2_buffer *buffer)
2785{
2786 struct iris_device *radio = video_get_drvdata(video_devdata(file));
2787 enum iris_buf_t buf_type = buffer->index;
2788 struct kfifo *data_fifo;
2789 unsigned char *buf = (unsigned char *)buffer->m.userptr;
2790 unsigned int len = buffer->length;
2791 if (!access_ok(VERIFY_WRITE, buf, len))
2792 return -EFAULT;
2793 if ((buf_type < IRIS_BUF_MAX) && (buf_type >= 0)) {
2794 data_fifo = &radio->data_buf[buf_type];
2795 if (buf_type == IRIS_BUF_EVENTS)
2796 if (wait_event_interruptible(radio->event_queue,
2797 kfifo_len(data_fifo)) < 0)
2798 return -EINTR;
2799 } else {
2800 FMDERR("invalid buffer type\n");
2801 return -EINVAL;
2802 }
2803 buffer->bytesused = kfifo_out_locked(data_fifo, buf, len,
2804 &radio->buf_lock[buf_type]);
2805
2806 return 0;
2807}
2808
2809static int iris_vidioc_g_fmt_type_private(struct file *file, void *priv,
2810 struct v4l2_format *f)
2811{
2812 return 0;
2813
2814}
2815
2816static int iris_vidioc_s_hw_freq_seek(struct file *file, void *priv,
2817 struct v4l2_hw_freq_seek *seek)
2818{
2819 struct iris_device *radio = video_get_drvdata(video_devdata(file));
2820 int dir;
2821 if (seek->seek_upward)
2822 dir = SRCH_DIR_UP;
2823 else
2824 dir = SRCH_DIR_DOWN;
2825 return iris_search(radio, CTRL_ON, dir);
2826}
2827
2828static int iris_vidioc_querycap(struct file *file, void *priv,
2829 struct v4l2_capability *capability)
2830{
2831 struct iris_device *radio;
2832 radio = video_get_drvdata(video_devdata(file));
2833 strlcpy(capability->driver, DRIVER_NAME, sizeof(capability->driver));
2834 strlcpy(capability->card, DRIVER_CARD, sizeof(capability->card));
2835 radio->g_cap = capability;
2836 return 0;
2837}
2838
2839
2840static const struct v4l2_ioctl_ops iris_ioctl_ops = {
2841 .vidioc_querycap = iris_vidioc_querycap,
2842 .vidioc_queryctrl = iris_vidioc_queryctrl,
2843 .vidioc_g_ctrl = iris_vidioc_g_ctrl,
2844 .vidioc_s_ctrl = iris_vidioc_s_ctrl,
2845 .vidioc_g_tuner = iris_vidioc_g_tuner,
2846 .vidioc_s_tuner = iris_vidioc_s_tuner,
2847 .vidioc_g_frequency = iris_vidioc_g_frequency,
2848 .vidioc_s_frequency = iris_vidioc_s_frequency,
2849 .vidioc_s_hw_freq_seek = iris_vidioc_s_hw_freq_seek,
2850 .vidioc_dqbuf = iris_vidioc_dqbuf,
2851 .vidioc_g_fmt_type_private = iris_vidioc_g_fmt_type_private,
2852 .vidioc_s_ext_ctrls = iris_vidioc_s_ext_ctrls,
2853};
2854
2855static const struct v4l2_file_operations iris_fops = {
2856 .owner = THIS_MODULE,
2857 .unlocked_ioctl = video_ioctl2,
2858};
2859
2860static struct video_device iris_viddev_template = {
2861 .fops = &iris_fops,
2862 .ioctl_ops = &iris_ioctl_ops,
2863 .name = DRIVER_NAME,
2864 .release = video_device_release,
2865};
2866
2867static struct video_device *video_get_dev(void)
2868{
2869 return priv_videodev;
2870}
2871
2872static int __init iris_probe(struct platform_device *pdev)
2873{
2874 struct iris_device *radio;
2875 int retval;
2876 int radio_nr = -1;
2877 int i;
2878
2879 if (!pdev) {
2880 FMDERR(": pdev is null\n");
2881 return -ENOMEM;
2882 }
2883
2884 radio = kzalloc(sizeof(struct iris_device), GFP_KERNEL);
2885 if (!radio) {
2886 FMDERR(": Could not allocate radio device\n");
2887 return -ENOMEM;
2888 }
2889
2890 radio->dev = &pdev->dev;
2891 platform_set_drvdata(pdev, radio);
2892
2893 radio->videodev = video_device_alloc();
2894 if (!radio->videodev) {
2895 FMDERR(": Could not allocate V4L device\n");
2896 kfree(radio);
2897 return -ENOMEM;
2898 }
2899
2900 memcpy(radio->videodev, &iris_viddev_template,
2901 sizeof(iris_viddev_template));
2902
2903 for (i = 0; i < IRIS_BUF_MAX; i++) {
2904 int kfifo_alloc_rc = 0;
2905 spin_lock_init(&radio->buf_lock[i]);
2906
Srinivasa Rao Uppala6cc0e322011-08-12 10:54:48 -07002907 if ((i == IRIS_BUF_RAW_RDS) || (i == IRIS_BUF_PEEK))
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002908 kfifo_alloc_rc = kfifo_alloc(&radio->data_buf[i],
2909 rds_buf*3, GFP_KERNEL);
2910 else
2911 kfifo_alloc_rc = kfifo_alloc(&radio->data_buf[i],
2912 STD_BUF_SIZE, GFP_KERNEL);
2913
2914 if (kfifo_alloc_rc != 0) {
2915 FMDERR("failed allocating buffers %d\n",
2916 kfifo_alloc_rc);
2917 for (; i > -1; i--) {
2918 kfifo_free(&radio->data_buf[i]);
2919 kfree(radio);
2920 return -ENOMEM;
2921 }
2922 }
2923 }
2924
2925 mutex_init(&radio->lock);
2926 init_completion(&radio->sync_xfr_start);
2927 radio->tune_req = 0;
Ankur Nandwani8f972e52011-08-24 11:48:32 -07002928 radio->prev_trans_rds = 2;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002929 init_waitqueue_head(&radio->event_queue);
2930 init_waitqueue_head(&radio->read_queue);
2931
2932 video_set_drvdata(radio->videodev, radio);
2933
2934 if (NULL == video_get_drvdata(radio->videodev))
2935 FMDERR(": video_get_drvdata failed\n");
2936
2937 retval = video_register_device(radio->videodev, VFL_TYPE_RADIO,
2938 radio_nr);
2939 if (retval) {
2940 FMDERR(": Could not register video device\n");
2941 video_device_release(radio->videodev);
2942 for (; i > -1; i--)
2943 kfifo_free(&radio->data_buf[i]);
2944 kfree(radio);
2945 return retval;
2946 } else {
2947 priv_videodev = kzalloc(sizeof(struct video_device),
2948 GFP_KERNEL);
2949 memcpy(priv_videodev, radio->videodev,
2950 sizeof(struct video_device));
2951 }
2952 return 0;
2953}
2954
2955
2956static int __devexit iris_remove(struct platform_device *pdev)
2957{
2958 int i;
2959 struct iris_device *radio = platform_get_drvdata(pdev);
2960
2961 video_unregister_device(radio->videodev);
2962
2963 for (i = 0; i < IRIS_BUF_MAX; i++)
2964 kfifo_free(&radio->data_buf[i]);
2965
2966 kfree(radio);
2967
2968 platform_set_drvdata(pdev, NULL);
2969
2970 return 0;
2971}
2972
2973static struct platform_driver iris_driver = {
2974 .driver = {
2975 .owner = THIS_MODULE,
2976 .name = "iris_fm",
2977 },
2978 .remove = __devexit_p(iris_remove),
2979};
2980
2981static int __init iris_radio_init(void)
2982{
2983 return platform_driver_probe(&iris_driver, iris_probe);
2984}
2985module_init(iris_radio_init);
2986
2987static void __exit iris_radio_exit(void)
2988{
2989 platform_driver_unregister(&iris_driver);
2990}
2991module_exit(iris_radio_exit);
2992
2993MODULE_LICENSE("GPL v2");
2994MODULE_AUTHOR(DRIVER_AUTHOR);
2995MODULE_DESCRIPTION(DRIVER_DESC);