blob: ab80613636bec5f60edea348fb37eb1cb081d5bf [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;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070063
64 struct video_device *videodev;
65
66 struct mutex lock;
67 spinlock_t buf_lock[IRIS_BUF_MAX];
68 wait_queue_head_t event_queue;
69 wait_queue_head_t read_queue;
70
71 struct radio_hci_dev *fm_hdev;
72
73 struct v4l2_capability *g_cap;
74 struct v4l2_control *g_ctl;
75
76 struct hci_fm_mute_mode_req mute_mode;
77 struct hci_fm_stereo_mode_req stereo_mode;
78 struct hci_fm_station_rsp fm_st_rsp;
79 struct hci_fm_search_station_req srch_st;
80 struct hci_fm_search_rds_station_req srch_rds;
81 struct hci_fm_search_station_list_req srch_st_list;
82 struct hci_fm_recv_conf_req recv_conf;
Ankur Nandwanid928d542011-08-11 13:15:41 -070083 struct hci_fm_trans_conf_req_struct trans_conf;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070084 struct hci_fm_rds_grp_req rds_grp;
85 unsigned char g_search_mode;
Ankur Nandwanid928d542011-08-11 13:15:41 -070086 unsigned int tone_freq;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070087 unsigned char g_scan_time;
88 unsigned int g_antenna;
89 unsigned int g_rds_grp_proc_ps;
90 enum iris_region_t region;
91 struct hci_fm_dbg_param_rsp st_dbg_param;
92 struct hci_ev_srch_list_compl srch_st_result;
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -070093 struct hci_fm_riva_poke riva_data_req;
94 struct hci_fm_ssbi_req ssbi_data_accs;
95 struct hci_fm_ssbi_peek ssbi_peek_reg;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070096};
97
98static struct video_device *priv_videodev;
99
100static struct v4l2_queryctrl iris_v4l2_queryctrl[] = {
101 {
102 .id = V4L2_CID_AUDIO_VOLUME,
103 .type = V4L2_CTRL_TYPE_INTEGER,
104 .name = "Volume",
105 .minimum = 0,
106 .maximum = 15,
107 .step = 1,
108 .default_value = 15,
109 },
110 {
111 .id = V4L2_CID_AUDIO_BALANCE,
112 .flags = V4L2_CTRL_FLAG_DISABLED,
113 },
114 {
115 .id = V4L2_CID_AUDIO_BASS,
116 .flags = V4L2_CTRL_FLAG_DISABLED,
117 },
118 {
119 .id = V4L2_CID_AUDIO_TREBLE,
120 .flags = V4L2_CTRL_FLAG_DISABLED,
121 },
122 {
123 .id = V4L2_CID_AUDIO_MUTE,
124 .type = V4L2_CTRL_TYPE_BOOLEAN,
125 .name = "Mute",
126 .minimum = 0,
127 .maximum = 1,
128 .step = 1,
129 .default_value = 1,
130 },
131 {
132 .id = V4L2_CID_AUDIO_LOUDNESS,
133 .flags = V4L2_CTRL_FLAG_DISABLED,
134 },
135 {
136 .id = V4L2_CID_PRIVATE_IRIS_SRCHMODE,
137 .type = V4L2_CTRL_TYPE_INTEGER,
138 .name = "Search mode",
139 .minimum = 0,
140 .maximum = 7,
141 .step = 1,
142 .default_value = 0,
143 },
144 {
145 .id = V4L2_CID_PRIVATE_IRIS_SCANDWELL,
146 .type = V4L2_CTRL_TYPE_INTEGER,
147 .name = "Search dwell time",
148 .minimum = 0,
149 .maximum = 7,
150 .step = 1,
151 .default_value = 0,
152 },
153 {
154 .id = V4L2_CID_PRIVATE_IRIS_SRCHON,
155 .type = V4L2_CTRL_TYPE_BOOLEAN,
156 .name = "Search on/off",
157 .minimum = 0,
158 .maximum = 1,
159 .step = 1,
160 .default_value = 1,
161
162 },
163 {
164 .id = V4L2_CID_PRIVATE_IRIS_STATE,
165 .type = V4L2_CTRL_TYPE_INTEGER,
166 .name = "radio 0ff/rx/tx/reset",
167 .minimum = 0,
168 .maximum = 3,
169 .step = 1,
170 .default_value = 1,
171
172 },
173 {
174 .id = V4L2_CID_PRIVATE_IRIS_REGION,
175 .type = V4L2_CTRL_TYPE_INTEGER,
176 .name = "radio standard",
177 .minimum = 0,
178 .maximum = 2,
179 .step = 1,
180 .default_value = 0,
181 },
182 {
183 .id = V4L2_CID_PRIVATE_IRIS_SIGNAL_TH,
184 .type = V4L2_CTRL_TYPE_INTEGER,
185 .name = "Signal Threshold",
186 .minimum = 0x80,
187 .maximum = 0x7F,
188 .step = 1,
189 .default_value = 0,
190 },
191 {
192 .id = V4L2_CID_PRIVATE_IRIS_SRCH_PTY,
193 .type = V4L2_CTRL_TYPE_INTEGER,
194 .name = "Search PTY",
195 .minimum = 0,
196 .maximum = 31,
197 .default_value = 0,
198 },
199 {
200 .id = V4L2_CID_PRIVATE_IRIS_SRCH_PI,
201 .type = V4L2_CTRL_TYPE_INTEGER,
202 .name = "Search PI",
203 .minimum = 0,
204 .maximum = 0xFF,
205 .default_value = 0,
206 },
207 {
208 .id = V4L2_CID_PRIVATE_IRIS_SRCH_CNT,
209 .type = V4L2_CTRL_TYPE_INTEGER,
210 .name = "Preset num",
211 .minimum = 0,
212 .maximum = 12,
213 .default_value = 0,
214 },
215 {
216 .id = V4L2_CID_PRIVATE_IRIS_EMPHASIS,
217 .type = V4L2_CTRL_TYPE_BOOLEAN,
218 .name = "Emphasis",
219 .minimum = 0,
220 .maximum = 1,
221 .default_value = 0,
222 },
223 {
224 .id = V4L2_CID_PRIVATE_IRIS_RDS_STD,
225 .type = V4L2_CTRL_TYPE_BOOLEAN,
226 .name = "RDS standard",
227 .minimum = 0,
228 .maximum = 1,
229 .default_value = 0,
230 },
231 {
232 .id = V4L2_CID_PRIVATE_IRIS_SPACING,
233 .type = V4L2_CTRL_TYPE_INTEGER,
234 .name = "Channel spacing",
235 .minimum = 0,
236 .maximum = 2,
237 .default_value = 0,
238 },
239 {
240 .id = V4L2_CID_PRIVATE_IRIS_RDSON,
241 .type = V4L2_CTRL_TYPE_BOOLEAN,
242 .name = "RDS on/off",
243 .minimum = 0,
244 .maximum = 1,
245 .default_value = 0,
246 },
247 {
248 .id = V4L2_CID_PRIVATE_IRIS_RDSGROUP_MASK,
249 .type = V4L2_CTRL_TYPE_INTEGER,
250 .name = "RDS group mask",
251 .minimum = 0,
252 .maximum = 0xFFFFFFFF,
253 .default_value = 0,
254 },
255 {
256 .id = V4L2_CID_PRIVATE_IRIS_RDSGROUP_PROC,
257 .type = V4L2_CTRL_TYPE_INTEGER,
258 .name = "RDS processing",
259 .minimum = 0,
260 .maximum = 0xFF,
261 .default_value = 0,
262 },
263 {
264 .id = V4L2_CID_PRIVATE_IRIS_RDSD_BUF,
265 .type = V4L2_CTRL_TYPE_INTEGER,
266 .name = "RDS data groups to buffer",
267 .minimum = 1,
268 .maximum = 21,
269 .default_value = 0,
270 },
271 {
272 .id = V4L2_CID_PRIVATE_IRIS_PSALL,
273 .type = V4L2_CTRL_TYPE_BOOLEAN,
274 .name = "pass all ps strings",
275 .minimum = 0,
276 .maximum = 1,
277 .default_value = 0,
278 },
279 {
280 .id = V4L2_CID_PRIVATE_IRIS_LP_MODE,
281 .type = V4L2_CTRL_TYPE_BOOLEAN,
282 .name = "Low power mode",
283 .minimum = 0,
284 .maximum = 1,
285 .default_value = 0,
286 },
287 {
288 .id = V4L2_CID_PRIVATE_IRIS_ANTENNA,
289 .type = V4L2_CTRL_TYPE_BOOLEAN,
290 .name = "headset/internal",
291 .minimum = 0,
292 .maximum = 1,
293 .default_value = 0,
294 },
295
296 {
297 .id = V4L2_CID_PRIVATE_IRIS_TX_SETPSREPEATCOUNT,
298 .type = V4L2_CTRL_TYPE_INTEGER,
299 .name = "Set PS REPEATCOUNT",
300 .minimum = 0,
301 .maximum = 15,
302 },
303 {
304 .id = V4L2_CID_PRIVATE_IRIS_STOP_RDS_TX_PS_NAME,
305 .type = V4L2_CTRL_TYPE_BOOLEAN,
306 .name = "Stop PS NAME",
307 .minimum = 0,
308 .maximum = 1,
309 },
310 {
311 .id = V4L2_CID_PRIVATE_IRIS_STOP_RDS_TX_RT,
312 .type = V4L2_CTRL_TYPE_BOOLEAN,
313 .name = "Stop RT",
314 .minimum = 0,
315 .maximum = 1,
316 },
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -0700317 {
318 .id = V4L2_CID_PRIVATE_IRIS_SOFT_MUTE,
319 .type = V4L2_CTRL_TYPE_BOOLEAN,
320 .name = "Soft Mute",
321 .minimum = 0,
322 .maximum = 1,
323 },
324 {
325 .id = V4L2_CID_PRIVATE_IRIS_RIVA_ACCS_ADDR,
326 .type = V4L2_CTRL_TYPE_BOOLEAN,
327 .name = "Riva addr",
328 .minimum = 0,
329 .maximum = 0x31E0004,
330 },
331 {
332 .id = V4L2_CID_PRIVATE_IRIS_RIVA_ACCS_LEN,
333 .type = V4L2_CTRL_TYPE_INTEGER,
334 .name = "Data len",
335 .minimum = 0,
336 .maximum = 0xFF,
337 },
338 {
339 .id = V4L2_CID_PRIVATE_IRIS_RIVA_PEEK,
340 .type = V4L2_CTRL_TYPE_BOOLEAN,
341 .name = "Riva peek",
342 .minimum = 0,
343 .maximum = 1,
344 },
345 {
346 .id = V4L2_CID_PRIVATE_IRIS_RIVA_POKE,
347 .type = V4L2_CTRL_TYPE_INTEGER,
348 .name = "Riva poke",
349 .minimum = 0,
350 .maximum = 0x31E0004,
351 },
352 {
353 .id = V4L2_CID_PRIVATE_IRIS_SSBI_ACCS_ADDR,
354 .type = V4L2_CTRL_TYPE_INTEGER,
355 .name = "Ssbi addr",
356 .minimum = 0,
357 .maximum = 0x37F,
358 },
359 {
360 .id = V4L2_CID_PRIVATE_IRIS_SSBI_PEEK,
361 .type = V4L2_CTRL_TYPE_INTEGER,
362 .name = "Ssbi peek",
363 .minimum = 0,
364 .maximum = 0x37F,
365 },
366 {
367 .id = V4L2_CID_PRIVATE_IRIS_SSBI_POKE,
368 .type = V4L2_CTRL_TYPE_INTEGER,
369 .name = "ssbi poke",
370 .minimum = 0x01,
371 .maximum = 0xFF,
372 },
373 {
374 .id = V4L2_CID_PRIVATE_IRIS_HLSI,
375 .type = V4L2_CTRL_TYPE_INTEGER,
376 .name = "set hlsi",
377 .minimum = 0,
378 .maximum = 2,
379 },
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700380
381};
382
383static void iris_q_event(struct iris_device *radio,
384 enum iris_evt_t event)
385{
386 struct kfifo *data_b = &radio->data_buf[IRIS_BUF_EVENTS];
387 unsigned char evt = event;
388 if (kfifo_in_locked(data_b, &evt, 1, &radio->buf_lock[IRIS_BUF_EVENTS]))
389 wake_up_interruptible(&radio->event_queue);
390}
391
392static int hci_send_frame(struct sk_buff *skb)
393{
394 struct radio_hci_dev *hdev = (struct radio_hci_dev *) skb->dev;
395
396 if (!hdev) {
397 kfree_skb(skb);
398 return -ENODEV;
399 }
400
401 __net_timestamp(skb);
402
403 skb_orphan(skb);
404 return hdev->send(skb);
405}
406
407static void radio_hci_cmd_task(unsigned long arg)
408{
409 struct radio_hci_dev *hdev = (struct radio_hci_dev *) arg;
410 struct sk_buff *skb;
411 if (!(atomic_read(&hdev->cmd_cnt))
412 && time_after(jiffies, hdev->cmd_last_tx + HZ)) {
413 FMDERR("%s command tx timeout", hdev->name);
414 atomic_set(&hdev->cmd_cnt, 1);
415 }
416
417 skb = skb_dequeue(&hdev->cmd_q);
418 if (atomic_read(&hdev->cmd_cnt) && skb) {
419 kfree_skb(hdev->sent_cmd);
420 hdev->sent_cmd = skb_clone(skb, GFP_ATOMIC);
421 if (hdev->sent_cmd) {
422 atomic_dec(&hdev->cmd_cnt);
423 hci_send_frame(skb);
424 hdev->cmd_last_tx = jiffies;
425 } else {
426 skb_queue_head(&hdev->cmd_q, skb);
427 tasklet_schedule(&hdev->cmd_task);
428 }
429 }
430
431}
432
433static void radio_hci_rx_task(unsigned long arg)
434{
435 struct radio_hci_dev *hdev = (struct radio_hci_dev *) arg;
436 struct sk_buff *skb;
437
438 read_lock(&hci_task_lock);
439
440 skb = skb_dequeue(&hdev->rx_q);
441 radio_hci_event_packet(hdev, skb);
442
443 read_unlock(&hci_task_lock);
444}
445
446int radio_hci_register_dev(struct radio_hci_dev *hdev)
447{
448 struct iris_device *radio = video_get_drvdata(video_get_dev());
449 if (!radio) {
450 FMDERR(":radio is null");
451 return -EINVAL;
452 }
453
454 if (!hdev) {
455 FMDERR("hdev is null");
456 return -EINVAL;
457 }
458
459 hdev->flags = 0;
460
461 tasklet_init(&hdev->cmd_task, radio_hci_cmd_task, (unsigned long)
462 hdev);
463 tasklet_init(&hdev->rx_task, radio_hci_rx_task, (unsigned long)
464 hdev);
465
466 init_waitqueue_head(&hdev->req_wait_q);
467
468 skb_queue_head_init(&hdev->rx_q);
469 skb_queue_head_init(&hdev->cmd_q);
470 skb_queue_head_init(&hdev->raw_q);
471
472 if (!radio)
473 FMDERR(":radio is null");
474
475 radio->fm_hdev = hdev;
476
477 return 0;
478}
479EXPORT_SYMBOL(radio_hci_register_dev);
480
481int radio_hci_unregister_dev(struct radio_hci_dev *hdev)
482{
483 struct iris_device *radio = video_get_drvdata(video_get_dev());
484 if (!radio) {
485 FMDERR(":radio is null");
486 return -EINVAL;
487 }
488
489 tasklet_kill(&hdev->rx_task);
490 tasklet_kill(&hdev->cmd_task);
491 skb_queue_purge(&hdev->rx_q);
492 skb_queue_purge(&hdev->cmd_q);
493 skb_queue_purge(&hdev->raw_q);
494 kfree(radio->fm_hdev);
495 kfree(radio->videodev);
496
497 return 0;
498}
499EXPORT_SYMBOL(radio_hci_unregister_dev);
500
501int radio_hci_recv_frame(struct sk_buff *skb)
502{
503 struct radio_hci_dev *hdev = (struct radio_hci_dev *) skb->dev;
504 if (!hdev) {
505 FMDERR("%s hdev is null while receiving frame", hdev->name);
506 kfree_skb(skb);
507 return -ENXIO;
508 }
509
510 __net_timestamp(skb);
511
512 radio_hci_event_packet(hdev, skb);
513
514 return 0;
515}
516EXPORT_SYMBOL(radio_hci_recv_frame);
517
518int radio_hci_send_cmd(struct radio_hci_dev *hdev, __u16 opcode, __u32 plen,
519 void *param)
520{
521 int len = RADIO_HCI_COMMAND_HDR_SIZE + plen;
522 struct radio_hci_command_hdr *hdr;
523 struct sk_buff *skb;
524 int ret = 0;
525
526 skb = alloc_skb(len, GFP_ATOMIC);
527 if (!skb) {
528 FMDERR("%s no memory for command", hdev->name);
529 return -ENOMEM;
530 }
531
532 hdr = (struct radio_hci_command_hdr *) skb_put(skb,
533 RADIO_HCI_COMMAND_HDR_SIZE);
534 hdr->opcode = cpu_to_le16(opcode);
535 hdr->plen = plen;
536
537 if (plen)
538 memcpy(skb_put(skb, plen), param, plen);
539
540 skb->dev = (void *) hdev;
541
542 ret = hci_send_frame(skb);
543
544 return ret;
545}
546EXPORT_SYMBOL(radio_hci_send_cmd);
547
548static int hci_fm_enable_recv_req(struct radio_hci_dev *hdev,
549 unsigned long param)
550{
551 __u16 opcode = 0;
552
553 opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
554 HCI_OCF_FM_ENABLE_RECV_REQ);
555 return radio_hci_send_cmd(hdev, opcode, 0, NULL);
556}
557
Ankur Nandwanid928d542011-08-11 13:15:41 -0700558static int hci_fm_tone_generator(struct radio_hci_dev *hdev,
559 unsigned long param)
560{
561 struct iris_device *radio = video_get_drvdata(video_get_dev());
562 __u16 opcode = 0;
563
564 opcode = hci_opcode_pack(HCI_OGF_FM_DIAGNOSTIC_CMD_REQ,
565 HCI_FM_SET_INTERNAL_TONE_GENRATOR);
566 return radio_hci_send_cmd(hdev, opcode,
567 sizeof(radio->tone_freq), &radio->tone_freq);
568}
569
570static int hci_fm_enable_trans_req(struct radio_hci_dev *hdev,
571 unsigned long param)
572{
573 __u16 opcode = 0;
574
575 opcode = hci_opcode_pack(HCI_OGF_FM_TRANS_CTRL_CMD_REQ,
576 HCI_OCF_FM_ENABLE_TRANS_REQ);
577 return radio_hci_send_cmd(hdev, opcode, 0, NULL);
578}
579
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700580static int hci_fm_disable_recv_req(struct radio_hci_dev *hdev,
581 unsigned long param)
582{
583 __u16 opcode = 0;
584
585 opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
586 HCI_OCF_FM_DISABLE_RECV_REQ);
587 return radio_hci_send_cmd(hdev, opcode, 0, NULL);
588}
589
Ankur Nandwanid928d542011-08-11 13:15:41 -0700590static int hci_fm_disable_trans_req(struct radio_hci_dev *hdev,
591 unsigned long param)
592{
593 __u16 opcode = 0;
594
595 opcode = hci_opcode_pack(HCI_OGF_FM_TRANS_CTRL_CMD_REQ,
596 HCI_OCF_FM_DISABLE_TRANS_REQ);
597 return radio_hci_send_cmd(hdev, opcode, 0, NULL);
598}
599
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700600static int hci_get_fm_recv_conf_req(struct radio_hci_dev *hdev,
601 unsigned long param)
602{
603 __u16 opcode = 0;
604
605 opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
606 HCI_OCF_FM_GET_RECV_CONF_REQ);
607 return radio_hci_send_cmd(hdev, opcode, 0, NULL);
608}
609
610static int hci_set_fm_recv_conf_req(struct radio_hci_dev *hdev,
611 unsigned long param)
612{
613 __u16 opcode = 0;
614
615 struct hci_fm_recv_conf_req *recv_conf_req =
616 (struct hci_fm_recv_conf_req *) param;
617
618 opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
619 HCI_OCF_FM_SET_RECV_CONF_REQ);
620 return radio_hci_send_cmd(hdev, opcode, sizeof((*recv_conf_req)),
621 recv_conf_req);
622}
623
Ankur Nandwanid928d542011-08-11 13:15:41 -0700624static int hci_set_fm_trans_conf_req(struct radio_hci_dev *hdev,
625 unsigned long param)
626{
627 __u16 opcode = 0;
628
629 struct hci_fm_trans_conf_req_struct *trans_conf_req =
630 (struct hci_fm_trans_conf_req_struct *) param;
631
632 opcode = hci_opcode_pack(HCI_OGF_FM_TRANS_CTRL_CMD_REQ,
633 HCI_OCF_FM_SET_TRANS_CONF_REQ);
634 return radio_hci_send_cmd(hdev, opcode, sizeof((*trans_conf_req)),
635 trans_conf_req);
636}
637
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700638static int hci_fm_get_station_param_req(struct radio_hci_dev *hdev,
639 unsigned long param)
640{
641 __u16 opcode = 0;
642
643 opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
644 HCI_OCF_FM_GET_STATION_PARAM_REQ);
645 return radio_hci_send_cmd(hdev, opcode, 0, NULL);
646}
647
648static int hci_set_fm_mute_mode_req(struct radio_hci_dev *hdev,
649 unsigned long param)
650{
651 __u16 opcode = 0;
652 struct hci_fm_mute_mode_req *mute_mode_req =
653 (struct hci_fm_mute_mode_req *) param;
654
655 opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
656 HCI_OCF_FM_SET_MUTE_MODE_REQ);
657 return radio_hci_send_cmd(hdev, opcode, sizeof((*mute_mode_req)),
658 mute_mode_req);
659}
660
Ankur Nandwanid928d542011-08-11 13:15:41 -0700661
662static int hci_trans_ps_req(struct radio_hci_dev *hdev,
663 unsigned long param)
664{
665 __u16 opcode = 0;
666 struct hci_fm_tx_ps *tx_ps_req =
667 (struct hci_fm_tx_ps *) param;
668
669 opcode = hci_opcode_pack(HCI_OGF_FM_TRANS_CTRL_CMD_REQ,
670 HCI_OCF_FM_RDS_PS_REQ);
671
672 return radio_hci_send_cmd(hdev, opcode, sizeof((*tx_ps_req)),
673 tx_ps_req);
674}
675
676static int hci_trans_rt_req(struct radio_hci_dev *hdev,
677 unsigned long param)
678{
679 __u16 opcode = 0;
680 struct hci_fm_tx_rt *tx_rt_req =
681 (struct hci_fm_tx_rt *) param;
682
683 opcode = hci_opcode_pack(HCI_OGF_FM_TRANS_CTRL_CMD_REQ,
684 HCI_OCF_FM_RDS_RT_REQ);
685
686 return radio_hci_send_cmd(hdev, opcode, sizeof((*tx_rt_req)),
687 tx_rt_req);
688}
689
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700690static int hci_set_fm_stereo_mode_req(struct radio_hci_dev *hdev,
691 unsigned long param)
692{
693 __u16 opcode = 0;
694 struct hci_fm_stereo_mode_req *stereo_mode_req =
695 (struct hci_fm_stereo_mode_req *) param;
696 opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
697 HCI_OCF_FM_SET_STEREO_MODE_REQ);
698 return radio_hci_send_cmd(hdev, opcode, sizeof((*stereo_mode_req)),
699 stereo_mode_req);
700}
701
702static int hci_fm_set_antenna_req(struct radio_hci_dev *hdev,
703 unsigned long param)
704{
705 __u16 opcode = 0;
706
707 __u8 antenna = param;
708
709 opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
710 HCI_OCF_FM_SET_ANTENNA);
711 return radio_hci_send_cmd(hdev, opcode, sizeof(antenna), &antenna);
712}
713
714static int hci_fm_set_sig_threshold_req(struct radio_hci_dev *hdev,
715 unsigned long param)
716{
717 __u16 opcode = 0;
718
719 __u8 sig_threshold = param;
720
721 opcode = hci_opcode_pack(HCI_OGF_FM_COMMON_CTRL_CMD_REQ,
722 HCI_OCF_FM_SET_SIGNAL_THRESHOLD);
723 return radio_hci_send_cmd(hdev, opcode, sizeof(sig_threshold),
724 &sig_threshold);
725}
726
727static int hci_fm_get_sig_threshold_req(struct radio_hci_dev *hdev,
728 unsigned long param)
729{
730 __u16 opcode = 0;
731
732 opcode = hci_opcode_pack(HCI_OGF_FM_COMMON_CTRL_CMD_REQ,
733 HCI_OCF_FM_GET_SIGNAL_THRESHOLD);
734 return radio_hci_send_cmd(hdev, opcode, 0, NULL);
735}
736
737static int hci_fm_get_program_service_req(struct radio_hci_dev *hdev,
738 unsigned long param)
739{
740 __u16 opcode = 0;
741
742 opcode = hci_opcode_pack(HCI_OGF_FM_COMMON_CTRL_CMD_REQ,
743 HCI_OCF_FM_GET_PROGRAM_SERVICE_REQ);
744 return radio_hci_send_cmd(hdev, opcode, 0, NULL);
745}
746
747static int hci_fm_get_radio_text_req(struct radio_hci_dev *hdev,
748 unsigned long param)
749{
750 __u16 opcode = 0;
751
752 opcode = hci_opcode_pack(HCI_OGF_FM_COMMON_CTRL_CMD_REQ,
753 HCI_OCF_FM_GET_RADIO_TEXT_REQ);
754 return radio_hci_send_cmd(hdev, opcode, 0, NULL);
755}
756
757static int hci_fm_get_af_list_req(struct radio_hci_dev *hdev,
758 unsigned long param)
759{
760 __u16 opcode = 0;
761
762 opcode = hci_opcode_pack(HCI_OGF_FM_COMMON_CTRL_CMD_REQ,
763 HCI_OCF_FM_GET_AF_LIST_REQ);
764 return radio_hci_send_cmd(hdev, opcode, 0, NULL);
765}
766
767static int hci_fm_search_stations_req(struct radio_hci_dev *hdev,
768 unsigned long param)
769{
770 __u16 opcode = 0;
771 struct hci_fm_search_station_req *srch_stations =
772 (struct hci_fm_search_station_req *) param;
773
774 opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
775 HCI_OCF_FM_SEARCH_STATIONS);
776 return radio_hci_send_cmd(hdev, opcode, sizeof((*srch_stations)),
777 srch_stations);
778}
779
780static int hci_fm_srch_rds_stations_req(struct radio_hci_dev *hdev,
781 unsigned long param)
782{
783 __u16 opcode = 0;
784 struct hci_fm_search_rds_station_req *srch_stations =
785 (struct hci_fm_search_rds_station_req *) param;
786
787 opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
788 HCI_OCF_FM_SEARCH_RDS_STATIONS);
789 return radio_hci_send_cmd(hdev, opcode, sizeof((*srch_stations)),
790 srch_stations);
791}
792
793static int hci_fm_srch_station_list_req(struct radio_hci_dev *hdev,
794 unsigned long param)
795{
796 __u16 opcode = 0;
797 struct hci_fm_search_station_list_req *srch_list =
798 (struct hci_fm_search_station_list_req *) param;
799
800 opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
801 HCI_OCF_FM_SEARCH_STATIONS_LIST);
802 return radio_hci_send_cmd(hdev, opcode, sizeof((*srch_list)),
803 srch_list);
804}
805
806static int hci_fm_cancel_search_req(struct radio_hci_dev *hdev,
807 unsigned long param)
808{
809 __u16 opcode = 0;
810
811 opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
812 HCI_OCF_FM_CANCEL_SEARCH);
813 return radio_hci_send_cmd(hdev, opcode, 0, NULL);
814}
815
816static int hci_fm_rds_grp_process_req(struct radio_hci_dev *hdev,
817 unsigned long param)
818{
819 __u16 opcode = 0;
820
821 __u32 fm_grps_process = param;
822
823 opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
824 HCI_OCF_FM_RDS_GRP_PROCESS);
825 return radio_hci_send_cmd(hdev, opcode, sizeof(fm_grps_process),
826 &fm_grps_process);
827}
828
829static int hci_fm_tune_station_req(struct radio_hci_dev *hdev,
830 unsigned long param)
831{
832 __u16 opcode = 0;
833
834 __u32 tune_freq = param;
835
836 opcode = hci_opcode_pack(HCI_OGF_FM_COMMON_CTRL_CMD_REQ,
837 HCI_OCF_FM_TUNE_STATION_REQ);
838 return radio_hci_send_cmd(hdev, opcode, sizeof(tune_freq), &tune_freq);
839}
840
841static int hci_def_data_read_req(struct radio_hci_dev *hdev,
842 unsigned long param)
843{
844 __u16 opcode = 0;
845 struct hci_fm_def_data_rd_req *def_data_rd =
846 (struct hci_fm_def_data_rd_req *) param;
847
848 opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
849 HCI_OCF_FM_DEFAULT_DATA_READ);
850 return radio_hci_send_cmd(hdev, opcode, sizeof((*def_data_rd)),
851 def_data_rd);
852}
853
854static int hci_def_data_write_req(struct radio_hci_dev *hdev,
855 unsigned long param)
856{
857 __u16 opcode = 0;
858 struct hci_fm_def_data_wr_req *def_data_wr =
859 (struct hci_fm_def_data_wr_req *) param;
860
861 opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
862 HCI_OCF_FM_DEFAULT_DATA_WRITE);
863 return radio_hci_send_cmd(hdev, opcode, sizeof((*def_data_wr)),
864 def_data_wr);
865}
866
867static int hci_fm_reset_req(struct radio_hci_dev *hdev, unsigned long param)
868{
869 __u16 opcode = 0;
870
871 opcode = hci_opcode_pack(HCI_OGF_FM_COMMON_CTRL_CMD_REQ,
872 HCI_OCF_FM_RESET);
873 return radio_hci_send_cmd(hdev, opcode, 0, NULL);
874}
875
876static int hci_fm_get_feature_lists_req(struct radio_hci_dev *hdev,
877 unsigned long param)
878{
879 __u16 opcode = 0;
880
881 opcode = hci_opcode_pack(HCI_OGF_FM_COMMON_CTRL_CMD_REQ,
882 HCI_OCF_FM_GET_FEATURE_LIST);
883 return radio_hci_send_cmd(hdev, opcode, 0, NULL);
884}
885
886static int hci_fm_do_calibration_req(struct radio_hci_dev *hdev,
887 unsigned long param)
888{
889 __u16 opcode = 0;
890
891 __u8 mode = param;
892
893 opcode = hci_opcode_pack(HCI_OGF_FM_COMMON_CTRL_CMD_REQ,
894 HCI_OCF_FM_DO_CALIBRATION);
895 return radio_hci_send_cmd(hdev, opcode, sizeof(mode), &mode);
896}
897
898static int hci_read_grp_counters_req(struct radio_hci_dev *hdev,
899 unsigned long param)
900{
901 __u16 opcode = 0;
902
903 __u8 reset_counters = param;
904
905 opcode = hci_opcode_pack(HCI_OGF_FM_COMMON_CTRL_CMD_REQ,
906 HCI_OCF_FM_READ_GRP_COUNTERS);
907 return radio_hci_send_cmd(hdev, opcode, sizeof(reset_counters),
908 &reset_counters);
909}
910
911static int hci_peek_data_req(struct radio_hci_dev *hdev, unsigned long param)
912{
913 __u16 opcode = 0;
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -0700914 struct hci_fm_riva_data *peek_data = (struct hci_fm_riva_data *)param;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700915
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -0700916 opcode = hci_opcode_pack(HCI_OGF_FM_DIAGNOSTIC_CMD_REQ,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700917 HCI_OCF_FM_PEEK_DATA);
918 return radio_hci_send_cmd(hdev, opcode, sizeof((*peek_data)),
919 peek_data);
920}
921
922static int hci_poke_data_req(struct radio_hci_dev *hdev, unsigned long param)
923{
924 __u16 opcode = 0;
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -0700925 struct hci_fm_riva_poke *poke_data = (struct hci_fm_riva_poke *) param;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700926
Srinivasa Rao Uppala6cc0e322011-08-12 10:54:48 -0700927 opcode = hci_opcode_pack(HCI_OGF_FM_DIAGNOSTIC_CMD_REQ,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700928 HCI_OCF_FM_POKE_DATA);
929 return radio_hci_send_cmd(hdev, opcode, sizeof((*poke_data)),
930 poke_data);
931}
932
933static int hci_ssbi_peek_reg_req(struct radio_hci_dev *hdev,
934 unsigned long param)
935{
936 __u16 opcode = 0;
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -0700937 struct hci_fm_ssbi_peek *ssbi_peek = (struct hci_fm_ssbi_peek *) param;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700938
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -0700939 opcode = hci_opcode_pack(HCI_OGF_FM_DIAGNOSTIC_CMD_REQ,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700940 HCI_OCF_FM_SSBI_PEEK_REG);
941 return radio_hci_send_cmd(hdev, opcode, sizeof((*ssbi_peek)),
942 ssbi_peek);
943}
944
945static int hci_ssbi_poke_reg_req(struct radio_hci_dev *hdev,
946 unsigned long param)
947{
948 __u16 opcode = 0;
949 struct hci_fm_ssbi_req *ssbi_poke = (struct hci_fm_ssbi_req *) param;
950
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -0700951 opcode = hci_opcode_pack(HCI_OGF_FM_DIAGNOSTIC_CMD_REQ,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700952 HCI_OCF_FM_SSBI_POKE_REG);
953 return radio_hci_send_cmd(hdev, opcode, sizeof((*ssbi_poke)),
954 ssbi_poke);
955}
956
957static int hci_fm_get_station_dbg_param_req(struct radio_hci_dev *hdev,
958 unsigned long param)
959{
960 __u16 opcode = 0;
961
962 opcode = hci_opcode_pack(HCI_OGF_FM_COMMON_CTRL_CMD_REQ,
963 HCI_OCF_FM_STATION_DBG_PARAM);
964 return radio_hci_send_cmd(hdev, opcode, 0, NULL);
965}
966
967static int radio_hci_err(__u16 code)
968{
969 switch (code) {
970 case 0:
971 return 0;
972 case 0x01:
973 return -EBADRQC;
974 case 0x02:
975 return -ENOTCONN;
976 case 0x03:
977 return -EIO;
978 case 0x07:
979 return -ENOMEM;
980 case 0x0c:
981 return -EBUSY;
982 case 0x11:
983 return -EOPNOTSUPP;
984 case 0x12:
985 return -EINVAL;
986 default:
987 return -ENOSYS;
988 }
989}
990
991static int __radio_hci_request(struct radio_hci_dev *hdev,
992 int (*req)(struct radio_hci_dev *hdev,
993 unsigned long param),
994 unsigned long param, __u32 timeout)
995{
996 int err = 0;
997
998 DECLARE_WAITQUEUE(wait, current);
999
1000 hdev->req_status = HCI_REQ_PEND;
1001
1002 add_wait_queue(&hdev->req_wait_q, &wait);
1003 set_current_state(TASK_INTERRUPTIBLE);
1004
1005 err = req(hdev, param);
1006
1007 schedule_timeout(timeout);
1008
1009 remove_wait_queue(&hdev->req_wait_q, &wait);
1010
1011 if (signal_pending(current))
1012 return -EINTR;
1013
1014 switch (hdev->req_status) {
1015 case HCI_REQ_DONE:
1016 case HCI_REQ_STATUS:
1017 err = radio_hci_err(hdev->req_result);
1018 break;
1019
1020 case HCI_REQ_CANCELED:
1021 err = -hdev->req_result;
1022 break;
1023
1024 default:
1025 err = -ETIMEDOUT;
1026 break;
1027 }
1028
1029 hdev->req_status = hdev->req_result = 0;
1030
1031 return err;
1032}
1033
1034static inline int radio_hci_request(struct radio_hci_dev *hdev,
1035 int (*req)(struct
1036 radio_hci_dev * hdev, unsigned long param),
1037 unsigned long param, __u32 timeout)
1038{
1039 int ret = 0;
1040
1041 ret = __radio_hci_request(hdev, req, param, timeout);
1042
1043 return ret;
1044}
1045
1046static int hci_set_fm_recv_conf(struct hci_fm_recv_conf_req *arg,
1047 struct radio_hci_dev *hdev)
1048{
1049 int ret = 0;
1050 struct hci_fm_recv_conf_req *set_recv_conf = arg;
1051
1052 ret = radio_hci_request(hdev, hci_set_fm_recv_conf_req, (unsigned
1053 long)set_recv_conf, RADIO_HCI_TIMEOUT);
1054
1055 return ret;
1056}
1057
Ankur Nandwanid928d542011-08-11 13:15:41 -07001058static int hci_set_fm_trans_conf(struct hci_fm_trans_conf_req_struct *arg,
1059 struct radio_hci_dev *hdev)
1060{
1061 int ret = 0;
1062 struct hci_fm_trans_conf_req_struct *set_trans_conf = arg;
1063
1064 ret = radio_hci_request(hdev, hci_set_fm_trans_conf_req, (unsigned
1065 long)set_trans_conf, RADIO_HCI_TIMEOUT);
1066
1067 return ret;
1068}
1069
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001070static int hci_fm_tune_station(__u32 *arg, struct radio_hci_dev *hdev)
1071{
1072 int ret = 0;
1073 __u32 tune_freq = *arg;
1074
1075 ret = radio_hci_request(hdev, hci_fm_tune_station_req, tune_freq,
1076 RADIO_HCI_TIMEOUT);
1077
1078 return ret;
1079}
1080
1081static int hci_set_fm_mute_mode(struct hci_fm_mute_mode_req *arg,
1082 struct radio_hci_dev *hdev)
1083{
1084 int ret = 0;
1085 struct hci_fm_mute_mode_req *set_mute_conf = arg;
1086
1087 ret = radio_hci_request(hdev, hci_set_fm_mute_mode_req, (unsigned
1088 long)set_mute_conf, RADIO_HCI_TIMEOUT);
1089
1090 return ret;
1091}
1092
1093static int hci_set_fm_stereo_mode(struct hci_fm_stereo_mode_req *arg,
1094 struct radio_hci_dev *hdev)
1095{
1096 int ret = 0;
1097 struct hci_fm_stereo_mode_req *set_stereo_conf = arg;
1098
1099 ret = radio_hci_request(hdev, hci_set_fm_stereo_mode_req, (unsigned
1100 long)set_stereo_conf, RADIO_HCI_TIMEOUT);
1101
1102 return ret;
1103}
1104
1105static int hci_fm_set_antenna(__u8 *arg, struct radio_hci_dev *hdev)
1106{
1107 int ret = 0;
1108 __u8 antenna = *arg;
1109
1110 ret = radio_hci_request(hdev, hci_fm_set_antenna_req, antenna,
1111 RADIO_HCI_TIMEOUT);
1112
1113 return ret;
1114}
1115
1116static int hci_fm_set_signal_threshold(__u8 *arg,
1117 struct radio_hci_dev *hdev)
1118{
1119 int ret = 0;
1120 __u8 sig_threshold = *arg;
1121
1122 ret = radio_hci_request(hdev, hci_fm_set_sig_threshold_req,
1123 sig_threshold, RADIO_HCI_TIMEOUT);
1124
1125 return ret;
1126}
1127
1128static int hci_fm_search_stations(struct hci_fm_search_station_req *arg,
1129 struct radio_hci_dev *hdev)
1130{
1131 int ret = 0;
1132 struct hci_fm_search_station_req *srch_stations = arg;
1133
1134 ret = radio_hci_request(hdev, hci_fm_search_stations_req, (unsigned
1135 long)srch_stations, RADIO_HCI_TIMEOUT);
1136
1137 return ret;
1138}
1139
1140static int hci_fm_search_rds_stations(struct hci_fm_search_rds_station_req *arg,
1141 struct radio_hci_dev *hdev)
1142{
1143 int ret = 0;
1144 struct hci_fm_search_rds_station_req *srch_stations = arg;
1145
1146 ret = radio_hci_request(hdev, hci_fm_srch_rds_stations_req, (unsigned
1147 long)srch_stations, RADIO_HCI_TIMEOUT);
1148
1149 return ret;
1150}
1151
1152static int hci_fm_search_station_list
1153 (struct hci_fm_search_station_list_req *arg,
1154 struct radio_hci_dev *hdev)
1155{
1156 int ret = 0;
1157 struct hci_fm_search_station_list_req *srch_list = arg;
1158
1159 ret = radio_hci_request(hdev, hci_fm_srch_station_list_req, (unsigned
1160 long)srch_list, RADIO_HCI_TIMEOUT);
1161
1162 return ret;
1163}
1164
1165static int hci_fm_rds_grp(struct hci_fm_rds_grp_req *arg,
1166 struct radio_hci_dev *hdev)
1167{
1168 return 0;
1169}
1170
1171static int hci_fm_rds_grps_process(__u32 *arg, struct radio_hci_dev *hdev)
1172{
1173 int ret = 0;
1174 __u32 fm_grps_process = *arg;
1175
1176 ret = radio_hci_request(hdev, hci_fm_rds_grp_process_req,
1177 fm_grps_process, RADIO_HCI_TIMEOUT);
1178
1179 return ret;
1180}
1181
1182int hci_def_data_read(struct hci_fm_def_data_rd_req *arg,
1183 struct radio_hci_dev *hdev)
1184{
1185 int ret = 0;
1186 struct hci_fm_def_data_rd_req *def_data_rd = arg;
1187
1188 ret = radio_hci_request(hdev, hci_def_data_read_req, (unsigned
1189 long)def_data_rd, RADIO_HCI_TIMEOUT);
1190
1191 return ret;
1192}
1193
1194int hci_def_data_write(struct hci_fm_def_data_wr_req *arg,
1195 struct radio_hci_dev *hdev)
1196{
1197 int ret = 0;
1198 struct hci_fm_def_data_wr_req *def_data_wr = arg;
1199
1200 ret = radio_hci_request(hdev, hci_def_data_write_req, (unsigned
1201 long)def_data_wr, RADIO_HCI_TIMEOUT);
1202
1203 return ret;
1204}
1205
1206int hci_fm_do_calibration(__u8 *arg, struct radio_hci_dev *hdev)
1207{
1208 int ret = 0;
1209 __u8 mode = *arg;
1210
1211 ret = radio_hci_request(hdev, hci_fm_do_calibration_req, mode,
1212 RADIO_HCI_TIMEOUT);
1213
1214 return ret;
1215}
1216
1217int hci_read_grp_counters(__u8 *arg, struct radio_hci_dev *hdev)
1218{
1219 int ret = 0;
1220 __u8 reset_counters = *arg;
1221
1222 ret = radio_hci_request(hdev, hci_read_grp_counters_req,
1223 reset_counters, RADIO_HCI_TIMEOUT);
1224
1225 return ret;
1226}
1227
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -07001228static int hci_peek_data(struct hci_fm_riva_data *arg,
1229 struct radio_hci_dev *hdev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001230{
1231 int ret = 0;
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -07001232 struct hci_fm_riva_data *peek_data = arg;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001233
1234 ret = radio_hci_request(hdev, hci_peek_data_req, (unsigned
1235 long)peek_data, RADIO_HCI_TIMEOUT);
1236
1237 return ret;
1238}
1239
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -07001240static int hci_poke_data(struct hci_fm_riva_poke *arg,
1241 struct radio_hci_dev *hdev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001242{
1243 int ret = 0;
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -07001244 struct hci_fm_riva_poke *poke_data = arg;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001245
1246 ret = radio_hci_request(hdev, hci_poke_data_req, (unsigned
1247 long)poke_data, RADIO_HCI_TIMEOUT);
1248
1249 return ret;
1250}
1251
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -07001252static int hci_ssbi_peek_reg(struct hci_fm_ssbi_peek *arg,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001253 struct radio_hci_dev *hdev)
1254{
1255 int ret = 0;
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -07001256 struct hci_fm_ssbi_peek *ssbi_peek_reg = arg;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001257
1258 ret = radio_hci_request(hdev, hci_ssbi_peek_reg_req, (unsigned
1259 long)ssbi_peek_reg, RADIO_HCI_TIMEOUT);
1260
1261 return ret;
1262}
1263
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -07001264static int hci_ssbi_poke_reg(struct hci_fm_ssbi_req *arg,
1265 struct radio_hci_dev *hdev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001266{
1267 int ret = 0;
1268 struct hci_fm_ssbi_req *ssbi_poke_reg = arg;
1269
1270 ret = radio_hci_request(hdev, hci_ssbi_poke_reg_req, (unsigned
1271 long)ssbi_poke_reg, RADIO_HCI_TIMEOUT);
1272
1273 return ret;
1274}
1275
1276static int hci_cmd(unsigned int cmd, struct radio_hci_dev *hdev)
1277{
1278 int ret = 0;
1279 unsigned long arg = 0;
1280
Ankur Nandwanid928d542011-08-11 13:15:41 -07001281 if (!hdev)
1282 return -ENODEV;
1283
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001284 switch (cmd) {
1285 case HCI_FM_ENABLE_RECV_CMD:
1286 ret = radio_hci_request(hdev, hci_fm_enable_recv_req, arg,
1287 msecs_to_jiffies(RADIO_HCI_TIMEOUT));
1288 break;
1289
1290 case HCI_FM_DISABLE_RECV_CMD:
1291 ret = radio_hci_request(hdev, hci_fm_disable_recv_req, arg,
1292 msecs_to_jiffies(RADIO_HCI_TIMEOUT));
1293 break;
1294
1295 case HCI_FM_GET_RECV_CONF_CMD:
1296 ret = radio_hci_request(hdev, hci_get_fm_recv_conf_req, arg,
1297 msecs_to_jiffies(RADIO_HCI_TIMEOUT));
1298 break;
1299
1300 case HCI_FM_GET_STATION_PARAM_CMD:
1301 ret = radio_hci_request(hdev,
1302 hci_fm_get_station_param_req, arg,
1303 msecs_to_jiffies(RADIO_HCI_TIMEOUT));
1304 break;
1305
1306 case HCI_FM_GET_SIGNAL_TH_CMD:
1307 ret = radio_hci_request(hdev,
1308 hci_fm_get_sig_threshold_req, arg,
1309 msecs_to_jiffies(RADIO_HCI_TIMEOUT));
1310 break;
1311
1312 case HCI_FM_GET_PROGRAM_SERVICE_CMD:
1313 ret = radio_hci_request(hdev,
1314 hci_fm_get_program_service_req, arg,
1315 msecs_to_jiffies(RADIO_HCI_TIMEOUT));
1316 break;
1317
1318 case HCI_FM_GET_RADIO_TEXT_CMD:
1319 ret = radio_hci_request(hdev, hci_fm_get_radio_text_req, arg,
1320 msecs_to_jiffies(RADIO_HCI_TIMEOUT));
1321 break;
1322
1323 case HCI_FM_GET_AF_LIST_CMD:
1324 ret = radio_hci_request(hdev, hci_fm_get_af_list_req, arg,
1325 msecs_to_jiffies(RADIO_HCI_TIMEOUT));
1326 break;
1327
1328 case HCI_FM_CANCEL_SEARCH_CMD:
1329 ret = radio_hci_request(hdev, hci_fm_cancel_search_req, arg,
1330 msecs_to_jiffies(RADIO_HCI_TIMEOUT));
1331 break;
1332
1333 case HCI_FM_RESET_CMD:
1334 ret = radio_hci_request(hdev, hci_fm_reset_req, arg,
1335 msecs_to_jiffies(RADIO_HCI_TIMEOUT));
1336 break;
1337
1338 case HCI_FM_GET_FEATURES_CMD:
1339 ret = radio_hci_request(hdev,
1340 hci_fm_get_feature_lists_req, arg,
1341 msecs_to_jiffies(RADIO_HCI_TIMEOUT));
1342 break;
1343
1344 case HCI_FM_STATION_DBG_PARAM_CMD:
1345 ret = radio_hci_request(hdev,
1346 hci_fm_get_station_dbg_param_req, arg,
1347 msecs_to_jiffies(RADIO_HCI_TIMEOUT));
1348 break;
1349
Ankur Nandwanid928d542011-08-11 13:15:41 -07001350 case HCI_FM_ENABLE_TRANS_CMD:
1351 ret = radio_hci_request(hdev, hci_fm_enable_trans_req, arg,
1352 msecs_to_jiffies(RADIO_HCI_TIMEOUT));
1353 break;
1354
1355 case HCI_FM_DISABLE_TRANS_CMD:
1356 ret = radio_hci_request(hdev, hci_fm_disable_trans_req, arg,
1357 msecs_to_jiffies(RADIO_HCI_TIMEOUT));
1358 break;
1359
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001360 default:
1361 ret = -EINVAL;
1362 break;
1363 }
1364
1365 return ret;
1366}
1367
1368static void radio_hci_req_complete(struct radio_hci_dev *hdev, int result)
1369{
1370 hdev->req_result = result;
1371 hdev->req_status = HCI_REQ_DONE;
1372 wake_up_interruptible(&hdev->req_wait_q);
1373}
1374
1375static void radio_hci_status_complete(struct radio_hci_dev *hdev, int result)
1376{
1377 hdev->req_result = result;
1378 hdev->req_status = HCI_REQ_STATUS;
1379 wake_up_interruptible(&hdev->req_wait_q);
1380}
1381
1382static void hci_cc_rsp(struct radio_hci_dev *hdev, struct sk_buff *skb)
1383{
1384 __u8 status = *((__u8 *) skb->data);
1385
1386 if (status)
1387 return;
1388
1389 radio_hci_req_complete(hdev, status);
1390}
1391
1392static void hci_cc_fm_disable_rsp(struct radio_hci_dev *hdev,
1393 struct sk_buff *skb)
1394{
1395 __u8 status = *((__u8 *) skb->data);
1396 struct iris_device *radio = video_get_drvdata(video_get_dev());
1397
1398 if (status)
1399 return;
1400
1401 iris_q_event(radio, IRIS_EVT_RADIO_READY);
1402
1403 radio_hci_req_complete(hdev, status);
1404}
1405
1406static void hci_cc_conf_rsp(struct radio_hci_dev *hdev, struct sk_buff *skb)
1407{
1408 struct hci_fm_conf_rsp *rsp = (void *)skb->data;
1409 struct iris_device *radio = video_get_drvdata(video_get_dev());
1410
1411 if (rsp->status)
1412 return;
1413
1414 radio->recv_conf = rsp->recv_conf_rsp;
1415 radio_hci_req_complete(hdev, rsp->status);
1416}
1417
1418static void hci_cc_fm_enable_rsp(struct radio_hci_dev *hdev,
1419 struct sk_buff *skb)
1420{
1421 struct hci_fm_conf_rsp *rsp = (void *)skb->data;
1422 struct iris_device *radio = video_get_drvdata(video_get_dev());
1423
1424 if (rsp->status)
1425 return;
1426
1427 iris_q_event(radio, IRIS_EVT_RADIO_READY);
1428
1429 radio_hci_req_complete(hdev, rsp->status);
1430}
1431
Ankur Nandwanid928d542011-08-11 13:15:41 -07001432
1433static void hci_cc_fm_trans_set_conf_rsp(struct radio_hci_dev *hdev,
1434 struct sk_buff *skb)
1435{
1436 struct hci_fm_conf_rsp *rsp = (void *)skb->data;
1437 struct iris_device *radio = video_get_drvdata(video_get_dev());
1438
1439 if (rsp->status)
1440 return;
1441
1442 iris_q_event(radio, HCI_EV_CMD_COMPLETE);
1443
1444 radio_hci_req_complete(hdev, rsp->status);
1445}
1446
1447
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001448static void hci_cc_sig_threshold_rsp(struct radio_hci_dev *hdev,
1449 struct sk_buff *skb)
1450{
1451 struct hci_fm_sig_threshold_rsp *rsp = (void *)skb->data;
1452 struct iris_device *radio = video_get_drvdata(video_get_dev());
1453 struct v4l2_control *v4l_ctl = radio->g_ctl;
1454
1455 if (rsp->status)
1456 return;
1457
1458 v4l_ctl->value = rsp->sig_threshold;
1459
1460 radio_hci_req_complete(hdev, rsp->status);
1461}
1462
1463static void hci_cc_station_rsp(struct radio_hci_dev *hdev, struct sk_buff *skb)
1464{
1465 struct iris_device *radio = video_get_drvdata(video_get_dev());
1466 struct hci_fm_station_rsp *rsp = (void *)skb->data;
1467 radio->fm_st_rsp = *(rsp);
1468
1469 /* Tune is always succesful */
1470 radio_hci_req_complete(hdev, 0);
1471}
1472
1473static void hci_cc_prg_srv_rsp(struct radio_hci_dev *hdev, struct sk_buff *skb)
1474{
1475 struct hci_fm_prgm_srv_rsp *rsp = (void *)skb->data;
1476
1477 if (rsp->status)
1478 return;
1479
1480 radio_hci_req_complete(hdev, rsp->status);
1481}
1482
1483static void hci_cc_rd_txt_rsp(struct radio_hci_dev *hdev, struct sk_buff *skb)
1484{
1485 struct hci_fm_radio_txt_rsp *rsp = (void *)skb->data;
1486
1487 if (rsp->status)
1488 return;
1489
1490 radio_hci_req_complete(hdev, rsp->status);
1491}
1492
1493static void hci_cc_af_list_rsp(struct radio_hci_dev *hdev, struct sk_buff *skb)
1494{
1495 struct hci_fm_af_list_rsp *rsp = (void *)skb->data;
1496
1497 if (rsp->status)
1498 return;
1499
1500 radio_hci_req_complete(hdev, rsp->status);
1501}
1502
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001503static void hci_cc_feature_list_rsp(struct radio_hci_dev *hdev,
1504 struct sk_buff *skb)
1505{
1506 struct hci_fm_feature_list_rsp *rsp = (void *)skb->data;
1507 struct iris_device *radio = video_get_drvdata(video_get_dev());
1508 struct v4l2_capability *v4l_cap = radio->g_cap;
1509
1510 if (rsp->status)
1511 return;
1512 v4l_cap->capabilities = (rsp->feature_mask & 0x000002) |
1513 (rsp->feature_mask & 0x000001);
1514
1515 radio_hci_req_complete(hdev, rsp->status);
1516}
1517
1518static void hci_cc_dbg_param_rsp(struct radio_hci_dev *hdev,
1519 struct sk_buff *skb)
1520{
1521 struct iris_device *radio = video_get_drvdata(video_get_dev());
1522 struct hci_fm_dbg_param_rsp *rsp = (void *)skb->data;
1523 radio->st_dbg_param = *(rsp);
1524
1525 if (radio->st_dbg_param.status)
1526 return;
1527
1528 radio_hci_req_complete(hdev, radio->st_dbg_param.status);
1529}
1530
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -07001531static void iris_q_evt_data(struct iris_device *radio,
1532 char *data, int len, int event)
1533{
1534 struct kfifo *data_b = &radio->data_buf[event];
1535 if (kfifo_in_locked(data_b, data, len, &radio->buf_lock[event]))
1536 wake_up_interruptible(&radio->event_queue);
1537}
1538
1539static void hci_cc_riva_peek_rsp(struct radio_hci_dev *hdev,
1540 struct sk_buff *skb)
1541{
1542 struct iris_device *radio = video_get_drvdata(video_get_dev());
1543 struct hci_fm_af_list_rsp *rsp = (void *)skb->data;
1544 int len;
1545 char *data;
1546
1547 if (rsp->status)
1548 return;
1549 len = skb->data[RIVA_PEEK_LEN_OFSET] + RIVA_PEEK_PARAM;
1550 data = kmalloc(len, GFP_ATOMIC);
1551
1552 if (!data) {
1553 FMDERR("Memory allocation failed");
1554 return;
1555 }
1556
1557 memcpy(data, &skb->data[PEEK_DATA_OFSET], len);
1558 iris_q_evt_data(radio, data, len, IRIS_BUF_PEEK);
1559 radio_hci_req_complete(hdev, rsp->status);
1560
1561
1562}
1563static void hci_cc_ssbi_peek_rsp(struct radio_hci_dev *hdev,
1564 struct sk_buff *skb)
1565{
1566 struct iris_device *radio = video_get_drvdata(video_get_dev());
1567 struct hci_fm_af_list_rsp *rsp = (void *)skb->data;
1568 char *data;
1569
1570 if (rsp->status)
1571 return;
1572 data = kmalloc(SSBI_PEEK_LEN, GFP_ATOMIC);
1573 if (!data) {
1574 FMDERR("Memory allocation failed");
1575 return;
1576 }
1577
1578 data[0] = skb->data[PEEK_DATA_OFSET];
1579 iris_q_evt_data(radio, data, SSBI_PEEK_LEN, IRIS_BUF_SSBI_PEEK);
1580 radio_hci_req_complete(hdev, rsp->status);
1581 kfree(data);
1582}
1583
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001584static inline void hci_cmd_complete_event(struct radio_hci_dev *hdev,
1585 struct sk_buff *skb)
1586{
1587 struct hci_ev_cmd_complete *cmd_compl_ev = (void *) skb->data;
1588 __u16 opcode;
1589
1590 skb_pull(skb, sizeof(*cmd_compl_ev));
1591
1592 opcode = __le16_to_cpu(cmd_compl_ev->cmd_opcode);
1593
1594 switch (opcode) {
1595 case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_ENABLE_RECV_REQ):
1596 hci_cc_fm_enable_rsp(hdev, skb);
1597 break;
1598 case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_GET_RECV_CONF_REQ):
1599 hci_cc_conf_rsp(hdev, skb);
1600 break;
1601
1602 case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_DISABLE_RECV_REQ):
1603 hci_cc_fm_disable_rsp(hdev, skb);
1604 break;
1605
1606 case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_SET_RECV_CONF_REQ):
1607 case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_SET_MUTE_MODE_REQ):
1608 case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_SET_STEREO_MODE_REQ):
1609 case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_SET_ANTENNA):
1610 case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_SET_SIGNAL_THRESHOLD):
1611 case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_CANCEL_SEARCH):
1612 case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_RDS_GRP):
1613 case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_RDS_GRP_PROCESS):
1614 case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_EN_WAN_AVD_CTRL):
1615 case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_EN_NOTCH_CTRL):
Ankur Nandwanid928d542011-08-11 13:15:41 -07001616 case hci_trans_ctrl_cmd_op_pack(HCI_OCF_FM_ENABLE_TRANS_REQ):
1617 case hci_trans_ctrl_cmd_op_pack(HCI_OCF_FM_DISABLE_TRANS_REQ):
1618 case hci_trans_ctrl_cmd_op_pack(HCI_OCF_FM_RDS_RT_REQ):
1619 case hci_trans_ctrl_cmd_op_pack(HCI_OCF_FM_RDS_PS_REQ):
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001620 case hci_common_cmd_op_pack(HCI_OCF_FM_DEFAULT_DATA_WRITE):
1621 case hci_common_cmd_op_pack(HCI_OCF_FM_RESET):
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001622 case hci_diagnostic_cmd_op_pack(HCI_OCF_FM_SSBI_POKE_REG):
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -07001623 case hci_diagnostic_cmd_op_pack(HCI_OCF_FM_POKE_DATA):
1624 case hci_status_param_op_pack(HCI_OCF_FM_READ_GRP_COUNTERS):
Ankur Nandwanid928d542011-08-11 13:15:41 -07001625 case hci_diagnostic_cmd_op_pack(HCI_FM_SET_INTERNAL_TONE_GENRATOR):
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001626 hci_cc_rsp(hdev, skb);
1627 break;
1628
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -07001629 case hci_diagnostic_cmd_op_pack(HCI_OCF_FM_SSBI_PEEK_REG):
1630 hci_cc_ssbi_peek_rsp(hdev, skb);
1631 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001632 case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_GET_SIGNAL_THRESHOLD):
1633 hci_cc_sig_threshold_rsp(hdev, skb);
1634 break;
1635
1636 case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_GET_STATION_PARAM_REQ):
1637 hci_cc_station_rsp(hdev, skb);
1638 break;
1639
1640 case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_GET_PROGRAM_SERVICE_REQ):
1641 hci_cc_prg_srv_rsp(hdev, skb);
1642 break;
1643
1644 case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_GET_RADIO_TEXT_REQ):
1645 hci_cc_rd_txt_rsp(hdev, skb);
1646 break;
1647
1648 case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_GET_AF_LIST_REQ):
1649 hci_cc_af_list_rsp(hdev, skb);
1650 break;
1651
1652 case hci_common_cmd_op_pack(HCI_OCF_FM_DEFAULT_DATA_READ):
1653 case hci_diagnostic_cmd_op_pack(HCI_OCF_FM_PEEK_DATA):
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -07001654 hci_cc_riva_peek_rsp(hdev, skb);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001655 break;
1656
1657 case hci_common_cmd_op_pack(HCI_OCF_FM_GET_FEATURE_LIST):
1658 hci_cc_feature_list_rsp(hdev, skb);
1659 break;
1660
1661 case hci_diagnostic_cmd_op_pack(HCI_OCF_FM_STATION_DBG_PARAM):
1662 hci_cc_dbg_param_rsp(hdev, skb);
1663 break;
1664
Ankur Nandwanid928d542011-08-11 13:15:41 -07001665 case hci_trans_ctrl_cmd_op_pack(HCI_OCF_FM_SET_TRANS_CONF_REQ):
1666 hci_cc_fm_trans_set_conf_rsp(hdev, skb);
1667 break;
1668
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001669 default:
1670 FMDERR("%s opcode 0x%x", hdev->name, opcode);
1671 break;
1672 }
1673
1674}
1675
1676static inline void hci_cmd_status_event(struct radio_hci_dev *hdev,
1677 struct sk_buff *skb)
1678{
1679 struct hci_ev_cmd_status *ev = (void *) skb->data;
1680 radio_hci_status_complete(hdev, ev->status);
1681}
1682
1683static inline void hci_ev_tune_status(struct radio_hci_dev *hdev,
1684 struct sk_buff *skb)
1685{
1686 int i;
1687 int len;
1688
1689 struct iris_device *radio = video_get_drvdata(video_get_dev());
1690
1691 len = sizeof(struct hci_fm_station_rsp);
1692
1693 memcpy(&radio->fm_st_rsp.station_rsp, skb_pull(skb, len), len);
1694
1695 iris_q_event(radio, IRIS_EVT_TUNE_SUCC);
1696
1697 for (i = 0; i < IRIS_BUF_MAX; i++) {
1698 if (i >= IRIS_BUF_RT_RDS)
1699 kfifo_reset(&radio->data_buf[i]);
1700 }
1701
1702 if (radio->fm_st_rsp.station_rsp.rssi)
1703 iris_q_event(radio, IRIS_EVT_ABOVE_TH);
1704 else
1705 iris_q_event(radio, IRIS_EVT_BELOW_TH);
1706
1707 if (radio->fm_st_rsp.station_rsp.stereo_prg)
1708 iris_q_event(radio, IRIS_EVT_STEREO);
1709
1710 if (radio->fm_st_rsp.station_rsp.mute_mode)
1711 iris_q_event(radio, IRIS_EVT_MONO);
1712
1713 if (radio->fm_st_rsp.station_rsp.rds_sync_status)
1714 iris_q_event(radio, IRIS_EVT_RDS_AVAIL);
1715 else
1716 iris_q_event(radio, IRIS_EVT_RDS_NOT_AVAIL);
1717}
1718
1719static inline void hci_ev_search_compl(struct radio_hci_dev *hdev,
1720 struct sk_buff *skb)
1721{
1722 struct iris_device *radio = video_get_drvdata(video_get_dev());
1723 iris_q_event(radio, IRIS_EVT_SEEK_COMPLETE);
1724}
1725
1726static inline void hci_ev_srch_st_list_compl(struct radio_hci_dev *hdev,
1727 struct sk_buff *skb)
1728{
1729 struct iris_device *radio = video_get_drvdata(video_get_dev());
Srinivasa Rao Uppala18fb80e2011-07-17 17:33:00 -07001730 struct hci_ev_srch_list_compl *ev ;
1731 int cnt;
1732 int stn_num;
1733 int rel_freq;
1734 int abs_freq;
1735 int len;
1736
1737 ev = kmalloc(sizeof(*ev), GFP_ATOMIC);
1738 if (!ev) {
1739 FMDERR("Memory allocation failed");
1740 return ;
1741 }
1742
1743 ev->num_stations_found = skb->data[STN_NUM_OFFSET];
1744 len = ev->num_stations_found * PARAMS_PER_STATION + STN_FREQ_OFFSET;
1745
1746 for (cnt = STN_FREQ_OFFSET, stn_num = 0;
1747 (cnt < len) && (stn_num < ev->num_stations_found);
1748 cnt += PARAMS_PER_STATION, stn_num++) {
1749 abs_freq = *((int *)&skb->data[cnt]);
1750 rel_freq = abs_freq - radio->recv_conf.band_low_limit;
1751 rel_freq = (rel_freq * 20) / KHZ_TO_MHZ;
1752
1753 ev->rel_freq[stn_num].rel_freq_lsb = GET_LSB(rel_freq);
1754 ev->rel_freq[stn_num].rel_freq_msb = GET_MSB(rel_freq);
1755 }
1756
1757 len = ev->num_stations_found * 2 + sizeof(ev->num_stations_found);
1758 iris_q_event(radio, IRIS_EVT_NEW_SRCH_LIST);
1759 iris_q_evt_data(radio, (char *)ev, len, IRIS_BUF_SRCH_LIST);
1760 kfree(ev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001761}
1762
1763static inline void hci_ev_search_next(struct radio_hci_dev *hdev,
1764 struct sk_buff *skb)
1765{
1766 struct iris_device *radio = video_get_drvdata(video_get_dev());
1767 iris_q_event(radio, IRIS_EVT_SCAN_NEXT);
1768}
1769
1770static inline void hci_ev_stereo_status(struct radio_hci_dev *hdev,
1771 struct sk_buff *skb)
1772{
1773 struct iris_device *radio = video_get_drvdata(video_get_dev());
1774 __u8 st_status = *((__u8 *) skb->data);
1775 if (st_status)
1776 iris_q_event(radio, IRIS_EVT_STEREO);
1777 else
1778 iris_q_event(radio, IRIS_EVT_MONO);
1779}
1780
Ankur Nandwani78a782b2011-07-07 21:11:21 -07001781
Ankur Nandwani78a782b2011-07-07 21:11:21 -07001782static inline void hci_ev_program_service(struct radio_hci_dev *hdev,
1783 struct sk_buff *skb)
1784{
1785 struct iris_device *radio = video_get_drvdata(video_get_dev());
1786 int len;
1787 char *data;
1788
1789 len = (skb->data[RDS_PS_LENGTH_OFFSET] * RDS_STRING) + RDS_OFFSET;
1790 iris_q_event(radio, IRIS_EVT_NEW_PS_RDS);
1791 data = kmalloc(len, GFP_ATOMIC);
1792 if (!data) {
1793 FMDERR("Failed to allocate memory");
1794 return;
1795 }
1796
1797 data[0] = skb->data[RDS_PS_LENGTH_OFFSET];
1798 data[1] = skb->data[RDS_PTYPE];
1799 data[2] = skb->data[RDS_PID_LOWER];
1800 data[3] = skb->data[RDS_PID_HIGHER];
1801 data[4] = 0;
1802
1803 memcpy(data+RDS_OFFSET, &skb->data[RDS_PS_DATA_OFFSET], len-RDS_OFFSET);
1804
Srinivasa Rao Uppala18fb80e2011-07-17 17:33:00 -07001805 iris_q_evt_data(radio, data, len, IRIS_BUF_PS_RDS);
Ankur Nandwani78a782b2011-07-07 21:11:21 -07001806
1807 kfree(data);
1808}
1809
1810
1811static inline void hci_ev_radio_text(struct radio_hci_dev *hdev,
1812 struct sk_buff *skb)
1813{
1814 struct iris_device *radio = video_get_drvdata(video_get_dev());
1815 int len = 0;
1816 char *data;
1817
1818 iris_q_event(radio, IRIS_EVT_NEW_RT_RDS);
1819
1820 while (skb->data[len+RDS_OFFSET] != 0x0d)
1821 len++;
1822 len++;
1823
1824 data = kmalloc(len+RDS_OFFSET, GFP_ATOMIC);
1825 if (!data) {
1826 FMDERR("Failed to allocate memory");
1827 return;
1828 }
1829
1830 data[0] = len;
1831 data[1] = skb->data[RDS_PTYPE];
1832 data[2] = skb->data[RDS_PID_LOWER];
1833 data[3] = skb->data[RDS_PID_HIGHER];
1834 data[4] = 0;
1835
1836 memcpy(data+RDS_OFFSET, &skb->data[RDS_OFFSET], len);
1837 data[len+RDS_OFFSET] = 0x00;
1838
Srinivasa Rao Uppala18fb80e2011-07-17 17:33:00 -07001839 iris_q_evt_data(radio, data, len+RDS_OFFSET, IRIS_BUF_RT_RDS);
Ankur Nandwani78a782b2011-07-07 21:11:21 -07001840
1841 kfree(data);
1842}
1843
1844
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001845void radio_hci_event_packet(struct radio_hci_dev *hdev, struct sk_buff *skb)
1846{
1847 struct radio_hci_event_hdr *hdr = (void *) skb->data;
1848 __u8 event = hdr->evt;
1849
1850 skb_pull(skb, RADIO_HCI_EVENT_HDR_SIZE);
1851
1852 switch (event) {
1853 case HCI_EV_TUNE_STATUS:
1854 hci_ev_tune_status(hdev, skb);
1855 break;
1856 case HCI_EV_SEARCH_PROGRESS:
1857 case HCI_EV_SEARCH_RDS_PROGRESS:
1858 case HCI_EV_SEARCH_LIST_PROGRESS:
1859 hci_ev_search_next(hdev, skb);
1860 break;
1861 case HCI_EV_STEREO_STATUS:
1862 hci_ev_stereo_status(hdev, skb);
1863 break;
1864 case HCI_EV_RDS_LOCK_STATUS:
1865 case HCI_EV_SERVICE_AVAILABLE:
1866 case HCI_EV_RDS_RX_DATA:
Ankur Nandwani78a782b2011-07-07 21:11:21 -07001867 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001868 case HCI_EV_PROGRAM_SERVICE:
Ankur Nandwani78a782b2011-07-07 21:11:21 -07001869 hci_ev_program_service(hdev, skb);
1870 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001871 case HCI_EV_RADIO_TEXT:
Ankur Nandwani78a782b2011-07-07 21:11:21 -07001872 hci_ev_radio_text(hdev, skb);
1873 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001874 case HCI_EV_FM_AF_LIST:
1875 case HCI_EV_TX_RDS_GRP_COMPL:
1876 case HCI_EV_TX_RDS_CONT_GRP_COMPL:
1877 break;
1878
1879 case HCI_EV_CMD_COMPLETE:
1880 hci_cmd_complete_event(hdev, skb);
1881 break;
1882
1883 case HCI_EV_CMD_STATUS:
1884 hci_cmd_status_event(hdev, skb);
1885 break;
1886
1887 case HCI_EV_SEARCH_COMPLETE:
1888 case HCI_EV_SEARCH_RDS_COMPLETE:
1889 hci_ev_search_compl(hdev, skb);
1890 break;
1891
1892 case HCI_EV_SEARCH_LIST_COMPLETE:
Srinivasa Rao Uppala18fb80e2011-07-17 17:33:00 -07001893 hci_ev_srch_st_list_compl(hdev, skb);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001894 break;
1895
1896 default:
1897 break;
1898 }
1899}
1900
1901/*
1902 * fops/IOCTL helper functions
1903 */
1904
1905static int iris_search(struct iris_device *radio, int on, int dir)
1906{
1907 int retval = 0;
1908 enum search_t srch = radio->g_search_mode & SRCH_MODE;
1909
1910 if (on) {
1911 switch (srch) {
1912 case SCAN_FOR_STRONG:
1913 case SCAN_FOR_WEAK:
1914 radio->srch_st_list.srch_list_dir = dir;
1915 radio->srch_st_list.srch_list_mode = srch;
1916 radio->srch_st_list.srch_list_max = 0;
1917 retval = hci_fm_search_station_list(
1918 &radio->srch_st_list, radio->fm_hdev);
1919 break;
1920 case RDS_SEEK_PTY:
1921 case RDS_SCAN_PTY:
1922 case RDS_SEEK_PI:
Srinivasa Rao Uppala7bb22102011-07-14 11:27:30 -07001923 srch = srch - SEARCH_RDS_STNS_MODE_OFFSET;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001924 radio->srch_rds.srch_station.srch_mode = srch;
1925 radio->srch_rds.srch_station.srch_dir = dir;
1926 radio->srch_rds.srch_station.scan_time =
1927 radio->g_scan_time;
1928 retval = hci_fm_search_rds_stations(&radio->srch_rds,
1929 radio->fm_hdev);
1930 break;
1931 default:
1932 radio->srch_st.srch_mode = srch;
1933 radio->srch_st.scan_time = radio->g_scan_time;
1934 radio->srch_st.srch_dir = dir;
1935 retval = hci_fm_search_stations(
1936 &radio->srch_st, radio->fm_hdev);
1937 break;
1938 }
1939
1940 } else {
1941 retval = hci_cmd(HCI_FM_CANCEL_SEARCH_CMD, radio->fm_hdev);
1942 }
1943
1944 return retval;
1945}
1946
Ankur Nandwanid928d542011-08-11 13:15:41 -07001947static int iris_recv_set_region(struct iris_device *radio, int req_region)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001948{
1949 int retval;
1950 radio->region = req_region;
1951
1952 switch (radio->region) {
1953 case IRIS_REGION_US:
Ankur Nandwanid928d542011-08-11 13:15:41 -07001954 radio->recv_conf.band_low_limit =
1955 REGION_US_EU_BAND_LOW;
1956 radio->recv_conf.band_high_limit =
1957 REGION_US_EU_BAND_HIGH;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001958 break;
1959 case IRIS_REGION_EU:
Ankur Nandwanid928d542011-08-11 13:15:41 -07001960 radio->recv_conf.band_low_limit =
1961 REGION_US_EU_BAND_LOW;
1962 radio->recv_conf.band_high_limit =
1963 REGION_US_EU_BAND_HIGH;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001964 break;
1965 case IRIS_REGION_JAPAN:
Ankur Nandwanid928d542011-08-11 13:15:41 -07001966 radio->recv_conf.band_low_limit =
Ankur Nandwanid928d542011-08-11 13:15:41 -07001967 REGION_JAPAN_STANDARD_BAND_LOW;
Srinivasa Rao Uppalacc62b1c2011-08-22 19:15:29 +05301968 radio->recv_conf.band_high_limit =
1969 REGION_JAPAN_STANDARD_BAND_HIGH;
Ankur Nandwanid928d542011-08-11 13:15:41 -07001970 break;
1971 case IRIS_REGION_JAPAN_WIDE:
1972 radio->recv_conf.band_low_limit =
1973 REGION_JAPAN_WIDE_BAND_LOW;
1974 radio->recv_conf.band_high_limit =
1975 REGION_JAPAN_WIDE_BAND_HIGH;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001976 break;
1977 default:
Ankur Nandwanid928d542011-08-11 13:15:41 -07001978 /* The user specifies the value.
1979 So nothing needs to be done */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001980 break;
1981 }
1982
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001983 retval = hci_set_fm_recv_conf(
1984 &radio->recv_conf,
1985 radio->fm_hdev);
1986
1987 return retval;
1988}
1989
Ankur Nandwanid928d542011-08-11 13:15:41 -07001990
1991static int iris_trans_set_region(struct iris_device *radio, int req_region)
1992{
1993 int retval;
1994 radio->region = req_region;
1995
1996 switch (radio->region) {
1997 case IRIS_REGION_US:
1998 radio->trans_conf.band_low_limit =
1999 REGION_US_EU_BAND_LOW;
2000 radio->trans_conf.band_high_limit =
2001 REGION_US_EU_BAND_HIGH;
2002 break;
2003 case IRIS_REGION_EU:
2004 radio->trans_conf.band_low_limit =
2005 REGION_US_EU_BAND_LOW;
2006 radio->trans_conf.band_high_limit =
2007 REGION_US_EU_BAND_HIGH;
2008 break;
2009 case IRIS_REGION_JAPAN:
2010 radio->trans_conf.band_low_limit =
Ankur Nandwanid928d542011-08-11 13:15:41 -07002011 REGION_JAPAN_STANDARD_BAND_LOW;
Srinivasa Rao Uppalacc62b1c2011-08-22 19:15:29 +05302012 radio->trans_conf.band_high_limit =
2013 REGION_JAPAN_STANDARD_BAND_HIGH;
Ankur Nandwanid928d542011-08-11 13:15:41 -07002014 break;
2015 case IRIS_REGION_JAPAN_WIDE:
2016 radio->recv_conf.band_low_limit =
2017 REGION_JAPAN_WIDE_BAND_LOW;
2018 radio->recv_conf.band_high_limit =
2019 REGION_JAPAN_WIDE_BAND_HIGH;
2020 default:
2021 break;
2022 }
2023
2024 retval = hci_set_fm_trans_conf(
2025 &radio->trans_conf,
2026 radio->fm_hdev);
2027 return retval;
2028}
2029
2030
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002031static int iris_set_freq(struct iris_device *radio, unsigned int freq)
2032{
2033
2034 int retval;
2035 retval = hci_fm_tune_station(&freq, radio->fm_hdev);
2036 if (retval < 0)
2037 FMDERR("Error while setting the frequency : %d\n", retval);
2038 return retval;
2039}
2040
2041
2042static int iris_vidioc_queryctrl(struct file *file, void *priv,
2043 struct v4l2_queryctrl *qc)
2044{
2045 unsigned char i;
2046 int retval = -EINVAL;
2047
2048 for (i = 0; i < ARRAY_SIZE(iris_v4l2_queryctrl); i++) {
2049 if (qc->id && qc->id == iris_v4l2_queryctrl[i].id) {
2050 memcpy(qc, &(iris_v4l2_queryctrl[i]), sizeof(*qc));
2051 retval = 0;
2052 break;
2053 }
2054 }
2055
2056 return retval;
2057}
2058
2059static int iris_vidioc_g_ctrl(struct file *file, void *priv,
2060 struct v4l2_control *ctrl)
2061{
2062 struct iris_device *radio = video_get_drvdata(video_devdata(file));
2063 int retval = 0;
2064
2065 switch (ctrl->id) {
2066 case V4L2_CID_AUDIO_VOLUME:
2067 break;
2068 case V4L2_CID_AUDIO_MUTE:
2069 ctrl->value = radio->mute_mode.hard_mute;
2070 break;
2071 case V4L2_CID_PRIVATE_IRIS_SRCHMODE:
2072 ctrl->value = radio->g_search_mode;
2073 break;
2074 case V4L2_CID_PRIVATE_IRIS_SCANDWELL:
2075 ctrl->value = radio->g_scan_time;
2076 break;
2077 case V4L2_CID_PRIVATE_IRIS_SRCHON:
2078 break;
2079 case V4L2_CID_PRIVATE_IRIS_STATE:
2080 break;
2081 case V4L2_CID_PRIVATE_IRIS_IOVERC:
2082 retval = hci_cmd(HCI_FM_STATION_DBG_PARAM_CMD, radio->fm_hdev);
2083 if (retval < 0)
2084 return retval;
2085 ctrl->value = radio->st_dbg_param.io_verc;
2086 break;
2087 case V4L2_CID_PRIVATE_IRIS_INTDET:
2088 retval = hci_cmd(HCI_FM_STATION_DBG_PARAM_CMD, radio->fm_hdev);
2089 if (retval < 0)
2090 return retval;
2091 ctrl->value = radio->st_dbg_param.in_det_out;
2092 break;
2093 case V4L2_CID_PRIVATE_IRIS_REGION:
2094 ctrl->value = radio->region;
2095 break;
2096 case V4L2_CID_PRIVATE_IRIS_SIGNAL_TH:
2097 retval = hci_cmd(HCI_FM_GET_SIGNAL_TH_CMD, radio->fm_hdev);
2098 break;
2099 case V4L2_CID_PRIVATE_IRIS_SRCH_PTY:
2100 break;
2101 case V4L2_CID_PRIVATE_IRIS_SRCH_PI:
2102 break;
2103 case V4L2_CID_PRIVATE_IRIS_SRCH_CNT:
2104 break;
2105 case V4L2_CID_PRIVATE_IRIS_EMPHASIS:
Ankur Nandwanid928d542011-08-11 13:15:41 -07002106 if (radio->mode == FM_RECV) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002107 ctrl->value = radio->recv_conf.emphasis;
Ankur Nandwanid928d542011-08-11 13:15:41 -07002108 } else if (radio->mode == FM_TRANS) {
2109 ctrl->value = radio->trans_conf.emphasis;
2110 } else {
2111 FMDERR("Error in radio mode"
2112 " %d\n", retval);
2113 return -EINVAL;
2114 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002115 break;
2116 case V4L2_CID_PRIVATE_IRIS_RDS_STD:
Ankur Nandwanid928d542011-08-11 13:15:41 -07002117 if (radio->mode == FM_RECV) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002118 ctrl->value = radio->recv_conf.rds_std;
Ankur Nandwanid928d542011-08-11 13:15:41 -07002119 } else if (radio->mode == FM_TRANS) {
2120 ctrl->value = radio->trans_conf.rds_std;
2121 } else {
2122 FMDERR("Error in radio mode"
2123 " %d\n", retval);
2124 return -EINVAL;
2125 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002126 break;
2127 case V4L2_CID_PRIVATE_IRIS_SPACING:
Ankur Nandwanid928d542011-08-11 13:15:41 -07002128 if (radio->mode == FM_RECV) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002129 ctrl->value = radio->recv_conf.ch_spacing;
Ankur Nandwanid928d542011-08-11 13:15:41 -07002130 } else {
2131 FMDERR("Error in radio mode"
2132 " %d\n", retval);
2133 return -EINVAL;
2134 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002135 break;
2136 case V4L2_CID_PRIVATE_IRIS_RDSON:
Ankur Nandwanid928d542011-08-11 13:15:41 -07002137 if (radio->mode == FM_RECV) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002138 ctrl->value = radio->recv_conf.rds_std;
Ankur Nandwanid928d542011-08-11 13:15:41 -07002139 } else {
2140 FMDERR("Error in radio mode"
2141 " %d\n", retval);
2142 return -EINVAL;
2143 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002144 break;
2145 case V4L2_CID_PRIVATE_IRIS_RDSGROUP_MASK:
2146 ctrl->value = radio->rds_grp.rds_grp_enable_mask;
2147 break;
2148 case V4L2_CID_PRIVATE_IRIS_RDSGROUP_PROC:
Srinivasa Rao Uppala58273f82011-08-10 19:07:45 -07002149 case V4L2_CID_PRIVATE_IRIS_PSALL:
2150 ctrl->value = (radio->g_rds_grp_proc_ps << RDS_CONFIG_OFFSET);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002151 break;
2152 case V4L2_CID_PRIVATE_IRIS_RDSD_BUF:
2153 ctrl->value = radio->rds_grp.rds_buf_size;
2154 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002155 case V4L2_CID_PRIVATE_IRIS_LP_MODE:
2156 break;
2157 case V4L2_CID_PRIVATE_IRIS_ANTENNA:
2158 ctrl->value = radio->g_antenna;
2159 break;
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -07002160 case V4L2_CID_PRIVATE_IRIS_SOFT_MUTE:
2161 ctrl->value = radio->mute_mode.soft_mute;
2162 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002163 default:
2164 retval = -EINVAL;
2165 }
2166 if (retval < 0)
2167 FMDERR("get control failed with %d, id: %d\n",
2168 retval, ctrl->id);
2169 return retval;
2170}
2171
2172static int iris_vidioc_s_ext_ctrls(struct file *file, void *priv,
2173 struct v4l2_ext_controls *ctrl)
2174{
Ankur Nandwanid928d542011-08-11 13:15:41 -07002175 int retval = 0;
2176 int bytes_to_copy;
2177 struct hci_fm_tx_ps tx_ps;
2178 struct hci_fm_tx_rt tx_rt;
2179
2180 struct iris_device *radio = video_get_drvdata(video_devdata(file));
2181 char *data = NULL;
2182
2183 switch ((ctrl->controls[0]).id) {
2184 case V4L2_CID_RDS_TX_PS_NAME:
2185 FMDBG("In V4L2_CID_RDS_TX_PS_NAME\n");
2186 /*Pass a sample PS string */
2187
2188 memset(tx_ps.ps_data, 0, MAX_PS_LENGTH);
2189 bytes_to_copy = min((int)(ctrl->controls[0]).size,
2190 MAX_PS_LENGTH);
2191 data = (ctrl->controls[0]).string;
2192
2193 if (copy_from_user(tx_ps.ps_data,
2194 data, bytes_to_copy))
2195 return -EFAULT;
2196 tx_ps.ps_control = 0x01;
2197 tx_ps.pi = radio->pi;
2198 tx_ps.pty = radio->pty;
2199 tx_ps.ps_repeatcount = radio->ps_repeatcount;
2200 tx_ps.ps_len = bytes_to_copy;
2201
2202 retval = radio_hci_request(radio->fm_hdev, hci_trans_ps_req,
2203 (unsigned long)&tx_ps, RADIO_HCI_TIMEOUT);
2204 break;
2205 case V4L2_CID_RDS_TX_RADIO_TEXT:
2206 bytes_to_copy =
2207 min((int)(ctrl->controls[0]).size, MAX_RT_LENGTH);
2208 data = (ctrl->controls[0]).string;
2209
2210 memset(tx_rt.rt_data, 0, MAX_RT_LENGTH);
2211
2212 if (copy_from_user(tx_rt.rt_data,
2213 data, bytes_to_copy))
2214 return -EFAULT;
2215
2216 tx_rt.rt_control = 0x01;
2217 tx_rt.pi = radio->pi;
2218 tx_rt.pty = radio->pty;
2219 tx_rt.ps_len = bytes_to_copy;
2220
2221 retval = radio_hci_request(radio->fm_hdev, hci_trans_rt_req,
2222 (unsigned long)&tx_rt, RADIO_HCI_TIMEOUT);
2223 break;
2224 default:
2225 FMDBG("Shouldn't reach here\n");
2226 retval = -1;
2227 }
2228 return retval;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002229}
2230
2231static int iris_vidioc_s_ctrl(struct file *file, void *priv,
2232 struct v4l2_control *ctrl)
2233{
2234 struct iris_device *radio = video_get_drvdata(video_devdata(file));
2235 int retval = 0;
2236 unsigned int rds_grps_proc = 0;
2237 __u8 temp_val = 0;
Ankur Nandwanid928d542011-08-11 13:15:41 -07002238 unsigned long arg = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002239
2240 switch (ctrl->id) {
Ankur Nandwanid928d542011-08-11 13:15:41 -07002241 case VL2_CID_PRIVATE_IRIS_TX_TONE:
2242 radio->tone_freq = ctrl->value;
2243 retval = radio_hci_request(radio->fm_hdev,
2244 hci_fm_tone_generator, arg,
2245 msecs_to_jiffies(RADIO_HCI_TIMEOUT));
2246 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002247 case V4L2_CID_AUDIO_VOLUME:
2248 break;
2249 case V4L2_CID_AUDIO_MUTE:
2250 radio->mute_mode.hard_mute = ctrl->value;
2251 radio->mute_mode.soft_mute = IOC_SFT_MUTE;
2252 retval = hci_set_fm_mute_mode(
2253 &radio->mute_mode,
2254 radio->fm_hdev);
2255 if (retval < 0)
2256 FMDERR("Error while set FM hard mute"" %d\n",
2257 retval);
2258 break;
2259 case V4L2_CID_PRIVATE_IRIS_SRCHMODE:
2260 radio->g_search_mode = ctrl->value;
2261 break;
2262 case V4L2_CID_PRIVATE_IRIS_SCANDWELL:
2263 radio->g_scan_time = ctrl->value;
2264 break;
2265 case V4L2_CID_PRIVATE_IRIS_SRCHON:
2266 iris_search(radio, ctrl->value, SRCH_DIR_UP);
2267 break;
2268 case V4L2_CID_PRIVATE_IRIS_STATE:
Srinivasa Rao Uppalacc62b1c2011-08-22 19:15:29 +05302269 radio->recv_conf.emphasis = 0;
2270 radio->recv_conf.ch_spacing = 0;
2271 radio->recv_conf.hlsi = 0;
2272 radio->recv_conf.band_low_limit =
2273 REGION_US_EU_BAND_LOW;
2274 radio->recv_conf.band_high_limit =
2275 REGION_US_EU_BAND_HIGH;
2276 radio->recv_conf.rds_std = 0;
Ankur Nandwanid928d542011-08-11 13:15:41 -07002277 switch (ctrl->value) {
2278 case FM_RECV:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002279 retval = hci_cmd(HCI_FM_ENABLE_RECV_CMD,
2280 radio->fm_hdev);
Ankur Nandwanid928d542011-08-11 13:15:41 -07002281
2282 radio->mode = FM_RECV;
2283
2284 if (retval < 0)
2285 FMDERR("Error while enabling RECV FM"
Srinivasa Rao Uppala0ffb5d62011-08-02 17:54:13 -07002286 " %d\n", retval);
Ankur Nandwanid928d542011-08-11 13:15:41 -07002287 radio->mute_mode.soft_mute = CTRL_ON;
2288 retval = hci_set_fm_mute_mode(
2289 &radio->mute_mode,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002290 radio->fm_hdev);
Ankur Nandwanid928d542011-08-11 13:15:41 -07002291 if (retval < 0)
2292 FMDERR("Failed to enable Smute\n");
2293 radio->stereo_mode.stereo_mode = CTRL_OFF;
2294 radio->stereo_mode.sig_blend = CTRL_ON;
2295 radio->stereo_mode.intf_blend = CTRL_ON;
2296 radio->stereo_mode.most_switch = CTRL_ON;
2297 retval = hci_set_fm_stereo_mode(
2298 &radio->stereo_mode,
2299 radio->fm_hdev);
2300 if (retval < 0)
2301 FMDERR("Failed to set stereo mode\n");
Srinivasa Rao Uppala07522d92011-08-16 05:09:30 -07002302 break;
Ankur Nandwanid928d542011-08-11 13:15:41 -07002303 case FM_TRANS:
2304 retval = hci_cmd(HCI_FM_ENABLE_TRANS_CMD,
2305 radio->fm_hdev);
2306 radio->mode = FM_TRANS;
2307
2308 if (retval < 0)
2309 FMDERR("Error while enabling TRANS FM"
2310 " %d\n", retval);
Srinivasa Rao Uppala07522d92011-08-16 05:09:30 -07002311 break;
Ankur Nandwanid928d542011-08-11 13:15:41 -07002312 case FM_OFF:
2313 switch (radio->mode) {
2314 case FM_RECV:
2315 retval = hci_cmd(HCI_FM_DISABLE_RECV_CMD,
2316 radio->fm_hdev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002317 if (retval < 0)
Ankur Nandwanid928d542011-08-11 13:15:41 -07002318 FMDERR("Err on disable recv FM"
2319 " %d\n", retval);
2320 break;
2321 case FM_TRANS:
2322 retval = hci_cmd(HCI_FM_DISABLE_TRANS_CMD,
2323 radio->fm_hdev);
2324
Srinivasa Rao Uppala0ffb5d62011-08-02 17:54:13 -07002325 if (retval < 0)
Ankur Nandwanid928d542011-08-11 13:15:41 -07002326 FMDERR("Err disabling trans FM"
Srinivasa Rao Uppala0ffb5d62011-08-02 17:54:13 -07002327 " %d\n", retval);
Ankur Nandwanid928d542011-08-11 13:15:41 -07002328 break;
2329 default:
2330 retval = -EINVAL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002331 }
Srinivasa Rao Uppalacc62b1c2011-08-22 19:15:29 +05302332 break;
Ankur Nandwanid928d542011-08-11 13:15:41 -07002333 default:
2334 retval = -EINVAL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002335 }
2336 break;
2337 case V4L2_CID_PRIVATE_IRIS_REGION:
Ankur Nandwanid928d542011-08-11 13:15:41 -07002338 if (radio->mode == FM_RECV) {
2339 retval = iris_recv_set_region(radio, ctrl->value);
2340 } else {
2341 if (radio->mode == FM_TRANS)
2342 retval = iris_trans_set_region(radio,
2343 ctrl->value);
2344 else
2345 retval = -EINVAL;
2346 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002347 break;
2348 case V4L2_CID_PRIVATE_IRIS_SIGNAL_TH:
2349 temp_val = ctrl->value;
2350 retval = hci_fm_set_signal_threshold(
2351 &temp_val,
2352 radio->fm_hdev);
2353 if (retval < 0) {
2354 FMDERR("Error while setting signal threshold\n");
2355 break;
2356 }
2357 break;
2358 case V4L2_CID_PRIVATE_IRIS_SRCH_PTY:
2359 radio->srch_rds.srch_pty = ctrl->value;
2360 radio->srch_st_list.srch_pty = ctrl->value;
2361 break;
2362 case V4L2_CID_PRIVATE_IRIS_SRCH_PI:
2363 radio->srch_rds.srch_pi = ctrl->value;
2364 break;
2365 case V4L2_CID_PRIVATE_IRIS_SRCH_CNT:
2366 break;
2367 case V4L2_CID_PRIVATE_IRIS_SPACING:
2368 radio->recv_conf.ch_spacing = ctrl->value;
2369 break;
2370 case V4L2_CID_PRIVATE_IRIS_EMPHASIS:
2371 radio->recv_conf.emphasis = ctrl->value;
Ankur Nandwanid928d542011-08-11 13:15:41 -07002372 retval = hci_set_fm_recv_conf(
2373 &radio->recv_conf,
2374 radio->fm_hdev);
2375 if (retval < 0) {
2376 FMDERR("Error in setting emphasis");
2377 break;
2378 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002379 break;
2380 case V4L2_CID_PRIVATE_IRIS_RDS_STD:
2381 radio->recv_conf.rds_std = ctrl->value;
Ankur Nandwanid928d542011-08-11 13:15:41 -07002382 retval = hci_set_fm_recv_conf(
2383 &radio->recv_conf,
2384 radio->fm_hdev);
2385 if (retval < 0) {
2386 FMDERR("Error in setting RDS_STD");
2387 break;
2388 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002389 break;
2390 case V4L2_CID_PRIVATE_IRIS_RDSON:
2391 radio->recv_conf.rds_std = ctrl->value;
2392 retval =
2393 hci_set_fm_recv_conf(&radio->recv_conf, radio->fm_hdev);
2394 break;
2395 case V4L2_CID_PRIVATE_IRIS_RDSGROUP_MASK:
2396 radio->rds_grp.rds_grp_enable_mask = ctrl->value;
2397 retval = hci_fm_rds_grp(&radio->rds_grp, radio->fm_hdev);
2398 break;
2399 case V4L2_CID_PRIVATE_IRIS_RDSGROUP_PROC:
2400 rds_grps_proc = radio->g_rds_grp_proc_ps | ctrl->value;
Srinivasa Rao Uppala58273f82011-08-10 19:07:45 -07002401 radio->g_rds_grp_proc_ps = (rds_grps_proc >> RDS_CONFIG_OFFSET);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002402 retval = hci_fm_rds_grps_process(
Srinivasa Rao Uppala58273f82011-08-10 19:07:45 -07002403 &radio->g_rds_grp_proc_ps,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002404 radio->fm_hdev);
2405 break;
2406 case V4L2_CID_PRIVATE_IRIS_RDSD_BUF:
2407 radio->rds_grp.rds_buf_size = ctrl->value;
2408 break;
2409 case V4L2_CID_PRIVATE_IRIS_PSALL:
Srinivasa Rao Uppala58273f82011-08-10 19:07:45 -07002410 rds_grps_proc = (ctrl->value << RDS_CONFIG_OFFSET);
2411 radio->g_rds_grp_proc_ps |= rds_grps_proc;
2412 retval = hci_fm_rds_grps_process(
2413 &radio->g_rds_grp_proc_ps,
2414 radio->fm_hdev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002415 break;
2416 case V4L2_CID_PRIVATE_IRIS_LP_MODE:
2417 break;
2418 case V4L2_CID_PRIVATE_IRIS_ANTENNA:
2419 temp_val = ctrl->value;
2420 retval = hci_fm_set_antenna(&temp_val, radio->fm_hdev);
2421 break;
2422 case V4L2_CID_RDS_TX_PTY:
Ankur Nandwanid928d542011-08-11 13:15:41 -07002423 radio->pty = ctrl->value;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002424 break;
2425 case V4L2_CID_RDS_TX_PI:
Ankur Nandwanid928d542011-08-11 13:15:41 -07002426 radio->pi = ctrl->value;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002427 break;
2428 case V4L2_CID_PRIVATE_IRIS_STOP_RDS_TX_PS_NAME:
2429 break;
2430 case V4L2_CID_PRIVATE_IRIS_STOP_RDS_TX_RT:
2431 break;
2432 case V4L2_CID_PRIVATE_IRIS_TX_SETPSREPEATCOUNT:
Ankur Nandwanid928d542011-08-11 13:15:41 -07002433 radio->ps_repeatcount = ctrl->value;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002434 break;
2435 case V4L2_CID_TUNE_POWER_LEVEL:
2436 break;
Srinivasa Rao Uppalaabc8efa2011-08-02 14:31:30 -07002437 case V4L2_CID_PRIVATE_IRIS_SOFT_MUTE:
2438 radio->mute_mode.soft_mute = ctrl->value;
2439 retval = hci_set_fm_mute_mode(
2440 &radio->mute_mode,
2441 radio->fm_hdev);
2442 if (retval < 0)
2443 FMDERR("Error while setting FM soft mute"" %d\n",
2444 retval);
2445 break;
2446 case V4L2_CID_PRIVATE_IRIS_RIVA_ACCS_ADDR:
2447 radio->riva_data_req.cmd_params.start_addr = ctrl->value;
2448 break;
2449 case V4L2_CID_PRIVATE_IRIS_RIVA_ACCS_LEN:
2450 radio->riva_data_req.cmd_params.length = ctrl->value;
2451 break;
2452 case V4L2_CID_PRIVATE_IRIS_RIVA_POKE:
2453 memcpy(radio->riva_data_req.data, (void *)ctrl->value,
2454 radio->riva_data_req.cmd_params.length);
2455 radio->riva_data_req.cmd_params.subopcode = RIVA_POKE_OPCODE;
2456 retval = hci_poke_data(&radio->riva_data_req , radio->fm_hdev);
2457 break;
2458 case V4L2_CID_PRIVATE_IRIS_SSBI_ACCS_ADDR:
2459 radio->ssbi_data_accs.start_addr = ctrl->value;
2460 break;
2461 case V4L2_CID_PRIVATE_IRIS_SSBI_POKE:
2462 radio->ssbi_data_accs.data = ctrl->value;
2463 retval = hci_ssbi_poke_reg(&radio->ssbi_data_accs ,
2464 radio->fm_hdev);
2465 break;
2466 case V4L2_CID_PRIVATE_IRIS_RIVA_PEEK:
2467 radio->riva_data_req.cmd_params.subopcode = RIVA_PEEK_OPCODE;
2468 ctrl->value = hci_peek_data(&radio->riva_data_req.cmd_params ,
2469 radio->fm_hdev);
2470 break;
2471 case V4L2_CID_PRIVATE_IRIS_SSBI_PEEK:
2472 radio->ssbi_peek_reg.start_address = ctrl->value;
2473 hci_ssbi_peek_reg(&radio->ssbi_peek_reg, radio->fm_hdev);
2474 break;
2475
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002476 default:
2477 retval = -EINVAL;
2478 }
2479 return retval;
2480}
2481
2482static int iris_vidioc_g_tuner(struct file *file, void *priv,
2483 struct v4l2_tuner *tuner)
2484{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002485 int retval;
Ankur Nandwanid928d542011-08-11 13:15:41 -07002486 struct iris_device *radio = video_get_drvdata(video_devdata(file));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002487 if (tuner->index > 0)
2488 return -EINVAL;
2489
2490 retval = hci_cmd(HCI_FM_GET_STATION_PARAM_CMD, radio->fm_hdev);
2491 if (retval < 0)
2492 return retval;
2493
2494 tuner->type = V4L2_TUNER_RADIO;
2495 tuner->rangelow = radio->recv_conf.band_low_limit * TUNE_PARAM;
2496 tuner->rangehigh = radio->recv_conf.band_high_limit * TUNE_PARAM;
2497 tuner->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
2498 tuner->capability = V4L2_TUNER_CAP_LOW;
2499 tuner->signal = radio->fm_st_rsp.station_rsp.rssi;
2500 tuner->audmode = radio->fm_st_rsp.station_rsp.stereo_prg;
2501 tuner->afc = 0;
2502
2503 return 0;
2504}
2505
2506static int iris_vidioc_s_tuner(struct file *file, void *priv,
2507 struct v4l2_tuner *tuner)
2508{
2509 struct iris_device *radio = video_get_drvdata(video_devdata(file));
Ankur Nandwanid928d542011-08-11 13:15:41 -07002510 int retval = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002511 if (tuner->index > 0)
2512 return -EINVAL;
2513
Ankur Nandwanid928d542011-08-11 13:15:41 -07002514 if (radio->mode == FM_RECV) {
2515 radio->recv_conf.band_low_limit = tuner->rangelow / TUNE_PARAM;
2516 radio->recv_conf.band_high_limit =
2517 tuner->rangehigh / TUNE_PARAM;
2518 if (tuner->audmode == V4L2_TUNER_MODE_MONO) {
2519 radio->stereo_mode.stereo_mode = 0x01;
2520 retval = hci_set_fm_stereo_mode(
2521 &radio->stereo_mode,
2522 radio->fm_hdev);
2523 } else {
2524 radio->stereo_mode.stereo_mode = 0x00;
2525 retval = hci_set_fm_stereo_mode(
2526 &radio->stereo_mode,
2527 radio->fm_hdev);
2528 }
2529 if (retval < 0)
2530 FMDERR(": set tuner failed with %d\n", retval);
2531 return retval;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002532 } else {
Ankur Nandwanid928d542011-08-11 13:15:41 -07002533 if (radio->mode == FM_TRANS) {
2534 radio->trans_conf.band_low_limit =
2535 tuner->rangelow / TUNE_PARAM;
2536 radio->trans_conf.band_high_limit =
2537 tuner->rangehigh / TUNE_PARAM;
2538 } else {
2539 return -EINVAL;
2540 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002541 }
Ankur Nandwanid928d542011-08-11 13:15:41 -07002542
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002543 return retval;
2544}
2545
2546static int iris_vidioc_g_frequency(struct file *file, void *priv,
2547 struct v4l2_frequency *freq)
2548{
2549 struct iris_device *radio = video_get_drvdata(video_devdata(file));
2550 int retval;
2551
2552 freq->type = V4L2_TUNER_RADIO;
2553 retval = hci_cmd(HCI_FM_GET_STATION_PARAM_CMD, radio->fm_hdev);
2554 if (retval < 0)
2555 FMDERR("get frequency failed %d\n", retval);
2556 else
2557 freq->frequency =
2558 radio->fm_st_rsp.station_rsp.station_freq * TUNE_PARAM;
2559 return retval;
2560}
2561
2562static int iris_vidioc_s_frequency(struct file *file, void *priv,
2563 struct v4l2_frequency *freq)
2564{
2565 struct iris_device *radio = video_get_drvdata(video_devdata(file));
2566 int retval = -1;
2567 freq->frequency = freq->frequency / TUNE_PARAM;
2568
2569 if (freq->type != V4L2_TUNER_RADIO)
2570 return -EINVAL;
2571
2572 retval = iris_set_freq(radio, freq->frequency);
2573 if (retval < 0)
2574 FMDERR(" set frequency failed with %d\n", retval);
2575 return retval;
2576}
2577
2578static int iris_vidioc_dqbuf(struct file *file, void *priv,
2579 struct v4l2_buffer *buffer)
2580{
2581 struct iris_device *radio = video_get_drvdata(video_devdata(file));
2582 enum iris_buf_t buf_type = buffer->index;
2583 struct kfifo *data_fifo;
2584 unsigned char *buf = (unsigned char *)buffer->m.userptr;
2585 unsigned int len = buffer->length;
2586 if (!access_ok(VERIFY_WRITE, buf, len))
2587 return -EFAULT;
2588 if ((buf_type < IRIS_BUF_MAX) && (buf_type >= 0)) {
2589 data_fifo = &radio->data_buf[buf_type];
2590 if (buf_type == IRIS_BUF_EVENTS)
2591 if (wait_event_interruptible(radio->event_queue,
2592 kfifo_len(data_fifo)) < 0)
2593 return -EINTR;
2594 } else {
2595 FMDERR("invalid buffer type\n");
2596 return -EINVAL;
2597 }
2598 buffer->bytesused = kfifo_out_locked(data_fifo, buf, len,
2599 &radio->buf_lock[buf_type]);
2600
2601 return 0;
2602}
2603
2604static int iris_vidioc_g_fmt_type_private(struct file *file, void *priv,
2605 struct v4l2_format *f)
2606{
2607 return 0;
2608
2609}
2610
2611static int iris_vidioc_s_hw_freq_seek(struct file *file, void *priv,
2612 struct v4l2_hw_freq_seek *seek)
2613{
2614 struct iris_device *radio = video_get_drvdata(video_devdata(file));
2615 int dir;
2616 if (seek->seek_upward)
2617 dir = SRCH_DIR_UP;
2618 else
2619 dir = SRCH_DIR_DOWN;
2620 return iris_search(radio, CTRL_ON, dir);
2621}
2622
2623static int iris_vidioc_querycap(struct file *file, void *priv,
2624 struct v4l2_capability *capability)
2625{
2626 struct iris_device *radio;
2627 radio = video_get_drvdata(video_devdata(file));
2628 strlcpy(capability->driver, DRIVER_NAME, sizeof(capability->driver));
2629 strlcpy(capability->card, DRIVER_CARD, sizeof(capability->card));
2630 radio->g_cap = capability;
2631 return 0;
2632}
2633
2634
2635static const struct v4l2_ioctl_ops iris_ioctl_ops = {
2636 .vidioc_querycap = iris_vidioc_querycap,
2637 .vidioc_queryctrl = iris_vidioc_queryctrl,
2638 .vidioc_g_ctrl = iris_vidioc_g_ctrl,
2639 .vidioc_s_ctrl = iris_vidioc_s_ctrl,
2640 .vidioc_g_tuner = iris_vidioc_g_tuner,
2641 .vidioc_s_tuner = iris_vidioc_s_tuner,
2642 .vidioc_g_frequency = iris_vidioc_g_frequency,
2643 .vidioc_s_frequency = iris_vidioc_s_frequency,
2644 .vidioc_s_hw_freq_seek = iris_vidioc_s_hw_freq_seek,
2645 .vidioc_dqbuf = iris_vidioc_dqbuf,
2646 .vidioc_g_fmt_type_private = iris_vidioc_g_fmt_type_private,
2647 .vidioc_s_ext_ctrls = iris_vidioc_s_ext_ctrls,
2648};
2649
2650static const struct v4l2_file_operations iris_fops = {
2651 .owner = THIS_MODULE,
2652 .unlocked_ioctl = video_ioctl2,
2653};
2654
2655static struct video_device iris_viddev_template = {
2656 .fops = &iris_fops,
2657 .ioctl_ops = &iris_ioctl_ops,
2658 .name = DRIVER_NAME,
2659 .release = video_device_release,
2660};
2661
2662static struct video_device *video_get_dev(void)
2663{
2664 return priv_videodev;
2665}
2666
2667static int __init iris_probe(struct platform_device *pdev)
2668{
2669 struct iris_device *radio;
2670 int retval;
2671 int radio_nr = -1;
2672 int i;
2673
2674 if (!pdev) {
2675 FMDERR(": pdev is null\n");
2676 return -ENOMEM;
2677 }
2678
2679 radio = kzalloc(sizeof(struct iris_device), GFP_KERNEL);
2680 if (!radio) {
2681 FMDERR(": Could not allocate radio device\n");
2682 return -ENOMEM;
2683 }
2684
2685 radio->dev = &pdev->dev;
2686 platform_set_drvdata(pdev, radio);
2687
2688 radio->videodev = video_device_alloc();
2689 if (!radio->videodev) {
2690 FMDERR(": Could not allocate V4L device\n");
2691 kfree(radio);
2692 return -ENOMEM;
2693 }
2694
2695 memcpy(radio->videodev, &iris_viddev_template,
2696 sizeof(iris_viddev_template));
2697
2698 for (i = 0; i < IRIS_BUF_MAX; i++) {
2699 int kfifo_alloc_rc = 0;
2700 spin_lock_init(&radio->buf_lock[i]);
2701
Srinivasa Rao Uppala6cc0e322011-08-12 10:54:48 -07002702 if ((i == IRIS_BUF_RAW_RDS) || (i == IRIS_BUF_PEEK))
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002703 kfifo_alloc_rc = kfifo_alloc(&radio->data_buf[i],
2704 rds_buf*3, GFP_KERNEL);
2705 else
2706 kfifo_alloc_rc = kfifo_alloc(&radio->data_buf[i],
2707 STD_BUF_SIZE, GFP_KERNEL);
2708
2709 if (kfifo_alloc_rc != 0) {
2710 FMDERR("failed allocating buffers %d\n",
2711 kfifo_alloc_rc);
2712 for (; i > -1; i--) {
2713 kfifo_free(&radio->data_buf[i]);
2714 kfree(radio);
2715 return -ENOMEM;
2716 }
2717 }
2718 }
2719
2720 mutex_init(&radio->lock);
2721 init_completion(&radio->sync_xfr_start);
2722 radio->tune_req = 0;
2723 init_waitqueue_head(&radio->event_queue);
2724 init_waitqueue_head(&radio->read_queue);
2725
2726 video_set_drvdata(radio->videodev, radio);
2727
2728 if (NULL == video_get_drvdata(radio->videodev))
2729 FMDERR(": video_get_drvdata failed\n");
2730
2731 retval = video_register_device(radio->videodev, VFL_TYPE_RADIO,
2732 radio_nr);
2733 if (retval) {
2734 FMDERR(": Could not register video device\n");
2735 video_device_release(radio->videodev);
2736 for (; i > -1; i--)
2737 kfifo_free(&radio->data_buf[i]);
2738 kfree(radio);
2739 return retval;
2740 } else {
2741 priv_videodev = kzalloc(sizeof(struct video_device),
2742 GFP_KERNEL);
2743 memcpy(priv_videodev, radio->videodev,
2744 sizeof(struct video_device));
2745 }
2746 return 0;
2747}
2748
2749
2750static int __devexit iris_remove(struct platform_device *pdev)
2751{
2752 int i;
2753 struct iris_device *radio = platform_get_drvdata(pdev);
2754
2755 video_unregister_device(radio->videodev);
2756
2757 for (i = 0; i < IRIS_BUF_MAX; i++)
2758 kfifo_free(&radio->data_buf[i]);
2759
2760 kfree(radio);
2761
2762 platform_set_drvdata(pdev, NULL);
2763
2764 return 0;
2765}
2766
2767static struct platform_driver iris_driver = {
2768 .driver = {
2769 .owner = THIS_MODULE,
2770 .name = "iris_fm",
2771 },
2772 .remove = __devexit_p(iris_remove),
2773};
2774
2775static int __init iris_radio_init(void)
2776{
2777 return platform_driver_probe(&iris_driver, iris_probe);
2778}
2779module_init(iris_radio_init);
2780
2781static void __exit iris_radio_exit(void)
2782{
2783 platform_driver_unregister(&iris_driver);
2784}
2785module_exit(iris_radio_exit);
2786
2787MODULE_LICENSE("GPL v2");
2788MODULE_AUTHOR(DRIVER_AUTHOR);
2789MODULE_DESCRIPTION(DRIVER_DESC);