Created
September 7, 2018 14:55
-
-
Save d-k-c/3d7aacb78f462b60011f1d753d526de7 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
From c12e9dca496c9f1c74af1772d53442045c0f9674 Mon Sep 17 00:00:00 2001 | |
From: Damien Riegel <damien.riegel@savoirfairelinux.com> | |
Date: Mon, 31 Jul 2017 11:33:42 -0400 | |
Subject: [PATCH 1/4] add radio iris | |
--- | |
drivers/media/radio/Kconfig | 11 + | |
drivers/media/radio/Makefile | 2 + | |
drivers/media/radio/radio-iris-transport.c | 215 ++ | |
drivers/media/radio/radio-iris-v4l2.c | 5511 ++++++++++++++++++++++++++++ | |
include/uapi/media/radio-iris-commands.h | 114 + | |
include/uapi/media/radio-iris.h | 1121 ++++++ | |
6 files changed, 6974 insertions(+) | |
create mode 100644 drivers/media/radio/radio-iris-transport.c | |
create mode 100644 drivers/media/radio/radio-iris-v4l2.c | |
create mode 100644 include/uapi/media/radio-iris-commands.h | |
create mode 100644 include/uapi/media/radio-iris.h | |
diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig | |
index 192f36f2f4aa..cce8b6cd11ee 100644 | |
--- a/drivers/media/radio/Kconfig | |
+++ b/drivers/media/radio/Kconfig | |
@@ -12,6 +12,17 @@ menuconfig RADIO_ADAPTERS | |
if RADIO_ADAPTERS && VIDEO_V4L2 | |
+config RADIO_IRIS | |
+ tristate "Qualcomm SMD based Radio Support" | |
+ depends on VIDEO_V4L2 | |
+ depends on QCOM_SMD | |
+ help | |
+ Say Y here if you want to use the Qualcomm FM chip (IRIS). | |
+ This FM chip uses SMD interface | |
+ | |
+ To compile this driver as a module, choose M here: the | |
+ module will be called radio-iris. | |
+ | |
config RADIO_TEA575X | |
tristate | |
diff --git a/drivers/media/radio/Makefile b/drivers/media/radio/Makefile | |
index 120e791199b2..23edd396b029 100644 | |
--- a/drivers/media/radio/Makefile | |
+++ b/drivers/media/radio/Makefile | |
@@ -4,6 +4,7 @@ | |
obj-$(CONFIG_RADIO_ISA) += radio-isa.o | |
obj-$(CONFIG_RADIO_AZTECH) += radio-aztech.o | |
+obj-$(CONFIG_RADIO_IRIS) += radio-iris.o | |
obj-$(CONFIG_RADIO_RTRACK2) += radio-rtrack2.o | |
obj-$(CONFIG_RADIO_SF16FMI) += radio-sf16fmi.o | |
obj-$(CONFIG_RADIO_SF16FMR2) += radio-sf16fmr2.o | |
@@ -34,6 +35,7 @@ obj-$(CONFIG_RADIO_WL128X) += wl128x/ | |
obj-$(CONFIG_RADIO_TEA575X) += tea575x.o | |
obj-$(CONFIG_USB_RAREMONO) += radio-raremono.o | |
+radio-iris-objs := radio-iris-v4l2.o radio-iris-transport.o | |
shark2-objs := radio-shark2.o radio-tea5777.o | |
ccflags-y += -Isound | |
diff --git a/drivers/media/radio/radio-iris-transport.c b/drivers/media/radio/radio-iris-transport.c | |
new file mode 100644 | |
index 000000000000..88133ce5cae8 | |
--- /dev/null | |
+++ b/drivers/media/radio/radio-iris-transport.c | |
@@ -0,0 +1,215 @@ | |
+/* | |
+ * Qualcomm's FM Shared Memory Transport Driver | |
+ * | |
+ * FM HCI_SMD ( FM HCI Shared Memory Driver) is Qualcomm's Shared memory driver | |
+ * for the HCI protocol. This file is based on drivers/bluetooth/hci_vhci.c | |
+ * | |
+ * Copyright (c) 2000-2001, 2011-2012, 2014 The Linux Foundation. | |
+ * All rights reserved. | |
+ * | |
+ * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com> | |
+ * Copyright (C) 2004-2006 Marcel Holtmann <marcel@holtmann.org> | |
+ * | |
+ * This program is free software; you can redistribute it and/or modify | |
+ * it under the terms of the GNU General Public License version 2 | |
+ * as published by the Free Software Foundation | |
+ * | |
+ * This program is distributed in the hope that it will be useful, | |
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
+ * GNU General Public License for more details. | |
+ */ | |
+ | |
+#include <linux/module.h> | |
+#include <linux/kernel.h> | |
+#include <linux/init.h> | |
+#include <linux/errno.h> | |
+#include <linux/string.h> | |
+#include <linux/skbuff.h> | |
+#include <linux/workqueue.h> | |
+#include <soc/qcom/smd.h> | |
+#include <media/radio-iris.h> | |
+ | |
+struct radio_data { | |
+ struct radio_hci_dev *hdev; | |
+ struct tasklet_struct rx_task; | |
+ struct smd_channel *fm_channel; | |
+}; | |
+struct radio_data hs; | |
+ | |
+static struct work_struct *reset_worker; | |
+ | |
+static void radio_hci_smd_destruct(struct radio_hci_dev *hdev) | |
+{ | |
+ radio_hci_unregister_dev(hs.hdev); | |
+} | |
+ | |
+ | |
+static void radio_hci_smd_recv_event(unsigned long temp) | |
+{ | |
+ int len; | |
+ int rc; | |
+ struct sk_buff *skb; | |
+ unsigned char *buf; | |
+ struct radio_data *hsmd = &hs; | |
+ len = smd_read_avail(hsmd->fm_channel); | |
+ | |
+ while (len) { | |
+ skb = alloc_skb(len, GFP_ATOMIC); | |
+ if (!skb) { | |
+ FMDERR("Memory not allocated for the socket"); | |
+ return; | |
+ } | |
+ | |
+ buf = kmalloc(len, GFP_ATOMIC); | |
+ if (!buf) { | |
+ kfree_skb(skb); | |
+ FMDERR("Error in allocating buffer memory"); | |
+ return; | |
+ } | |
+ | |
+ rc = smd_read(hsmd->fm_channel, (void *)buf, len); | |
+ | |
+ memcpy(skb_put(skb, len), buf, len); | |
+ | |
+ skb_orphan(skb); | |
+ skb->dev = (struct net_device *)hs.hdev; | |
+ | |
+ rc = radio_hci_recv_frame(skb); | |
+ | |
+ kfree(buf); | |
+ len = smd_read_avail(hsmd->fm_channel); | |
+ } | |
+} | |
+ | |
+static int radio_hci_smd_send_frame(struct sk_buff *skb) | |
+{ | |
+ int len = 0; | |
+ | |
+ len = smd_write(hs.fm_channel, skb->data, skb->len); | |
+ if (len < skb->len) { | |
+ FMDERR("Failed to write Data %d", len); | |
+ kfree_skb(skb); | |
+ return -ENODEV; | |
+ } | |
+ kfree_skb(skb); | |
+ return 0; | |
+} | |
+ | |
+ | |
+static void send_disable_event(struct work_struct *worker) | |
+{ | |
+ struct sk_buff *skb; | |
+ unsigned char buf[6] = { 0x0f, 0x04, 0x01, 0x02, 0x4c, 0x00 }; | |
+ int len = sizeof(buf); | |
+ | |
+ skb = alloc_skb(len, GFP_ATOMIC); | |
+ if (!skb) { | |
+ FMDERR("Memory not allocated for the socket"); | |
+ kfree(worker); | |
+ return; | |
+ } | |
+ | |
+ FMDERR("FM INSERT DISABLE Rx Event"); | |
+ | |
+ memcpy(skb_put(skb, len), buf, len); | |
+ | |
+ skb_orphan(skb); | |
+ skb->dev = (struct net_device *)hs.hdev; | |
+ | |
+ radio_hci_recv_frame(skb); | |
+ kfree(worker); | |
+} | |
+ | |
+static void radio_hci_smd_notify_cmd(void *data, unsigned int event) | |
+{ | |
+ struct radio_hci_dev *hdev = hs.hdev; | |
+ | |
+ if (!hdev) { | |
+ FMDERR("Frame for unknown HCI device (hdev=NULL)"); | |
+ return; | |
+ } | |
+ | |
+ switch (event) { | |
+ case SMD_EVENT_DATA: | |
+ tasklet_schedule(&hs.rx_task); | |
+ break; | |
+ case SMD_EVENT_OPEN: | |
+ break; | |
+ case SMD_EVENT_CLOSE: | |
+ reset_worker = kzalloc(sizeof(*reset_worker), GFP_ATOMIC); | |
+ if (!reset_worker) { | |
+ FMDERR("Out of memory"); | |
+ break; | |
+ } | |
+ INIT_WORK(reset_worker, send_disable_event); | |
+ schedule_work(reset_worker); | |
+ break; | |
+ default: | |
+ break; | |
+ } | |
+} | |
+ | |
+static int radio_hci_smd_register_dev(struct radio_data *hsmd) | |
+{ | |
+ struct radio_hci_dev *hdev; | |
+ int rc; | |
+ | |
+ if (hsmd == NULL) | |
+ return -ENODEV; | |
+ | |
+ hdev = kmalloc(sizeof(struct radio_hci_dev), GFP_KERNEL); | |
+ if (hdev == NULL) | |
+ return -ENODEV; | |
+ | |
+ hsmd->hdev = hdev; | |
+ tasklet_init(&hsmd->rx_task, radio_hci_smd_recv_event, | |
+ (unsigned long) hsmd); | |
+ hdev->send = radio_hci_smd_send_frame; | |
+ hdev->destruct = radio_hci_smd_destruct; | |
+ | |
+ /* Open the SMD Channel and device and register the callback function */ | |
+ rc = smd_named_open_on_edge("APPS_FM", SMD_APPS_WCNSS, | |
+ &hsmd->fm_channel, hdev, radio_hci_smd_notify_cmd); | |
+ | |
+ if (rc < 0) { | |
+ FMDERR("Cannot open the command channel"); | |
+ hsmd->hdev = NULL; | |
+ kfree(hdev); | |
+ return -ENODEV; | |
+ } | |
+ | |
+ smd_disable_read_intr(hsmd->fm_channel); | |
+ | |
+ if (radio_hci_register_dev(hdev) < 0) { | |
+ FMDERR("Can't register HCI device"); | |
+ smd_close(hsmd->fm_channel); | |
+ hsmd->hdev = NULL; | |
+ kfree(hdev); | |
+ return -ENODEV; | |
+ } | |
+ | |
+ return 0; | |
+} | |
+ | |
+static void radio_hci_smd_deregister(void) | |
+{ | |
+ smd_close(hs.fm_channel); | |
+ hs.fm_channel = 0; | |
+} | |
+ | |
+static int radio_hci_smd_init(void) | |
+{ | |
+ return radio_hci_smd_register_dev(&hs); | |
+} | |
+module_init(radio_hci_smd_init); | |
+ | |
+static void __exit radio_hci_smd_exit(void) | |
+{ | |
+ radio_hci_smd_deregister(); | |
+} | |
+module_exit(radio_hci_smd_exit); | |
+ | |
+MODULE_DESCRIPTION("Bluetooth SMD driver"); | |
+MODULE_AUTHOR("Ankur Nandwani <ankurn@codeaurora.org>"); | |
+MODULE_LICENSE("GPL v2"); | |
diff --git a/drivers/media/radio/radio-iris-v4l2.c b/drivers/media/radio/radio-iris-v4l2.c | |
new file mode 100644 | |
index 000000000000..e027aaac2441 | |
--- /dev/null | |
+++ b/drivers/media/radio/radio-iris-v4l2.c | |
@@ -0,0 +1,5511 @@ | |
+/* Copyright (c) 2011-2014, The Linux Foundation. All rights reserved | |
+ * | |
+ * This program is free software; you can redistribute it and/or modify | |
+ * it under the terms of the GNU General Public License version 2 and | |
+ * only version 2 as published by the Free Software Foundation. | |
+ * | |
+ * This program is distributed in the hope that it will be useful, | |
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
+ * GNU General Public License for more details. | |
+ */ | |
+ | |
+#define DRIVER_AUTHOR "Archana Ramchandran <archanar@codeaurora.org>" | |
+#define DRIVER_NAME "radio-iris" | |
+#define DRIVER_CARD "Qualcomm FM Radio Transceiver" | |
+#define DRIVER_DESC "Driver for Qualcomm FM Radio Transceiver " | |
+ | |
+#include <linux/version.h> | |
+#include <linux/init.h> | |
+#include <linux/delay.h> | |
+#include <linux/uaccess.h> | |
+#include <linux/kfifo.h> | |
+#include <linux/param.h> | |
+#include <linux/interrupt.h> | |
+#include <linux/kernel.h> | |
+#include <linux/module.h> | |
+#include <linux/sched.h> | |
+#include <linux/version.h> | |
+#include <linux/videodev2.h> | |
+#include <linux/mutex.h> | |
+#include <linux/unistd.h> | |
+#include <linux/atomic.h> | |
+#include <linux/platform_device.h> | |
+#include <linux/workqueue.h> | |
+#include <linux/slab.h> | |
+#include <media/v4l2-common.h> | |
+#include <media/v4l2-ioctl.h> | |
+#include <media/radio-iris.h> | |
+#include <asm/unaligned.h> | |
+ | |
+static unsigned int rds_buf = 100; | |
+static int oda_agt; | |
+static int grp_mask; | |
+static int rt_plus_carrier = -1; | |
+static int ert_carrier = -1; | |
+static unsigned char ert_buf[256]; | |
+static unsigned char ert_len; | |
+static unsigned char c_byt_pair_index; | |
+static char utf_8_flag; | |
+static char rt_ert_flag; | |
+static char formatting_dir; | |
+static unsigned char sig_blend = CTRL_ON; | |
+static DEFINE_MUTEX(iris_fm); | |
+ | |
+module_param(rds_buf, uint, 0); | |
+MODULE_PARM_DESC(rds_buf, "RDS buffer entries: *100*"); | |
+ | |
+module_param(sig_blend, byte, S_IWUSR | S_IRUGO); | |
+MODULE_PARM_DESC(sig_blend, "signal blending switch: 0:OFF 1:ON"); | |
+ | |
+static void radio_hci_cmd_task(unsigned long arg); | |
+static void radio_hci_rx_task(unsigned long arg); | |
+static struct video_device *video_get_dev(void); | |
+static DEFINE_RWLOCK(hci_task_lock); | |
+ | |
+struct iris_device { | |
+ struct device *dev; | |
+ struct kfifo data_buf[IRIS_BUF_MAX]; | |
+ | |
+ int pending_xfrs[IRIS_XFR_MAX]; | |
+ int xfr_bytes_left; | |
+ int xfr_in_progress; | |
+ struct completion sync_xfr_start; | |
+ int tune_req; | |
+ unsigned int mode; | |
+ | |
+ __u16 pi; | |
+ __u8 pty; | |
+ __u8 ps_repeatcount; | |
+ __u8 prev_trans_rds; | |
+ __u8 af_jump_bit; | |
+ struct video_device *videodev; | |
+ | |
+ struct mutex lock; | |
+ spinlock_t buf_lock[IRIS_BUF_MAX]; | |
+ wait_queue_head_t event_queue; | |
+ wait_queue_head_t read_queue; | |
+ | |
+ struct radio_hci_dev *fm_hdev; | |
+ | |
+ struct v4l2_capability g_cap; | |
+ struct v4l2_control *g_ctl; | |
+ | |
+ struct hci_fm_mute_mode_req mute_mode; | |
+ struct hci_fm_stereo_mode_req stereo_mode; | |
+ struct hci_fm_station_rsp fm_st_rsp; | |
+ struct hci_fm_search_station_req srch_st; | |
+ struct hci_fm_search_rds_station_req srch_rds; | |
+ struct hci_fm_search_station_list_req srch_st_list; | |
+ struct hci_fm_recv_conf_req recv_conf; | |
+ struct hci_fm_trans_conf_req_struct trans_conf; | |
+ struct hci_fm_rds_grp_req rds_grp; | |
+ unsigned char g_search_mode; | |
+ unsigned char power_mode; | |
+ int search_on; | |
+ unsigned int tone_freq; | |
+ unsigned char spur_table_size; | |
+ unsigned char g_scan_time; | |
+ unsigned int g_antenna; | |
+ unsigned int g_rds_grp_proc_ps; | |
+ unsigned char event_mask; | |
+ enum iris_region_t region; | |
+ struct hci_fm_dbg_param_rsp st_dbg_param; | |
+ struct hci_ev_srch_list_compl srch_st_result; | |
+ struct hci_fm_riva_poke riva_data_req; | |
+ struct hci_fm_ssbi_req ssbi_data_accs; | |
+ struct hci_fm_ssbi_peek ssbi_peek_reg; | |
+ struct hci_fm_sig_threshold_rsp sig_th; | |
+ struct hci_fm_ch_det_threshold ch_det_threshold; | |
+ struct hci_fm_data_rd_rsp default_data; | |
+ struct hci_fm_spur_data spur_data; | |
+ unsigned char is_station_valid; | |
+ struct hci_fm_blend_table blend_tbl; | |
+}; | |
+ | |
+static struct video_device *priv_videodev; | |
+static int iris_do_calibration(struct iris_device *radio); | |
+static void hci_buff_ert(struct iris_device *radio, | |
+ struct rds_grp_data *rds_buf); | |
+static void hci_ev_rt_plus(struct iris_device *radio, | |
+ struct rds_grp_data rds_buf); | |
+static void hci_ev_ert(struct iris_device *radio); | |
+static int update_spur_table(struct iris_device *radio); | |
+static int initialise_recv(struct iris_device *radio); | |
+static int initialise_trans(struct iris_device *radio); | |
+static int is_enable_rx_possible(struct iris_device *radio); | |
+static int is_enable_tx_possible(struct iris_device *radio); | |
+ | |
+static struct v4l2_queryctrl iris_v4l2_queryctrl[] = { | |
+ { | |
+ .id = V4L2_CID_AUDIO_VOLUME, | |
+ .type = V4L2_CTRL_TYPE_INTEGER, | |
+ .name = "Volume", | |
+ .minimum = 0, | |
+ .maximum = 15, | |
+ .step = 1, | |
+ .default_value = 15, | |
+ }, | |
+ { | |
+ .id = V4L2_CID_AUDIO_BALANCE, | |
+ .flags = V4L2_CTRL_FLAG_DISABLED, | |
+ }, | |
+ { | |
+ .id = V4L2_CID_AUDIO_BASS, | |
+ .flags = V4L2_CTRL_FLAG_DISABLED, | |
+ }, | |
+ { | |
+ .id = V4L2_CID_AUDIO_TREBLE, | |
+ .flags = V4L2_CTRL_FLAG_DISABLED, | |
+ }, | |
+ { | |
+ .id = V4L2_CID_AUDIO_MUTE, | |
+ .type = V4L2_CTRL_TYPE_BOOLEAN, | |
+ .name = "Mute", | |
+ .minimum = 0, | |
+ .maximum = 1, | |
+ .step = 1, | |
+ .default_value = 1, | |
+ }, | |
+ { | |
+ .id = V4L2_CID_AUDIO_LOUDNESS, | |
+ .flags = V4L2_CTRL_FLAG_DISABLED, | |
+ }, | |
+ { | |
+ .id = V4L2_CID_PRIVATE_IRIS_SRCHMODE, | |
+ .type = V4L2_CTRL_TYPE_INTEGER, | |
+ .name = "Search mode", | |
+ .minimum = 0, | |
+ .maximum = 7, | |
+ .step = 1, | |
+ .default_value = 0, | |
+ }, | |
+ { | |
+ .id = V4L2_CID_PRIVATE_IRIS_SCANDWELL, | |
+ .type = V4L2_CTRL_TYPE_INTEGER, | |
+ .name = "Search dwell time", | |
+ .minimum = 0, | |
+ .maximum = 7, | |
+ .step = 1, | |
+ .default_value = 0, | |
+ }, | |
+ { | |
+ .id = V4L2_CID_PRIVATE_IRIS_SRCHON, | |
+ .type = V4L2_CTRL_TYPE_BOOLEAN, | |
+ .name = "Search on/off", | |
+ .minimum = 0, | |
+ .maximum = 1, | |
+ .step = 1, | |
+ .default_value = 1, | |
+ | |
+ }, | |
+ { | |
+ .id = V4L2_CID_PRIVATE_IRIS_STATE, | |
+ .type = V4L2_CTRL_TYPE_INTEGER, | |
+ .name = "radio 0ff/rx/tx/reset", | |
+ .minimum = 0, | |
+ .maximum = 3, | |
+ .step = 1, | |
+ .default_value = 1, | |
+ | |
+ }, | |
+ { | |
+ .id = V4L2_CID_PRIVATE_IRIS_REGION, | |
+ .type = V4L2_CTRL_TYPE_INTEGER, | |
+ .name = "radio standard", | |
+ .minimum = 0, | |
+ .maximum = 2, | |
+ .step = 1, | |
+ .default_value = 0, | |
+ }, | |
+ { | |
+ .id = V4L2_CID_PRIVATE_IRIS_SIGNAL_TH, | |
+ .type = V4L2_CTRL_TYPE_INTEGER, | |
+ .name = "Signal Threshold", | |
+ .minimum = 0x80, | |
+ .maximum = 0x7F, | |
+ .step = 1, | |
+ .default_value = 0, | |
+ }, | |
+ { | |
+ .id = V4L2_CID_PRIVATE_IRIS_SRCH_PTY, | |
+ .type = V4L2_CTRL_TYPE_INTEGER, | |
+ .name = "Search PTY", | |
+ .minimum = 0, | |
+ .maximum = 31, | |
+ .default_value = 0, | |
+ }, | |
+ { | |
+ .id = V4L2_CID_PRIVATE_IRIS_SRCH_PI, | |
+ .type = V4L2_CTRL_TYPE_INTEGER, | |
+ .name = "Search PI", | |
+ .minimum = 0, | |
+ .maximum = 0xFF, | |
+ .default_value = 0, | |
+ }, | |
+ { | |
+ .id = V4L2_CID_PRIVATE_IRIS_SRCH_CNT, | |
+ .type = V4L2_CTRL_TYPE_INTEGER, | |
+ .name = "Preset num", | |
+ .minimum = 0, | |
+ .maximum = 12, | |
+ .default_value = 0, | |
+ }, | |
+ { | |
+ .id = V4L2_CID_PRIVATE_IRIS_EMPHASIS, | |
+ .type = V4L2_CTRL_TYPE_BOOLEAN, | |
+ .name = "Emphasis", | |
+ .minimum = 0, | |
+ .maximum = 1, | |
+ .default_value = 0, | |
+ }, | |
+ { | |
+ .id = V4L2_CID_PRIVATE_IRIS_RDS_STD, | |
+ .type = V4L2_CTRL_TYPE_BOOLEAN, | |
+ .name = "RDS standard", | |
+ .minimum = 0, | |
+ .maximum = 1, | |
+ .default_value = 0, | |
+ }, | |
+ { | |
+ .id = V4L2_CID_PRIVATE_IRIS_SPACING, | |
+ .type = V4L2_CTRL_TYPE_INTEGER, | |
+ .name = "Channel spacing", | |
+ .minimum = 0, | |
+ .maximum = 2, | |
+ .default_value = 0, | |
+ }, | |
+ { | |
+ .id = V4L2_CID_PRIVATE_IRIS_RDSON, | |
+ .type = V4L2_CTRL_TYPE_BOOLEAN, | |
+ .name = "RDS on/off", | |
+ .minimum = 0, | |
+ .maximum = 1, | |
+ .default_value = 0, | |
+ }, | |
+ { | |
+ .id = V4L2_CID_PRIVATE_IRIS_RDSGROUP_MASK, | |
+ .type = V4L2_CTRL_TYPE_INTEGER, | |
+ .name = "RDS group mask", | |
+ .minimum = 0, | |
+ .maximum = 0xFFFFFFFF, | |
+ .default_value = 0, | |
+ }, | |
+ { | |
+ .id = V4L2_CID_PRIVATE_IRIS_RDSGROUP_PROC, | |
+ .type = V4L2_CTRL_TYPE_INTEGER, | |
+ .name = "RDS processing", | |
+ .minimum = 0, | |
+ .maximum = 0xFF, | |
+ .default_value = 0, | |
+ }, | |
+ { | |
+ .id = V4L2_CID_PRIVATE_IRIS_RDSD_BUF, | |
+ .type = V4L2_CTRL_TYPE_INTEGER, | |
+ .name = "RDS data groups to buffer", | |
+ .minimum = 1, | |
+ .maximum = 21, | |
+ .default_value = 0, | |
+ }, | |
+ { | |
+ .id = V4L2_CID_PRIVATE_IRIS_PSALL, | |
+ .type = V4L2_CTRL_TYPE_BOOLEAN, | |
+ .name = "pass all ps strings", | |
+ .minimum = 0, | |
+ .maximum = 1, | |
+ .default_value = 0, | |
+ }, | |
+ { | |
+ .id = V4L2_CID_PRIVATE_IRIS_LP_MODE, | |
+ .type = V4L2_CTRL_TYPE_BOOLEAN, | |
+ .name = "Low power mode", | |
+ .minimum = 0, | |
+ .maximum = 1, | |
+ .default_value = 0, | |
+ }, | |
+ { | |
+ .id = V4L2_CID_PRIVATE_IRIS_ANTENNA, | |
+ .type = V4L2_CTRL_TYPE_BOOLEAN, | |
+ .name = "headset/internal", | |
+ .minimum = 0, | |
+ .maximum = 1, | |
+ .default_value = 0, | |
+ }, | |
+ { | |
+ .id = V4L2_CID_PRIVATE_IRIS_TX_SETPSREPEATCOUNT, | |
+ .type = V4L2_CTRL_TYPE_INTEGER, | |
+ .name = "Set PS REPEATCOUNT", | |
+ .minimum = 0, | |
+ .maximum = 15, | |
+ }, | |
+ { | |
+ .id = V4L2_CID_PRIVATE_IRIS_STOP_RDS_TX_PS_NAME, | |
+ .type = V4L2_CTRL_TYPE_BOOLEAN, | |
+ .name = "Stop PS NAME", | |
+ .minimum = 0, | |
+ .maximum = 1, | |
+ }, | |
+ { | |
+ .id = V4L2_CID_PRIVATE_IRIS_STOP_RDS_TX_RT, | |
+ .type = V4L2_CTRL_TYPE_BOOLEAN, | |
+ .name = "Stop RT", | |
+ .minimum = 0, | |
+ .maximum = 1, | |
+ }, | |
+ { | |
+ .id = V4L2_CID_PRIVATE_IRIS_SOFT_MUTE, | |
+ .type = V4L2_CTRL_TYPE_BOOLEAN, | |
+ .name = "Soft Mute", | |
+ .minimum = 0, | |
+ .maximum = 1, | |
+ }, | |
+ { | |
+ .id = V4L2_CID_PRIVATE_IRIS_RIVA_ACCS_ADDR, | |
+ .type = V4L2_CTRL_TYPE_BOOLEAN, | |
+ .name = "Riva addr", | |
+ .minimum = 0x3180000, | |
+ .maximum = 0x31E0004, | |
+ }, | |
+ { | |
+ .id = V4L2_CID_PRIVATE_IRIS_RIVA_ACCS_LEN, | |
+ .type = V4L2_CTRL_TYPE_INTEGER, | |
+ .name = "Data len", | |
+ .minimum = 0, | |
+ .maximum = 0xFF, | |
+ }, | |
+ { | |
+ .id = V4L2_CID_PRIVATE_IRIS_RIVA_PEEK, | |
+ .type = V4L2_CTRL_TYPE_BOOLEAN, | |
+ .name = "Riva peek", | |
+ .minimum = 0, | |
+ .maximum = 1, | |
+ }, | |
+ { | |
+ .id = V4L2_CID_PRIVATE_IRIS_RIVA_POKE, | |
+ .type = V4L2_CTRL_TYPE_INTEGER, | |
+ .name = "Riva poke", | |
+ .minimum = 0x3180000, | |
+ .maximum = 0x31E0004, | |
+ }, | |
+ { | |
+ .id = V4L2_CID_PRIVATE_IRIS_SSBI_ACCS_ADDR, | |
+ .type = V4L2_CTRL_TYPE_INTEGER, | |
+ .name = "Ssbi addr", | |
+ .minimum = 0x280, | |
+ .maximum = 0x37F, | |
+ }, | |
+ { | |
+ .id = V4L2_CID_PRIVATE_IRIS_SSBI_PEEK, | |
+ .type = V4L2_CTRL_TYPE_INTEGER, | |
+ .name = "Ssbi peek", | |
+ .minimum = 0, | |
+ .maximum = 0x37F, | |
+ }, | |
+ { | |
+ .id = V4L2_CID_PRIVATE_IRIS_SSBI_POKE, | |
+ .type = V4L2_CTRL_TYPE_INTEGER, | |
+ .name = "ssbi poke", | |
+ .minimum = 0x01, | |
+ .maximum = 0xFF, | |
+ }, | |
+ { | |
+ .id = V4L2_CID_PRIVATE_IRIS_HLSI, | |
+ .type = V4L2_CTRL_TYPE_INTEGER, | |
+ .name = "set hlsi", | |
+ .minimum = 0, | |
+ .maximum = 2, | |
+ }, | |
+ { | |
+ .id = V4L2_CID_PRIVATE_IRIS_RDS_GRP_COUNTERS, | |
+ .type = V4L2_CTRL_TYPE_BOOLEAN, | |
+ .name = "RDS grp", | |
+ .minimum = 0, | |
+ .maximum = 1, | |
+ }, | |
+ { | |
+ .id = V4L2_CID_PRIVATE_IRIS_SET_NOTCH_FILTER, | |
+ .type = V4L2_CTRL_TYPE_INTEGER, | |
+ .name = "Notch filter", | |
+ .minimum = 0, | |
+ .maximum = 2, | |
+ }, | |
+ { | |
+ .id = V4L2_CID_PRIVATE_IRIS_READ_DEFAULT, | |
+ .type = V4L2_CTRL_TYPE_INTEGER, | |
+ .name = "Read default", | |
+ }, | |
+ { | |
+ .id = V4L2_CID_PRIVATE_IRIS_WRITE_DEFAULT, | |
+ .type = V4L2_CTRL_TYPE_INTEGER, | |
+ .name = "Write default", | |
+ }, | |
+ { | |
+ .id = V4L2_CID_PRIVATE_IRIS_SET_CALIBRATION, | |
+ .type = V4L2_CTRL_TYPE_BOOLEAN, | |
+ .name = "SET Calibration", | |
+ .minimum = 0, | |
+ .maximum = 1, | |
+ }, | |
+ { | |
+ .id = V4L2_CID_PRIVATE_IRIS_DO_CALIBRATION, | |
+ .type = V4L2_CTRL_TYPE_BOOLEAN, | |
+ .name = "SET Calibration", | |
+ .minimum = 0, | |
+ .maximum = 1, | |
+ }, | |
+ { | |
+ .id = V4L2_CID_PRIVATE_IRIS_GET_SINR, | |
+ .type = V4L2_CTRL_TYPE_INTEGER, | |
+ .name = "GET SINR", | |
+ .minimum = -128, | |
+ .maximum = 127, | |
+ }, | |
+ { | |
+ .id = V4L2_CID_PRIVATE_INTF_HIGH_THRESHOLD, | |
+ .type = V4L2_CTRL_TYPE_INTEGER, | |
+ .name = "Intf High Threshold", | |
+ .minimum = 0, | |
+ .maximum = 0xFF, | |
+ .default_value = 0, | |
+ }, | |
+ { | |
+ .id = V4L2_CID_PRIVATE_INTF_LOW_THRESHOLD, | |
+ .type = V4L2_CTRL_TYPE_INTEGER, | |
+ .name = "Intf low Threshold", | |
+ .minimum = 0, | |
+ .maximum = 0xFF, | |
+ .default_value = 0, | |
+ }, | |
+ { | |
+ .id = V4L2_CID_PRIVATE_SINR_THRESHOLD, | |
+ .type = V4L2_CTRL_TYPE_INTEGER, | |
+ .name = "SINR Threshold", | |
+ .minimum = -128, | |
+ .maximum = 127, | |
+ .default_value = 0, | |
+ }, | |
+ { | |
+ .id = V4L2_CID_PRIVATE_SINR_SAMPLES, | |
+ .type = V4L2_CTRL_TYPE_INTEGER, | |
+ .name = "SINR samples", | |
+ .minimum = 1, | |
+ .maximum = 0xFF, | |
+ .default_value = 0, | |
+ }, | |
+}; | |
+ | |
+static void iris_q_event(struct iris_device *radio, | |
+ enum iris_evt_t event) | |
+{ | |
+ struct kfifo *data_b; | |
+ unsigned char evt = event; | |
+ | |
+ if (unlikely(radio == NULL)) { | |
+ FMDERR(":radio is null"); | |
+ return; | |
+ } | |
+ | |
+ data_b = &radio->data_buf[IRIS_BUF_EVENTS]; | |
+ if (kfifo_in_locked(data_b, &evt, 1, &radio->buf_lock[IRIS_BUF_EVENTS])) | |
+ wake_up_interruptible(&radio->event_queue); | |
+} | |
+ | |
+static int hci_send_frame(struct sk_buff *skb) | |
+{ | |
+ struct radio_hci_dev *hdev; | |
+ | |
+ if (unlikely(skb == NULL)) { | |
+ FMDERR("%s, socket buffer is null\n", __func__); | |
+ return -EINVAL; | |
+ } | |
+ hdev = (struct radio_hci_dev *) skb->dev; | |
+ if (unlikely(!hdev)) { | |
+ kfree_skb(skb); | |
+ return -ENODEV; | |
+ } | |
+ | |
+ __net_timestamp(skb); | |
+ | |
+ skb_orphan(skb); | |
+ return hdev->send(skb); | |
+} | |
+ | |
+static void radio_hci_cmd_task(unsigned long arg) | |
+{ | |
+ struct radio_hci_dev *hdev = (struct radio_hci_dev *) arg; | |
+ struct sk_buff *skb; | |
+ | |
+ if (unlikely(hdev == NULL)) { | |
+ FMDERR("%s, HCI Device is null\n", __func__); | |
+ return; | |
+ } | |
+ if (!(atomic_read(&hdev->cmd_cnt)) | |
+ && time_after(jiffies, hdev->cmd_last_tx + HZ)) { | |
+ FMDERR("%s command tx timeout", hdev->name); | |
+ atomic_set(&hdev->cmd_cnt, 1); | |
+ } | |
+ | |
+ skb = skb_dequeue(&hdev->cmd_q); | |
+ if (atomic_read(&hdev->cmd_cnt) && skb) { | |
+ kfree_skb(hdev->sent_cmd); | |
+ hdev->sent_cmd = skb_clone(skb, GFP_ATOMIC); | |
+ if (hdev->sent_cmd) { | |
+ atomic_dec(&hdev->cmd_cnt); | |
+ hci_send_frame(skb); | |
+ hdev->cmd_last_tx = jiffies; | |
+ } else { | |
+ skb_queue_head(&hdev->cmd_q, skb); | |
+ tasklet_schedule(&hdev->cmd_task); | |
+ } | |
+ } | |
+ | |
+} | |
+ | |
+static void radio_hci_rx_task(unsigned long arg) | |
+{ | |
+ struct radio_hci_dev *hdev = (struct radio_hci_dev *) arg; | |
+ struct sk_buff *skb; | |
+ | |
+ if (unlikely(hdev == NULL)) { | |
+ FMDERR("%s, HCI Device is null\n", __func__); | |
+ return; | |
+ } | |
+ read_lock(&hci_task_lock); | |
+ | |
+ skb = skb_dequeue(&hdev->rx_q); | |
+ radio_hci_event_packet(hdev, skb); | |
+ | |
+ read_unlock(&hci_task_lock); | |
+} | |
+ | |
+int radio_hci_register_dev(struct radio_hci_dev *hdev) | |
+{ | |
+ struct iris_device *radio = video_get_drvdata(video_get_dev()); | |
+ if (!radio) { | |
+ FMDERR(":radio is null"); | |
+ return -EINVAL; | |
+ } | |
+ | |
+ if (!hdev) { | |
+ FMDERR("hdev is null"); | |
+ return -EINVAL; | |
+ } | |
+ | |
+ hdev->flags = 0; | |
+ | |
+ tasklet_init(&hdev->cmd_task, radio_hci_cmd_task, (unsigned long) | |
+ hdev); | |
+ tasklet_init(&hdev->rx_task, radio_hci_rx_task, (unsigned long) | |
+ hdev); | |
+ | |
+ init_waitqueue_head(&hdev->req_wait_q); | |
+ | |
+ skb_queue_head_init(&hdev->rx_q); | |
+ skb_queue_head_init(&hdev->cmd_q); | |
+ skb_queue_head_init(&hdev->raw_q); | |
+ | |
+ | |
+ radio->fm_hdev = hdev; | |
+ | |
+ return 0; | |
+} | |
+EXPORT_SYMBOL(radio_hci_register_dev); | |
+ | |
+int radio_hci_unregister_dev(struct radio_hci_dev *hdev) | |
+{ | |
+ struct iris_device *radio = video_get_drvdata(video_get_dev()); | |
+ if (!radio) { | |
+ FMDERR(":radio is null"); | |
+ return -EINVAL; | |
+ } | |
+ | |
+ tasklet_kill(&hdev->rx_task); | |
+ tasklet_kill(&hdev->cmd_task); | |
+ skb_queue_purge(&hdev->rx_q); | |
+ skb_queue_purge(&hdev->cmd_q); | |
+ skb_queue_purge(&hdev->raw_q); | |
+ kfree(radio->fm_hdev); | |
+ kfree(radio->videodev); | |
+ | |
+ return 0; | |
+} | |
+EXPORT_SYMBOL(radio_hci_unregister_dev); | |
+ | |
+int radio_hci_recv_frame(struct sk_buff *skb) | |
+{ | |
+ struct radio_hci_dev *hdev; | |
+ | |
+ if (unlikely(skb == NULL)) { | |
+ FMDERR("%s, socket buffer is null\n", __func__); | |
+ return -EINVAL; | |
+ } | |
+ hdev = (struct radio_hci_dev *) skb->dev; | |
+ if (unlikely(!hdev)) { | |
+ FMDERR("%s hdev is null while receiving frame", hdev->name); | |
+ kfree_skb(skb); | |
+ return -ENXIO; | |
+ } | |
+ | |
+ __net_timestamp(skb); | |
+ | |
+ radio_hci_event_packet(hdev, skb); | |
+ kfree_skb(skb); | |
+ return 0; | |
+} | |
+EXPORT_SYMBOL(radio_hci_recv_frame); | |
+ | |
+int radio_hci_send_cmd(struct radio_hci_dev *hdev, __u16 opcode, __u32 plen, | |
+ void *param) | |
+{ | |
+ int len = RADIO_HCI_COMMAND_HDR_SIZE + plen; | |
+ struct radio_hci_command_hdr *hdr; | |
+ struct sk_buff *skb; | |
+ int ret = 0; | |
+ | |
+ if (unlikely(hdev == NULL)) { | |
+ FMDERR("%s, hci device is null\n", __func__); | |
+ return -EINVAL; | |
+ } | |
+ skb = alloc_skb(len, GFP_ATOMIC); | |
+ if (!skb) { | |
+ FMDERR("%s no memory for command", hdev->name); | |
+ return -ENOMEM; | |
+ } | |
+ | |
+ hdr = (struct radio_hci_command_hdr *) skb_put(skb, | |
+ RADIO_HCI_COMMAND_HDR_SIZE); | |
+ hdr->opcode = cpu_to_le16(opcode); | |
+ hdr->plen = plen; | |
+ | |
+ if (plen) | |
+ memcpy(skb_put(skb, plen), param, plen); | |
+ | |
+ skb->dev = (void *) hdev; | |
+ | |
+ ret = hci_send_frame(skb); | |
+ | |
+ return ret; | |
+} | |
+EXPORT_SYMBOL(radio_hci_send_cmd); | |
+ | |
+static int hci_fm_enable_recv_req(struct radio_hci_dev *hdev, | |
+ unsigned long param) | |
+{ | |
+ __u16 opcode = 0; | |
+ | |
+ opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ, | |
+ HCI_OCF_FM_ENABLE_RECV_REQ); | |
+ return radio_hci_send_cmd(hdev, opcode, 0, NULL); | |
+} | |
+ | |
+static int hci_fm_tone_generator(struct radio_hci_dev *hdev, | |
+ unsigned long param) | |
+{ | |
+ struct iris_device *radio = video_get_drvdata(video_get_dev()); | |
+ __u16 opcode = 0; | |
+ | |
+ if (unlikely(radio == NULL)) { | |
+ FMDERR(":radio is null"); | |
+ return -EINVAL; | |
+ } | |
+ opcode = hci_opcode_pack(HCI_OGF_FM_DIAGNOSTIC_CMD_REQ, | |
+ HCI_FM_SET_INTERNAL_TONE_GENRATOR); | |
+ return radio_hci_send_cmd(hdev, opcode, | |
+ sizeof(radio->tone_freq), &radio->tone_freq); | |
+} | |
+ | |
+static int hci_fm_enable_trans_req(struct radio_hci_dev *hdev, | |
+ unsigned long param) | |
+{ | |
+ __u16 opcode = 0; | |
+ | |
+ opcode = hci_opcode_pack(HCI_OGF_FM_TRANS_CTRL_CMD_REQ, | |
+ HCI_OCF_FM_ENABLE_TRANS_REQ); | |
+ return radio_hci_send_cmd(hdev, opcode, 0, NULL); | |
+} | |
+ | |
+static int hci_fm_disable_recv_req(struct radio_hci_dev *hdev, | |
+ unsigned long param) | |
+{ | |
+ __u16 opcode = 0; | |
+ | |
+ opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ, | |
+ HCI_OCF_FM_DISABLE_RECV_REQ); | |
+ return radio_hci_send_cmd(hdev, opcode, 0, NULL); | |
+} | |
+ | |
+static int hci_fm_disable_trans_req(struct radio_hci_dev *hdev, | |
+ unsigned long param) | |
+{ | |
+ __u16 opcode = 0; | |
+ | |
+ opcode = hci_opcode_pack(HCI_OGF_FM_TRANS_CTRL_CMD_REQ, | |
+ HCI_OCF_FM_DISABLE_TRANS_REQ); | |
+ return radio_hci_send_cmd(hdev, opcode, 0, NULL); | |
+} | |
+ | |
+static int hci_get_fm_recv_conf_req(struct radio_hci_dev *hdev, | |
+ unsigned long param) | |
+{ | |
+ __u16 opcode = 0; | |
+ | |
+ opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ, | |
+ HCI_OCF_FM_GET_RECV_CONF_REQ); | |
+ return radio_hci_send_cmd(hdev, opcode, 0, NULL); | |
+} | |
+ | |
+static int hci_get_fm_trans_conf_req(struct radio_hci_dev *hdev, | |
+ unsigned long param) | |
+{ | |
+ u16 opcode = 0; | |
+ | |
+ opcode = hci_opcode_pack(HCI_OGF_FM_TRANS_CTRL_CMD_REQ, | |
+ HCI_OCF_FM_GET_TRANS_CONF_REQ); | |
+ return radio_hci_send_cmd(hdev, opcode, 0, NULL); | |
+} | |
+static int hci_set_fm_recv_conf_req(struct radio_hci_dev *hdev, | |
+ unsigned long param) | |
+{ | |
+ __u16 opcode = 0; | |
+ | |
+ struct hci_fm_recv_conf_req *recv_conf_req = | |
+ (struct hci_fm_recv_conf_req *) param; | |
+ | |
+ if (recv_conf_req == NULL) { | |
+ FMDERR("%s, recv conf is null\n", __func__); | |
+ return -EINVAL; | |
+ } | |
+ opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ, | |
+ HCI_OCF_FM_SET_RECV_CONF_REQ); | |
+ return radio_hci_send_cmd(hdev, opcode, sizeof((*recv_conf_req)), | |
+ recv_conf_req); | |
+} | |
+ | |
+static int hci_set_fm_trans_conf_req(struct radio_hci_dev *hdev, | |
+ unsigned long param) | |
+{ | |
+ __u16 opcode = 0; | |
+ | |
+ struct hci_fm_trans_conf_req_struct *trans_conf_req = | |
+ (struct hci_fm_trans_conf_req_struct *) param; | |
+ | |
+ if (trans_conf_req == NULL) { | |
+ FMDERR("%s, tx conf is null\n", __func__); | |
+ return -EINVAL; | |
+ } | |
+ | |
+ opcode = hci_opcode_pack(HCI_OGF_FM_TRANS_CTRL_CMD_REQ, | |
+ HCI_OCF_FM_SET_TRANS_CONF_REQ); | |
+ return radio_hci_send_cmd(hdev, opcode, sizeof((*trans_conf_req)), | |
+ trans_conf_req); | |
+} | |
+ | |
+static int hci_fm_get_station_param_req(struct radio_hci_dev *hdev, | |
+ unsigned long param) | |
+{ | |
+ __u16 opcode = 0; | |
+ | |
+ opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ, | |
+ HCI_OCF_FM_GET_STATION_PARAM_REQ); | |
+ return radio_hci_send_cmd(hdev, opcode, 0, NULL); | |
+} | |
+ | |
+static int hci_set_fm_mute_mode_req(struct radio_hci_dev *hdev, | |
+ unsigned long param) | |
+{ | |
+ __u16 opcode = 0; | |
+ struct hci_fm_mute_mode_req *mute_mode_req = | |
+ (struct hci_fm_mute_mode_req *) param; | |
+ | |
+ if (mute_mode_req == NULL) { | |
+ FMDERR("%s, mute mode is null\n", __func__); | |
+ return -EINVAL; | |
+ } | |
+ opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ, | |
+ HCI_OCF_FM_SET_MUTE_MODE_REQ); | |
+ return radio_hci_send_cmd(hdev, opcode, sizeof((*mute_mode_req)), | |
+ mute_mode_req); | |
+} | |
+ | |
+ | |
+static int hci_trans_ps_req(struct radio_hci_dev *hdev, | |
+ unsigned long param) | |
+{ | |
+ __u16 opcode = 0; | |
+ struct hci_fm_tx_ps *tx_ps_req = | |
+ (struct hci_fm_tx_ps *) param; | |
+ | |
+ if (tx_ps_req == NULL) { | |
+ FMDERR("%s, tx ps req is null\n", __func__); | |
+ return -EINVAL; | |
+ } | |
+ opcode = hci_opcode_pack(HCI_OGF_FM_TRANS_CTRL_CMD_REQ, | |
+ HCI_OCF_FM_RDS_PS_REQ); | |
+ | |
+ return radio_hci_send_cmd(hdev, opcode, sizeof((*tx_ps_req)), | |
+ tx_ps_req); | |
+} | |
+ | |
+static int hci_trans_rt_req(struct radio_hci_dev *hdev, | |
+ unsigned long param) | |
+{ | |
+ __u16 opcode = 0; | |
+ struct hci_fm_tx_rt *tx_rt_req = | |
+ (struct hci_fm_tx_rt *) param; | |
+ | |
+ if (tx_rt_req == NULL) { | |
+ FMDERR("%s, tx rt req is null\n", __func__); | |
+ return -EINVAL; | |
+ } | |
+ opcode = hci_opcode_pack(HCI_OGF_FM_TRANS_CTRL_CMD_REQ, | |
+ HCI_OCF_FM_RDS_RT_REQ); | |
+ | |
+ return radio_hci_send_cmd(hdev, opcode, sizeof((*tx_rt_req)), | |
+ tx_rt_req); | |
+} | |
+ | |
+static int hci_set_fm_stereo_mode_req(struct radio_hci_dev *hdev, | |
+ unsigned long param) | |
+{ | |
+ __u16 opcode = 0; | |
+ struct hci_fm_stereo_mode_req *stereo_mode_req = | |
+ (struct hci_fm_stereo_mode_req *) param; | |
+ | |
+ if (stereo_mode_req == NULL) { | |
+ FMDERR("%s, stere mode req is null\n", __func__); | |
+ return -EINVAL; | |
+ } | |
+ opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ, | |
+ HCI_OCF_FM_SET_STEREO_MODE_REQ); | |
+ return radio_hci_send_cmd(hdev, opcode, sizeof((*stereo_mode_req)), | |
+ stereo_mode_req); | |
+} | |
+ | |
+static int hci_fm_set_antenna_req(struct radio_hci_dev *hdev, | |
+ unsigned long param) | |
+{ | |
+ __u16 opcode = 0; | |
+ | |
+ __u8 antenna = param; | |
+ | |
+ opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ, | |
+ HCI_OCF_FM_SET_ANTENNA); | |
+ return radio_hci_send_cmd(hdev, opcode, sizeof(antenna), &antenna); | |
+} | |
+ | |
+static int hci_fm_set_sig_threshold_req(struct radio_hci_dev *hdev, | |
+ unsigned long param) | |
+{ | |
+ __u16 opcode = 0; | |
+ | |
+ __u8 sig_threshold = param; | |
+ | |
+ opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ, | |
+ HCI_OCF_FM_SET_SIGNAL_THRESHOLD); | |
+ return radio_hci_send_cmd(hdev, opcode, sizeof(sig_threshold), | |
+ &sig_threshold); | |
+} | |
+ | |
+static int hci_fm_set_event_mask(struct radio_hci_dev *hdev, | |
+ unsigned long param) | |
+{ | |
+ u16 opcode = 0; | |
+ u8 event_mask = param; | |
+ | |
+ opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ, | |
+ HCI_OCF_FM_SET_EVENT_MASK); | |
+ return radio_hci_send_cmd(hdev, opcode, sizeof(event_mask), | |
+ &event_mask); | |
+} | |
+static int hci_fm_get_sig_threshold_req(struct radio_hci_dev *hdev, | |
+ unsigned long param) | |
+{ | |
+ __u16 opcode = 0; | |
+ | |
+ opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ, | |
+ HCI_OCF_FM_GET_SIGNAL_THRESHOLD); | |
+ return radio_hci_send_cmd(hdev, opcode, 0, NULL); | |
+} | |
+ | |
+static int hci_fm_get_program_service_req(struct radio_hci_dev *hdev, | |
+ unsigned long param) | |
+{ | |
+ __u16 opcode = 0; | |
+ | |
+ opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ, | |
+ HCI_OCF_FM_GET_PROGRAM_SERVICE_REQ); | |
+ return radio_hci_send_cmd(hdev, opcode, 0, NULL); | |
+} | |
+ | |
+static int hci_fm_get_radio_text_req(struct radio_hci_dev *hdev, | |
+ unsigned long param) | |
+{ | |
+ __u16 opcode = 0; | |
+ | |
+ opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ, | |
+ HCI_OCF_FM_GET_RADIO_TEXT_REQ); | |
+ return radio_hci_send_cmd(hdev, opcode, 0, NULL); | |
+} | |
+ | |
+static int hci_fm_get_af_list_req(struct radio_hci_dev *hdev, | |
+ unsigned long param) | |
+{ | |
+ __u16 opcode = 0; | |
+ | |
+ opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ, | |
+ HCI_OCF_FM_GET_AF_LIST_REQ); | |
+ return radio_hci_send_cmd(hdev, opcode, 0, NULL); | |
+} | |
+ | |
+static int hci_fm_search_stations_req(struct radio_hci_dev *hdev, | |
+ unsigned long param) | |
+{ | |
+ __u16 opcode = 0; | |
+ struct hci_fm_search_station_req *srch_stations = | |
+ (struct hci_fm_search_station_req *) param; | |
+ | |
+ if (srch_stations == NULL) { | |
+ FMDERR("%s, search station param is null\n", __func__); | |
+ return -EINVAL; | |
+ } | |
+ opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ, | |
+ HCI_OCF_FM_SEARCH_STATIONS); | |
+ return radio_hci_send_cmd(hdev, opcode, sizeof((*srch_stations)), | |
+ srch_stations); | |
+} | |
+ | |
+static int hci_fm_srch_rds_stations_req(struct radio_hci_dev *hdev, | |
+ unsigned long param) | |
+{ | |
+ __u16 opcode = 0; | |
+ struct hci_fm_search_rds_station_req *srch_stations = | |
+ (struct hci_fm_search_rds_station_req *) param; | |
+ | |
+ if (srch_stations == NULL) { | |
+ FMDERR("%s, rds stations param is null\n", __func__); | |
+ return -EINVAL; | |
+ } | |
+ opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ, | |
+ HCI_OCF_FM_SEARCH_RDS_STATIONS); | |
+ return radio_hci_send_cmd(hdev, opcode, sizeof((*srch_stations)), | |
+ srch_stations); | |
+} | |
+ | |
+static int hci_fm_srch_station_list_req(struct radio_hci_dev *hdev, | |
+ unsigned long param) | |
+{ | |
+ __u16 opcode = 0; | |
+ struct hci_fm_search_station_list_req *srch_list = | |
+ (struct hci_fm_search_station_list_req *) param; | |
+ | |
+ if (srch_list == NULL) { | |
+ FMDERR("%s, search list param is null\n", __func__); | |
+ return -EINVAL; | |
+ } | |
+ opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ, | |
+ HCI_OCF_FM_SEARCH_STATIONS_LIST); | |
+ return radio_hci_send_cmd(hdev, opcode, sizeof((*srch_list)), | |
+ srch_list); | |
+} | |
+ | |
+static int hci_fm_cancel_search_req(struct radio_hci_dev *hdev, | |
+ unsigned long param) | |
+{ | |
+ __u16 opcode = 0; | |
+ | |
+ opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ, | |
+ HCI_OCF_FM_CANCEL_SEARCH); | |
+ return radio_hci_send_cmd(hdev, opcode, 0, NULL); | |
+} | |
+ | |
+static int hci_fm_rds_grp_mask_req(struct radio_hci_dev *hdev, | |
+ unsigned long param) | |
+{ | |
+ __u16 opcode = 0; | |
+ | |
+ struct hci_fm_rds_grp_req *fm_grp_mask = | |
+ (struct hci_fm_rds_grp_req *)param; | |
+ | |
+ if (fm_grp_mask == NULL) { | |
+ FMDERR("%s, grp mask param is null\n", __func__); | |
+ return -EINVAL; | |
+ } | |
+ opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ, | |
+ HCI_OCF_FM_RDS_GRP); | |
+ return radio_hci_send_cmd(hdev, opcode, sizeof(*fm_grp_mask), | |
+ fm_grp_mask); | |
+} | |
+ | |
+static int hci_fm_rds_grp_process_req(struct radio_hci_dev *hdev, | |
+ unsigned long param) | |
+{ | |
+ __u16 opcode = 0; | |
+ | |
+ __u32 fm_grps_process = param; | |
+ | |
+ opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ, | |
+ HCI_OCF_FM_RDS_GRP_PROCESS); | |
+ return radio_hci_send_cmd(hdev, opcode, sizeof(fm_grps_process), | |
+ &fm_grps_process); | |
+} | |
+ | |
+static int hci_fm_tune_station_req(struct radio_hci_dev *hdev, | |
+ unsigned long param) | |
+{ | |
+ __u16 opcode = 0; | |
+ | |
+ __u32 tune_freq = param; | |
+ | |
+ opcode = hci_opcode_pack(HCI_OGF_FM_COMMON_CTRL_CMD_REQ, | |
+ HCI_OCF_FM_TUNE_STATION_REQ); | |
+ return radio_hci_send_cmd(hdev, opcode, sizeof(tune_freq), &tune_freq); | |
+} | |
+ | |
+static int hci_def_data_read_req(struct radio_hci_dev *hdev, | |
+ unsigned long param) | |
+{ | |
+ __u16 opcode = 0; | |
+ struct hci_fm_def_data_rd_req *def_data_rd = | |
+ (struct hci_fm_def_data_rd_req *) param; | |
+ | |
+ if (def_data_rd == NULL) { | |
+ FMDERR("%s, def data read param is null\n", __func__); | |
+ return -EINVAL; | |
+ } | |
+ opcode = hci_opcode_pack(HCI_OGF_FM_COMMON_CTRL_CMD_REQ, | |
+ HCI_OCF_FM_DEFAULT_DATA_READ); | |
+ return radio_hci_send_cmd(hdev, opcode, sizeof((*def_data_rd)), | |
+ def_data_rd); | |
+} | |
+ | |
+static int hci_def_data_write_req(struct radio_hci_dev *hdev, | |
+ unsigned long param) | |
+{ | |
+ __u16 opcode = 0; | |
+ struct hci_fm_def_data_wr_req *def_data_wr = | |
+ (struct hci_fm_def_data_wr_req *) param; | |
+ | |
+ if (def_data_wr == NULL) { | |
+ FMDERR("%s, def data write param is null\n", __func__); | |
+ return -EINVAL; | |
+ } | |
+ opcode = hci_opcode_pack(HCI_OGF_FM_COMMON_CTRL_CMD_REQ, | |
+ HCI_OCF_FM_DEFAULT_DATA_WRITE); | |
+ | |
+ return radio_hci_send_cmd(hdev, opcode, (def_data_wr->length+2), | |
+ def_data_wr); | |
+} | |
+ | |
+static int hci_set_notch_filter_req(struct radio_hci_dev *hdev, | |
+ unsigned long param) | |
+{ | |
+ __u16 opcode = 0; | |
+ __u8 notch_filter_val = param; | |
+ | |
+ opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ, | |
+ HCI_OCF_FM_EN_NOTCH_CTRL); | |
+ return radio_hci_send_cmd(hdev, opcode, sizeof(notch_filter_val), | |
+ ¬ch_filter_val); | |
+} | |
+ | |
+ | |
+ | |
+static int hci_fm_reset_req(struct radio_hci_dev *hdev, unsigned long param) | |
+{ | |
+ __u16 opcode = 0; | |
+ | |
+ opcode = hci_opcode_pack(HCI_OGF_FM_COMMON_CTRL_CMD_REQ, | |
+ HCI_OCF_FM_RESET); | |
+ return radio_hci_send_cmd(hdev, opcode, 0, NULL); | |
+} | |
+ | |
+static int hci_fm_get_feature_lists_req(struct radio_hci_dev *hdev, | |
+ unsigned long param) | |
+{ | |
+ __u16 opcode = 0; | |
+ | |
+ opcode = hci_opcode_pack(HCI_OGF_FM_COMMON_CTRL_CMD_REQ, | |
+ HCI_OCF_FM_GET_FEATURE_LIST); | |
+ return radio_hci_send_cmd(hdev, opcode, 0, NULL); | |
+} | |
+ | |
+static int hci_fm_do_calibration_req(struct radio_hci_dev *hdev, | |
+ unsigned long param) | |
+{ | |
+ __u16 opcode = 0; | |
+ | |
+ __u8 mode = param; | |
+ | |
+ opcode = hci_opcode_pack(HCI_OGF_FM_COMMON_CTRL_CMD_REQ, | |
+ HCI_OCF_FM_DO_CALIBRATION); | |
+ return radio_hci_send_cmd(hdev, opcode, sizeof(mode), &mode); | |
+} | |
+ | |
+static int hci_read_grp_counters_req(struct radio_hci_dev *hdev, | |
+ unsigned long param) | |
+{ | |
+ __u16 opcode = 0; | |
+ | |
+ __u8 reset_counters = param; | |
+ opcode = hci_opcode_pack(HCI_OGF_FM_STATUS_PARAMETERS_CMD_REQ, | |
+ HCI_OCF_FM_READ_GRP_COUNTERS); | |
+ return radio_hci_send_cmd(hdev, opcode, sizeof(reset_counters), | |
+ &reset_counters); | |
+} | |
+ | |
+static int hci_peek_data_req(struct radio_hci_dev *hdev, unsigned long param) | |
+{ | |
+ __u16 opcode = 0; | |
+ struct hci_fm_riva_data *peek_data = (struct hci_fm_riva_data *)param; | |
+ | |
+ if (peek_data == NULL) { | |
+ FMDERR("%s, peek data param is null\n", __func__); | |
+ return -EINVAL; | |
+ } | |
+ opcode = hci_opcode_pack(HCI_OGF_FM_DIAGNOSTIC_CMD_REQ, | |
+ HCI_OCF_FM_PEEK_DATA); | |
+ return radio_hci_send_cmd(hdev, opcode, sizeof((*peek_data)), | |
+ peek_data); | |
+} | |
+ | |
+static int hci_poke_data_req(struct radio_hci_dev *hdev, unsigned long param) | |
+{ | |
+ __u16 opcode = 0; | |
+ struct hci_fm_riva_poke *poke_data = (struct hci_fm_riva_poke *) param; | |
+ | |
+ if (poke_data == NULL) { | |
+ FMDERR("%s, poke data param is null\n", __func__); | |
+ return -EINVAL; | |
+ } | |
+ opcode = hci_opcode_pack(HCI_OGF_FM_DIAGNOSTIC_CMD_REQ, | |
+ HCI_OCF_FM_POKE_DATA); | |
+ return radio_hci_send_cmd(hdev, opcode, sizeof((*poke_data)), | |
+ poke_data); | |
+} | |
+ | |
+static int hci_ssbi_peek_reg_req(struct radio_hci_dev *hdev, | |
+ unsigned long param) | |
+{ | |
+ __u16 opcode = 0; | |
+ struct hci_fm_ssbi_peek *ssbi_peek = (struct hci_fm_ssbi_peek *) param; | |
+ | |
+ if (ssbi_peek == NULL) { | |
+ FMDERR("%s, ssbi peek param is null\n", __func__); | |
+ return -EINVAL; | |
+ } | |
+ opcode = hci_opcode_pack(HCI_OGF_FM_DIAGNOSTIC_CMD_REQ, | |
+ HCI_OCF_FM_SSBI_PEEK_REG); | |
+ return radio_hci_send_cmd(hdev, opcode, sizeof((*ssbi_peek)), | |
+ ssbi_peek); | |
+} | |
+ | |
+static int hci_ssbi_poke_reg_req(struct radio_hci_dev *hdev, | |
+ unsigned long param) | |
+{ | |
+ __u16 opcode = 0; | |
+ struct hci_fm_ssbi_req *ssbi_poke = (struct hci_fm_ssbi_req *) param; | |
+ | |
+ if (ssbi_poke == NULL) { | |
+ FMDERR("%s, ssbi poke param is null\n", __func__); | |
+ return -EINVAL; | |
+ } | |
+ opcode = hci_opcode_pack(HCI_OGF_FM_DIAGNOSTIC_CMD_REQ, | |
+ HCI_OCF_FM_SSBI_POKE_REG); | |
+ return radio_hci_send_cmd(hdev, opcode, sizeof((*ssbi_poke)), | |
+ ssbi_poke); | |
+} | |
+ | |
+static int hci_fm_get_station_dbg_param_req(struct radio_hci_dev *hdev, | |
+ unsigned long param) | |
+{ | |
+ __u16 opcode = 0; | |
+ | |
+ opcode = hci_opcode_pack(HCI_OGF_FM_DIAGNOSTIC_CMD_REQ, | |
+ HCI_OCF_FM_STATION_DBG_PARAM); | |
+ return radio_hci_send_cmd(hdev, opcode, 0, NULL); | |
+} | |
+ | |
+static int hci_fm_set_ch_det_th(struct radio_hci_dev *hdev, | |
+ unsigned long param) | |
+{ | |
+ struct hci_fm_ch_det_threshold *ch_det_th = | |
+ (struct hci_fm_ch_det_threshold *) param; | |
+ u16 opcode; | |
+ | |
+ if (ch_det_th == NULL) { | |
+ FMDERR("%s, channel det thrshld is null\n", __func__); | |
+ return -EINVAL; | |
+ } | |
+ opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ, | |
+ HCI_OCF_FM_SET_CH_DET_THRESHOLD); | |
+ return radio_hci_send_cmd(hdev, opcode, sizeof((*ch_det_th)), | |
+ ch_det_th); | |
+} | |
+ | |
+static int hci_fm_get_ch_det_th(struct radio_hci_dev *hdev, | |
+ unsigned long param) | |
+{ | |
+ u16 opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ, | |
+ HCI_OCF_FM_GET_CH_DET_THRESHOLD); | |
+ return radio_hci_send_cmd(hdev, opcode, 0, NULL); | |
+} | |
+ | |
+static int hci_fm_get_blend_tbl(struct radio_hci_dev *hdev, | |
+ unsigned long param) | |
+{ | |
+ u16 opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ, | |
+ HCI_OCF_FM_GET_BLND_TBL); | |
+ return radio_hci_send_cmd(hdev, opcode, 0, NULL); | |
+} | |
+ | |
+static int hci_fm_set_blend_tbl(struct radio_hci_dev *hdev, | |
+ unsigned long param) | |
+{ | |
+ struct hci_fm_blend_table *blnd_tbl = | |
+ (struct hci_fm_blend_table *) param; | |
+ u16 opcode; | |
+ | |
+ if (blnd_tbl == NULL) { | |
+ FMDERR("%s, blend tbl is null\n", __func__); | |
+ return -EINVAL; | |
+ } | |
+ opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ, | |
+ HCI_OCF_FM_SET_BLND_TBL); | |
+ return radio_hci_send_cmd(hdev, opcode, | |
+ sizeof(struct hci_fm_blend_table), blnd_tbl); | |
+} | |
+ | |
+static int radio_hci_err(__u32 code) | |
+{ | |
+ switch (code) { | |
+ case 0: | |
+ return 0; | |
+ case 0x01: | |
+ return -EBADRQC; | |
+ case 0x02: | |
+ return -ENOTCONN; | |
+ case 0x03: | |
+ return -EIO; | |
+ case 0x07: | |
+ return -ENOMEM; | |
+ case 0x0c: | |
+ return -EBUSY; | |
+ case 0x11: | |
+ return -EOPNOTSUPP; | |
+ case 0x12: | |
+ return -EINVAL; | |
+ default: | |
+ return -ENOSYS; | |
+ } | |
+} | |
+ | |
+static int __radio_hci_request(struct radio_hci_dev *hdev, | |
+ int (*req)(struct radio_hci_dev *hdev, | |
+ unsigned long param), | |
+ unsigned long param, __u32 timeout) | |
+{ | |
+ int err = 0; | |
+ DECLARE_WAITQUEUE(wait, current); | |
+ | |
+ if (unlikely(hdev == NULL)) { | |
+ FMDERR("%s, hci dev is null\n", __func__); | |
+ return -EINVAL; | |
+ } | |
+ | |
+ mutex_lock(&iris_fm); | |
+ hdev->req_status = HCI_REQ_PEND; | |
+ | |
+ add_wait_queue(&hdev->req_wait_q, &wait); | |
+ set_current_state(TASK_INTERRUPTIBLE); | |
+ | |
+ err = req(hdev, param); | |
+ | |
+ schedule_timeout(timeout); | |
+ | |
+ remove_wait_queue(&hdev->req_wait_q, &wait); | |
+ | |
+ if (signal_pending(current)) { | |
+ mutex_unlock(&iris_fm); | |
+ return -EINTR; | |
+ } | |
+ | |
+ switch (hdev->req_status) { | |
+ case HCI_REQ_DONE: | |
+ case HCI_REQ_STATUS: | |
+ err = radio_hci_err(hdev->req_result); | |
+ break; | |
+ default: | |
+ err = -ETIMEDOUT; | |
+ break; | |
+ } | |
+ | |
+ hdev->req_status = hdev->req_result = 0; | |
+ mutex_unlock(&iris_fm); | |
+ | |
+ return err; | |
+} | |
+ | |
+static inline int radio_hci_request(struct radio_hci_dev *hdev, | |
+ int (*req)(struct | |
+ radio_hci_dev * hdev, unsigned long param), | |
+ unsigned long param, __u32 timeout) | |
+{ | |
+ int ret = 0; | |
+ | |
+ ret = __radio_hci_request(hdev, req, param, timeout); | |
+ | |
+ return ret; | |
+} | |
+ | |
+static inline int hci_conf_event_mask(__u8 *arg, | |
+ struct radio_hci_dev *hdev) | |
+{ | |
+ u8 event_mask; | |
+ | |
+ if (arg == NULL) { | |
+ FMDERR("%s, arg is null\n", __func__); | |
+ return -EINVAL; | |
+ } | |
+ event_mask = *arg; | |
+ return radio_hci_request(hdev, hci_fm_set_event_mask, | |
+ event_mask, RADIO_HCI_TIMEOUT); | |
+} | |
+static int hci_set_fm_recv_conf(struct hci_fm_recv_conf_req *arg, | |
+ struct radio_hci_dev *hdev) | |
+{ | |
+ int ret = 0; | |
+ struct hci_fm_recv_conf_req *set_recv_conf = arg; | |
+ | |
+ ret = radio_hci_request(hdev, hci_set_fm_recv_conf_req, (unsigned | |
+ long)set_recv_conf, RADIO_HCI_TIMEOUT); | |
+ | |
+ return ret; | |
+} | |
+ | |
+static int hci_set_fm_trans_conf(struct hci_fm_trans_conf_req_struct *arg, | |
+ struct radio_hci_dev *hdev) | |
+{ | |
+ int ret = 0; | |
+ struct hci_fm_trans_conf_req_struct *set_trans_conf = arg; | |
+ | |
+ ret = radio_hci_request(hdev, hci_set_fm_trans_conf_req, (unsigned | |
+ long)set_trans_conf, RADIO_HCI_TIMEOUT); | |
+ | |
+ return ret; | |
+} | |
+ | |
+static int hci_fm_tune_station(__u32 *arg, struct radio_hci_dev *hdev) | |
+{ | |
+ int ret = 0; | |
+ __u32 tune_freq; | |
+ | |
+ if (arg == NULL) { | |
+ FMDERR("%s, arg is null\n", __func__); | |
+ return -EINVAL; | |
+ } | |
+ tune_freq = *arg; | |
+ ret = radio_hci_request(hdev, hci_fm_tune_station_req, tune_freq, | |
+ RADIO_HCI_TIMEOUT); | |
+ | |
+ return ret; | |
+} | |
+ | |
+static int hci_set_fm_mute_mode(struct hci_fm_mute_mode_req *arg, | |
+ struct radio_hci_dev *hdev) | |
+{ | |
+ int ret = 0; | |
+ struct hci_fm_mute_mode_req *set_mute_conf = arg; | |
+ | |
+ ret = radio_hci_request(hdev, hci_set_fm_mute_mode_req, (unsigned | |
+ long)set_mute_conf, RADIO_HCI_TIMEOUT); | |
+ | |
+ return ret; | |
+} | |
+ | |
+static int hci_set_fm_stereo_mode(struct hci_fm_stereo_mode_req *arg, | |
+ struct radio_hci_dev *hdev) | |
+{ | |
+ int ret = 0; | |
+ struct hci_fm_stereo_mode_req *set_stereo_conf = arg; | |
+ | |
+ ret = radio_hci_request(hdev, hci_set_fm_stereo_mode_req, (unsigned | |
+ long)set_stereo_conf, RADIO_HCI_TIMEOUT); | |
+ | |
+ return ret; | |
+} | |
+ | |
+static int hci_fm_set_antenna(__u8 *arg, struct radio_hci_dev *hdev) | |
+{ | |
+ int ret = 0; | |
+ __u8 antenna; | |
+ | |
+ if (arg == NULL) { | |
+ FMDERR("%s, arg is null\n", __func__); | |
+ return -EINVAL; | |
+ } | |
+ antenna = *arg; | |
+ ret = radio_hci_request(hdev, hci_fm_set_antenna_req, antenna, | |
+ RADIO_HCI_TIMEOUT); | |
+ | |
+ return ret; | |
+} | |
+ | |
+static int hci_fm_set_signal_threshold(__u8 *arg, | |
+ struct radio_hci_dev *hdev) | |
+{ | |
+ int ret = 0; | |
+ __u8 sig_threshold; | |
+ | |
+ if (arg == NULL) { | |
+ FMDERR("%s, arg is null\n", __func__); | |
+ return -EINVAL; | |
+ } | |
+ sig_threshold = *arg; | |
+ ret = radio_hci_request(hdev, hci_fm_set_sig_threshold_req, | |
+ sig_threshold, RADIO_HCI_TIMEOUT); | |
+ | |
+ return ret; | |
+} | |
+ | |
+static int hci_fm_search_stations(struct hci_fm_search_station_req *arg, | |
+ struct radio_hci_dev *hdev) | |
+{ | |
+ int ret = 0; | |
+ struct hci_fm_search_station_req *srch_stations = arg; | |
+ | |
+ ret = radio_hci_request(hdev, hci_fm_search_stations_req, (unsigned | |
+ long)srch_stations, RADIO_HCI_TIMEOUT); | |
+ | |
+ return ret; | |
+} | |
+ | |
+static int hci_fm_search_rds_stations(struct hci_fm_search_rds_station_req *arg, | |
+ struct radio_hci_dev *hdev) | |
+{ | |
+ int ret = 0; | |
+ struct hci_fm_search_rds_station_req *srch_stations = arg; | |
+ | |
+ ret = radio_hci_request(hdev, hci_fm_srch_rds_stations_req, (unsigned | |
+ long)srch_stations, RADIO_HCI_TIMEOUT); | |
+ | |
+ return ret; | |
+} | |
+ | |
+static int hci_fm_search_station_list | |
+ (struct hci_fm_search_station_list_req *arg, | |
+ struct radio_hci_dev *hdev) | |
+{ | |
+ int ret = 0; | |
+ struct hci_fm_search_station_list_req *srch_list = arg; | |
+ | |
+ ret = radio_hci_request(hdev, hci_fm_srch_station_list_req, (unsigned | |
+ long)srch_list, RADIO_HCI_TIMEOUT); | |
+ | |
+ return ret; | |
+} | |
+ | |
+static int hci_fm_rds_grp(struct hci_fm_rds_grp_req *arg, | |
+ struct radio_hci_dev *hdev) | |
+{ | |
+ int ret = 0; | |
+ struct hci_fm_rds_grp_req *fm_grp_mask = arg; | |
+ | |
+ ret = radio_hci_request(hdev, hci_fm_rds_grp_mask_req, (unsigned | |
+ long)fm_grp_mask, RADIO_HCI_TIMEOUT); | |
+ | |
+ return ret; | |
+} | |
+ | |
+static int hci_fm_rds_grps_process(__u32 *arg, struct radio_hci_dev *hdev) | |
+{ | |
+ int ret = 0; | |
+ __u32 fm_grps_process; | |
+ | |
+ if (arg == NULL) { | |
+ FMDERR("%s, arg is null\n", __func__); | |
+ return -EINVAL; | |
+ } | |
+ fm_grps_process = *arg; | |
+ ret = radio_hci_request(hdev, hci_fm_rds_grp_process_req, | |
+ fm_grps_process, RADIO_HCI_TIMEOUT); | |
+ | |
+ return ret; | |
+} | |
+ | |
+int hci_def_data_read(struct hci_fm_def_data_rd_req *arg, | |
+ struct radio_hci_dev *hdev) | |
+{ | |
+ int ret = 0; | |
+ struct hci_fm_def_data_rd_req *def_data_rd = arg; | |
+ ret = radio_hci_request(hdev, hci_def_data_read_req, (unsigned | |
+ long)def_data_rd, RADIO_HCI_TIMEOUT); | |
+ | |
+ return ret; | |
+} | |
+ | |
+int hci_def_data_write(struct hci_fm_def_data_wr_req *arg, | |
+ struct radio_hci_dev *hdev) | |
+{ | |
+ int ret = 0; | |
+ struct hci_fm_def_data_wr_req *def_data_wr = arg; | |
+ ret = radio_hci_request(hdev, hci_def_data_write_req, (unsigned | |
+ long)def_data_wr, RADIO_HCI_TIMEOUT); | |
+ | |
+ return ret; | |
+} | |
+ | |
+int hci_fm_do_calibration(__u8 *arg, struct radio_hci_dev *hdev) | |
+{ | |
+ int ret = 0; | |
+ __u8 mode; | |
+ | |
+ if (arg == NULL) { | |
+ FMDERR("%s, arg is null\n", __func__); | |
+ return -EINVAL; | |
+ } | |
+ mode = *arg; | |
+ ret = radio_hci_request(hdev, hci_fm_do_calibration_req, mode, | |
+ RADIO_HCI_TIMEOUT); | |
+ | |
+ return ret; | |
+} | |
+ | |
+static int hci_read_grp_counters(__u8 *arg, struct radio_hci_dev *hdev) | |
+{ | |
+ int ret = 0; | |
+ __u8 reset_counters; | |
+ | |
+ if (arg == NULL) { | |
+ FMDERR("%s, arg is null\n", __func__); | |
+ return -EINVAL; | |
+ } | |
+ reset_counters = *arg; | |
+ ret = radio_hci_request(hdev, hci_read_grp_counters_req, | |
+ reset_counters, RADIO_HCI_TIMEOUT); | |
+ | |
+ return ret; | |
+} | |
+ | |
+static int hci_set_notch_filter(__u8 *arg, struct radio_hci_dev *hdev) | |
+{ | |
+ int ret = 0; | |
+ __u8 notch_filter; | |
+ | |
+ if (arg == NULL) { | |
+ FMDERR("%s, arg is null\n", __func__); | |
+ return -EINVAL; | |
+ } | |
+ | |
+ notch_filter = *arg; | |
+ ret = radio_hci_request(hdev, hci_set_notch_filter_req, | |
+ notch_filter, RADIO_HCI_TIMEOUT); | |
+ | |
+ return ret; | |
+} | |
+ | |
+static int hci_peek_data(struct hci_fm_riva_data *arg, | |
+ struct radio_hci_dev *hdev) | |
+{ | |
+ int ret = 0; | |
+ struct hci_fm_riva_data *peek_data = arg; | |
+ | |
+ ret = radio_hci_request(hdev, hci_peek_data_req, (unsigned | |
+ long)peek_data, RADIO_HCI_TIMEOUT); | |
+ | |
+ return ret; | |
+} | |
+ | |
+static int hci_poke_data(struct hci_fm_riva_poke *arg, | |
+ struct radio_hci_dev *hdev) | |
+{ | |
+ int ret = 0; | |
+ struct hci_fm_riva_poke *poke_data = arg; | |
+ | |
+ ret = radio_hci_request(hdev, hci_poke_data_req, (unsigned | |
+ long)poke_data, RADIO_HCI_TIMEOUT); | |
+ | |
+ return ret; | |
+} | |
+ | |
+static int hci_ssbi_peek_reg(struct hci_fm_ssbi_peek *arg, | |
+ struct radio_hci_dev *hdev) | |
+{ | |
+ int ret = 0; | |
+ struct hci_fm_ssbi_peek *ssbi_peek_reg = arg; | |
+ | |
+ ret = radio_hci_request(hdev, hci_ssbi_peek_reg_req, (unsigned | |
+ long)ssbi_peek_reg, RADIO_HCI_TIMEOUT); | |
+ | |
+ return ret; | |
+} | |
+ | |
+static int hci_ssbi_poke_reg(struct hci_fm_ssbi_req *arg, | |
+ struct radio_hci_dev *hdev) | |
+{ | |
+ int ret = 0; | |
+ struct hci_fm_ssbi_req *ssbi_poke_reg = arg; | |
+ | |
+ ret = radio_hci_request(hdev, hci_ssbi_poke_reg_req, (unsigned | |
+ long)ssbi_poke_reg, RADIO_HCI_TIMEOUT); | |
+ | |
+ return ret; | |
+} | |
+ | |
+static int hci_set_ch_det_thresholds_req(struct hci_fm_ch_det_threshold *arg, | |
+ struct radio_hci_dev *hdev) | |
+{ | |
+ int ret = 0; | |
+ struct hci_fm_ch_det_threshold *ch_det_threshold = arg; | |
+ ret = radio_hci_request(hdev, hci_fm_set_ch_det_th, | |
+ (unsigned long)ch_det_threshold, RADIO_HCI_TIMEOUT); | |
+ return ret; | |
+} | |
+ | |
+static int hci_fm_set_cal_req_proc(struct radio_hci_dev *hdev, | |
+ unsigned long param) | |
+{ | |
+ u16 opcode = 0; | |
+ struct hci_fm_set_cal_req_proc *cal_req = | |
+ (struct hci_fm_set_cal_req_proc *)param; | |
+ | |
+ opcode = hci_opcode_pack(HCI_OGF_FM_COMMON_CTRL_CMD_REQ, | |
+ HCI_OCF_FM_SET_CALIBRATION); | |
+ return radio_hci_send_cmd(hdev, opcode, | |
+ sizeof(struct hci_fm_set_cal_req_proc), cal_req); | |
+} | |
+ | |
+static int hci_fm_do_cal_req(struct radio_hci_dev *hdev, | |
+ unsigned long param) | |
+{ | |
+ u16 opcode = 0; | |
+ u8 cal_mode = param; | |
+ | |
+ opcode = hci_opcode_pack(HCI_OGF_FM_COMMON_CTRL_CMD_REQ, | |
+ HCI_OCF_FM_DO_CALIBRATION); | |
+ return radio_hci_send_cmd(hdev, opcode, sizeof(cal_mode), | |
+ &cal_mode); | |
+ | |
+} | |
+ | |
+static int hci_fm_set_spur_tbl_req(struct radio_hci_dev *hdev, | |
+ unsigned long param) | |
+{ | |
+ u16 opcode = 0, len = 0; | |
+ struct hci_fm_set_spur_table_req *spur_req = | |
+ (struct hci_fm_set_spur_table_req *)param; | |
+ | |
+ opcode = hci_opcode_pack(HCI_OGF_FM_COMMON_CTRL_CMD_REQ, | |
+ HCI_OCF_FM_SET_SPUR_TABLE); | |
+ if (spur_req->no_of_freqs_entries > ENTRIES_EACH_CMD) | |
+ len = (ENTRIES_EACH_CMD * SPUR_DATA_LEN) | |
+ + SPUR_DATA_INDEX; | |
+ else | |
+ len = (spur_req->no_of_freqs_entries * SPUR_DATA_LEN) | |
+ + SPUR_DATA_INDEX; | |
+ | |
+ return radio_hci_send_cmd(hdev, opcode, len, spur_req); | |
+} | |
+ | |
+static int hci_fm_get_spur_tbl_data(struct radio_hci_dev *hdev, | |
+ unsigned long param) | |
+{ | |
+ u16 opcode = 0; | |
+ unsigned int spur_freq = (unsigned int)param; | |
+ | |
+ opcode = hci_opcode_pack(HCI_OGF_FM_COMMON_CTRL_CMD_REQ, | |
+ HCI_OCF_FM_GET_SPUR_TABLE); | |
+ return radio_hci_send_cmd(hdev, opcode, sizeof(int), &spur_freq); | |
+} | |
+ | |
+static int hci_set_blend_tbl_req(struct hci_fm_blend_table *arg, | |
+ struct radio_hci_dev *hdev) | |
+{ | |
+ int ret = 0; | |
+ struct hci_fm_blend_table *blend_tbl = arg; | |
+ ret = radio_hci_request(hdev, hci_fm_set_blend_tbl, | |
+ (unsigned long)blend_tbl, RADIO_HCI_TIMEOUT); | |
+ return ret; | |
+} | |
+ | |
+static int hci_cmd(unsigned int cmd, struct radio_hci_dev *hdev) | |
+{ | |
+ int ret = 0; | |
+ unsigned long arg = 0; | |
+ | |
+ if (!hdev) | |
+ return -ENODEV; | |
+ | |
+ switch (cmd) { | |
+ case HCI_FM_ENABLE_RECV_CMD: | |
+ ret = radio_hci_request(hdev, hci_fm_enable_recv_req, arg, | |
+ msecs_to_jiffies(RADIO_HCI_TIMEOUT)); | |
+ break; | |
+ | |
+ case HCI_FM_DISABLE_RECV_CMD: | |
+ ret = radio_hci_request(hdev, hci_fm_disable_recv_req, arg, | |
+ msecs_to_jiffies(RADIO_HCI_TIMEOUT)); | |
+ break; | |
+ | |
+ case HCI_FM_GET_RECV_CONF_CMD: | |
+ ret = radio_hci_request(hdev, hci_get_fm_recv_conf_req, arg, | |
+ msecs_to_jiffies(RADIO_HCI_TIMEOUT)); | |
+ break; | |
+ | |
+ case HCI_FM_GET_STATION_PARAM_CMD: | |
+ ret = radio_hci_request(hdev, | |
+ hci_fm_get_station_param_req, arg, | |
+ msecs_to_jiffies(RADIO_HCI_TIMEOUT)); | |
+ break; | |
+ | |
+ case HCI_FM_GET_SIGNAL_TH_CMD: | |
+ ret = radio_hci_request(hdev, | |
+ hci_fm_get_sig_threshold_req, arg, | |
+ msecs_to_jiffies(RADIO_HCI_TIMEOUT)); | |
+ break; | |
+ | |
+ case HCI_FM_GET_PROGRAM_SERVICE_CMD: | |
+ ret = radio_hci_request(hdev, | |
+ hci_fm_get_program_service_req, arg, | |
+ msecs_to_jiffies(RADIO_HCI_TIMEOUT)); | |
+ break; | |
+ | |
+ case HCI_FM_GET_RADIO_TEXT_CMD: | |
+ ret = radio_hci_request(hdev, hci_fm_get_radio_text_req, arg, | |
+ msecs_to_jiffies(RADIO_HCI_TIMEOUT)); | |
+ break; | |
+ | |
+ case HCI_FM_GET_AF_LIST_CMD: | |
+ ret = radio_hci_request(hdev, hci_fm_get_af_list_req, arg, | |
+ msecs_to_jiffies(RADIO_HCI_TIMEOUT)); | |
+ break; | |
+ | |
+ case HCI_FM_CANCEL_SEARCH_CMD: | |
+ ret = radio_hci_request(hdev, hci_fm_cancel_search_req, arg, | |
+ msecs_to_jiffies(RADIO_HCI_TIMEOUT)); | |
+ break; | |
+ | |
+ case HCI_FM_RESET_CMD: | |
+ ret = radio_hci_request(hdev, hci_fm_reset_req, arg, | |
+ msecs_to_jiffies(RADIO_HCI_TIMEOUT)); | |
+ break; | |
+ | |
+ case HCI_FM_GET_FEATURES_CMD: | |
+ ret = radio_hci_request(hdev, | |
+ hci_fm_get_feature_lists_req, arg, | |
+ msecs_to_jiffies(RADIO_HCI_TIMEOUT)); | |
+ break; | |
+ | |
+ case HCI_FM_STATION_DBG_PARAM_CMD: | |
+ ret = radio_hci_request(hdev, | |
+ hci_fm_get_station_dbg_param_req, arg, | |
+ msecs_to_jiffies(RADIO_HCI_TIMEOUT)); | |
+ break; | |
+ | |
+ case HCI_FM_ENABLE_TRANS_CMD: | |
+ ret = radio_hci_request(hdev, hci_fm_enable_trans_req, arg, | |
+ msecs_to_jiffies(RADIO_HCI_TIMEOUT)); | |
+ break; | |
+ | |
+ case HCI_FM_DISABLE_TRANS_CMD: | |
+ ret = radio_hci_request(hdev, hci_fm_disable_trans_req, arg, | |
+ msecs_to_jiffies(RADIO_HCI_TIMEOUT)); | |
+ break; | |
+ | |
+ case HCI_FM_GET_TX_CONFIG: | |
+ ret = radio_hci_request(hdev, hci_get_fm_trans_conf_req, arg, | |
+ msecs_to_jiffies(RADIO_HCI_TIMEOUT)); | |
+ break; | |
+ case HCI_FM_GET_DET_CH_TH_CMD: | |
+ ret = radio_hci_request(hdev, hci_fm_get_ch_det_th, arg, | |
+ msecs_to_jiffies(RADIO_HCI_TIMEOUT)); | |
+ break; | |
+ case HCI_FM_GET_BLND_TBL_CMD: | |
+ ret = radio_hci_request(hdev, hci_fm_get_blend_tbl, arg, | |
+ msecs_to_jiffies(RADIO_HCI_TIMEOUT)); | |
+ break; | |
+ default: | |
+ ret = -EINVAL; | |
+ break; | |
+ } | |
+ | |
+ return ret; | |
+} | |
+ | |
+static void radio_hci_req_complete(struct radio_hci_dev *hdev, int result) | |
+{ | |
+ | |
+ if (unlikely(hdev == NULL)) { | |
+ FMDERR("%s, hci device is null\n", __func__); | |
+ return; | |
+ } | |
+ hdev->req_result = result; | |
+ hdev->req_status = HCI_REQ_DONE; | |
+ wake_up_interruptible(&hdev->req_wait_q); | |
+} | |
+ | |
+static void radio_hci_status_complete(struct radio_hci_dev *hdev, int result) | |
+{ | |
+ if (unlikely(hdev == NULL)) { | |
+ FMDERR("%s, hci device is null\n", __func__); | |
+ return; | |
+ } | |
+ hdev->req_result = result; | |
+ hdev->req_status = HCI_REQ_STATUS; | |
+ wake_up_interruptible(&hdev->req_wait_q); | |
+} | |
+ | |
+static void hci_cc_rsp(struct radio_hci_dev *hdev, struct sk_buff *skb) | |
+{ | |
+ __u8 status; | |
+ | |
+ if (unlikely(skb == NULL)) { | |
+ FMDERR("%s, socket buffer is null\n", __func__); | |
+ return; | |
+ } | |
+ status = *((__u8 *) skb->data); | |
+ | |
+ radio_hci_req_complete(hdev, status); | |
+} | |
+ | |
+static void hci_cc_fm_disable_rsp(struct radio_hci_dev *hdev, | |
+ struct sk_buff *skb) | |
+{ | |
+ __u8 status; | |
+ struct iris_device *radio = video_get_drvdata(video_get_dev()); | |
+ | |
+ if (unlikely(radio == NULL)) { | |
+ FMDERR(":radio is null"); | |
+ return; | |
+ } | |
+ | |
+ if (unlikely(skb == NULL)) { | |
+ FMDERR("%s, socket buffer is null\n", __func__); | |
+ return; | |
+ } | |
+ | |
+ status = *((__u8 *) skb->data); | |
+ if ((radio->mode == FM_TURNING_OFF) && (status == 0)) { | |
+ iris_q_event(radio, IRIS_EVT_RADIO_DISABLED); | |
+ radio_hci_req_complete(hdev, status); | |
+ radio->mode = FM_OFF; | |
+ } else if (radio->mode == FM_CALIB) { | |
+ radio_hci_req_complete(hdev, status); | |
+ } else if ((radio->mode == FM_RECV) || (radio->mode == FM_TRANS)) { | |
+ iris_q_event(radio, IRIS_EVT_RADIO_DISABLED); | |
+ radio->mode = FM_OFF; | |
+ } else if ((radio->mode == FM_TURNING_OFF) && (status != 0)) { | |
+ radio_hci_req_complete(hdev, status); | |
+ } | |
+} | |
+ | |
+static void hci_cc_conf_rsp(struct radio_hci_dev *hdev, struct sk_buff *skb) | |
+{ | |
+ struct hci_fm_conf_rsp *rsp; | |
+ struct iris_device *radio = video_get_drvdata(video_get_dev()); | |
+ | |
+ if (unlikely(radio == NULL)) { | |
+ FMDERR(":radio is null"); | |
+ return; | |
+ } | |
+ if (unlikely(skb == NULL)) { | |
+ FMDERR("%s, socket buffer is null\n", __func__); | |
+ return; | |
+ } | |
+ rsp = (struct hci_fm_conf_rsp *)skb->data; | |
+ if (!rsp->status) | |
+ radio->recv_conf = rsp->recv_conf_rsp; | |
+ radio_hci_req_complete(hdev, rsp->status); | |
+} | |
+ | |
+static void hci_cc_fm_trans_get_conf_rsp(struct radio_hci_dev *hdev, | |
+ struct sk_buff *skb) | |
+{ | |
+ struct hci_fm_get_trans_conf_rsp *rsp; | |
+ struct iris_device *radio = video_get_drvdata(video_get_dev()); | |
+ | |
+ if (unlikely(radio == NULL)) { | |
+ FMDERR(":radio is null"); | |
+ return; | |
+ } | |
+ if (unlikely(skb == NULL)) { | |
+ FMDERR("%s, socket buffer is null\n", __func__); | |
+ return; | |
+ } | |
+ | |
+ rsp = (struct hci_fm_get_trans_conf_rsp *)skb->data; | |
+ if (!rsp->status) | |
+ memcpy((void *)&radio->trans_conf, | |
+ (void *)&rsp->trans_conf_rsp, | |
+ sizeof(rsp->trans_conf_rsp)); | |
+ | |
+ radio_hci_req_complete(hdev, rsp->status); | |
+} | |
+ | |
+static void hci_cc_fm_enable_rsp(struct radio_hci_dev *hdev, | |
+ struct sk_buff *skb) | |
+{ | |
+ struct hci_fm_conf_rsp *rsp; | |
+ struct iris_device *radio = video_get_drvdata(video_get_dev()); | |
+ | |
+ if (unlikely(radio == NULL)) { | |
+ FMDERR(":radio is null"); | |
+ return; | |
+ } | |
+ | |
+ if (unlikely(skb == NULL)) { | |
+ FMDERR("%s, socket buffer is null\n", __func__); | |
+ return; | |
+ } | |
+ | |
+ rsp = (struct hci_fm_conf_rsp *)skb->data; | |
+ if (rsp->status) { | |
+ radio_hci_req_complete(hdev, rsp->status); | |
+ return; | |
+ } | |
+ | |
+ radio_hci_req_complete(hdev, rsp->status); | |
+} | |
+ | |
+ | |
+static void hci_cc_fm_trans_set_conf_rsp(struct radio_hci_dev *hdev, | |
+ struct sk_buff *skb) | |
+{ | |
+ struct hci_fm_conf_rsp *rsp; | |
+ struct iris_device *radio = video_get_drvdata(video_get_dev()); | |
+ | |
+ if (unlikely(radio == NULL)) { | |
+ FMDERR(":radio is null"); | |
+ return; | |
+ } | |
+ | |
+ if (unlikely(skb == NULL)) { | |
+ FMDERR("%s, socket buffer is null\n", __func__); | |
+ return; | |
+ } | |
+ rsp = (struct hci_fm_conf_rsp *)skb->data; | |
+ if (!rsp->status) | |
+ iris_q_event(radio, HCI_EV_CMD_COMPLETE); | |
+ | |
+ radio_hci_req_complete(hdev, rsp->status); | |
+} | |
+ | |
+ | |
+static void hci_cc_sig_threshold_rsp(struct radio_hci_dev *hdev, | |
+ struct sk_buff *skb) | |
+{ | |
+ struct hci_fm_sig_threshold_rsp *rsp; | |
+ struct iris_device *radio = video_get_drvdata(video_get_dev()); | |
+ | |
+ if (unlikely(radio == NULL)) { | |
+ FMDERR(":radio is null"); | |
+ return; | |
+ } | |
+ if (unlikely(skb == NULL)) { | |
+ FMDERR("%s, socket buffer is null\n", __func__); | |
+ return; | |
+ } | |
+ | |
+ rsp = (struct hci_fm_sig_threshold_rsp *)skb->data; | |
+ if (!rsp->status) | |
+ memcpy(&radio->sig_th, rsp, | |
+ sizeof(struct hci_fm_sig_threshold_rsp)); | |
+ radio_hci_req_complete(hdev, rsp->status); | |
+} | |
+ | |
+static void hci_cc_station_rsp(struct radio_hci_dev *hdev, struct sk_buff *skb) | |
+{ | |
+ struct iris_device *radio = video_get_drvdata(video_get_dev()); | |
+ struct hci_fm_station_rsp *rsp; | |
+ | |
+ if (unlikely(radio == NULL)) { | |
+ FMDERR(":radio is null"); | |
+ return; | |
+ } | |
+ if (unlikely(skb == NULL)) { | |
+ FMDERR("%s, socket buffer is null\n", __func__); | |
+ return; | |
+ } | |
+ | |
+ rsp = (struct hci_fm_station_rsp *)skb->data; | |
+ radio->fm_st_rsp = *(rsp); | |
+ | |
+ /* Tune is always succesful */ | |
+ radio_hci_req_complete(hdev, 0); | |
+} | |
+ | |
+static void hci_cc_prg_srv_rsp(struct radio_hci_dev *hdev, struct sk_buff *skb) | |
+{ | |
+ struct hci_fm_prgm_srv_rsp *rsp; | |
+ | |
+ if (unlikely(skb == NULL)) { | |
+ FMDERR("%s, socket buffer is null\n", __func__); | |
+ return; | |
+ } | |
+ | |
+ rsp = (struct hci_fm_prgm_srv_rsp *)skb->data; | |
+ | |
+ radio_hci_req_complete(hdev, rsp->status); | |
+} | |
+ | |
+static void hci_cc_rd_txt_rsp(struct radio_hci_dev *hdev, struct sk_buff *skb) | |
+{ | |
+ struct hci_fm_radio_txt_rsp *rsp; | |
+ | |
+ if (unlikely(skb == NULL)) { | |
+ FMDERR("%s, socket buffer is null\n", __func__); | |
+ return; | |
+ } | |
+ | |
+ rsp = (struct hci_fm_radio_txt_rsp *)skb->data; | |
+ radio_hci_req_complete(hdev, rsp->status); | |
+} | |
+ | |
+static void hci_cc_af_list_rsp(struct radio_hci_dev *hdev, struct sk_buff *skb) | |
+{ | |
+ struct hci_fm_af_list_rsp *rsp; | |
+ | |
+ if (unlikely(skb == NULL)) { | |
+ FMDERR("%s, socket buffer is null\n", __func__); | |
+ return; | |
+ } | |
+ | |
+ rsp = (struct hci_fm_af_list_rsp *)skb->data; | |
+ radio_hci_req_complete(hdev, rsp->status); | |
+} | |
+ | |
+static void hci_cc_feature_list_rsp(struct radio_hci_dev *hdev, | |
+ struct sk_buff *skb) | |
+{ | |
+ struct v4l2_capability *v4l_cap; | |
+ struct hci_fm_feature_list_rsp *rsp; | |
+ struct iris_device *radio = video_get_drvdata(video_get_dev()); | |
+ | |
+ if (unlikely(radio == NULL)) { | |
+ FMDERR(":radio is null"); | |
+ return; | |
+ } | |
+ | |
+ if (unlikely(skb == NULL)) { | |
+ FMDERR("%s, socket buffer is null\n", __func__); | |
+ return; | |
+ } | |
+ | |
+ rsp = (struct hci_fm_feature_list_rsp *)skb->data; | |
+ v4l_cap = &radio->g_cap; | |
+ | |
+ if (!rsp->status) | |
+ v4l_cap->capabilities = (rsp->feature_mask & 0x000002) | | |
+ (rsp->feature_mask & 0x000001); | |
+ | |
+ radio_hci_req_complete(hdev, rsp->status); | |
+} | |
+ | |
+static void hci_cc_dbg_param_rsp(struct radio_hci_dev *hdev, | |
+ struct sk_buff *skb) | |
+{ | |
+ struct iris_device *radio = video_get_drvdata(video_get_dev()); | |
+ struct hci_fm_dbg_param_rsp *rsp; | |
+ | |
+ if (unlikely(radio == NULL)) { | |
+ FMDERR(":radio is null"); | |
+ return; | |
+ } | |
+ | |
+ if (unlikely(skb == NULL)) { | |
+ FMDERR("%s, socket buffer is null\n", __func__); | |
+ return; | |
+ } | |
+ | |
+ rsp = (struct hci_fm_dbg_param_rsp *)skb->data; | |
+ radio->st_dbg_param = *(rsp); | |
+ radio_hci_req_complete(hdev, radio->st_dbg_param.status); | |
+} | |
+ | |
+static void iris_q_evt_data(struct iris_device *radio, | |
+ char *data, int len, int event) | |
+{ | |
+ struct kfifo *data_b; | |
+ | |
+ if (unlikely(radio == NULL)) { | |
+ FMDERR(":radio is null"); | |
+ return; | |
+ } | |
+ data_b = &radio->data_buf[event]; | |
+ if (kfifo_in_locked(data_b, data, len, &radio->buf_lock[event])) | |
+ wake_up_interruptible(&radio->event_queue); | |
+} | |
+ | |
+static void hci_cc_riva_peek_rsp(struct radio_hci_dev *hdev, | |
+ struct sk_buff *skb) | |
+{ | |
+ struct iris_device *radio = video_get_drvdata(video_get_dev()); | |
+ __u8 status; | |
+ int len; | |
+ char *data; | |
+ | |
+ if (unlikely(skb == NULL)) { | |
+ FMDERR("%s, socket buffer is null\n", __func__); | |
+ return; | |
+ } | |
+ status = *((__u8 *) skb->data); | |
+ if (!status) { | |
+ len = skb->data[RIVA_PEEK_LEN_OFSET] + RIVA_PEEK_PARAM; | |
+ data = kmalloc(len, GFP_ATOMIC); | |
+ | |
+ if (data != NULL) { | |
+ memcpy(data, &skb->data[PEEK_DATA_OFSET], len); | |
+ iris_q_evt_data(radio, data, len, IRIS_BUF_PEEK); | |
+ kfree(data); | |
+ } else { | |
+ FMDERR("Memory allocation failed"); | |
+ } | |
+ } | |
+ | |
+ radio_hci_req_complete(hdev, status); | |
+} | |
+ | |
+static void hci_cc_riva_read_default_rsp(struct radio_hci_dev *hdev, | |
+ struct sk_buff *skb) | |
+{ | |
+ struct iris_device *radio = video_get_drvdata(video_get_dev()); | |
+ __u8 status; | |
+ __u8 len; | |
+ | |
+ if (unlikely(radio == NULL)) { | |
+ FMDERR(":radio is null"); | |
+ return; | |
+ } | |
+ if (unlikely(skb == NULL)) { | |
+ FMDERR("%s, socket buffer is null\n", __func__); | |
+ return; | |
+ } | |
+ status = *((__u8 *) skb->data); | |
+ if (!status) { | |
+ len = skb->data[1]; | |
+ memset(&radio->default_data, 0, | |
+ sizeof(struct hci_fm_data_rd_rsp)); | |
+ memcpy(&radio->default_data, &skb->data[0], len+2); | |
+ iris_q_evt_data(radio, &skb->data[0], len+2, | |
+ IRIS_BUF_RD_DEFAULT); | |
+ } | |
+ radio_hci_req_complete(hdev, status); | |
+} | |
+ | |
+static void hci_cc_get_spur_tbl(struct radio_hci_dev *hdev, | |
+ struct sk_buff *skb) | |
+{ | |
+ struct iris_device *radio = video_get_drvdata(video_get_dev()); | |
+ __u8 status; | |
+ | |
+ if (unlikely(radio == NULL)) { | |
+ FMDERR(":radio is null"); | |
+ return; | |
+ } | |
+ if (unlikely(skb == NULL)) { | |
+ FMDERR("%s, socket buffer is null\n", __func__); | |
+ return; | |
+ } | |
+ status = *((__u8 *) skb->data); | |
+ if (!status) { | |
+ iris_q_evt_data(radio, &skb->data[1], SPUR_DATA_LEN, | |
+ IRIS_BUF_SPUR); | |
+ iris_q_event(radio, IRIS_EVT_SPUR_TBL); | |
+ } | |
+ radio_hci_req_complete(hdev, status); | |
+} | |
+ | |
+static void hci_cc_ssbi_peek_rsp(struct radio_hci_dev *hdev, | |
+ struct sk_buff *skb) | |
+{ | |
+ struct iris_device *radio = video_get_drvdata(video_get_dev()); | |
+ __u8 status; | |
+ char *data; | |
+ | |
+ if (unlikely(skb == NULL)) { | |
+ FMDERR("%s, socket buffer is null\n", __func__); | |
+ return; | |
+ } | |
+ status = *((__u8 *) skb->data); | |
+ if (!status) { | |
+ data = kmalloc(SSBI_PEEK_LEN, GFP_ATOMIC); | |
+ if (data != NULL) { | |
+ data[0] = skb->data[PEEK_DATA_OFSET]; | |
+ iris_q_evt_data(radio, data, SSBI_PEEK_LEN, | |
+ IRIS_BUF_SSBI_PEEK); | |
+ kfree(data); | |
+ } else { | |
+ FMDERR("Memory allocation failed"); | |
+ } | |
+ } | |
+ | |
+ radio_hci_req_complete(hdev, status); | |
+} | |
+ | |
+static void hci_cc_rds_grp_cntrs_rsp(struct radio_hci_dev *hdev, | |
+ struct sk_buff *skb) | |
+{ | |
+ struct iris_device *radio = video_get_drvdata(video_get_dev()); | |
+ __u8 status; | |
+ char *data; | |
+ | |
+ if (unlikely(skb == NULL)) { | |
+ FMDERR("%s, socket buffer is null\n", __func__); | |
+ return; | |
+ } | |
+ status = *((__u8 *) skb->data); | |
+ if (!status) { | |
+ data = kmalloc(RDS_GRP_CNTR_LEN, GFP_ATOMIC); | |
+ if (data != NULL) { | |
+ memcpy(data, &skb->data[1], RDS_GRP_CNTR_LEN); | |
+ iris_q_evt_data(radio, data, RDS_GRP_CNTR_LEN, | |
+ IRIS_BUF_RDS_CNTRS); | |
+ kfree(data); | |
+ } else { | |
+ FMDERR("memory allocation failed"); | |
+ } | |
+ } | |
+ radio_hci_req_complete(hdev, status); | |
+} | |
+ | |
+static void hci_cc_do_calibration_rsp(struct radio_hci_dev *hdev, | |
+ struct sk_buff *skb) | |
+{ | |
+ struct iris_device *radio = video_get_drvdata(video_get_dev()); | |
+ static struct hci_cc_do_calibration_rsp rsp; | |
+ | |
+ if (unlikely(skb == NULL)) { | |
+ FMDERR("%s, socket buffer is null\n", __func__); | |
+ return; | |
+ } | |
+ | |
+ rsp.status = skb->data[0]; | |
+ rsp.mode = skb->data[CALIB_MODE_OFSET]; | |
+ | |
+ if (!rsp.status) { | |
+ if (rsp.mode == PROCS_CALIB_MODE) { | |
+ memcpy(&rsp.data[0], &skb->data[CALIB_DATA_OFSET], | |
+ PROCS_CALIB_SIZE); | |
+ iris_q_evt_data(radio, rsp.data, PROCS_CALIB_SIZE, | |
+ IRIS_BUF_CAL_DATA); | |
+ } | |
+ } | |
+ radio_hci_req_complete(hdev, rsp.status); | |
+} | |
+ | |
+static void hci_cc_get_ch_det_threshold_rsp(struct radio_hci_dev *hdev, | |
+ struct sk_buff *skb) | |
+{ | |
+ struct iris_device *radio = video_get_drvdata(video_get_dev()); | |
+ u8 status; | |
+ | |
+ if (unlikely(radio == NULL)) { | |
+ FMDERR(":radio is null"); | |
+ return; | |
+ } | |
+ if (unlikely(skb == NULL)) { | |
+ FMDERR("%s, socket buffer is null\n", __func__); | |
+ return; | |
+ } | |
+ status = skb->data[0]; | |
+ if (!status) | |
+ memcpy(&radio->ch_det_threshold, &skb->data[1], | |
+ sizeof(struct hci_fm_ch_det_threshold)); | |
+ | |
+ radio_hci_req_complete(hdev, status); | |
+} | |
+ | |
+static void hci_cc_get_blend_tbl_rsp(struct radio_hci_dev *hdev, | |
+ struct sk_buff *skb) | |
+{ | |
+ struct iris_device *radio = video_get_drvdata(video_get_dev()); | |
+ u8 status; | |
+ | |
+ if (unlikely(radio == NULL)) { | |
+ FMDERR(":radio is null"); | |
+ return; | |
+ } | |
+ if (unlikely(skb == NULL)) { | |
+ FMDERR("%s, socket buffer is null\n", __func__); | |
+ return; | |
+ } | |
+ status = skb->data[0]; | |
+ if (!status) | |
+ memcpy(&radio->blend_tbl, &skb->data[1], | |
+ sizeof(struct hci_fm_blend_table)); | |
+ | |
+ radio_hci_req_complete(hdev, status); | |
+} | |
+ | |
+static inline void hci_cmd_complete_event(struct radio_hci_dev *hdev, | |
+ struct sk_buff *skb) | |
+{ | |
+ struct hci_ev_cmd_complete *cmd_compl_ev; | |
+ __u16 opcode; | |
+ | |
+ | |
+ if (unlikely(skb == NULL)) { | |
+ FMDERR("%s, socket buffer is null\n", __func__); | |
+ return; | |
+ } | |
+ | |
+ cmd_compl_ev = (struct hci_ev_cmd_complete *)skb->data; | |
+ skb_pull(skb, sizeof(*cmd_compl_ev)); | |
+ | |
+ opcode = __le16_to_cpu(cmd_compl_ev->cmd_opcode); | |
+ | |
+ switch (opcode) { | |
+ case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_ENABLE_RECV_REQ): | |
+ case hci_trans_ctrl_cmd_op_pack(HCI_OCF_FM_ENABLE_TRANS_REQ): | |
+ hci_cc_fm_enable_rsp(hdev, skb); | |
+ break; | |
+ case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_GET_RECV_CONF_REQ): | |
+ hci_cc_conf_rsp(hdev, skb); | |
+ break; | |
+ | |
+ case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_DISABLE_RECV_REQ): | |
+ case hci_trans_ctrl_cmd_op_pack(HCI_OCF_FM_DISABLE_TRANS_REQ): | |
+ hci_cc_fm_disable_rsp(hdev, skb); | |
+ break; | |
+ | |
+ case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_SET_RECV_CONF_REQ): | |
+ case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_SET_MUTE_MODE_REQ): | |
+ case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_SET_STEREO_MODE_REQ): | |
+ case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_SET_ANTENNA): | |
+ case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_SET_SIGNAL_THRESHOLD): | |
+ case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_CANCEL_SEARCH): | |
+ case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_RDS_GRP): | |
+ case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_RDS_GRP_PROCESS): | |
+ case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_EN_WAN_AVD_CTRL): | |
+ case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_EN_NOTCH_CTRL): | |
+ case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_SET_CH_DET_THRESHOLD): | |
+ case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_SET_BLND_TBL): | |
+ case hci_trans_ctrl_cmd_op_pack(HCI_OCF_FM_RDS_RT_REQ): | |
+ case hci_trans_ctrl_cmd_op_pack(HCI_OCF_FM_RDS_PS_REQ): | |
+ case hci_common_cmd_op_pack(HCI_OCF_FM_DEFAULT_DATA_WRITE): | |
+ hci_cc_rsp(hdev, skb); | |
+ break; | |
+ case hci_common_cmd_op_pack(HCI_OCF_FM_RESET): | |
+ case hci_diagnostic_cmd_op_pack(HCI_OCF_FM_SSBI_POKE_REG): | |
+ case hci_diagnostic_cmd_op_pack(HCI_OCF_FM_POKE_DATA): | |
+ case hci_diagnostic_cmd_op_pack(HCI_FM_SET_INTERNAL_TONE_GENRATOR): | |
+ case hci_common_cmd_op_pack(HCI_OCF_FM_SET_CALIBRATION): | |
+ case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_SET_EVENT_MASK): | |
+ case hci_common_cmd_op_pack(HCI_OCF_FM_SET_SPUR_TABLE): | |
+ hci_cc_rsp(hdev, skb); | |
+ break; | |
+ case hci_common_cmd_op_pack(HCI_OCF_FM_GET_SPUR_TABLE): | |
+ hci_cc_get_spur_tbl(hdev, skb); | |
+ break; | |
+ case hci_diagnostic_cmd_op_pack(HCI_OCF_FM_SSBI_PEEK_REG): | |
+ hci_cc_ssbi_peek_rsp(hdev, skb); | |
+ break; | |
+ case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_GET_SIGNAL_THRESHOLD): | |
+ hci_cc_sig_threshold_rsp(hdev, skb); | |
+ break; | |
+ | |
+ case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_GET_STATION_PARAM_REQ): | |
+ hci_cc_station_rsp(hdev, skb); | |
+ break; | |
+ | |
+ case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_GET_PROGRAM_SERVICE_REQ): | |
+ hci_cc_prg_srv_rsp(hdev, skb); | |
+ break; | |
+ | |
+ case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_GET_RADIO_TEXT_REQ): | |
+ hci_cc_rd_txt_rsp(hdev, skb); | |
+ break; | |
+ | |
+ case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_GET_AF_LIST_REQ): | |
+ hci_cc_af_list_rsp(hdev, skb); | |
+ break; | |
+ | |
+ case hci_common_cmd_op_pack(HCI_OCF_FM_DEFAULT_DATA_READ): | |
+ hci_cc_riva_read_default_rsp(hdev, skb); | |
+ break; | |
+ | |
+ case hci_diagnostic_cmd_op_pack(HCI_OCF_FM_PEEK_DATA): | |
+ hci_cc_riva_peek_rsp(hdev, skb); | |
+ break; | |
+ | |
+ case hci_common_cmd_op_pack(HCI_OCF_FM_GET_FEATURE_LIST): | |
+ hci_cc_feature_list_rsp(hdev, skb); | |
+ break; | |
+ | |
+ case hci_diagnostic_cmd_op_pack(HCI_OCF_FM_STATION_DBG_PARAM): | |
+ hci_cc_dbg_param_rsp(hdev, skb); | |
+ break; | |
+ case hci_trans_ctrl_cmd_op_pack(HCI_OCF_FM_SET_TRANS_CONF_REQ): | |
+ hci_cc_fm_trans_set_conf_rsp(hdev, skb); | |
+ break; | |
+ | |
+ case hci_status_param_op_pack(HCI_OCF_FM_READ_GRP_COUNTERS): | |
+ hci_cc_rds_grp_cntrs_rsp(hdev, skb); | |
+ break; | |
+ case hci_common_cmd_op_pack(HCI_OCF_FM_DO_CALIBRATION): | |
+ hci_cc_do_calibration_rsp(hdev, skb); | |
+ break; | |
+ | |
+ case hci_trans_ctrl_cmd_op_pack(HCI_OCF_FM_GET_TRANS_CONF_REQ): | |
+ hci_cc_fm_trans_get_conf_rsp(hdev, skb); | |
+ break; | |
+ case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_GET_CH_DET_THRESHOLD): | |
+ hci_cc_get_ch_det_threshold_rsp(hdev, skb); | |
+ break; | |
+ case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_GET_BLND_TBL): | |
+ hci_cc_get_blend_tbl_rsp(hdev, skb); | |
+ break; | |
+ default: | |
+ FMDERR("%s opcode 0x%x", hdev->name, opcode); | |
+ break; | |
+ } | |
+ | |
+} | |
+ | |
+static inline void hci_cmd_status_event(struct radio_hci_dev *hdev, | |
+ struct sk_buff *skb) | |
+{ | |
+ struct hci_ev_cmd_status *ev = (void *) skb->data; | |
+ radio_hci_status_complete(hdev, ev->status); | |
+} | |
+ | |
+static inline void hci_ev_tune_status(struct radio_hci_dev *hdev, | |
+ struct sk_buff *skb) | |
+{ | |
+ int i; | |
+ struct iris_device *radio = video_get_drvdata(video_get_dev()); | |
+ | |
+ if (unlikely(radio == NULL)) { | |
+ FMDERR(":radio is null"); | |
+ return; | |
+ } | |
+ memcpy(&radio->fm_st_rsp.station_rsp, &skb->data[0], | |
+ sizeof(struct hci_ev_tune_status)); | |
+ iris_q_event(radio, IRIS_EVT_TUNE_SUCC); | |
+ | |
+ for (i = 0; i < IRIS_BUF_MAX; i++) { | |
+ if (i >= IRIS_BUF_RT_RDS) | |
+ kfifo_reset(&radio->data_buf[i]); | |
+ } | |
+ if (radio->fm_st_rsp.station_rsp.serv_avble) | |
+ iris_q_event(radio, IRIS_EVT_ABOVE_TH); | |
+ else | |
+ iris_q_event(radio, IRIS_EVT_BELOW_TH); | |
+ | |
+ if (radio->fm_st_rsp.station_rsp.stereo_prg) | |
+ iris_q_event(radio, IRIS_EVT_STEREO); | |
+ else if (radio->fm_st_rsp.station_rsp.stereo_prg == 0) | |
+ iris_q_event(radio, IRIS_EVT_MONO); | |
+ | |
+ if (radio->fm_st_rsp.station_rsp.rds_sync_status) | |
+ iris_q_event(radio, IRIS_EVT_RDS_AVAIL); | |
+ else | |
+ iris_q_event(radio, IRIS_EVT_RDS_NOT_AVAIL); | |
+} | |
+ | |
+static inline void hci_ev_search_compl(struct radio_hci_dev *hdev, | |
+ struct sk_buff *skb) | |
+{ | |
+ struct iris_device *radio = video_get_drvdata(video_get_dev()); | |
+ radio->search_on = 0; | |
+ iris_q_event(radio, IRIS_EVT_SEEK_COMPLETE); | |
+} | |
+ | |
+static inline void hci_ev_srch_st_list_compl(struct radio_hci_dev *hdev, | |
+ struct sk_buff *skb) | |
+{ | |
+ struct iris_device *radio = video_get_drvdata(video_get_dev()); | |
+ struct hci_ev_srch_list_compl *ev ; | |
+ int cnt; | |
+ int stn_num; | |
+ int rel_freq; | |
+ int abs_freq; | |
+ int len; | |
+ | |
+ if (unlikely(radio == NULL)) { | |
+ FMDERR(":radio is null"); | |
+ return; | |
+ } | |
+ | |
+ if (unlikely(skb == NULL)) { | |
+ FMDERR("%s, socket buffer is null\n", __func__); | |
+ return; | |
+ } | |
+ ev = kmalloc(sizeof(*ev), GFP_ATOMIC); | |
+ if (!ev) { | |
+ FMDERR("Memory allocation failed"); | |
+ return ; | |
+ } | |
+ | |
+ ev->num_stations_found = skb->data[STN_NUM_OFFSET]; | |
+ len = ev->num_stations_found * PARAMS_PER_STATION + STN_FREQ_OFFSET; | |
+ | |
+ for (cnt = STN_FREQ_OFFSET, stn_num = 0; | |
+ (cnt < len) && (stn_num < ev->num_stations_found) | |
+ && (stn_num < ARRAY_SIZE(ev->rel_freq)); | |
+ cnt += PARAMS_PER_STATION, stn_num++) { | |
+ abs_freq = *((int *)&skb->data[cnt]); | |
+ rel_freq = abs_freq - radio->recv_conf.band_low_limit; | |
+ rel_freq = (rel_freq * 20) / KHZ_TO_MHZ; | |
+ | |
+ ev->rel_freq[stn_num].rel_freq_lsb = GET_LSB(rel_freq); | |
+ ev->rel_freq[stn_num].rel_freq_msb = GET_MSB(rel_freq); | |
+ } | |
+ | |
+ len = ev->num_stations_found * 2 + sizeof(ev->num_stations_found); | |
+ iris_q_event(radio, IRIS_EVT_NEW_SRCH_LIST); | |
+ iris_q_evt_data(radio, (char *)ev, len, IRIS_BUF_SRCH_LIST); | |
+ kfree(ev); | |
+} | |
+ | |
+static inline void hci_ev_search_next(struct radio_hci_dev *hdev, | |
+ struct sk_buff *skb) | |
+{ | |
+ struct iris_device *radio = video_get_drvdata(video_get_dev()); | |
+ iris_q_event(radio, IRIS_EVT_SCAN_NEXT); | |
+} | |
+ | |
+static inline void hci_ev_stereo_status(struct radio_hci_dev *hdev, | |
+ struct sk_buff *skb) | |
+{ | |
+ struct iris_device *radio = video_get_drvdata(video_get_dev()); | |
+ __u8 st_status; | |
+ | |
+ if (unlikely(skb == NULL)) { | |
+ FMDERR("%s, socket buffer is null\n", __func__); | |
+ return; | |
+ } | |
+ st_status = *((__u8 *) skb->data); | |
+ if (st_status) | |
+ iris_q_event(radio, IRIS_EVT_STEREO); | |
+ else | |
+ iris_q_event(radio, IRIS_EVT_MONO); | |
+} | |
+ | |
+static void hci_ev_raw_rds_group_data(struct radio_hci_dev *hdev, | |
+ struct sk_buff *skb) | |
+{ | |
+ struct iris_device *radio; | |
+ unsigned char blocknum, index; | |
+ struct rds_grp_data temp; | |
+ unsigned int mask_bit; | |
+ unsigned short int aid, agt, gtc; | |
+ unsigned short int carrier; | |
+ | |
+ radio = video_get_drvdata(video_get_dev()); | |
+ index = RDSGRP_DATA_OFFSET; | |
+ | |
+ if (unlikely(radio == NULL)) { | |
+ FMDERR(":radio is null"); | |
+ return; | |
+ } | |
+ | |
+ if (unlikely(skb == NULL)) { | |
+ FMDERR("%s, socket buffer is null\n", __func__); | |
+ return; | |
+ } | |
+ | |
+ for (blocknum = 0; blocknum < RDS_BLOCKS_NUM; blocknum++) { | |
+ temp.rdsBlk[blocknum].rdsLsb = | |
+ (skb->data[index]); | |
+ temp.rdsBlk[blocknum].rdsMsb = | |
+ (skb->data[index+1]); | |
+ index = index + 2; | |
+ } | |
+ | |
+ aid = AID(temp.rdsBlk[3].rdsLsb, temp.rdsBlk[3].rdsMsb); | |
+ gtc = GTC(temp.rdsBlk[1].rdsMsb); | |
+ agt = AGT(temp.rdsBlk[1].rdsLsb); | |
+ | |
+ if (gtc == GRP_3A) { | |
+ switch (aid) { | |
+ case ERT_AID: | |
+ /* calculate the grp mask for RDS grp | |
+ * which will contain actual eRT text | |
+ * | |
+ * Bit Pos 0 1 2 3 4 5 6 7 | |
+ * Grp Type 0A 0B 1A 1B 2A 2B 3A 3B | |
+ * | |
+ * similary for rest grps | |
+ */ | |
+ mask_bit = (((agt >> 1) << 1) + (agt & 1)); | |
+ oda_agt = (1 << mask_bit); | |
+ utf_8_flag = (temp.rdsBlk[2].rdsLsb & 1); | |
+ formatting_dir = EXTRACT_BIT(temp.rdsBlk[2].rdsLsb, | |
+ ERT_FORMAT_DIR_BIT); | |
+ if (ert_carrier != agt) | |
+ iris_q_event(radio, IRIS_EVT_NEW_ODA); | |
+ ert_carrier = agt; | |
+ break; | |
+ case RT_PLUS_AID: | |
+ /* calculate the grp mask for RDS grp | |
+ * which will contain actual eRT text | |
+ * | |
+ * Bit Pos 0 1 2 3 4 5 6 7 | |
+ * Grp Type 0A 0B 1A 1B 2A 2B 3A 3B | |
+ * | |
+ * similary for rest grps | |
+ */ | |
+ mask_bit = (((agt >> 1) << 1) + (agt & 1)); | |
+ oda_agt = (1 << mask_bit); | |
+ /*Extract 5th bit of MSB (b7b6b5b4b3b2b1b0)*/ | |
+ rt_ert_flag = EXTRACT_BIT(temp.rdsBlk[2].rdsMsb, | |
+ RT_ERT_FLAG_BIT); | |
+ if (rt_plus_carrier != agt) | |
+ iris_q_event(radio, IRIS_EVT_NEW_ODA); | |
+ rt_plus_carrier = agt; | |
+ break; | |
+ default: | |
+ oda_agt = 0; | |
+ break; | |
+ } | |
+ } else { | |
+ carrier = gtc; | |
+ if ((carrier == rt_plus_carrier)) | |
+ hci_ev_rt_plus(radio, temp); | |
+ else if (carrier == ert_carrier) | |
+ hci_buff_ert(radio, &temp); | |
+ } | |
+} | |
+ | |
+static void hci_buff_ert(struct iris_device *radio, | |
+ struct rds_grp_data *rds_buf) | |
+{ | |
+ int i; | |
+ unsigned short int info_byte = 0; | |
+ unsigned short int byte_pair_index; | |
+ | |
+ if (rds_buf == NULL) { | |
+ FMDERR("%s, rds buffer is null\n", __func__); | |
+ return; | |
+ } | |
+ byte_pair_index = AGT(rds_buf->rdsBlk[1].rdsLsb); | |
+ if (byte_pair_index == 0) { | |
+ c_byt_pair_index = 0; | |
+ ert_len = 0; | |
+ } | |
+ if (c_byt_pair_index == byte_pair_index) { | |
+ c_byt_pair_index++; | |
+ for (i = 2; i <= 3; i++) { | |
+ info_byte = rds_buf->rdsBlk[i].rdsLsb; | |
+ info_byte |= (rds_buf->rdsBlk[i].rdsMsb << 8); | |
+ ert_buf[ert_len++] = rds_buf->rdsBlk[i].rdsMsb; | |
+ ert_buf[ert_len++] = rds_buf->rdsBlk[i].rdsLsb; | |
+ if ((utf_8_flag == 0) | |
+ && (info_byte == CARRIAGE_RETURN)) { | |
+ ert_len -= 2; | |
+ break; | |
+ } else if ((utf_8_flag == 1) | |
+ && | |
+ (rds_buf->rdsBlk[i].rdsMsb | |
+ == CARRIAGE_RETURN)) { | |
+ info_byte = CARRIAGE_RETURN; | |
+ ert_len -= 2; | |
+ break; | |
+ } else if ((utf_8_flag == 1) | |
+ && | |
+ (rds_buf->rdsBlk[i].rdsLsb | |
+ == CARRIAGE_RETURN)) { | |
+ info_byte = CARRIAGE_RETURN; | |
+ ert_len--; | |
+ break; | |
+ } | |
+ } | |
+ if ((byte_pair_index == MAX_ERT_SEGMENT) || | |
+ (info_byte == CARRIAGE_RETURN)) { | |
+ hci_ev_ert(radio); | |
+ c_byt_pair_index = 0; | |
+ ert_len = 0; | |
+ } | |
+ } else { | |
+ ert_len = 0; | |
+ c_byt_pair_index = 0; | |
+ } | |
+} | |
+static void hci_ev_ert(struct iris_device *radio) | |
+ | |
+{ | |
+ char *data = NULL; | |
+ | |
+ if (ert_len <= 0) | |
+ return; | |
+ data = kmalloc((ert_len + 3), GFP_ATOMIC); | |
+ if (data != NULL) { | |
+ data[0] = ert_len; | |
+ data[1] = utf_8_flag; | |
+ data[2] = formatting_dir; | |
+ memcpy((data + 3), ert_buf, ert_len); | |
+ iris_q_evt_data(radio, data, (ert_len + 3), IRIS_BUF_ERT); | |
+ iris_q_event(radio, IRIS_EVT_NEW_ERT); | |
+ kfree(data); | |
+ } | |
+} | |
+ | |
+static void hci_ev_rt_plus(struct iris_device *radio, | |
+ struct rds_grp_data rds_buf) | |
+{ | |
+ char tag_type1, tag_type2; | |
+ char *data = NULL; | |
+ int len = 0; | |
+ unsigned short int agt; | |
+ | |
+ agt = AGT(rds_buf.rdsBlk[1].rdsLsb); | |
+ /*right most 3 bits of Lsb of block 2 | |
+ * and left most 3 bits of Msb of block 3 | |
+ */ | |
+ tag_type1 = (((agt & TAG1_MSB_MASK) << TAG1_MSB_OFFSET) | | |
+ (rds_buf.rdsBlk[2].rdsMsb >> TAG1_LSB_OFFSET)); | |
+ | |
+ /*right most 1 bit of lsb of 3rd block | |
+ * and left most 5 bits of Msb of 4th block | |
+ */ | |
+ tag_type2 = (((rds_buf.rdsBlk[2].rdsLsb & TAG2_MSB_MASK) | |
+ << TAG2_MSB_OFFSET) | | |
+ (rds_buf.rdsBlk[3].rdsMsb >> TAG2_LSB_OFFSET)); | |
+ | |
+ if (tag_type1 != DUMMY_CLASS) | |
+ len += RT_PLUS_LEN_1_TAG; | |
+ if (tag_type2 != DUMMY_CLASS) | |
+ len += RT_PLUS_LEN_1_TAG; | |
+ | |
+ if (len != 0) { | |
+ len += 2; | |
+ data = kmalloc(len, GFP_ATOMIC); | |
+ } else { | |
+ FMDERR("Len is zero\n"); | |
+ return ; | |
+ } | |
+ if (data != NULL) { | |
+ data[0] = len; | |
+ len = 1; | |
+ data[len++] = rt_ert_flag; | |
+ if (tag_type1 != DUMMY_CLASS) { | |
+ data[len++] = tag_type1; | |
+ /*start position of tag1 | |
+ *right most 5 bits of msb of 3rd block | |
+ *and left most bit of lsb of 3rd block | |
+ */ | |
+ data[len++] = (((rds_buf.rdsBlk[2].rdsMsb & | |
+ TAG1_POS_MSB_MASK) | |
+ << TAG1_POS_MSB_OFFSET) | |
+ | | |
+ (rds_buf.rdsBlk[2].rdsLsb >> | |
+ TAG1_POS_LSB_OFFSET)); | |
+ /*length of tag1 | |
+ *left most 6 bits of lsb of 3rd block | |
+ */ | |
+ data[len++] = ((rds_buf.rdsBlk[2].rdsLsb | |
+ >> TAG1_LEN_OFFSET) | |
+ & | |
+ TAG1_LEN_MASK) + 1; | |
+ } | |
+ if (tag_type2 != DUMMY_CLASS) { | |
+ data[len++] = tag_type2; | |
+ /*start position of tag2 | |
+ *right most 3 bit of msb of 4th block | |
+ *and left most 3 bits of lsb of 4th block | |
+ */ | |
+ data[len++] = (((rds_buf.rdsBlk[3].rdsMsb | |
+ & TAG2_POS_MSB_MASK) | |
+ << TAG2_POS_MSB_OFFSET) | |
+ | | |
+ (rds_buf.rdsBlk[3].rdsLsb | |
+ >> TAG2_POS_LSB_OFFSET)); | |
+ /*length of tag2 | |
+ *right most 5 bits of lsb of 4th block | |
+ */ | |
+ data[len++] = (rds_buf.rdsBlk[3].rdsLsb | |
+ & TAG2_LEN_MASK) + 1; | |
+ } | |
+ iris_q_evt_data(radio, data, len, IRIS_BUF_RT_PLUS); | |
+ iris_q_event(radio, IRIS_EVT_NEW_RT_PLUS); | |
+ kfree(data); | |
+ } else { | |
+ FMDERR("memory allocation failed\n"); | |
+ } | |
+} | |
+ | |
+static inline void hci_ev_program_service(struct radio_hci_dev *hdev, | |
+ struct sk_buff *skb) | |
+{ | |
+ struct iris_device *radio = video_get_drvdata(video_get_dev()); | |
+ int len; | |
+ char *data; | |
+ | |
+ len = (skb->data[RDS_PS_LENGTH_OFFSET] * RDS_STRING) + RDS_OFFSET; | |
+ iris_q_event(radio, IRIS_EVT_NEW_PS_RDS); | |
+ data = kmalloc(len, GFP_ATOMIC); | |
+ if (!data) { | |
+ FMDERR("Failed to allocate memory"); | |
+ return; | |
+ } | |
+ | |
+ data[0] = skb->data[RDS_PS_LENGTH_OFFSET]; | |
+ data[1] = skb->data[RDS_PTYPE]; | |
+ data[2] = skb->data[RDS_PID_LOWER]; | |
+ data[3] = skb->data[RDS_PID_HIGHER]; | |
+ data[4] = 0; | |
+ | |
+ memcpy(data+RDS_OFFSET, &skb->data[RDS_PS_DATA_OFFSET], len-RDS_OFFSET); | |
+ | |
+ iris_q_evt_data(radio, data, len, IRIS_BUF_PS_RDS); | |
+ | |
+ kfree(data); | |
+} | |
+ | |
+ | |
+static inline void hci_ev_radio_text(struct radio_hci_dev *hdev, | |
+ struct sk_buff *skb) | |
+{ | |
+ struct iris_device *radio = video_get_drvdata(video_get_dev()); | |
+ int len = 0; | |
+ char *data; | |
+ | |
+ if (unlikely(skb == NULL)) { | |
+ FMDERR("%s, socket buffer is null\n", __func__); | |
+ return; | |
+ } | |
+ iris_q_event(radio, IRIS_EVT_NEW_RT_RDS); | |
+ | |
+ while ((skb->data[len+RDS_OFFSET] != 0x0d) && (len < MAX_RT_LENGTH)) | |
+ len++; | |
+ data = kmalloc(len+RDS_OFFSET, GFP_ATOMIC); | |
+ if (!data) { | |
+ FMDERR("Failed to allocate memory"); | |
+ return; | |
+ } | |
+ | |
+ data[0] = len; | |
+ data[1] = skb->data[RDS_PTYPE]; | |
+ data[2] = skb->data[RDS_PID_LOWER]; | |
+ data[3] = skb->data[RDS_PID_HIGHER]; | |
+ data[4] = skb->data[RT_A_B_FLAG_OFFSET]; | |
+ | |
+ memcpy(data+RDS_OFFSET, &skb->data[RDS_OFFSET], len); | |
+ data[len+RDS_OFFSET] = 0x00; | |
+ | |
+ iris_q_evt_data(radio, data, len+RDS_OFFSET, IRIS_BUF_RT_RDS); | |
+ | |
+ kfree(data); | |
+} | |
+ | |
+static void hci_ev_af_list(struct radio_hci_dev *hdev, | |
+ struct sk_buff *skb) | |
+{ | |
+ struct iris_device *radio = video_get_drvdata(video_get_dev()); | |
+ struct hci_ev_af_list ev; | |
+ | |
+ if (unlikely(skb == NULL)) { | |
+ FMDERR("%s, socket buffer is null\n", __func__); | |
+ return; | |
+ } | |
+ ev.tune_freq = *((int *) &skb->data[0]); | |
+ ev.pi_code = *((__le16 *) &skb->data[PI_CODE_OFFSET]); | |
+ ev.af_size = skb->data[AF_SIZE_OFFSET]; | |
+ if (ev.af_size > AF_LIST_MAX) { | |
+ FMDERR("AF list size received more than available size"); | |
+ return; | |
+ } | |
+ memcpy(&ev.af_list[0], &skb->data[AF_LIST_OFFSET], | |
+ ev.af_size * sizeof(int)); | |
+ iris_q_event(radio, IRIS_EVT_NEW_AF_LIST); | |
+ iris_q_evt_data(radio, (char *)&ev, (7 + ev.af_size * sizeof(int)), | |
+ IRIS_BUF_AF_LIST); | |
+} | |
+ | |
+static void hci_ev_rds_lock_status(struct radio_hci_dev *hdev, | |
+ struct sk_buff *skb) | |
+{ | |
+ struct iris_device *radio = video_get_drvdata(video_get_dev()); | |
+ __u8 rds_status; | |
+ | |
+ if (unlikely(skb == NULL)) { | |
+ FMDERR("%s, socket buffer is null\n", __func__); | |
+ return; | |
+ } | |
+ | |
+ rds_status = skb->data[0]; | |
+ | |
+ if (rds_status) | |
+ iris_q_event(radio, IRIS_EVT_RDS_AVAIL); | |
+ else | |
+ iris_q_event(radio, IRIS_EVT_RDS_NOT_AVAIL); | |
+} | |
+ | |
+static void hci_ev_service_available(struct radio_hci_dev *hdev, | |
+ struct sk_buff *skb) | |
+{ | |
+ struct iris_device *radio = video_get_drvdata(video_get_dev()); | |
+ u8 serv_avble; | |
+ | |
+ if (unlikely(skb == NULL)) { | |
+ FMDERR("%s, socket buffer is null\n", __func__); | |
+ return; | |
+ } | |
+ serv_avble = skb->data[0]; | |
+ if (serv_avble) | |
+ iris_q_event(radio, IRIS_EVT_ABOVE_TH); | |
+ else | |
+ iris_q_event(radio, IRIS_EVT_BELOW_TH); | |
+} | |
+ | |
+static void hci_ev_rds_grp_complete(struct radio_hci_dev *hdev, | |
+ struct sk_buff *skb) | |
+{ | |
+ struct iris_device *radio = video_get_drvdata(video_get_dev()); | |
+ iris_q_event(radio, IRIS_EVT_TXRDSDONE); | |
+} | |
+ | |
+void radio_hci_event_packet(struct radio_hci_dev *hdev, struct sk_buff *skb) | |
+{ | |
+ struct radio_hci_event_hdr *hdr; | |
+ u8 event; | |
+ | |
+ if (skb == NULL) { | |
+ FMDERR("Socket buffer is NULL"); | |
+ return; | |
+ } | |
+ | |
+ hdr = (void *) skb->data; | |
+ event = hdr->evt; | |
+ | |
+ skb_pull(skb, RADIO_HCI_EVENT_HDR_SIZE); | |
+ | |
+ switch (event) { | |
+ case HCI_EV_TUNE_STATUS: | |
+ hci_ev_tune_status(hdev, skb); | |
+ break; | |
+ case HCI_EV_SEARCH_PROGRESS: | |
+ case HCI_EV_SEARCH_RDS_PROGRESS: | |
+ case HCI_EV_SEARCH_LIST_PROGRESS: | |
+ hci_ev_search_next(hdev, skb); | |
+ break; | |
+ case HCI_EV_STEREO_STATUS: | |
+ hci_ev_stereo_status(hdev, skb); | |
+ break; | |
+ case HCI_EV_RDS_LOCK_STATUS: | |
+ hci_ev_rds_lock_status(hdev, skb); | |
+ break; | |
+ case HCI_EV_SERVICE_AVAILABLE: | |
+ hci_ev_service_available(hdev, skb); | |
+ break; | |
+ case HCI_EV_RDS_RX_DATA: | |
+ hci_ev_raw_rds_group_data(hdev, skb); | |
+ break; | |
+ case HCI_EV_PROGRAM_SERVICE: | |
+ hci_ev_program_service(hdev, skb); | |
+ break; | |
+ case HCI_EV_RADIO_TEXT: | |
+ hci_ev_radio_text(hdev, skb); | |
+ break; | |
+ case HCI_EV_FM_AF_LIST: | |
+ hci_ev_af_list(hdev, skb); | |
+ break; | |
+ case HCI_EV_TX_RDS_GRP_COMPL: | |
+ hci_ev_rds_grp_complete(hdev, skb); | |
+ break; | |
+ case HCI_EV_TX_RDS_CONT_GRP_COMPL: | |
+ break; | |
+ | |
+ case HCI_EV_CMD_COMPLETE: | |
+ hci_cmd_complete_event(hdev, skb); | |
+ break; | |
+ | |
+ case HCI_EV_CMD_STATUS: | |
+ hci_cmd_status_event(hdev, skb); | |
+ break; | |
+ | |
+ case HCI_EV_SEARCH_COMPLETE: | |
+ case HCI_EV_SEARCH_RDS_COMPLETE: | |
+ hci_ev_search_compl(hdev, skb); | |
+ break; | |
+ | |
+ case HCI_EV_SEARCH_LIST_COMPLETE: | |
+ hci_ev_srch_st_list_compl(hdev, skb); | |
+ break; | |
+ | |
+ default: | |
+ break; | |
+ } | |
+} | |
+ | |
+/* | |
+ * fops/IOCTL helper functions | |
+ */ | |
+ | |
+static int iris_search(struct iris_device *radio, int on, int dir) | |
+{ | |
+ int retval = 0; | |
+ enum search_t srch; | |
+ int saved_val; | |
+ | |
+ if (unlikely(radio == NULL)) { | |
+ FMDERR(":radio is null"); | |
+ return -EINVAL; | |
+ } | |
+ | |
+ srch = radio->g_search_mode & SRCH_MODE; | |
+ saved_val = radio->search_on; | |
+ radio->search_on = on; | |
+ if (on) { | |
+ switch (srch) { | |
+ case SCAN_FOR_STRONG: | |
+ case SCAN_FOR_WEAK: | |
+ radio->srch_st_list.srch_list_dir = dir; | |
+ radio->srch_st_list.srch_list_mode = srch; | |
+ retval = hci_fm_search_station_list( | |
+ &radio->srch_st_list, radio->fm_hdev); | |
+ break; | |
+ case RDS_SEEK_PTY: | |
+ case RDS_SCAN_PTY: | |
+ case RDS_SEEK_PI: | |
+ srch = srch - SEARCH_RDS_STNS_MODE_OFFSET; | |
+ radio->srch_rds.srch_station.srch_mode = srch; | |
+ radio->srch_rds.srch_station.srch_dir = dir; | |
+ radio->srch_rds.srch_station.scan_time = | |
+ radio->g_scan_time; | |
+ retval = hci_fm_search_rds_stations(&radio->srch_rds, | |
+ radio->fm_hdev); | |
+ break; | |
+ default: | |
+ radio->srch_st.srch_mode = srch; | |
+ radio->srch_st.scan_time = radio->g_scan_time; | |
+ radio->srch_st.srch_dir = dir; | |
+ retval = hci_fm_search_stations( | |
+ &radio->srch_st, radio->fm_hdev); | |
+ break; | |
+ } | |
+ | |
+ } else { | |
+ retval = hci_cmd(HCI_FM_CANCEL_SEARCH_CMD, radio->fm_hdev); | |
+ } | |
+ | |
+ if (retval < 0) | |
+ radio->search_on = saved_val; | |
+ return retval; | |
+} | |
+ | |
+static int set_low_power_mode(struct iris_device *radio, int power_mode) | |
+{ | |
+ | |
+ int rds_grps_proc = 0x00; | |
+ int retval = 0; | |
+ | |
+ if (unlikely(radio == NULL)) { | |
+ FMDERR(":radio is null"); | |
+ return -EINVAL; | |
+ } | |
+ | |
+ if (radio->power_mode != power_mode) { | |
+ | |
+ if (power_mode) { | |
+ radio->event_mask = 0x00; | |
+ if (radio->af_jump_bit) | |
+ rds_grps_proc = 0x00 | AF_JUMP_ENABLE; | |
+ else | |
+ rds_grps_proc = 0x00; | |
+ retval = hci_fm_rds_grps_process( | |
+ &rds_grps_proc, | |
+ radio->fm_hdev); | |
+ if (retval < 0) { | |
+ FMDERR("Disable RDS failed"); | |
+ return retval; | |
+ } | |
+ retval = hci_conf_event_mask(&radio->event_mask, | |
+ radio->fm_hdev); | |
+ } else { | |
+ | |
+ radio->event_mask = SIG_LEVEL_INTR | | |
+ RDS_SYNC_INTR | AUDIO_CTRL_INTR; | |
+ retval = hci_conf_event_mask(&radio->event_mask, | |
+ radio->fm_hdev); | |
+ if (retval < 0) { | |
+ FMDERR("Enable Async events failed"); | |
+ return retval; | |
+ } | |
+ retval = hci_fm_rds_grps_process( | |
+ &radio->g_rds_grp_proc_ps, | |
+ radio->fm_hdev); | |
+ } | |
+ radio->power_mode = power_mode; | |
+ } | |
+ return retval; | |
+} | |
+static int iris_recv_set_region(struct iris_device *radio, int req_region) | |
+{ | |
+ int retval; | |
+ int saved_val; | |
+ | |
+ if (unlikely(radio == NULL)) { | |
+ FMDERR(":radio is null"); | |
+ return -EINVAL; | |
+ } | |
+ saved_val = radio->region; | |
+ radio->region = req_region; | |
+ | |
+ retval = hci_set_fm_recv_conf( | |
+ &radio->recv_conf, | |
+ radio->fm_hdev); | |
+ | |
+ if (retval < 0) | |
+ radio->region = saved_val; | |
+ | |
+ return retval; | |
+} | |
+ | |
+ | |
+static int iris_trans_set_region(struct iris_device *radio, int req_region) | |
+{ | |
+ int retval; | |
+ int saved_val; | |
+ | |
+ if (unlikely(radio == NULL)) { | |
+ FMDERR(":radio is null"); | |
+ return -EINVAL; | |
+ } | |
+ | |
+ saved_val = radio->region; | |
+ radio->region = req_region; | |
+ | |
+ retval = hci_set_fm_trans_conf( | |
+ &radio->trans_conf, | |
+ radio->fm_hdev); | |
+ | |
+ if (retval < 0) | |
+ radio->region = saved_val; | |
+ return retval; | |
+} | |
+ | |
+ | |
+static int iris_set_freq(struct iris_device *radio, unsigned int freq) | |
+{ | |
+ | |
+ int retval; | |
+ | |
+ if (unlikely(radio == NULL)) { | |
+ FMDERR(":radio is null"); | |
+ return -EINVAL; | |
+ } | |
+ retval = hci_fm_tune_station(&freq, radio->fm_hdev); | |
+ if (retval < 0) | |
+ FMDERR("Error while setting the frequency : %d\n", retval); | |
+ return retval; | |
+} | |
+ | |
+ | |
+static int iris_vidioc_queryctrl(struct file *file, void *priv, | |
+ struct v4l2_queryctrl *qc) | |
+{ | |
+ unsigned char i; | |
+ int retval = -EINVAL; | |
+ | |
+ if (qc == NULL) { | |
+ FMDERR("%s, query ctrl is null\n", __func__); | |
+ return retval; | |
+ } | |
+ for (i = 0; i < ARRAY_SIZE(iris_v4l2_queryctrl); i++) { | |
+ if (qc->id && qc->id == iris_v4l2_queryctrl[i].id) { | |
+ memcpy(qc, &(iris_v4l2_queryctrl[i]), sizeof(*qc)); | |
+ retval = 0; | |
+ break; | |
+ } | |
+ } | |
+ | |
+ return retval; | |
+} | |
+ | |
+static int iris_do_calibration(struct iris_device *radio) | |
+{ | |
+ char cal_mode = 0x00; | |
+ int retval = 0x00; | |
+ | |
+ if (unlikely(radio == NULL)) { | |
+ FMDERR(":radio is null"); | |
+ return -EINVAL; | |
+ } | |
+ | |
+ cal_mode = PROCS_CALIB_MODE; | |
+ radio->mode = FM_CALIB; | |
+ retval = hci_cmd(HCI_FM_ENABLE_RECV_CMD, | |
+ radio->fm_hdev); | |
+ if (retval < 0) { | |
+ FMDERR("Enable failed before calibration %x", retval); | |
+ radio->mode = FM_OFF; | |
+ return retval; | |
+ } | |
+ retval = radio_hci_request(radio->fm_hdev, hci_fm_do_cal_req, | |
+ (unsigned long)cal_mode, RADIO_HCI_TIMEOUT); | |
+ if (retval < 0) { | |
+ FMDERR("Do Process calibration failed %x", retval); | |
+ radio->mode = FM_RECV; | |
+ return retval; | |
+ } | |
+ retval = hci_cmd(HCI_FM_DISABLE_RECV_CMD, | |
+ radio->fm_hdev); | |
+ if (retval < 0) | |
+ FMDERR("Disable Failed after calibration %d", retval); | |
+ else | |
+ radio->mode = FM_OFF; | |
+ | |
+ return retval; | |
+} | |
+static int iris_vidioc_g_ctrl(struct file *file, void *priv, | |
+ struct v4l2_control *ctrl) | |
+{ | |
+ struct iris_device *radio = video_get_drvdata(video_devdata(file)); | |
+ int retval = 0; | |
+ int cf0; | |
+ struct hci_fm_def_data_rd_req rd; | |
+ int lsb, msb; | |
+ | |
+ if (unlikely(radio == NULL)) { | |
+ FMDERR(":radio is null"); | |
+ retval = -EINVAL; | |
+ goto END; | |
+ } | |
+ | |
+ if (ctrl == NULL) { | |
+ FMDERR("%s, v4l2 ctrl is null\n", __func__); | |
+ retval = -EINVAL; | |
+ goto END; | |
+ } | |
+ switch (ctrl->id) { | |
+ case V4L2_CID_AUDIO_VOLUME: | |
+ break; | |
+ case V4L2_CID_AUDIO_MUTE: | |
+ if (is_valid_hard_mute(radio->mute_mode.hard_mute)) | |
+ ctrl->value = radio->mute_mode.hard_mute; | |
+ else | |
+ retval = -EINVAL; | |
+ break; | |
+ case V4L2_CID_PRIVATE_IRIS_SRCHMODE: | |
+ if (is_valid_srch_mode(radio->g_search_mode)) | |
+ ctrl->value = radio->g_search_mode; | |
+ else | |
+ retval = -EINVAL; | |
+ break; | |
+ case V4L2_CID_PRIVATE_IRIS_SCANDWELL: | |
+ if (is_valid_scan_dwell_prd(radio->g_scan_time)) | |
+ ctrl->value = radio->g_scan_time; | |
+ else | |
+ retval = -EINVAL; | |
+ break; | |
+ case V4L2_CID_PRIVATE_IRIS_SRCHON: | |
+ ctrl->value = radio->search_on; | |
+ break; | |
+ case V4L2_CID_PRIVATE_IRIS_STATE: | |
+ if (is_valid_fm_state(radio->mode)) | |
+ ctrl->value = radio->mode; | |
+ else | |
+ retval = -EINVAL; | |
+ break; | |
+ case V4L2_CID_PRIVATE_IRIS_IOVERC: | |
+ retval = hci_cmd(HCI_FM_STATION_DBG_PARAM_CMD, radio->fm_hdev); | |
+ if (retval < 0) | |
+ return retval; | |
+ ctrl->value = radio->st_dbg_param.io_verc; | |
+ break; | |
+ case V4L2_CID_PRIVATE_IRIS_INTDET: | |
+ retval = hci_cmd(HCI_FM_STATION_DBG_PARAM_CMD, radio->fm_hdev); | |
+ if (retval == 0) | |
+ ctrl->value = radio->st_dbg_param.in_det_out; | |
+ else | |
+ retval = -EINVAL; | |
+ break; | |
+ case V4L2_CID_PRIVATE_IRIS_REGION: | |
+ ctrl->value = radio->region; | |
+ break; | |
+ case V4L2_CID_PRIVATE_IRIS_SIGNAL_TH: | |
+ retval = hci_cmd(HCI_FM_GET_SIGNAL_TH_CMD, radio->fm_hdev); | |
+ if ((retval == 0) && | |
+ is_valid_sig_th(radio->sig_th.sig_threshold)) | |
+ ctrl->value = radio->sig_th.sig_threshold; | |
+ else | |
+ retval = -EINVAL; | |
+ break; | |
+ case V4L2_CID_PRIVATE_IRIS_SRCH_PTY: | |
+ if (is_valid_pty(radio->srch_rds.srch_pty)) | |
+ ctrl->value = radio->srch_rds.srch_pty; | |
+ else | |
+ retval = -EINVAL; | |
+ break; | |
+ case V4L2_CID_PRIVATE_IRIS_SRCH_PI: | |
+ if (is_valid_pi(radio->srch_rds.srch_pi)) | |
+ ctrl->value = radio->srch_rds.srch_pi; | |
+ else | |
+ retval = -EINVAL; | |
+ break; | |
+ case V4L2_CID_PRIVATE_IRIS_SRCH_CNT: | |
+ if (is_valid_srch_station_cnt( | |
+ radio->srch_st_result.num_stations_found)) | |
+ ctrl->value = radio->srch_st_result.num_stations_found; | |
+ else | |
+ retval = -EINVAL; | |
+ break; | |
+ case V4L2_CID_PRIVATE_IRIS_EMPHASIS: | |
+ if (radio->mode == FM_RECV) { | |
+ retval = hci_cmd(HCI_FM_GET_RECV_CONF_CMD, | |
+ radio->fm_hdev); | |
+ if ((retval == 0) && | |
+ is_valid_emphasis(radio->recv_conf.emphasis)) | |
+ ctrl->value = radio->recv_conf.emphasis; | |
+ else | |
+ retval = -EINVAL; | |
+ } else if (radio->mode == FM_TRANS) { | |
+ retval = hci_cmd(HCI_FM_GET_TX_CONFIG, | |
+ radio->fm_hdev); | |
+ if ((retval == 0) && | |
+ is_valid_emphasis(radio->trans_conf.emphasis)) | |
+ ctrl->value = radio->trans_conf.emphasis; | |
+ else | |
+ retval = -EINVAL; | |
+ } else { | |
+ retval = -EINVAL; | |
+ FMDERR("Error in radio mode"" %d\n", retval); | |
+ } | |
+ break; | |
+ case V4L2_CID_PRIVATE_IRIS_RDS_STD: | |
+ if (radio->mode == FM_RECV) { | |
+ retval = hci_cmd(HCI_FM_GET_RECV_CONF_CMD, | |
+ radio->fm_hdev); | |
+ if ((retval == 0) && | |
+ is_valid_rds_std(radio->recv_conf.rds_std)) | |
+ ctrl->value = radio->recv_conf.rds_std; | |
+ else | |
+ retval = -EINVAL; | |
+ } else if (radio->mode == FM_TRANS) { | |
+ retval = hci_cmd(HCI_FM_GET_TX_CONFIG, | |
+ radio->fm_hdev); | |
+ if ((retval == 0) && | |
+ is_valid_rds_std(radio->trans_conf.rds_std)) | |
+ ctrl->value = radio->trans_conf.rds_std; | |
+ else | |
+ retval = -EINVAL; | |
+ } else { | |
+ retval = -EINVAL; | |
+ FMDERR("Error in radio mode" | |
+ " %d\n", retval); | |
+ } | |
+ break; | |
+ case V4L2_CID_PRIVATE_IRIS_SPACING: | |
+ if (radio->mode == FM_RECV) { | |
+ retval = hci_cmd(HCI_FM_GET_RECV_CONF_CMD, | |
+ radio->fm_hdev); | |
+ if ((retval == 0) && | |
+ is_valid_chan_spacing( | |
+ radio->recv_conf.ch_spacing)) | |
+ ctrl->value = radio->recv_conf.ch_spacing; | |
+ else | |
+ retval = -EINVAL; | |
+ } else { | |
+ retval = -EINVAL; | |
+ FMDERR("Error in radio mode" | |
+ " %d\n", retval); | |
+ } | |
+ break; | |
+ case V4L2_CID_PRIVATE_IRIS_RDSON: | |
+ if (radio->mode == FM_RECV) { | |
+ retval = hci_cmd(HCI_FM_GET_RECV_CONF_CMD, | |
+ radio->fm_hdev); | |
+ if ((retval == 0) && | |
+ is_valid_rds_std(radio->recv_conf.rds_std)) | |
+ ctrl->value = radio->recv_conf.rds_std; | |
+ else | |
+ retval = -EINVAL; | |
+ } else { | |
+ retval = -EINVAL; | |
+ FMDERR("Error in radio mode" | |
+ " %d\n", retval); | |
+ } | |
+ break; | |
+ case V4L2_CID_PRIVATE_IRIS_RDSGROUP_MASK: | |
+ ctrl->value = radio->rds_grp.rds_grp_enable_mask; | |
+ break; | |
+ case V4L2_CID_PRIVATE_IRIS_RDSGROUP_PROC: | |
+ case V4L2_CID_PRIVATE_IRIS_PSALL: | |
+ ctrl->value = (radio->g_rds_grp_proc_ps << RDS_CONFIG_OFFSET); | |
+ break; | |
+ case V4L2_CID_PRIVATE_IRIS_RDSD_BUF: | |
+ ctrl->value = radio->rds_grp.rds_buf_size; | |
+ break; | |
+ case V4L2_CID_PRIVATE_IRIS_LP_MODE: | |
+ ctrl->value = radio->power_mode; | |
+ break; | |
+ case V4L2_CID_PRIVATE_IRIS_ANTENNA: | |
+ ctrl->value = radio->g_antenna; | |
+ break; | |
+ case V4L2_CID_PRIVATE_IRIS_SOFT_MUTE: | |
+ retval = hci_cmd(HCI_FM_STATION_DBG_PARAM_CMD, radio->fm_hdev); | |
+ if ((retval == 0) && | |
+ is_valid_soft_mute(radio->mute_mode.soft_mute)) | |
+ ctrl->value = radio->mute_mode.soft_mute; | |
+ else | |
+ retval = -EINVAL; | |
+ break; | |
+ case V4L2_CID_PRIVATE_IRIS_DO_CALIBRATION: | |
+ retval = iris_do_calibration(radio); | |
+ break; | |
+ case V4L2_CID_PRIVATE_IRIS_GET_SINR: | |
+ if (radio->mode == FM_RECV) { | |
+ retval = hci_cmd(HCI_FM_GET_STATION_PARAM_CMD, | |
+ radio->fm_hdev); | |
+ if (retval == 0) | |
+ ctrl->value = radio->fm_st_rsp.station_rsp.sinr; | |
+ } else | |
+ retval = -EINVAL; | |
+ break; | |
+ case V4L2_CID_PRIVATE_INTF_HIGH_THRESHOLD: | |
+ retval = hci_cmd(HCI_FM_GET_DET_CH_TH_CMD, radio->fm_hdev); | |
+ if (retval == 0) | |
+ ctrl->value = radio->ch_det_threshold.high_th; | |
+ break; | |
+ case V4L2_CID_PRIVATE_INTF_LOW_THRESHOLD: | |
+ retval = hci_cmd(HCI_FM_GET_DET_CH_TH_CMD, radio->fm_hdev); | |
+ if (retval == 0) | |
+ ctrl->value = radio->ch_det_threshold.low_th; | |
+ break; | |
+ case V4L2_CID_PRIVATE_SINR_THRESHOLD: | |
+ retval = hci_cmd(HCI_FM_GET_DET_CH_TH_CMD, radio->fm_hdev); | |
+ if (retval == 0) | |
+ ctrl->value = radio->ch_det_threshold.sinr; | |
+ break; | |
+ case V4L2_CID_PRIVATE_SINR_SAMPLES: | |
+ retval = hci_cmd(HCI_FM_GET_DET_CH_TH_CMD, radio->fm_hdev); | |
+ if (retval == 0) | |
+ ctrl->value = radio->ch_det_threshold.sinr_samples; | |
+ break; | |
+ case V4L2_CID_PRIVATE_VALID_CHANNEL: | |
+ ctrl->value = radio->is_station_valid; | |
+ break; | |
+ case V4L2_CID_PRIVATE_AF_RMSSI_TH: | |
+ rd.mode = FM_RDS_CNFG_MODE; | |
+ rd.length = FM_RDS_CNFG_LEN; | |
+ rd.param_len = 0; | |
+ rd.param = 0; | |
+ | |
+ retval = hci_def_data_read(&rd, radio->fm_hdev); | |
+ if (retval == 0) { | |
+ lsb = radio->default_data.data[AF_RMSSI_TH_LSB_OFFSET]; | |
+ msb = radio->default_data.data[AF_RMSSI_TH_MSB_OFFSET]; | |
+ ctrl->value = ((msb << 8) | lsb); | |
+ } | |
+ break; | |
+ case V4L2_CID_PRIVATE_AF_RMSSI_SAMPLES: | |
+ rd.mode = FM_RDS_CNFG_MODE; | |
+ rd.length = FM_RDS_CNFG_LEN; | |
+ rd.param_len = 0; | |
+ rd.param = 0; | |
+ | |
+ retval = hci_def_data_read(&rd, radio->fm_hdev); | |
+ if (retval == 0) | |
+ ctrl->value = | |
+ radio->default_data.data[AF_RMSSI_SAMPLES_OFFSET]; | |
+ break; | |
+ case V4L2_CID_PRIVATE_GOOD_CH_RMSSI_TH: | |
+ rd.mode = FM_RX_CONFG_MODE; | |
+ rd.length = FM_RX_CNFG_LEN; | |
+ rd.param_len = 0; | |
+ rd.param = 0; | |
+ | |
+ retval = hci_def_data_read(&rd, radio->fm_hdev); | |
+ if (retval == 0) { | |
+ ctrl->value = | |
+ radio->default_data.data[GD_CH_RMSSI_TH_OFFSET]; | |
+ if (ctrl->value > MAX_GD_CH_RMSSI_TH) | |
+ ctrl->value -= 256; | |
+ } | |
+ break; | |
+ case V4L2_CID_PRIVATE_SRCHALGOTYPE: | |
+ rd.mode = FM_RX_CONFG_MODE; | |
+ rd.length = FM_RX_CNFG_LEN; | |
+ rd.param_len = 0; | |
+ rd.param = 0; | |
+ | |
+ retval = hci_def_data_read(&rd, radio->fm_hdev); | |
+ if (retval == 0) | |
+ ctrl->value = | |
+ radio->default_data.data[SRCH_ALGO_TYPE_OFFSET]; | |
+ break; | |
+ case V4L2_CID_PRIVATE_SINRFIRSTSTAGE: | |
+ rd.mode = FM_RX_CONFG_MODE; | |
+ rd.length = FM_RX_CNFG_LEN; | |
+ rd.param_len = 0; | |
+ rd.param = 0; | |
+ | |
+ retval = hci_def_data_read(&rd, radio->fm_hdev); | |
+ if (retval == 0) { | |
+ ctrl->value = | |
+ radio->default_data.data[SINRFIRSTSTAGE_OFFSET]; | |
+ if (ctrl->value > MAX_SINR_FIRSTSTAGE) | |
+ ctrl->value -= 256; | |
+ } | |
+ break; | |
+ case V4L2_CID_PRIVATE_RMSSIFIRSTSTAGE: | |
+ rd.mode = FM_RX_CONFG_MODE; | |
+ rd.length = FM_RX_CNFG_LEN; | |
+ rd.param_len = 0; | |
+ rd.param = 0; | |
+ | |
+ retval = hci_def_data_read(&rd, radio->fm_hdev); | |
+ if (retval == 0) { | |
+ ctrl->value = | |
+ radio->default_data.data[RMSSIFIRSTSTAGE_OFFSET]; | |
+ if (ctrl->value > MAX_RMSSI_FIRSTSTAGE) | |
+ ctrl->value -= 256; | |
+ } | |
+ break; | |
+ case V4L2_CID_PRIVATE_CF0TH12: | |
+ rd.mode = FM_RX_CONFG_MODE; | |
+ rd.length = FM_RX_CNFG_LEN; | |
+ rd.param_len = 0; | |
+ rd.param = 0; | |
+ | |
+ retval = hci_def_data_read(&rd, radio->fm_hdev); | |
+ if (retval == 0) { | |
+ ctrl->value = | |
+ radio->default_data.data[CF0TH12_BYTE1_OFFSET]; | |
+ cf0 = radio->default_data.data[CF0TH12_BYTE2_OFFSET]; | |
+ ctrl->value |= (cf0 << 8); | |
+ cf0 = radio->default_data.data[CF0TH12_BYTE3_OFFSET]; | |
+ ctrl->value |= (cf0 << 16); | |
+ cf0 = radio->default_data.data[CF0TH12_BYTE4_OFFSET]; | |
+ if (cf0 > 127) | |
+ cf0 -= 256; | |
+ ctrl->value |= (cf0 << 24); | |
+ } | |
+ break; | |
+ case V4L2_CID_PRIVATE_BLEND_SINRHI: | |
+ retval = hci_cmd(HCI_FM_GET_BLND_TBL_CMD, radio->fm_hdev); | |
+ if (retval < 0) { | |
+ FMDERR("Failed to get blend table %d", retval); | |
+ goto END; | |
+ } | |
+ ctrl->value = radio->blend_tbl.scBlendSinrHi; | |
+ break; | |
+ case V4L2_CID_PRIVATE_BLEND_RMSSIHI: | |
+ retval = hci_cmd(HCI_FM_GET_BLND_TBL_CMD, radio->fm_hdev); | |
+ if (retval < 0) { | |
+ FMDERR("Failed to get blend table %d", retval); | |
+ goto END; | |
+ } | |
+ ctrl->value = radio->blend_tbl.scBlendRmssiHi; | |
+ break; | |
+ default: | |
+ retval = -EINVAL; | |
+ break; | |
+ } | |
+ | |
+END: | |
+ if (retval > 0) | |
+ retval = -EINVAL; | |
+ if (retval < 0) | |
+ FMDERR("get control failed with %d\n", retval); | |
+ if (ctrl != NULL) | |
+ FMDERR("get control failed id: %d\n", ctrl->id); | |
+ | |
+ return retval; | |
+} | |
+ | |
+static int iris_vidioc_g_ext_ctrls(struct file *file, void *priv, | |
+ struct v4l2_ext_controls *ctrl) | |
+{ | |
+ int retval = 0; | |
+ char *data = NULL; | |
+ struct iris_device *radio = video_get_drvdata(video_devdata(file)); | |
+ struct hci_fm_def_data_rd_req default_data_rd; | |
+ | |
+ if (unlikely(radio == NULL)) { | |
+ FMDERR(":radio is null"); | |
+ retval = -EINVAL; | |
+ goto END; | |
+ } | |
+ | |
+ if ((ctrl == NULL) || (ctrl->count == 0) | |
+ || (ctrl->controls == NULL)) { | |
+ FMDERR("%s, invalid v4l2 ctrl\n", __func__); | |
+ retval = -EINVAL; | |
+ goto END; | |
+ } | |
+ switch ((ctrl->controls[0]).id) { | |
+ case V4L2_CID_PRIVATE_IRIS_READ_DEFAULT: | |
+ data = (ctrl->controls[0]).string; | |
+ memset(&default_data_rd, 0, sizeof(default_data_rd)); | |
+ if (copy_from_user(&default_data_rd.mode, data, | |
+ sizeof(default_data_rd))) { | |
+ retval = -EFAULT; | |
+ goto END; | |
+ } | |
+ retval = hci_def_data_read(&default_data_rd, radio->fm_hdev); | |
+ break; | |
+ default: | |
+ retval = -EINVAL; | |
+ break; | |
+ } | |
+ | |
+END: | |
+ if (retval > 0) | |
+ retval = -EINVAL; | |
+ | |
+ return retval; | |
+} | |
+ | |
+static int iris_vidioc_s_ext_ctrls(struct file *file, void *priv, | |
+ struct v4l2_ext_controls *ctrl) | |
+{ | |
+ int retval = 0; | |
+ size_t bytes_to_copy; | |
+ struct hci_fm_tx_ps tx_ps; | |
+ struct hci_fm_tx_rt tx_rt; | |
+ struct hci_fm_def_data_wr_req default_data; | |
+ struct hci_fm_set_cal_req_proc proc_cal_req; | |
+ struct hci_fm_set_spur_table_req spur_tbl_req; | |
+ char *spur_data; | |
+ | |
+ struct iris_device *radio = video_get_drvdata(video_devdata(file)); | |
+ char *data = NULL; | |
+ | |
+ if (unlikely(radio == NULL)) { | |
+ FMDERR(":radio is null"); | |
+ retval = -EINVAL; | |
+ goto END; | |
+ } | |
+ | |
+ if ((ctrl == NULL) || (ctrl->count == 0) | |
+ || (ctrl->controls == NULL)) { | |
+ FMDERR("%s, invalid v4l2 ctrl\n", __func__); | |
+ retval = -EINVAL; | |
+ goto END; | |
+ } | |
+ | |
+ switch ((ctrl->controls[0]).id) { | |
+ case V4L2_CID_RDS_TX_PS_NAME: | |
+ FMDBG("In V4L2_CID_RDS_TX_PS_NAME\n"); | |
+ /*Pass a sample PS string */ | |
+ | |
+ memset(tx_ps.ps_data, 0, MAX_PS_LENGTH); | |
+ bytes_to_copy = min_t(size_t, ctrl->controls[0].size, | |
+ MAX_PS_LENGTH); | |
+ data = (ctrl->controls[0]).string; | |
+ | |
+ if (copy_from_user(tx_ps.ps_data, | |
+ data, bytes_to_copy)) { | |
+ FMDERR("%s: copy from user for tx ps name failed\n", | |
+ __func__); | |
+ retval = -EFAULT; | |
+ goto END; | |
+ } else { | |
+ tx_ps.ps_control = 0x01; | |
+ tx_ps.pi = radio->pi; | |
+ tx_ps.pty = radio->pty; | |
+ tx_ps.ps_repeatcount = radio->ps_repeatcount; | |
+ tx_ps.ps_num = (bytes_to_copy / PS_STRING_LEN); | |
+ | |
+ retval = radio_hci_request(radio->fm_hdev, | |
+ hci_trans_ps_req, | |
+ (unsigned long)&tx_ps, | |
+ RADIO_HCI_TIMEOUT); | |
+ } | |
+ break; | |
+ case V4L2_CID_RDS_TX_RADIO_TEXT: | |
+ bytes_to_copy = | |
+ min_t(size_t, (ctrl->controls[0]).size, MAX_RT_LENGTH); | |
+ data = (ctrl->controls[0]).string; | |
+ | |
+ memset(tx_rt.rt_data, 0, MAX_RT_LENGTH); | |
+ | |
+ if (copy_from_user(tx_rt.rt_data, | |
+ data, bytes_to_copy)) { | |
+ FMDERR("%s: copy from user for tx rt failed\n", | |
+ __func__); | |
+ retval = -EFAULT; | |
+ goto END; | |
+ } else { | |
+ tx_rt.rt_control = 0x01; | |
+ tx_rt.pi = radio->pi; | |
+ tx_rt.pty = radio->pty; | |
+ tx_rt.rt_len = bytes_to_copy; | |
+ | |
+ retval = radio_hci_request(radio->fm_hdev, | |
+ hci_trans_rt_req, | |
+ (unsigned long)&tx_rt, | |
+ RADIO_HCI_TIMEOUT); | |
+ } | |
+ break; | |
+ case V4L2_CID_PRIVATE_IRIS_WRITE_DEFAULT: | |
+ data = (ctrl->controls[0]).string; | |
+ memset(&default_data, 0, sizeof(default_data)); | |
+ /* | |
+ * Check if length of the 'FM Default Data' to be sent | |
+ * is within the maximum 'FM Default Data' packet limit. | |
+ * Max. 'FM Default Data' packet length is 251 bytes: | |
+ * 1 byte - XFR Mode | |
+ * 1 byte - length of the default data | |
+ * 249 bytes - actual data to be configured | |
+ */ | |
+ if (ctrl->controls[0].size > (DEFAULT_DATA_SIZE + 2)) { | |
+ pr_err("%s: Default data buffer overflow!\n", __func__); | |
+ retval = -EINVAL; | |
+ goto END; | |
+ } | |
+ | |
+ /* copy only 'size' bytes of data as requested by user */ | |
+ retval = copy_from_user(&default_data, data, | |
+ ctrl->controls[0].size); | |
+ if (retval > 0) { | |
+ pr_err("%s: Failed to copy %d bytes of default data" | |
+ " passed by user\n", __func__, retval); | |
+ retval = -EFAULT; | |
+ goto END; | |
+ } | |
+ FMDBG("%s: XFR Mode\t: 0x%x\n", __func__, default_data.mode); | |
+ FMDBG("%s: XFR Data Length\t: %d\n", __func__, | |
+ default_data.length); | |
+ /* | |
+ * Check if the 'length' of the actual XFR data to be configured | |
+ * is valid or not. Length of actual XFR data should be always | |
+ * 2 bytes less than the total length of the 'FM Default Data'. | |
+ * Length of 'FM Default Data' DEF_DATA_LEN: (1+1+XFR Data Size) | |
+ * Length of 'Actual XFR Data' XFR_DATA_LEN: (DEF_DATA_LEN - 2) | |
+ */ | |
+ if (default_data.length != (ctrl->controls[0].size - 2)) { | |
+ pr_err("%s: Invalid 'length' parameter passed for " | |
+ "actual xfr data\n", __func__); | |
+ retval = -EINVAL; | |
+ goto END; | |
+ } | |
+ retval = hci_def_data_write(&default_data, radio->fm_hdev); | |
+ break; | |
+ case V4L2_CID_PRIVATE_IRIS_SET_CALIBRATION: | |
+ data = (ctrl->controls[0]).string; | |
+ bytes_to_copy = (ctrl->controls[0]).size; | |
+ if (bytes_to_copy < PROCS_CALIB_SIZE) { | |
+ FMDERR("data is less than required size"); | |
+ retval = -EFAULT; | |
+ goto END; | |
+ } | |
+ memset(proc_cal_req.data, 0, PROCS_CALIB_SIZE); | |
+ proc_cal_req.mode = PROCS_CALIB_MODE; | |
+ if (copy_from_user(&proc_cal_req.data[0], | |
+ data, sizeof(proc_cal_req.data))) { | |
+ retval = -EFAULT; | |
+ goto END; | |
+ } | |
+ retval = radio_hci_request(radio->fm_hdev, | |
+ hci_fm_set_cal_req_proc, | |
+ (unsigned long)&proc_cal_req, | |
+ RADIO_HCI_TIMEOUT); | |
+ break; | |
+ case V4L2_CID_PRIVATE_IRIS_SET_SPURTABLE: | |
+ memset(&spur_tbl_req, 0, sizeof(spur_tbl_req)); | |
+ data = (ctrl->controls[0]).string; | |
+ bytes_to_copy = (ctrl->controls[0]).size; | |
+ spur_tbl_req.mode = data[0]; | |
+ spur_tbl_req.no_of_freqs_entries = data[1]; | |
+ spur_data = kmalloc((data[1] * SPUR_DATA_LEN) + 2, | |
+ GFP_ATOMIC); | |
+ if (!spur_data) { | |
+ FMDERR("Allocation failed for Spur data"); | |
+ retval = -EFAULT; | |
+ goto END; | |
+ } | |
+ if (copy_from_user(spur_data, | |
+ &data[2], (bytes_to_copy - 2))) { | |
+ kfree(spur_data); | |
+ retval = -EFAULT; | |
+ goto END; | |
+ } | |
+ | |
+ if (spur_tbl_req.no_of_freqs_entries <= ENTRIES_EACH_CMD) { | |
+ memcpy(&spur_tbl_req.spur_data[0], spur_data, | |
+ (data[1] * SPUR_DATA_LEN)); | |
+ retval = radio_hci_request(radio->fm_hdev, | |
+ hci_fm_set_spur_tbl_req, | |
+ (unsigned long)&spur_tbl_req, | |
+ RADIO_HCI_TIMEOUT); | |
+ } else { | |
+ memcpy(&spur_tbl_req.spur_data[0], spur_data, | |
+ (ENTRIES_EACH_CMD * SPUR_DATA_LEN)); | |
+ retval = radio_hci_request(radio->fm_hdev, | |
+ hci_fm_set_spur_tbl_req, | |
+ (unsigned long)&spur_tbl_req, | |
+ RADIO_HCI_TIMEOUT); | |
+ if (retval < 0) { | |
+ FMDERR("Spur command failed to execute"); | |
+ kfree(spur_data); | |
+ goto END; | |
+ } | |
+ spur_tbl_req.mode = 0x02;/* 02-Continue mode */ | |
+ spur_tbl_req.no_of_freqs_entries = | |
+ spur_tbl_req.no_of_freqs_entries | |
+ - ENTRIES_EACH_CMD; | |
+ memcpy(&spur_tbl_req.spur_data[0], | |
+ &spur_data[ENTRIES_EACH_CMD * SPUR_DATA_LEN], | |
+ (spur_tbl_req.no_of_freqs_entries * SPUR_DATA_LEN)); | |
+ retval = radio_hci_request(radio->fm_hdev, | |
+ hci_fm_set_spur_tbl_req, | |
+ (unsigned long)&spur_tbl_req, | |
+ RADIO_HCI_TIMEOUT); | |
+ } | |
+ kfree(spur_data); | |
+ break; | |
+ default: | |
+ FMDBG("Shouldn't reach here\n"); | |
+ retval = -1; | |
+ goto END; | |
+ break; | |
+ } | |
+ | |
+END: | |
+ if (retval > 0) | |
+ retval = -EINVAL; | |
+ | |
+ return retval; | |
+} | |
+ | |
+static int iris_vidioc_s_ctrl(struct file *file, void *priv, | |
+ struct v4l2_control *ctrl) | |
+{ | |
+ struct iris_device *radio = video_get_drvdata(video_devdata(file)); | |
+ int retval = 0; | |
+ unsigned int rds_grps_proc = 0; | |
+ __u8 temp_val = 0; | |
+ int saved_val; | |
+ unsigned long arg = 0; | |
+ struct hci_fm_tx_ps tx_ps = {0}; | |
+ struct hci_fm_tx_rt tx_rt = {0}; | |
+ struct hci_fm_def_data_rd_req rd; | |
+ struct hci_fm_def_data_wr_req wrd; | |
+ char sinr_th, sinr; | |
+ __u8 intf_det_low_th, intf_det_high_th, intf_det_out; | |
+ unsigned int spur_freq; | |
+ | |
+ if (unlikely(radio == NULL)) { | |
+ FMDERR(":radio is null"); | |
+ retval = -EINVAL; | |
+ goto END; | |
+ } | |
+ | |
+ if (ctrl == NULL) { | |
+ FMDERR("%s, v4l2 ctrl is null\n", __func__); | |
+ retval = -EINVAL; | |
+ goto END; | |
+ } | |
+ switch (ctrl->id) { | |
+ case V4L2_CID_PRIVATE_IRIS_TX_TONE: | |
+ if (!is_valid_tone(ctrl->value)) { | |
+ retval = -EINVAL; | |
+ FMDERR("%s: tone value is not valid\n", __func__); | |
+ goto END; | |
+ } | |
+ saved_val = radio->tone_freq; | |
+ radio->tone_freq = ctrl->value; | |
+ retval = radio_hci_request(radio->fm_hdev, | |
+ hci_fm_tone_generator, arg, | |
+ msecs_to_jiffies(RADIO_HCI_TIMEOUT)); | |
+ if (retval < 0) { | |
+ FMDERR("Error while setting the tone %d", retval); | |
+ radio->tone_freq = saved_val; | |
+ } | |
+ break; | |
+ case V4L2_CID_AUDIO_VOLUME: | |
+ break; | |
+ case V4L2_CID_AUDIO_MUTE: | |
+ if (!is_valid_hard_mute(ctrl->value)) { | |
+ retval = -EINVAL; | |
+ FMDERR("%s: hard mute value is not valid\n", __func__); | |
+ goto END; | |
+ } | |
+ saved_val = radio->mute_mode.hard_mute; | |
+ radio->mute_mode.hard_mute = ctrl->value; | |
+ retval = hci_set_fm_mute_mode( | |
+ &radio->mute_mode, | |
+ radio->fm_hdev); | |
+ if (retval < 0) { | |
+ FMDERR("Error while set FM hard mute"" %d\n", | |
+ retval); | |
+ radio->mute_mode.hard_mute = saved_val; | |
+ } | |
+ break; | |
+ case V4L2_CID_PRIVATE_IRIS_SRCHMODE: | |
+ if (is_valid_srch_mode(ctrl->value)) { | |
+ radio->g_search_mode = ctrl->value; | |
+ } else { | |
+ FMDERR("%s: srch mode is not valid\n", __func__); | |
+ retval = -EINVAL; | |
+ goto END; | |
+ } | |
+ break; | |
+ case V4L2_CID_PRIVATE_IRIS_SCANDWELL: | |
+ if (is_valid_scan_dwell_prd(ctrl->value)) { | |
+ radio->g_scan_time = ctrl->value; | |
+ } else { | |
+ FMDERR("%s: scandwell period is not valid\n", __func__); | |
+ retval = -EINVAL; | |
+ goto END; | |
+ } | |
+ break; | |
+ case V4L2_CID_PRIVATE_IRIS_SRCHON: | |
+ iris_search(radio, ctrl->value, SRCH_DIR_UP); | |
+ break; | |
+ case V4L2_CID_PRIVATE_IRIS_STATE: | |
+ switch (ctrl->value) { | |
+ case FM_RECV: | |
+ if (is_enable_rx_possible(radio) != 0) { | |
+ FMDERR("%s: fm is not in proper state\n", | |
+ __func__); | |
+ retval = -EINVAL; | |
+ goto END; | |
+ } | |
+ radio->mode = FM_RECV_TURNING_ON; | |
+ retval = hci_cmd(HCI_FM_ENABLE_RECV_CMD, | |
+ radio->fm_hdev); | |
+ if (retval < 0) { | |
+ FMDERR("Error while enabling RECV FM" | |
+ " %d\n", retval); | |
+ radio->mode = FM_OFF; | |
+ goto END; | |
+ } else { | |
+ retval = initialise_recv(radio); | |
+ if (retval < 0) { | |
+ FMDERR("Error while initialising"); | |
+ FMDERR("radio %d\n", retval); | |
+ hci_cmd(HCI_FM_DISABLE_RECV_CMD, | |
+ radio->fm_hdev); | |
+ radio->mode = FM_OFF; | |
+ goto END; | |
+ } | |
+ } | |
+ if (radio->mode == FM_RECV_TURNING_ON) { | |
+ radio->mode = FM_RECV; | |
+ iris_q_event(radio, IRIS_EVT_RADIO_READY); | |
+ } | |
+ break; | |
+ case FM_TRANS: | |
+ if (is_enable_tx_possible(radio) != 0) { | |
+ retval = -EINVAL; | |
+ goto END; | |
+ } | |
+ radio->mode = FM_TRANS_TURNING_ON; | |
+ retval = hci_cmd(HCI_FM_ENABLE_TRANS_CMD, | |
+ radio->fm_hdev); | |
+ if (retval < 0) { | |
+ FMDERR("Error while enabling TRANS FM" | |
+ " %d\n", retval); | |
+ radio->mode = FM_OFF; | |
+ goto END; | |
+ } else { | |
+ retval = initialise_trans(radio); | |
+ if (retval < 0) { | |
+ FMDERR("Error while initialising"); | |
+ FMDERR("radio %d\n", retval); | |
+ hci_cmd(HCI_FM_DISABLE_TRANS_CMD, | |
+ radio->fm_hdev); | |
+ radio->mode = FM_OFF; | |
+ goto END; | |
+ } | |
+ } | |
+ if (radio->mode == FM_TRANS_TURNING_ON) { | |
+ radio->mode = FM_TRANS; | |
+ iris_q_event(radio, IRIS_EVT_RADIO_READY); | |
+ } | |
+ break; | |
+ case FM_OFF: | |
+ radio->spur_table_size = 0; | |
+ switch (radio->mode) { | |
+ case FM_RECV: | |
+ radio->mode = FM_TURNING_OFF; | |
+ retval = hci_cmd(HCI_FM_DISABLE_RECV_CMD, | |
+ radio->fm_hdev); | |
+ if (retval < 0) { | |
+ FMDERR("Err on disable recv FM" | |
+ " %d\n", retval); | |
+ radio->mode = FM_RECV; | |
+ goto END; | |
+ } | |
+ break; | |
+ case FM_TRANS: | |
+ radio->mode = FM_TURNING_OFF; | |
+ retval = hci_cmd(HCI_FM_DISABLE_TRANS_CMD, | |
+ radio->fm_hdev); | |
+ | |
+ if (retval < 0) { | |
+ FMDERR("Err disabling trans FM" | |
+ " %d\n", retval); | |
+ radio->mode = FM_TRANS; | |
+ goto END; | |
+ } | |
+ break; | |
+ default: | |
+ retval = -EINVAL; | |
+ } | |
+ break; | |
+ default: | |
+ retval = -EINVAL; | |
+ } | |
+ break; | |
+ case V4L2_CID_PRIVATE_IRIS_REGION: | |
+ if (radio->mode == FM_RECV) { | |
+ retval = iris_recv_set_region(radio, ctrl->value); | |
+ } else { | |
+ if (radio->mode == FM_TRANS) { | |
+ retval = iris_trans_set_region(radio, | |
+ ctrl->value); | |
+ } else { | |
+ FMDERR("%s: fm is not in proper state\n", | |
+ __func__); | |
+ retval = -EINVAL; | |
+ goto END; | |
+ } | |
+ } | |
+ break; | |
+ case V4L2_CID_PRIVATE_IRIS_SIGNAL_TH: | |
+ if (!is_valid_sig_th(ctrl->value)) { | |
+ retval = -EINVAL; | |
+ FMDERR("%s: sig threshold is not valid\n", __func__); | |
+ goto END; | |
+ } | |
+ temp_val = ctrl->value; | |
+ retval = hci_fm_set_signal_threshold( | |
+ &temp_val, | |
+ radio->fm_hdev); | |
+ if (retval < 0) { | |
+ FMDERR("Error while setting signal threshold\n"); | |
+ goto END; | |
+ } | |
+ break; | |
+ case V4L2_CID_PRIVATE_IRIS_SRCH_PTY: | |
+ if (is_valid_pty(ctrl->value)) { | |
+ radio->srch_rds.srch_pty = ctrl->value; | |
+ radio->srch_st_list.srch_pty = ctrl->value; | |
+ } else { | |
+ FMDERR("%s: pty is not valid\n", __func__); | |
+ retval = -EINVAL; | |
+ goto END; | |
+ } | |
+ break; | |
+ case V4L2_CID_PRIVATE_IRIS_SRCH_PI: | |
+ if (is_valid_pi(ctrl->value)) { | |
+ radio->srch_rds.srch_pi = ctrl->value; | |
+ } else { | |
+ retval = -EINVAL; | |
+ FMDERR("%s: Pi is not valid\n", __func__); | |
+ goto END; | |
+ } | |
+ break; | |
+ case V4L2_CID_PRIVATE_IRIS_SRCH_CNT: | |
+ if (is_valid_srch_station_cnt(ctrl->value)) { | |
+ radio->srch_st_list.srch_list_max = ctrl->value; | |
+ } else { | |
+ retval = -EINVAL; | |
+ FMDERR("%s: srch station count is not valid\n", | |
+ __func__); | |
+ goto END; | |
+ } | |
+ break; | |
+ case V4L2_CID_PRIVATE_IRIS_SPACING: | |
+ if (!is_valid_chan_spacing(ctrl->value)) { | |
+ retval = -EINVAL; | |
+ FMDERR("%s: channel spacing is not valid\n", __func__); | |
+ goto END; | |
+ } | |
+ if (radio->mode == FM_RECV) { | |
+ saved_val = radio->recv_conf.ch_spacing; | |
+ radio->recv_conf.ch_spacing = ctrl->value; | |
+ retval = hci_set_fm_recv_conf( | |
+ &radio->recv_conf, | |
+ radio->fm_hdev); | |
+ if (retval < 0) { | |
+ FMDERR("Error in setting channel spacing"); | |
+ radio->recv_conf.ch_spacing = saved_val; | |
+ goto END; | |
+ } | |
+ } | |
+ break; | |
+ case V4L2_CID_PRIVATE_IRIS_EMPHASIS: | |
+ if (!is_valid_emphasis(ctrl->value)) { | |
+ retval = -EINVAL; | |
+ FMDERR("%s, emphasis is not valid\n", __func__); | |
+ goto END; | |
+ } | |
+ switch (radio->mode) { | |
+ case FM_RECV: | |
+ saved_val = radio->recv_conf.emphasis; | |
+ radio->recv_conf.emphasis = ctrl->value; | |
+ retval = hci_set_fm_recv_conf( | |
+ &radio->recv_conf, | |
+ radio->fm_hdev); | |
+ if (retval < 0) { | |
+ FMDERR("Error in setting emphasis"); | |
+ radio->recv_conf.emphasis = saved_val; | |
+ goto END; | |
+ } | |
+ break; | |
+ case FM_TRANS: | |
+ saved_val = radio->trans_conf.emphasis; | |
+ radio->trans_conf.emphasis = ctrl->value; | |
+ retval = hci_set_fm_trans_conf( | |
+ &radio->trans_conf, | |
+ radio->fm_hdev); | |
+ if (retval < 0) { | |
+ FMDERR("Error in setting emphasis"); | |
+ radio->trans_conf.emphasis = saved_val; | |
+ goto END; | |
+ } | |
+ break; | |
+ default: | |
+ retval = -EINVAL; | |
+ FMDERR("%s, FM is not in proper state\n", __func__); | |
+ goto END; | |
+ break; | |
+ } | |
+ break; | |
+ case V4L2_CID_PRIVATE_IRIS_RDS_STD: | |
+ if (!is_valid_rds_std(ctrl->value)) { | |
+ retval = -EINVAL; | |
+ FMDERR("%s: rds std is not valid\n", __func__); | |
+ goto END; | |
+ } | |
+ switch (radio->mode) { | |
+ case FM_RECV: | |
+ saved_val = radio->recv_conf.rds_std; | |
+ radio->recv_conf.rds_std = ctrl->value; | |
+ retval = hci_set_fm_recv_conf( | |
+ &radio->recv_conf, | |
+ radio->fm_hdev); | |
+ if (retval < 0) { | |
+ FMDERR("Error in rds_std"); | |
+ radio->recv_conf.rds_std = saved_val; | |
+ goto END; | |
+ } | |
+ break; | |
+ case FM_TRANS: | |
+ saved_val = radio->trans_conf.rds_std; | |
+ radio->trans_conf.rds_std = ctrl->value; | |
+ retval = hci_set_fm_trans_conf( | |
+ &radio->trans_conf, | |
+ radio->fm_hdev); | |
+ if (retval < 0) { | |
+ FMDERR("Error in rds_Std"); | |
+ radio->trans_conf.rds_std = saved_val; | |
+ goto END; | |
+ } | |
+ break; | |
+ default: | |
+ retval = -EINVAL; | |
+ FMDERR("%s: fm is not in proper state\n", __func__); | |
+ goto END; | |
+ break; | |
+ } | |
+ break; | |
+ case V4L2_CID_PRIVATE_IRIS_RDSON: | |
+ if (!is_valid_rds_std(ctrl->value)) { | |
+ retval = -EINVAL; | |
+ FMDERR("%s: rds std is not valid\n", __func__); | |
+ goto END; | |
+ } | |
+ switch (radio->mode) { | |
+ case FM_RECV: | |
+ saved_val = radio->recv_conf.rds_std; | |
+ radio->recv_conf.rds_std = ctrl->value; | |
+ retval = hci_set_fm_recv_conf( | |
+ &radio->recv_conf, | |
+ radio->fm_hdev); | |
+ if (retval < 0) { | |
+ FMDERR("Error in rds_std"); | |
+ radio->recv_conf.rds_std = saved_val; | |
+ goto END; | |
+ } | |
+ break; | |
+ case FM_TRANS: | |
+ saved_val = radio->trans_conf.rds_std; | |
+ radio->trans_conf.rds_std = ctrl->value; | |
+ retval = hci_set_fm_trans_conf( | |
+ &radio->trans_conf, | |
+ radio->fm_hdev); | |
+ if (retval < 0) { | |
+ FMDERR("Error in rds_Std"); | |
+ radio->trans_conf.rds_std = saved_val; | |
+ goto END; | |
+ } | |
+ break; | |
+ default: | |
+ retval = -EINVAL; | |
+ FMDERR("%s: fm is not in proper state\n", __func__); | |
+ goto END; | |
+ break; | |
+ } | |
+ break; | |
+ case V4L2_CID_PRIVATE_IRIS_RDSGROUP_MASK: | |
+ saved_val = radio->rds_grp.rds_grp_enable_mask; | |
+ grp_mask = (grp_mask | oda_agt | ctrl->value); | |
+ radio->rds_grp.rds_grp_enable_mask = grp_mask; | |
+ radio->rds_grp.rds_buf_size = 1; | |
+ radio->rds_grp.en_rds_change_filter = 0; | |
+ retval = hci_fm_rds_grp(&radio->rds_grp, radio->fm_hdev); | |
+ if (retval < 0) { | |
+ FMDERR("error in setting group mask\n"); | |
+ radio->rds_grp.rds_grp_enable_mask = saved_val; | |
+ goto END; | |
+ } | |
+ break; | |
+ case V4L2_CID_PRIVATE_IRIS_RDSGROUP_PROC: | |
+ saved_val = radio->g_rds_grp_proc_ps; | |
+ rds_grps_proc = radio->g_rds_grp_proc_ps | ctrl->value; | |
+ radio->g_rds_grp_proc_ps = (rds_grps_proc >> RDS_CONFIG_OFFSET); | |
+ retval = hci_fm_rds_grps_process( | |
+ &radio->g_rds_grp_proc_ps, | |
+ radio->fm_hdev); | |
+ if (retval < 0) { | |
+ radio->g_rds_grp_proc_ps = saved_val; | |
+ goto END; | |
+ } | |
+ break; | |
+ case V4L2_CID_PRIVATE_IRIS_RDSD_BUF: | |
+ radio->rds_grp.rds_buf_size = ctrl->value; | |
+ break; | |
+ case V4L2_CID_PRIVATE_IRIS_PSALL: | |
+ saved_val = radio->g_rds_grp_proc_ps; | |
+ rds_grps_proc = (ctrl->value << RDS_CONFIG_OFFSET); | |
+ radio->g_rds_grp_proc_ps |= rds_grps_proc; | |
+ retval = hci_fm_rds_grps_process( | |
+ &radio->g_rds_grp_proc_ps, | |
+ radio->fm_hdev); | |
+ if (retval < 0) { | |
+ radio->g_rds_grp_proc_ps = saved_val; | |
+ goto END; | |
+ } | |
+ break; | |
+ case V4L2_CID_PRIVATE_IRIS_AF_JUMP: | |
+ saved_val = radio->g_rds_grp_proc_ps; | |
+ /*Clear the current AF jump settings*/ | |
+ radio->g_rds_grp_proc_ps &= ~(1 << RDS_AF_JUMP_OFFSET); | |
+ radio->af_jump_bit = ctrl->value; | |
+ rds_grps_proc = 0x00; | |
+ rds_grps_proc = (ctrl->value << RDS_AF_JUMP_OFFSET); | |
+ radio->g_rds_grp_proc_ps |= rds_grps_proc; | |
+ retval = hci_fm_rds_grps_process( | |
+ &radio->g_rds_grp_proc_ps, | |
+ radio->fm_hdev); | |
+ if (retval < 0) { | |
+ radio->g_rds_grp_proc_ps = saved_val; | |
+ goto END; | |
+ } | |
+ break; | |
+ case V4L2_CID_PRIVATE_IRIS_LP_MODE: | |
+ set_low_power_mode(radio, ctrl->value); | |
+ break; | |
+ case V4L2_CID_PRIVATE_IRIS_ANTENNA: | |
+ if (!is_valid_antenna(ctrl->value)) { | |
+ retval = -EINVAL; | |
+ FMDERR("%s: antenna type is not valid\n", __func__); | |
+ goto END; | |
+ } | |
+ temp_val = ctrl->value; | |
+ retval = hci_fm_set_antenna(&temp_val, radio->fm_hdev); | |
+ if (retval < 0) { | |
+ FMDERR("Set Antenna failed retval = %x", retval); | |
+ goto END; | |
+ } | |
+ radio->g_antenna = ctrl->value; | |
+ break; | |
+ case V4L2_CID_RDS_TX_PTY: | |
+ if (is_valid_pty(ctrl->value)) { | |
+ radio->pty = ctrl->value; | |
+ } else { | |
+ retval = -EINVAL; | |
+ FMDERR("%s: pty is not valid\n", __func__); | |
+ goto END; | |
+ } | |
+ break; | |
+ case V4L2_CID_RDS_TX_PI: | |
+ if (is_valid_pi(ctrl->value)) { | |
+ radio->pi = ctrl->value; | |
+ } else { | |
+ retval = -EINVAL; | |
+ FMDERR("%s: pi is not valid\n", __func__); | |
+ goto END; | |
+ } | |
+ break; | |
+ case V4L2_CID_PRIVATE_IRIS_STOP_RDS_TX_PS_NAME: | |
+ tx_ps.ps_control = 0x00; | |
+ retval = radio_hci_request(radio->fm_hdev, hci_trans_ps_req, | |
+ (unsigned long)&tx_ps, RADIO_HCI_TIMEOUT); | |
+ break; | |
+ case V4L2_CID_PRIVATE_IRIS_STOP_RDS_TX_RT: | |
+ tx_rt.rt_control = 0x00; | |
+ retval = radio_hci_request(radio->fm_hdev, hci_trans_rt_req, | |
+ (unsigned long)&tx_rt, RADIO_HCI_TIMEOUT); | |
+ break; | |
+ case V4L2_CID_PRIVATE_IRIS_TX_SETPSREPEATCOUNT: | |
+ if (is_valid_ps_repeat_cnt(ctrl->value)) { | |
+ radio->ps_repeatcount = ctrl->value; | |
+ } else { | |
+ retval = -EINVAL; | |
+ FMDERR("%s: ps repeat count is not valid\n", __func__); | |
+ goto END; | |
+ } | |
+ break; | |
+ case V4L2_CID_TUNE_POWER_LEVEL: | |
+ if (ctrl->value > FM_TX_PWR_LVL_MAX) | |
+ ctrl->value = FM_TX_PWR_LVL_MAX; | |
+ if (ctrl->value < FM_TX_PWR_LVL_0) | |
+ ctrl->value = FM_TX_PWR_LVL_0; | |
+ rd.mode = FM_TX_PHY_CFG_MODE; | |
+ rd.length = FM_TX_PHY_CFG_LEN; | |
+ rd.param_len = 0x00; | |
+ rd.param = 0x00; | |
+ | |
+ retval = hci_def_data_read(&rd, radio->fm_hdev); | |
+ if (retval < 0) { | |
+ FMDERR("Default data read failed for PHY_CFG %d\n", | |
+ retval); | |
+ goto END; | |
+ } | |
+ memset(&wrd, 0, sizeof(wrd)); | |
+ wrd.mode = FM_TX_PHY_CFG_MODE; | |
+ wrd.length = FM_TX_PHY_CFG_LEN; | |
+ memcpy(&wrd.data, &radio->default_data.data, | |
+ radio->default_data.ret_data_len); | |
+ wrd.data[FM_TX_PWR_GAIN_OFFSET] = | |
+ (ctrl->value) * FM_TX_PWR_LVL_STEP_SIZE; | |
+ retval = hci_def_data_write(&wrd, radio->fm_hdev); | |
+ if (retval < 0) | |
+ FMDERR("Default write failed for PHY_TXGAIN %d\n", | |
+ retval); | |
+ break; | |
+ case V4L2_CID_PRIVATE_IRIS_SOFT_MUTE: | |
+ if (!is_valid_soft_mute(ctrl->value)) { | |
+ retval = -EINVAL; | |
+ FMDERR("%s: soft mute is not valid\n", __func__); | |
+ goto END; | |
+ } | |
+ saved_val = radio->mute_mode.soft_mute; | |
+ radio->mute_mode.soft_mute = ctrl->value; | |
+ retval = hci_set_fm_mute_mode( | |
+ &radio->mute_mode, | |
+ radio->fm_hdev); | |
+ if (retval < 0) { | |
+ FMDERR("Error while setting FM soft mute"" %d\n", | |
+ retval); | |
+ radio->mute_mode.soft_mute = saved_val; | |
+ goto END; | |
+ } | |
+ break; | |
+ case V4L2_CID_PRIVATE_IRIS_RIVA_ACCS_ADDR: | |
+ radio->riva_data_req.cmd_params.start_addr = ctrl->value; | |
+ break; | |
+ case V4L2_CID_PRIVATE_IRIS_RIVA_ACCS_LEN: | |
+ if (is_valid_peek_len(ctrl->value)) { | |
+ radio->riva_data_req.cmd_params.length = ctrl->value; | |
+ } else { | |
+ retval = -EINVAL; | |
+ FMDERR("%s: riva access len is not valid\n", __func__); | |
+ goto END; | |
+ } | |
+ break; | |
+ case V4L2_CID_PRIVATE_IRIS_RIVA_POKE: | |
+ if (radio->riva_data_req.cmd_params.length <= | |
+ MAX_RIVA_PEEK_RSP_SIZE) { | |
+#ifdef CONFIG_COMPAT | |
+ retval = copy_from_user( | |
+ radio->riva_data_req.data, | |
+ (void *)(__s64)ctrl->value, | |
+ radio->riva_data_req.cmd_params.length); | |
+#else | |
+ retval = copy_from_user( | |
+ radio->riva_data_req.data, | |
+ (void *)ctrl->value, | |
+ radio->riva_data_req.cmd_params.length); | |
+#endif | |
+ if (retval != 0) { | |
+ retval = -retval; | |
+ goto END; | |
+ } | |
+ radio->riva_data_req.cmd_params.subopcode = | |
+ RIVA_POKE_OPCODE; | |
+ retval = hci_poke_data( | |
+ &radio->riva_data_req, | |
+ radio->fm_hdev); | |
+ } else { | |
+ FMDERR("Can not copy into driver's buffer.\n"); | |
+ retval = -EINVAL; | |
+ goto END; | |
+ } | |
+ break; | |
+ case V4L2_CID_PRIVATE_IRIS_SSBI_ACCS_ADDR: | |
+ radio->ssbi_data_accs.start_addr = ctrl->value; | |
+ break; | |
+ case V4L2_CID_PRIVATE_IRIS_SSBI_POKE: | |
+ radio->ssbi_data_accs.data = ctrl->value; | |
+ retval = hci_ssbi_poke_reg(&radio->ssbi_data_accs , | |
+ radio->fm_hdev); | |
+ break; | |
+ case V4L2_CID_PRIVATE_IRIS_RIVA_PEEK: | |
+ radio->riva_data_req.cmd_params.subopcode = RIVA_PEEK_OPCODE; | |
+ ctrl->value = hci_peek_data(&radio->riva_data_req.cmd_params , | |
+ radio->fm_hdev); | |
+ break; | |
+ case V4L2_CID_PRIVATE_IRIS_SSBI_PEEK: | |
+ radio->ssbi_peek_reg.start_address = ctrl->value; | |
+ hci_ssbi_peek_reg(&radio->ssbi_peek_reg, radio->fm_hdev); | |
+ break; | |
+ case V4L2_CID_PRIVATE_IRIS_RDS_GRP_COUNTERS: | |
+ if (is_valid_reset_cntr(ctrl->value)) { | |
+ temp_val = ctrl->value; | |
+ hci_read_grp_counters(&temp_val, radio->fm_hdev); | |
+ } else { | |
+ FMDERR("%s: reset counter value is not valid\n", | |
+ __func__); | |
+ retval = -EINVAL; | |
+ goto END; | |
+ } | |
+ break; | |
+ case V4L2_CID_PRIVATE_IRIS_HLSI: | |
+ if (!is_valid_hlsi(ctrl->value)) { | |
+ FMDERR("%s: hlsi value is not valid\n", __func__); | |
+ retval = -EINVAL; | |
+ goto END; | |
+ } | |
+ retval = hci_cmd(HCI_FM_GET_RECV_CONF_CMD, | |
+ radio->fm_hdev); | |
+ if (retval) | |
+ goto END; | |
+ saved_val = radio->recv_conf.hlsi; | |
+ radio->recv_conf.hlsi = ctrl->value; | |
+ retval = hci_set_fm_recv_conf( | |
+ &radio->recv_conf, | |
+ radio->fm_hdev); | |
+ if (retval < 0) | |
+ radio->recv_conf.hlsi = saved_val; | |
+ break; | |
+ case V4L2_CID_PRIVATE_IRIS_SET_NOTCH_FILTER: | |
+ if (is_valid_notch_filter(ctrl->value)) { | |
+ temp_val = ctrl->value; | |
+ retval = hci_set_notch_filter(&temp_val, | |
+ radio->fm_hdev); | |
+ } else { | |
+ FMDERR("%s: notch filter is not valid\n", __func__); | |
+ retval = -EINVAL; | |
+ goto END; | |
+ } | |
+ break; | |
+ case V4L2_CID_PRIVATE_INTF_HIGH_THRESHOLD: | |
+ if (!is_valid_intf_det_hgh_th(ctrl->value)) { | |
+ FMDERR("%s: intf high threshold is not valid\n", | |
+ __func__); | |
+ retval = -EINVAL; | |
+ goto END; | |
+ } | |
+ retval = hci_cmd(HCI_FM_GET_DET_CH_TH_CMD, radio->fm_hdev); | |
+ if (retval < 0) { | |
+ FMDERR("Failed to get chnl det thresholds %d", retval); | |
+ goto END; | |
+ } | |
+ saved_val = radio->ch_det_threshold.high_th; | |
+ radio->ch_det_threshold.high_th = ctrl->value; | |
+ retval = hci_set_ch_det_thresholds_req(&radio->ch_det_threshold, | |
+ radio->fm_hdev); | |
+ if (retval < 0) { | |
+ FMDERR("Failed to set High det threshold %d ", retval); | |
+ radio->ch_det_threshold.high_th = saved_val; | |
+ goto END; | |
+ } | |
+ break; | |
+ | |
+ case V4L2_CID_PRIVATE_INTF_LOW_THRESHOLD: | |
+ if (!is_valid_intf_det_low_th(ctrl->value)) { | |
+ FMDERR("%s: intf det low threshold is not valid\n", | |
+ __func__); | |
+ retval = -EINVAL; | |
+ goto END; | |
+ } | |
+ retval = hci_cmd(HCI_FM_GET_DET_CH_TH_CMD, radio->fm_hdev); | |
+ if (retval < 0) { | |
+ FMDERR("Failed to get chnl det thresholds %d", retval); | |
+ goto END; | |
+ } | |
+ saved_val = radio->ch_det_threshold.low_th; | |
+ radio->ch_det_threshold.low_th = ctrl->value; | |
+ retval = hci_set_ch_det_thresholds_req(&radio->ch_det_threshold, | |
+ radio->fm_hdev); | |
+ if (retval < 0) { | |
+ FMDERR("Failed to Set Low det threshold %d", retval); | |
+ radio->ch_det_threshold.low_th = saved_val; | |
+ goto END; | |
+ } | |
+ break; | |
+ | |
+ case V4L2_CID_PRIVATE_SINR_THRESHOLD: | |
+ if (!is_valid_sinr_th(ctrl->value)) { | |
+ FMDERR("%s: sinr threshold is not valid\n", __func__); | |
+ retval = -EINVAL; | |
+ goto END; | |
+ } | |
+ retval = hci_cmd(HCI_FM_GET_DET_CH_TH_CMD, radio->fm_hdev); | |
+ if (retval < 0) { | |
+ FMDERR("Failed to get chnl det thresholds %d", retval); | |
+ goto END; | |
+ } | |
+ saved_val = radio->ch_det_threshold.sinr; | |
+ radio->ch_det_threshold.sinr = ctrl->value; | |
+ retval = hci_set_ch_det_thresholds_req(&radio->ch_det_threshold, | |
+ radio->fm_hdev); | |
+ if (retval < 0) { | |
+ FMDERR("Failed to set SINR threshold %d", retval); | |
+ radio->ch_det_threshold.sinr = saved_val; | |
+ goto END; | |
+ } | |
+ break; | |
+ | |
+ case V4L2_CID_PRIVATE_SINR_SAMPLES: | |
+ if (!is_valid_sinr_samples(ctrl->value)) { | |
+ FMDERR("%s: sinr samples count is not valid\n", | |
+ __func__); | |
+ retval = -EINVAL; | |
+ goto END; | |
+ } | |
+ retval = hci_cmd(HCI_FM_GET_DET_CH_TH_CMD, radio->fm_hdev); | |
+ if (retval < 0) { | |
+ FMDERR("Failed to get chnl det thresholds %d", retval); | |
+ goto END; | |
+ } | |
+ saved_val = radio->ch_det_threshold.sinr_samples; | |
+ radio->ch_det_threshold.sinr_samples = ctrl->value; | |
+ retval = hci_set_ch_det_thresholds_req(&radio->ch_det_threshold, | |
+ radio->fm_hdev); | |
+ if (retval < 0) { | |
+ FMDERR("Failed to set SINR samples %d", retval); | |
+ radio->ch_det_threshold.sinr_samples = saved_val; | |
+ goto END; | |
+ } | |
+ break; | |
+ | |
+ case V4L2_CID_PRIVATE_IRIS_SRCH_ALGORITHM: | |
+ case V4L2_CID_PRIVATE_IRIS_SET_AUDIO_PATH: | |
+ /* | |
+ These private controls are place holders to keep the | |
+ driver compatible with changes done in the frameworks | |
+ which are specific to TAVARUA. | |
+ */ | |
+ retval = 0; | |
+ break; | |
+ case V4L2_CID_PRIVATE_SPUR_FREQ: | |
+ if (radio->spur_table_size >= MAX_SPUR_FREQ_LIMIT) { | |
+ FMDERR("%s: Spur Table Full!\n", __func__); | |
+ retval = -1; | |
+ } else | |
+ radio->spur_data.freq[radio->spur_table_size] = | |
+ ctrl->value; | |
+ break; | |
+ case V4L2_CID_PRIVATE_SPUR_FREQ_RMSSI: | |
+ if (radio->spur_table_size >= MAX_SPUR_FREQ_LIMIT) { | |
+ FMDERR("%s: Spur Table Full!\n", __func__); | |
+ retval = -1; | |
+ } else | |
+ radio->spur_data.rmssi[radio->spur_table_size] = | |
+ ctrl->value; | |
+ break; | |
+ case V4L2_CID_PRIVATE_SPUR_SELECTION: | |
+ if (radio->spur_table_size >= MAX_SPUR_FREQ_LIMIT) { | |
+ FMDERR("%s: Spur Table Full!\n", __func__); | |
+ retval = -1; | |
+ } else { | |
+ radio->spur_data.enable[radio->spur_table_size] = | |
+ ctrl->value; | |
+ radio->spur_table_size++; | |
+ } | |
+ break; | |
+ case V4L2_CID_PRIVATE_UPDATE_SPUR_TABLE: | |
+ update_spur_table(radio); | |
+ break; | |
+ case V4L2_CID_PRIVATE_VALID_CHANNEL: | |
+ retval = hci_cmd(HCI_FM_GET_DET_CH_TH_CMD, radio->fm_hdev); | |
+ if (retval < 0) { | |
+ FMDERR("%s: Failed to determine channel's validity\n", | |
+ __func__); | |
+ goto END; | |
+ } else { | |
+ sinr_th = radio->ch_det_threshold.sinr; | |
+ intf_det_low_th = radio->ch_det_threshold.low_th; | |
+ intf_det_high_th = radio->ch_det_threshold.high_th; | |
+ } | |
+ if (!is_valid_sinr_th(sinr_th) || | |
+ !is_valid_intf_det_low_th(intf_det_low_th) || | |
+ !is_valid_intf_det_hgh_th(intf_det_high_th)) { | |
+ retval = -EINVAL; | |
+ goto END; | |
+ } | |
+ retval = hci_cmd(HCI_FM_GET_STATION_PARAM_CMD, radio->fm_hdev); | |
+ if (retval < 0) { | |
+ FMDERR("%s: Failed to determine channel's validity\n", | |
+ __func__); | |
+ goto END; | |
+ } else | |
+ sinr = radio->fm_st_rsp.station_rsp.sinr; | |
+ | |
+ retval = hci_cmd(HCI_FM_STATION_DBG_PARAM_CMD, radio->fm_hdev); | |
+ if (retval < 0) { | |
+ FMDERR("%s: Failed to determine channel's validity\n", | |
+ __func__); | |
+ goto END; | |
+ } else | |
+ intf_det_out = radio->st_dbg_param.in_det_out; | |
+ | |
+ if ((sinr >= sinr_th) && (intf_det_out >= intf_det_low_th) && | |
+ (intf_det_out <= intf_det_high_th)) | |
+ radio->is_station_valid = VALID_CHANNEL; | |
+ else | |
+ radio->is_station_valid = INVALID_CHANNEL; | |
+ break; | |
+ case V4L2_CID_PRIVATE_AF_RMSSI_TH: | |
+ rd.mode = FM_RDS_CNFG_MODE; | |
+ rd.length = FM_RDS_CNFG_LEN; | |
+ rd.param_len = 0; | |
+ rd.param = 0; | |
+ | |
+ retval = hci_def_data_read(&rd, radio->fm_hdev); | |
+ if (retval < 0) { | |
+ FMDERR("default data read failed %x", retval); | |
+ goto END; | |
+ } | |
+ wrd.mode = FM_RDS_CNFG_MODE; | |
+ wrd.length = FM_RDS_CNFG_LEN; | |
+ memcpy(&wrd.data, &radio->default_data.data, | |
+ radio->default_data.ret_data_len); | |
+ wrd.data[AF_RMSSI_TH_LSB_OFFSET] = ((ctrl->value) & 255); | |
+ wrd.data[AF_RMSSI_TH_MSB_OFFSET] = ((ctrl->value) >> 8); | |
+ retval = hci_def_data_write(&wrd, radio->fm_hdev); | |
+ if (retval < 0) | |
+ FMDERR("set AF jump RMSSI threshold failed\n"); | |
+ break; | |
+ case V4L2_CID_PRIVATE_AF_RMSSI_SAMPLES: | |
+ rd.mode = FM_RDS_CNFG_MODE; | |
+ rd.length = FM_RDS_CNFG_LEN; | |
+ rd.param_len = 0; | |
+ rd.param = 0; | |
+ | |
+ retval = hci_def_data_read(&rd, radio->fm_hdev); | |
+ if (retval < 0) { | |
+ FMDERR("default data read failed %x", retval); | |
+ goto END; | |
+ } | |
+ wrd.mode = FM_RDS_CNFG_MODE; | |
+ wrd.length = FM_RDS_CNFG_LEN; | |
+ memcpy(&wrd.data, &radio->default_data.data, | |
+ radio->default_data.ret_data_len); | |
+ wrd.data[AF_RMSSI_SAMPLES_OFFSET] = ctrl->value; | |
+ retval = hci_def_data_write(&wrd, radio->fm_hdev); | |
+ if (retval < 0) | |
+ FMDERR("set AF jump RMSSI Samples failed\n"); | |
+ break; | |
+ case V4L2_CID_PRIVATE_GOOD_CH_RMSSI_TH: | |
+ rd.mode = FM_RX_CONFG_MODE; | |
+ rd.length = FM_RX_CNFG_LEN; | |
+ rd.param_len = 0; | |
+ rd.param = 0; | |
+ | |
+ retval = hci_def_data_read(&rd, radio->fm_hdev); | |
+ if (retval < 0) { | |
+ FMDERR("default data read failed %x", retval); | |
+ goto END; | |
+ } | |
+ wrd.mode = FM_RX_CONFG_MODE; | |
+ wrd.length = FM_RX_CNFG_LEN; | |
+ memcpy(&wrd.data, &radio->default_data.data, | |
+ radio->default_data.ret_data_len); | |
+ wrd.data[GD_CH_RMSSI_TH_OFFSET] = ctrl->value; | |
+ retval = hci_def_data_write(&wrd, radio->fm_hdev); | |
+ if (retval < 0) | |
+ FMDERR("set good channel RMSSI th failed\n"); | |
+ break; | |
+ case V4L2_CID_PRIVATE_SRCHALGOTYPE: | |
+ rd.mode = FM_RX_CONFG_MODE; | |
+ rd.length = FM_RX_CNFG_LEN; | |
+ rd.param_len = 0; | |
+ rd.param = 0; | |
+ | |
+ retval = hci_def_data_read(&rd, radio->fm_hdev); | |
+ if (retval < 0) { | |
+ FMDERR("default data read failed %x", retval); | |
+ goto END; | |
+ } | |
+ wrd.mode = FM_RX_CONFG_MODE; | |
+ wrd.length = FM_RX_CNFG_LEN; | |
+ memcpy(&wrd.data, &radio->default_data.data, | |
+ radio->default_data.ret_data_len); | |
+ wrd.data[SRCH_ALGO_TYPE_OFFSET] = ctrl->value; | |
+ retval = hci_def_data_write(&wrd, radio->fm_hdev); | |
+ if (retval < 0) | |
+ FMDERR("set Search Algo Type failed\n"); | |
+ break; | |
+ case V4L2_CID_PRIVATE_SINRFIRSTSTAGE: | |
+ rd.mode = FM_RX_CONFG_MODE; | |
+ rd.length = FM_RX_CNFG_LEN; | |
+ rd.param_len = 0; | |
+ rd.param = 0; | |
+ | |
+ retval = hci_def_data_read(&rd, radio->fm_hdev); | |
+ if (retval < 0) { | |
+ FMDERR("default data read failed %x", retval); | |
+ goto END; | |
+ } | |
+ wrd.mode = FM_RX_CONFG_MODE; | |
+ wrd.length = FM_RX_CNFG_LEN; | |
+ memcpy(&wrd.data, &radio->default_data.data, | |
+ radio->default_data.ret_data_len); | |
+ wrd.data[SINRFIRSTSTAGE_OFFSET] = ctrl->value; | |
+ retval = hci_def_data_write(&wrd, radio->fm_hdev); | |
+ if (retval < 0) | |
+ FMDERR("set SINR First Stage failed\n"); | |
+ break; | |
+ case V4L2_CID_PRIVATE_RMSSIFIRSTSTAGE: | |
+ rd.mode = FM_RX_CONFG_MODE; | |
+ rd.length = FM_RX_CNFG_LEN; | |
+ rd.param_len = 0; | |
+ rd.param = 0; | |
+ | |
+ retval = hci_def_data_read(&rd, radio->fm_hdev); | |
+ if (retval < 0) { | |
+ FMDERR("default data read failed %x", retval); | |
+ goto END; | |
+ } | |
+ wrd.mode = FM_RX_CONFG_MODE; | |
+ wrd.length = FM_RX_CNFG_LEN; | |
+ memcpy(&wrd.data, &radio->default_data.data, | |
+ radio->default_data.ret_data_len); | |
+ wrd.data[RMSSIFIRSTSTAGE_OFFSET] = ctrl->value; | |
+ retval = hci_def_data_write(&wrd, radio->fm_hdev); | |
+ if (retval < 0) | |
+ FMDERR("set RMSSI First Stage failed\n"); | |
+ break; | |
+ case V4L2_CID_PRIVATE_CF0TH12: | |
+ rd.mode = FM_RX_CONFG_MODE; | |
+ rd.length = FM_RX_CNFG_LEN; | |
+ rd.param_len = 0; | |
+ rd.param = 0; | |
+ | |
+ retval = hci_def_data_read(&rd, radio->fm_hdev); | |
+ if (retval < 0) { | |
+ FMDERR("default data read failed %x", retval); | |
+ goto END; | |
+ } | |
+ wrd.mode = FM_RX_CONFG_MODE; | |
+ wrd.length = FM_RX_CNFG_LEN; | |
+ memcpy(&wrd.data, &radio->default_data.data, | |
+ radio->default_data.ret_data_len); | |
+ wrd.data[CF0TH12_BYTE1_OFFSET] = (ctrl->value & 255); | |
+ wrd.data[CF0TH12_BYTE2_OFFSET] = ((ctrl->value >> 8) & 255); | |
+ wrd.data[CF0TH12_BYTE3_OFFSET] = ((ctrl->value >> 16) & 255); | |
+ wrd.data[CF0TH12_BYTE4_OFFSET] = ((ctrl->value >> 24) & 255); | |
+ retval = hci_def_data_write(&wrd, radio->fm_hdev); | |
+ if (retval < 0) | |
+ FMDERR("set CF0 Threshold failed\n"); | |
+ break; | |
+ case V4L2_CID_PRIVATE_RXREPEATCOUNT: | |
+ rd.mode = RDS_PS0_XFR_MODE; | |
+ rd.length = RDS_PS0_LEN; | |
+ rd.param_len = 0; | |
+ rd.param = 0; | |
+ | |
+ retval = hci_def_data_read(&rd, radio->fm_hdev); | |
+ if (retval < 0) { | |
+ FMDERR("default data read failed for PS0 %x", retval); | |
+ goto END; | |
+ } | |
+ wrd.mode = RDS_PS0_XFR_MODE; | |
+ wrd.length = RDS_PS0_LEN; | |
+ memcpy(&wrd.data, &radio->default_data.data, | |
+ radio->default_data.ret_data_len); | |
+ wrd.data[RX_REPEATE_BYTE_OFFSET] = ctrl->value; | |
+ | |
+ retval = hci_def_data_write(&wrd, radio->fm_hdev); | |
+ if (retval < 0) | |
+ FMDERR("set RxRePeat count failed\n"); | |
+ break; | |
+ case V4L2_CID_PRIVATE_IRIS_GET_SPUR_TBL: | |
+ spur_freq = ctrl->value; | |
+ retval = radio_hci_request(radio->fm_hdev, | |
+ hci_fm_get_spur_tbl_data, | |
+ (unsigned long)spur_freq, | |
+ RADIO_HCI_TIMEOUT); | |
+ if (retval < 0) | |
+ FMDERR("get Spur data failed\n"); | |
+ break; | |
+ case V4L2_CID_PRIVATE_BLEND_SINRHI: | |
+ if (!is_valid_blend_value(ctrl->value)) { | |
+ FMDERR("%s: blend sinr count is not valid\n", | |
+ __func__); | |
+ retval = -EINVAL; | |
+ goto END; | |
+ } | |
+ retval = hci_cmd(HCI_FM_GET_BLND_TBL_CMD, radio->fm_hdev); | |
+ if (retval < 0) { | |
+ FMDERR("Failed to get blend table %d", retval); | |
+ goto END; | |
+ } | |
+ radio->blend_tbl.scBlendSinrHi = ctrl->value; | |
+ retval = hci_set_blend_tbl_req(&radio->blend_tbl, | |
+ radio->fm_hdev); | |
+ if (retval < 0) { | |
+ FMDERR("Failed to set blend tble %d ", retval); | |
+ goto END; | |
+ } | |
+ break; | |
+ case V4L2_CID_PRIVATE_BLEND_RMSSIHI: | |
+ if (!is_valid_blend_value(ctrl->value)) { | |
+ FMDERR("%s: blend rmssi count is not valid\n", | |
+ __func__); | |
+ retval = -EINVAL; | |
+ goto END; | |
+ } | |
+ retval = hci_cmd(HCI_FM_GET_BLND_TBL_CMD, radio->fm_hdev); | |
+ if (retval < 0) { | |
+ FMDERR("Failed to get blend table %d", retval); | |
+ goto END; | |
+ } | |
+ radio->blend_tbl.scBlendRmssiHi = ctrl->value; | |
+ retval = hci_set_blend_tbl_req(&radio->blend_tbl, | |
+ radio->fm_hdev); | |
+ if (retval < 0) { | |
+ FMDERR("Failed to set blend tble %d ", retval); | |
+ goto END; | |
+ } | |
+ break; | |
+ default: | |
+ retval = -EINVAL; | |
+ break; | |
+ } | |
+ | |
+END: | |
+ if (retval > 0) | |
+ retval = -EINVAL; | |
+ | |
+ return retval; | |
+} | |
+ | |
+static int update_spur_table(struct iris_device *radio) | |
+{ | |
+ struct hci_fm_def_data_wr_req default_data; | |
+ int len = 0, index = 0, offset = 0, i = 0; | |
+ int retval = 0, temp = 0, cnt = 0; | |
+ | |
+ memset(&default_data, 0, sizeof(default_data)); | |
+ | |
+ /* Pass the mode of SPUR_CLK */ | |
+ default_data.mode = CKK_SPUR; | |
+ | |
+ if (unlikely(radio == NULL)) { | |
+ FMDERR(":radio is null"); | |
+ return -EINVAL; | |
+ } | |
+ temp = radio->spur_table_size; | |
+ for (cnt = 0; cnt < (temp / 5); cnt++) { | |
+ offset = 0; | |
+ /* | |
+ * Program the spur entries in spur table in following order: | |
+ * Spur index | |
+ * Length of the spur data | |
+ * Spur Data: | |
+ * MSB of the spur frequency | |
+ * LSB of the spur frequency | |
+ * Enable/Disable the spur frequency | |
+ * RMSSI value of the spur frequency | |
+ */ | |
+ default_data.data[offset++] = ENTRY_0 + cnt; | |
+ for (i = 0; i < SPUR_ENTRIES_PER_ID; i++) { | |
+ default_data.data[offset++] = GET_FREQ(COMPUTE_SPUR( | |
+ radio->spur_data.freq[index]), 0); | |
+ default_data.data[offset++] = GET_FREQ(COMPUTE_SPUR( | |
+ radio->spur_data.freq[index]), 1); | |
+ default_data.data[offset++] = | |
+ radio->spur_data.enable[index]; | |
+ default_data.data[offset++] = | |
+ radio->spur_data.rmssi[index]; | |
+ index++; | |
+ } | |
+ len = (SPUR_ENTRIES_PER_ID * SPUR_DATA_SIZE); | |
+ default_data.length = (len + 1); | |
+ retval = hci_def_data_write(&default_data, radio->fm_hdev); | |
+ if (retval < 0) { | |
+ FMDBG("%s: Failed to configure entries for ID : %d\n", | |
+ __func__, default_data.data[0]); | |
+ return retval; | |
+ } | |
+ } | |
+ | |
+ /* Compute balance SPUR frequencies to be programmed */ | |
+ temp %= SPUR_ENTRIES_PER_ID; | |
+ if (temp > 0) { | |
+ offset = 0; | |
+ default_data.data[offset++] = (radio->spur_table_size / 5); | |
+ for (i = 0; i < temp; i++) { | |
+ default_data.data[offset++] = GET_FREQ(COMPUTE_SPUR( | |
+ radio->spur_data.freq[index]), 0); | |
+ default_data.data[offset++] = GET_FREQ(COMPUTE_SPUR( | |
+ radio->spur_data.freq[index]), 1); | |
+ default_data.data[offset++] = | |
+ radio->spur_data.enable[index]; | |
+ default_data.data[offset++] = | |
+ radio->spur_data.rmssi[index]; | |
+ index++; | |
+ } | |
+ len = (temp * SPUR_DATA_SIZE); | |
+ default_data.length = (len + 1); | |
+ retval = hci_def_data_write(&default_data, radio->fm_hdev); | |
+ if (retval < 0) { | |
+ FMDERR("%s: Failed to configure entries for ID : %d\n", | |
+ __func__, default_data.data[0]); | |
+ return retval; | |
+ } | |
+ } | |
+ | |
+ return retval; | |
+} | |
+ | |
+static int iris_vidioc_g_tuner(struct file *file, void *priv, | |
+ struct v4l2_tuner *tuner) | |
+{ | |
+ int retval; | |
+ struct iris_device *radio = video_get_drvdata(video_devdata(file)); | |
+ | |
+ if (unlikely(radio == NULL)) { | |
+ FMDERR(":radio is null"); | |
+ return -EINVAL; | |
+ } | |
+ if (tuner == NULL) { | |
+ FMDERR("%s, tuner is null\n", __func__); | |
+ return -EINVAL; | |
+ } | |
+ if (tuner->index > 0) { | |
+ FMDERR("Invalid Tuner Index"); | |
+ return -EINVAL; | |
+ } | |
+ if (radio->mode == FM_RECV) { | |
+ retval = hci_cmd(HCI_FM_GET_STATION_PARAM_CMD, radio->fm_hdev); | |
+ if (retval < 0) { | |
+ FMDERR("Failed to Get station params"); | |
+ return retval; | |
+ } | |
+ tuner->type = V4L2_TUNER_RADIO; | |
+ tuner->rangelow = | |
+ radio->recv_conf.band_low_limit * TUNE_PARAM; | |
+ tuner->rangehigh = | |
+ radio->recv_conf.band_high_limit * TUNE_PARAM; | |
+ tuner->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; | |
+ tuner->capability = V4L2_TUNER_CAP_LOW; | |
+ tuner->signal = radio->fm_st_rsp.station_rsp.rssi; | |
+ tuner->audmode = radio->fm_st_rsp.station_rsp.stereo_prg; | |
+ tuner->afc = 0; | |
+ } else if (radio->mode == FM_TRANS) { | |
+ retval = hci_cmd(HCI_FM_GET_TX_CONFIG, radio->fm_hdev); | |
+ if (retval < 0) { | |
+ FMDERR("get Tx config failed %d\n", retval); | |
+ return retval; | |
+ } else { | |
+ tuner->type = V4L2_TUNER_RADIO; | |
+ tuner->rangelow = | |
+ radio->trans_conf.band_low_limit * TUNE_PARAM; | |
+ tuner->rangehigh = | |
+ radio->trans_conf.band_high_limit * TUNE_PARAM; | |
+ } | |
+ | |
+ } else | |
+ return -EINVAL; | |
+ return 0; | |
+} | |
+ | |
+static int iris_vidioc_s_tuner(struct file *file, void *priv, | |
+ const struct v4l2_tuner *tuner) | |
+{ | |
+ struct iris_device *radio = video_get_drvdata(video_devdata(file)); | |
+ int retval = 0; | |
+ | |
+ if (unlikely(radio == NULL)) { | |
+ FMDERR(":radio is null"); | |
+ return -EINVAL; | |
+ } | |
+ | |
+ if (tuner == NULL) { | |
+ FMDERR("%s, tuner is null\n", __func__); | |
+ return -EINVAL; | |
+ } | |
+ | |
+ if (tuner->index > 0) | |
+ return -EINVAL; | |
+ | |
+ if (radio->mode == FM_RECV) { | |
+ radio->recv_conf.band_low_limit = tuner->rangelow / TUNE_PARAM; | |
+ radio->recv_conf.band_high_limit = | |
+ tuner->rangehigh / TUNE_PARAM; | |
+ if (tuner->audmode == V4L2_TUNER_MODE_MONO) { | |
+ radio->stereo_mode.stereo_mode = 0x01; | |
+ retval = hci_set_fm_stereo_mode( | |
+ &radio->stereo_mode, | |
+ radio->fm_hdev); | |
+ } else { | |
+ radio->stereo_mode.stereo_mode = 0x00; | |
+ retval = hci_set_fm_stereo_mode( | |
+ &radio->stereo_mode, | |
+ radio->fm_hdev); | |
+ } | |
+ if (retval < 0) | |
+ FMDERR(": set tuner failed with %d\n", retval); | |
+ return retval; | |
+ } else if (radio->mode == FM_TRANS) { | |
+ radio->trans_conf.band_low_limit = | |
+ tuner->rangelow / TUNE_PARAM; | |
+ radio->trans_conf.band_high_limit = | |
+ tuner->rangehigh / TUNE_PARAM; | |
+ } else | |
+ return -EINVAL; | |
+ | |
+ return retval; | |
+} | |
+ | |
+static int iris_vidioc_g_frequency(struct file *file, void *priv, | |
+ struct v4l2_frequency *freq) | |
+{ | |
+ struct iris_device *radio = video_get_drvdata(video_devdata(file)); | |
+ if ((freq != NULL) && (radio != NULL)) { | |
+ freq->frequency = | |
+ radio->fm_st_rsp.station_rsp.station_freq * TUNE_PARAM; | |
+ } else | |
+ return -EINVAL; | |
+ return 0; | |
+} | |
+ | |
+static int iris_vidioc_s_frequency(struct file *file, void *priv, | |
+ const struct v4l2_frequency *freq) | |
+{ | |
+ struct iris_device *radio = video_get_drvdata(video_devdata(file)); | |
+ int retval = -1; | |
+ u32 f; | |
+ | |
+ if (freq == NULL) { | |
+ FMDERR("%s, v4l2 freq is null\n", __func__); | |
+ return -EINVAL; | |
+ } | |
+ f = (freq->frequency / TUNE_PARAM); | |
+ | |
+ if (unlikely(radio == NULL)) { | |
+ FMDERR(":radio is null"); | |
+ return -EINVAL; | |
+ } | |
+ if (freq->type != V4L2_TUNER_RADIO) | |
+ return -EINVAL; | |
+ | |
+ /* We turn off RDS prior to tuning to a new station. | |
+ because of a bug in SoC which prevents tuning | |
+ during RDS transmission. | |
+ */ | |
+ if (radio->mode == FM_TRANS | |
+ && (radio->trans_conf.rds_std == 0 || | |
+ radio->trans_conf.rds_std == 1)) { | |
+ radio->prev_trans_rds = radio->trans_conf.rds_std; | |
+ radio->trans_conf.rds_std = 2; | |
+ hci_set_fm_trans_conf(&radio->trans_conf, | |
+ radio->fm_hdev); | |
+ } | |
+ | |
+ retval = iris_set_freq(radio, f); | |
+ | |
+ if (radio->mode == FM_TRANS | |
+ && radio->trans_conf.rds_std == 2 | |
+ && (radio->prev_trans_rds == 1 | |
+ || radio->prev_trans_rds == 0)) { | |
+ radio->trans_conf.rds_std = radio->prev_trans_rds; | |
+ hci_set_fm_trans_conf(&radio->trans_conf, | |
+ radio->fm_hdev); | |
+ } | |
+ | |
+ if (retval < 0) | |
+ FMDERR(" set frequency failed with %d\n", retval); | |
+ return retval; | |
+} | |
+ | |
+static int iris_fops_release(struct file *file) | |
+{ | |
+ struct iris_device *radio = video_get_drvdata(video_devdata(file)); | |
+ int retval = 0; | |
+ | |
+ FMDBG("Enter %s ", __func__); | |
+ if (radio == NULL) | |
+ return -EINVAL; | |
+ | |
+ if (radio->mode == FM_OFF) | |
+ return 0; | |
+ | |
+ if (radio->mode == FM_RECV) { | |
+ radio->mode = FM_OFF; | |
+ retval = hci_cmd(HCI_FM_DISABLE_RECV_CMD, | |
+ radio->fm_hdev); | |
+ } else if (radio->mode == FM_TRANS) { | |
+ radio->mode = FM_OFF; | |
+ retval = hci_cmd(HCI_FM_DISABLE_TRANS_CMD, | |
+ radio->fm_hdev); | |
+ } | |
+ if (retval < 0) | |
+ FMDERR("Err on disable FM %d\n", retval); | |
+ | |
+ return retval; | |
+} | |
+ | |
+static int iris_vidioc_dqbuf(struct file *file, void *priv, | |
+ struct v4l2_buffer *buffer) | |
+{ | |
+ struct iris_device *radio = video_get_drvdata(video_devdata(file)); | |
+ enum iris_buf_t buf_type = -1; | |
+ unsigned char buf_fifo[STD_BUF_SIZE] = {0}; | |
+ struct kfifo *data_fifo = NULL; | |
+ unsigned char *buf = NULL; | |
+ unsigned int len = 0, retval = -1; | |
+ | |
+ if ((radio == NULL) || (buffer == NULL)) { | |
+ FMDERR("radio/buffer is NULL\n"); | |
+ return -ENXIO; | |
+ } | |
+ buf_type = buffer->index; | |
+ buf = (unsigned char *)buffer->m.userptr; | |
+ len = buffer->length; | |
+ | |
+ if ((buf_type < IRIS_BUF_MAX) && (buf_type >= 0)) { | |
+ data_fifo = &radio->data_buf[buf_type]; | |
+ if (buf_type == IRIS_BUF_EVENTS) | |
+ if (wait_event_interruptible(radio->event_queue, | |
+ kfifo_len(data_fifo)) < 0) | |
+ return -EINTR; | |
+ } else { | |
+ FMDERR("invalid buffer type\n"); | |
+ return -EINVAL; | |
+ } | |
+ if (len <= STD_BUF_SIZE) { | |
+ buffer->bytesused = kfifo_out_locked(data_fifo, &buf_fifo[0], | |
+ len, &radio->buf_lock[buf_type]); | |
+ } else { | |
+ FMDERR("kfifo_out_locked can not use len more than 128\n"); | |
+ return -EINVAL; | |
+ } | |
+ retval = copy_to_user(buf, &buf_fifo[0], buffer->bytesused); | |
+ if (retval > 0) { | |
+ FMDERR("Failed to copy %d bytes of data\n", retval); | |
+ return -EAGAIN; | |
+ } | |
+ | |
+ return retval; | |
+} | |
+ | |
+static int iris_vidioc_g_fmt_type_private(struct file *file, void *priv, | |
+ struct v4l2_format *f) | |
+{ | |
+ return 0; | |
+ | |
+} | |
+ | |
+static int iris_vidioc_s_hw_freq_seek(struct file *file, void *priv, | |
+ const struct v4l2_hw_freq_seek *seek) | |
+{ | |
+ struct iris_device *radio = video_get_drvdata(video_devdata(file)); | |
+ int dir; | |
+ | |
+ if (seek == NULL) { | |
+ FMDERR("%s, v4l2_hw_freq_seek is null\n", __func__); | |
+ return -EINVAL; | |
+ } | |
+ if (seek->seek_upward) | |
+ dir = SRCH_DIR_UP; | |
+ else | |
+ dir = SRCH_DIR_DOWN; | |
+ return iris_search(radio, CTRL_ON, dir); | |
+} | |
+ | |
+static int iris_vidioc_querycap(struct file *file, void *priv, | |
+ struct v4l2_capability *capability) | |
+{ | |
+ struct iris_device *radio; | |
+ radio = video_get_drvdata(video_devdata(file)); | |
+ | |
+ if (unlikely(radio == NULL)) { | |
+ FMDERR(":radio is null"); | |
+ return -EINVAL; | |
+ } | |
+ if (capability == NULL) { | |
+ FMDERR("%s, capability struct is null\n", __func__); | |
+ return -EINVAL; | |
+ } | |
+ strlcpy(capability->driver, DRIVER_NAME, sizeof(capability->driver)); | |
+ strlcpy(capability->card, DRIVER_CARD, sizeof(capability->card)); | |
+ | |
+ strlcpy(radio->g_cap.driver, DRIVER_NAME, sizeof(radio->g_cap.driver)); | |
+ strlcpy(radio->g_cap.card, DRIVER_CARD, sizeof(radio->g_cap.card)); | |
+ | |
+ radio->g_cap.capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO; | |
+ capability->capabilities = radio->g_cap.capabilities; | |
+ return 0; | |
+} | |
+ | |
+static int initialise_recv(struct iris_device *radio) | |
+{ | |
+ int retval; | |
+ | |
+ if (unlikely(radio == NULL)) { | |
+ FMDERR(":radio is null"); | |
+ return -EINVAL; | |
+ } | |
+ | |
+ radio->mute_mode.soft_mute = CTRL_ON; | |
+ retval = hci_set_fm_mute_mode(&radio->mute_mode, | |
+ radio->fm_hdev); | |
+ | |
+ if (retval < 0) { | |
+ FMDERR("Failed to enable Smute\n"); | |
+ return retval; | |
+ } | |
+ | |
+ radio->stereo_mode.stereo_mode = CTRL_OFF; | |
+ radio->stereo_mode.sig_blend = sig_blend; | |
+ radio->stereo_mode.intf_blend = CTRL_ON; | |
+ radio->stereo_mode.most_switch = CTRL_ON; | |
+ retval = hci_set_fm_stereo_mode(&radio->stereo_mode, | |
+ radio->fm_hdev); | |
+ | |
+ if (retval < 0) { | |
+ FMDERR("Failed to set stereo mode\n"); | |
+ return retval; | |
+ } | |
+ | |
+ radio->event_mask = SIG_LEVEL_INTR | RDS_SYNC_INTR | AUDIO_CTRL_INTR; | |
+ retval = hci_conf_event_mask(&radio->event_mask, radio->fm_hdev); | |
+ if (retval < 0) { | |
+ FMDERR("Enable Async events failed"); | |
+ return retval; | |
+ } | |
+ | |
+ retval = hci_cmd(HCI_FM_GET_RECV_CONF_CMD, radio->fm_hdev); | |
+ if (retval < 0) | |
+ FMDERR("Failed to get the Recv Config\n"); | |
+ return retval; | |
+} | |
+ | |
+static int initialise_trans(struct iris_device *radio) | |
+{ | |
+ | |
+ int retval; | |
+ | |
+ if (unlikely(radio == NULL)) { | |
+ FMDERR(":radio is null"); | |
+ return -EINVAL; | |
+ } | |
+ | |
+ retval = hci_cmd(HCI_FM_GET_TX_CONFIG, radio->fm_hdev); | |
+ if (retval < 0) | |
+ FMDERR("get frequency failed %d\n", retval); | |
+ | |
+ return retval; | |
+} | |
+ | |
+static int is_enable_rx_possible(struct iris_device *radio) | |
+{ | |
+ int retval = 1; | |
+ | |
+ if (unlikely(radio == NULL)) { | |
+ FMDERR(":radio is null"); | |
+ return -EINVAL; | |
+ } | |
+ | |
+ if (radio->mode == FM_OFF || radio->mode == FM_RECV) | |
+ retval = 0; | |
+ | |
+ return retval; | |
+} | |
+ | |
+static int is_enable_tx_possible(struct iris_device *radio) | |
+{ | |
+ int retval = 1; | |
+ | |
+ if (radio->mode == FM_OFF || radio->mode == FM_TRANS) | |
+ retval = 0; | |
+ | |
+ return retval; | |
+} | |
+ | |
+static const struct v4l2_ioctl_ops iris_ioctl_ops = { | |
+ .vidioc_querycap = iris_vidioc_querycap, | |
+ .vidioc_queryctrl = iris_vidioc_queryctrl, | |
+ .vidioc_g_ctrl = iris_vidioc_g_ctrl, | |
+ .vidioc_s_ctrl = iris_vidioc_s_ctrl, | |
+ .vidioc_g_tuner = iris_vidioc_g_tuner, | |
+ .vidioc_s_tuner = iris_vidioc_s_tuner, | |
+ .vidioc_g_frequency = iris_vidioc_g_frequency, | |
+ .vidioc_s_frequency = iris_vidioc_s_frequency, | |
+ .vidioc_s_hw_freq_seek = iris_vidioc_s_hw_freq_seek, | |
+ .vidioc_dqbuf = iris_vidioc_dqbuf, | |
+ .vidioc_g_fmt_type_private = iris_vidioc_g_fmt_type_private, | |
+ .vidioc_s_ext_ctrls = iris_vidioc_s_ext_ctrls, | |
+ .vidioc_g_ext_ctrls = iris_vidioc_g_ext_ctrls, | |
+}; | |
+ | |
+static const struct v4l2_file_operations iris_fops = { | |
+ .owner = THIS_MODULE, | |
+ .unlocked_ioctl = video_ioctl2, | |
+#ifdef CONFIG_COMPAT | |
+ .compat_ioctl32 = v4l2_compat_ioctl32, | |
+#endif | |
+ .release = iris_fops_release, | |
+}; | |
+ | |
+static struct video_device iris_viddev_template = { | |
+ .fops = &iris_fops, | |
+ .ioctl_ops = &iris_ioctl_ops, | |
+ .name = DRIVER_NAME, | |
+ .release = video_device_release, | |
+}; | |
+ | |
+static struct video_device *video_get_dev(void) | |
+{ | |
+ return priv_videodev; | |
+} | |
+ | |
+static int __init iris_probe(struct platform_device *pdev) | |
+{ | |
+ struct iris_device *radio; | |
+ int retval; | |
+ int radio_nr = -1; | |
+ int i; | |
+ | |
+ if (!pdev) { | |
+ FMDERR(": pdev is null\n"); | |
+ return -ENOMEM; | |
+ } | |
+ | |
+ radio = kzalloc(sizeof(struct iris_device), GFP_KERNEL); | |
+ if (!radio) { | |
+ FMDERR(": Could not allocate radio device\n"); | |
+ return -ENOMEM; | |
+ } | |
+ | |
+ radio->dev = &pdev->dev; | |
+ platform_set_drvdata(pdev, radio); | |
+ | |
+ radio->videodev = video_device_alloc(); | |
+ if (!radio->videodev) { | |
+ FMDERR(": Could not allocate V4L device\n"); | |
+ kfree(radio); | |
+ return -ENOMEM; | |
+ } | |
+ | |
+ memcpy(radio->videodev, &iris_viddev_template, | |
+ sizeof(iris_viddev_template)); | |
+ | |
+ for (i = 0; i < IRIS_BUF_MAX; i++) { | |
+ int kfifo_alloc_rc = 0; | |
+ spin_lock_init(&radio->buf_lock[i]); | |
+ | |
+ if ((i == IRIS_BUF_RAW_RDS) || (i == IRIS_BUF_PEEK)) | |
+ kfifo_alloc_rc = kfifo_alloc(&radio->data_buf[i], | |
+ rds_buf*3, GFP_KERNEL); | |
+ else if ((i == IRIS_BUF_CAL_DATA) || (i == IRIS_BUF_RT_RDS)) | |
+ kfifo_alloc_rc = kfifo_alloc(&radio->data_buf[i], | |
+ STD_BUF_SIZE*2, GFP_KERNEL); | |
+ else | |
+ kfifo_alloc_rc = kfifo_alloc(&radio->data_buf[i], | |
+ STD_BUF_SIZE, GFP_KERNEL); | |
+ | |
+ if (kfifo_alloc_rc != 0) { | |
+ FMDERR("failed allocating buffers %d\n", | |
+ kfifo_alloc_rc); | |
+ for (; i > -1; i--) | |
+ kfifo_free(&radio->data_buf[i]); | |
+ video_device_release(radio->videodev); | |
+ kfree(radio); | |
+ return -ENOMEM; | |
+ } | |
+ } | |
+ | |
+ mutex_init(&radio->lock); | |
+ init_completion(&radio->sync_xfr_start); | |
+ radio->tune_req = 0; | |
+ radio->prev_trans_rds = 2; | |
+ init_waitqueue_head(&radio->event_queue); | |
+ init_waitqueue_head(&radio->read_queue); | |
+ | |
+ video_set_drvdata(radio->videodev, radio); | |
+ | |
+ if (NULL == video_get_drvdata(radio->videodev)) | |
+ FMDERR(": video_get_drvdata failed\n"); | |
+ | |
+ retval = video_register_device(radio->videodev, VFL_TYPE_RADIO, | |
+ radio_nr); | |
+ if (retval) { | |
+ FMDERR(": Could not register video device\n"); | |
+ video_device_release(radio->videodev); | |
+ for (; i > -1; i--) | |
+ kfifo_free(&radio->data_buf[i]); | |
+ kfree(radio); | |
+ return retval; | |
+ } else { | |
+ priv_videodev = kzalloc(sizeof(struct video_device), | |
+ GFP_KERNEL); | |
+ if (priv_videodev != NULL) { | |
+ memcpy(priv_videodev, radio->videodev, | |
+ sizeof(struct video_device)); | |
+ } else { | |
+ video_unregister_device(radio->videodev); | |
+ video_device_release(radio->videodev); | |
+ for (; i > -1; i--) | |
+ kfifo_free(&radio->data_buf[i]); | |
+ kfree(radio); | |
+ } | |
+ } | |
+ return 0; | |
+} | |
+ | |
+ | |
+static int iris_remove(struct platform_device *pdev) | |
+{ | |
+ int i; | |
+ struct iris_device *radio = platform_get_drvdata(pdev); | |
+ | |
+ if (radio == NULL) { | |
+ FMDERR(":radio is null"); | |
+ return -EINVAL; | |
+ } | |
+ video_unregister_device(radio->videodev); | |
+ | |
+ for (i = 0; i < IRIS_BUF_MAX; i++) | |
+ kfifo_free(&radio->data_buf[i]); | |
+ | |
+ kfree(radio); | |
+ | |
+ platform_set_drvdata(pdev, NULL); | |
+ | |
+ return 0; | |
+} | |
+ | |
+static const struct of_device_id iris_fm_match[] = { | |
+ {.compatible = "qcom,iris_fm"}, | |
+ {} | |
+}; | |
+ | |
+static struct platform_driver iris_driver = { | |
+ .driver = { | |
+ .owner = THIS_MODULE, | |
+ .name = "iris_fm", | |
+ .of_match_table = iris_fm_match, | |
+ }, | |
+ .remove = iris_remove, | |
+}; | |
+ | |
+static int __init iris_radio_init(void) | |
+{ | |
+ return platform_driver_probe(&iris_driver, iris_probe); | |
+} | |
+module_init(iris_radio_init); | |
+ | |
+static void __exit iris_radio_exit(void) | |
+{ | |
+ platform_driver_unregister(&iris_driver); | |
+} | |
+module_exit(iris_radio_exit); | |
+ | |
+MODULE_LICENSE("GPL v2"); | |
+MODULE_AUTHOR(DRIVER_AUTHOR); | |
+MODULE_DESCRIPTION(DRIVER_DESC); | |
diff --git a/include/uapi/media/radio-iris-commands.h b/include/uapi/media/radio-iris-commands.h | |
new file mode 100644 | |
index 000000000000..08a09f06321e | |
--- /dev/null | |
+++ b/include/uapi/media/radio-iris-commands.h | |
@@ -0,0 +1,114 @@ | |
+#ifndef __RADIO_IRIS_COMMANDS_H | |
+#define __RADIO_IRIS_COMMANDS_H | |
+ | |
+#include <linux/skbuff.h> | |
+#include <linux/interrupt.h> | |
+#include <linux/mutex.h> | |
+#include <linux/atomic.h> | |
+ | |
+enum v4l2_cid_private_iris_t { | |
+ V4L2_CID_PRIVATE_IRIS_SRCHMODE = (0x08000000 + 1), | |
+ V4L2_CID_PRIVATE_IRIS_SCANDWELL, | |
+ V4L2_CID_PRIVATE_IRIS_SRCHON, | |
+ V4L2_CID_PRIVATE_IRIS_STATE, | |
+ V4L2_CID_PRIVATE_IRIS_TRANSMIT_MODE, | |
+ V4L2_CID_PRIVATE_IRIS_RDSGROUP_MASK, | |
+ V4L2_CID_PRIVATE_IRIS_REGION, | |
+ V4L2_CID_PRIVATE_IRIS_SIGNAL_TH, | |
+ V4L2_CID_PRIVATE_IRIS_SRCH_PTY, | |
+ V4L2_CID_PRIVATE_IRIS_SRCH_PI, | |
+ V4L2_CID_PRIVATE_IRIS_SRCH_CNT, | |
+ V4L2_CID_PRIVATE_IRIS_EMPHASIS, | |
+ V4L2_CID_PRIVATE_IRIS_RDS_STD, | |
+ V4L2_CID_PRIVATE_IRIS_SPACING, | |
+ V4L2_CID_PRIVATE_IRIS_RDSON, | |
+ V4L2_CID_PRIVATE_IRIS_RDSGROUP_PROC, | |
+ V4L2_CID_PRIVATE_IRIS_LP_MODE, | |
+ V4L2_CID_PRIVATE_IRIS_ANTENNA, | |
+ V4L2_CID_PRIVATE_IRIS_RDSD_BUF, | |
+ V4L2_CID_PRIVATE_IRIS_PSALL, /*0x8000014*/ | |
+ | |
+ /*v4l2 Tx controls*/ | |
+ V4L2_CID_PRIVATE_IRIS_TX_SETPSREPEATCOUNT, | |
+ V4L2_CID_PRIVATE_IRIS_STOP_RDS_TX_PS_NAME, | |
+ V4L2_CID_PRIVATE_IRIS_STOP_RDS_TX_RT, | |
+ V4L2_CID_PRIVATE_IRIS_IOVERC, | |
+ V4L2_CID_PRIVATE_IRIS_INTDET, | |
+ V4L2_CID_PRIVATE_IRIS_MPX_DCC, | |
+ V4L2_CID_PRIVATE_IRIS_AF_JUMP, | |
+ V4L2_CID_PRIVATE_IRIS_RSSI_DELTA, | |
+ V4L2_CID_PRIVATE_IRIS_HLSI, /*0x800001d*/ | |
+ | |
+ /*Diagnostic commands*/ | |
+ V4L2_CID_PRIVATE_IRIS_SOFT_MUTE, | |
+ V4L2_CID_PRIVATE_IRIS_RIVA_ACCS_ADDR, | |
+ V4L2_CID_PRIVATE_IRIS_RIVA_ACCS_LEN, | |
+ V4L2_CID_PRIVATE_IRIS_RIVA_PEEK, | |
+ V4L2_CID_PRIVATE_IRIS_RIVA_POKE, | |
+ V4L2_CID_PRIVATE_IRIS_SSBI_ACCS_ADDR, | |
+ V4L2_CID_PRIVATE_IRIS_SSBI_PEEK, | |
+ V4L2_CID_PRIVATE_IRIS_SSBI_POKE, | |
+ V4L2_CID_PRIVATE_IRIS_TX_TONE, | |
+ V4L2_CID_PRIVATE_IRIS_RDS_GRP_COUNTERS, | |
+ V4L2_CID_PRIVATE_IRIS_SET_NOTCH_FILTER, /* 0x8000028 */ | |
+ V4L2_CID_PRIVATE_IRIS_SET_AUDIO_PATH, /* TAVARUA specific command */ | |
+ V4L2_CID_PRIVATE_IRIS_DO_CALIBRATION, | |
+ V4L2_CID_PRIVATE_IRIS_SRCH_ALGORITHM, /* TAVARUA specific command */ | |
+ V4L2_CID_PRIVATE_IRIS_GET_SINR, | |
+ V4L2_CID_PRIVATE_INTF_LOW_THRESHOLD, | |
+ V4L2_CID_PRIVATE_INTF_HIGH_THRESHOLD, | |
+ V4L2_CID_PRIVATE_SINR_THRESHOLD, | |
+ V4L2_CID_PRIVATE_SINR_SAMPLES, | |
+ V4L2_CID_PRIVATE_SPUR_FREQ, | |
+ V4L2_CID_PRIVATE_SPUR_FREQ_RMSSI, | |
+ V4L2_CID_PRIVATE_SPUR_SELECTION, | |
+ V4L2_CID_PRIVATE_UPDATE_SPUR_TABLE, | |
+ V4L2_CID_PRIVATE_VALID_CHANNEL, | |
+ V4L2_CID_PRIVATE_AF_RMSSI_TH, | |
+ V4L2_CID_PRIVATE_AF_RMSSI_SAMPLES, | |
+ V4L2_CID_PRIVATE_GOOD_CH_RMSSI_TH, | |
+ V4L2_CID_PRIVATE_SRCHALGOTYPE, | |
+ V4L2_CID_PRIVATE_CF0TH12, | |
+ V4L2_CID_PRIVATE_SINRFIRSTSTAGE, | |
+ V4L2_CID_PRIVATE_RMSSIFIRSTSTAGE, | |
+ V4L2_CID_PRIVATE_RXREPEATCOUNT, | |
+ V4L2_CID_PRIVATE_IRIS_RSSI_TH, | |
+ V4L2_CID_PRIVATE_IRIS_AF_JUMP_RSSI_TH, | |
+ V4L2_CID_PRIVATE_BLEND_SINRHI, | |
+ V4L2_CID_PRIVATE_BLEND_RMSSIHI, | |
+ | |
+ /*using private CIDs under userclass*/ | |
+ V4L2_CID_PRIVATE_IRIS_READ_DEFAULT = 0x00980928, | |
+ V4L2_CID_PRIVATE_IRIS_WRITE_DEFAULT, | |
+ V4L2_CID_PRIVATE_IRIS_SET_CALIBRATION, | |
+ V4L2_CID_PRIVATE_IRIS_SET_SPURTABLE = 0x0098092D, | |
+ V4L2_CID_PRIVATE_IRIS_GET_SPUR_TBL = 0x0098092E, | |
+}; | |
+ | |
+enum iris_evt_t { | |
+ IRIS_EVT_RADIO_READY, | |
+ IRIS_EVT_TUNE_SUCC, | |
+ IRIS_EVT_SEEK_COMPLETE, | |
+ IRIS_EVT_SCAN_NEXT, | |
+ IRIS_EVT_NEW_RAW_RDS, | |
+ IRIS_EVT_NEW_RT_RDS, | |
+ IRIS_EVT_NEW_PS_RDS, | |
+ IRIS_EVT_ERROR, | |
+ IRIS_EVT_BELOW_TH, | |
+ IRIS_EVT_ABOVE_TH, | |
+ IRIS_EVT_STEREO, | |
+ IRIS_EVT_MONO, | |
+ IRIS_EVT_RDS_AVAIL, | |
+ IRIS_EVT_RDS_NOT_AVAIL, | |
+ IRIS_EVT_NEW_SRCH_LIST, | |
+ IRIS_EVT_NEW_AF_LIST, | |
+ IRIS_EVT_TXRDSDAT, | |
+ IRIS_EVT_TXRDSDONE, | |
+ IRIS_EVT_RADIO_DISABLED, | |
+ IRIS_EVT_NEW_ODA, | |
+ IRIS_EVT_NEW_RT_PLUS, | |
+ IRIS_EVT_NEW_ERT, | |
+ IRIS_EVT_SPUR_TBL, | |
+}; | |
+#endif /* __RADIO_IRIS_COMMANDS_H */ | |
+ | |
diff --git a/include/uapi/media/radio-iris.h b/include/uapi/media/radio-iris.h | |
new file mode 100644 | |
index 000000000000..03ae828724be | |
--- /dev/null | |
+++ b/include/uapi/media/radio-iris.h | |
@@ -0,0 +1,1121 @@ | |
+/* | |
+ * | |
+ * Copyright (c) 2011-2014 The Linux Foundation. All rights reserved. | |
+ * | |
+ * This file is based on include/net/bluetooth/hci_core.h | |
+ * | |
+ * Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com> | |
+ * | |
+ * This program is free software; you can redistribute it and/or modify | |
+ * it under the terms of the GNU General Public License version 2 as | |
+ * published by the Free Software Foundation; | |
+ * | |
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS | |
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. | |
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY | |
+ * CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES | |
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
+ * | |
+ * ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, | |
+ * COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS | |
+ * SOFTWARE IS DISCLAIMED. | |
+ */ | |
+ | |
+#ifndef __UAPI_RADIO_HCI_CORE_H | |
+#define __UAPI_RADIO_HCI_CORE_H | |
+ | |
+#include <linux/skbuff.h> | |
+#include <linux/interrupt.h> | |
+#include <linux/mutex.h> | |
+#include <linux/atomic.h> | |
+#include <media/radio-iris-commands.h> | |
+ | |
+const unsigned char MIN_TX_TONE_VAL = 0x00; | |
+const unsigned char MAX_TX_TONE_VAL = 0x07; | |
+const unsigned char MIN_HARD_MUTE_VAL = 0x00; | |
+const unsigned char MAX_HARD_MUTE_VAL = 0x03; | |
+const unsigned char MIN_SRCH_MODE = 0x00; | |
+const unsigned char MAX_SRCH_MODE = 0x09; | |
+const unsigned char MIN_SCAN_DWELL = 0x00; | |
+const unsigned char MAX_SCAN_DWELL = 0x0F; | |
+const unsigned char MIN_SIG_TH = 0x00; | |
+const unsigned char MAX_SIG_TH = 0x03; | |
+const unsigned char MIN_PTY = 0X00; | |
+const unsigned char MAX_PTY = 0x1F; | |
+const unsigned short MIN_PI = 0x0000; | |
+const unsigned short MAX_PI = 0xFFFF; | |
+const unsigned char MIN_SRCH_STATIONS_CNT = 0x00; | |
+const unsigned char MAX_SRCH_STATIONS_CNT = 0x14; | |
+const unsigned char MIN_CHAN_SPACING = 0x00; | |
+const unsigned char MAX_CHAN_SPACING = 0x02; | |
+const unsigned char MIN_EMPHASIS = 0x00; | |
+const unsigned char MAX_EMPHASIS = 0x01; | |
+const unsigned char MIN_RDS_STD = 0x00; | |
+const unsigned char MAX_RDS_STD = 0x02; | |
+const unsigned char MIN_ANTENNA_VAL = 0x00; | |
+const unsigned char MAX_ANTENNA_VAL = 0x01; | |
+const unsigned char MIN_TX_PS_REPEAT_CNT = 0x01; | |
+const unsigned char MAX_TX_PS_REPEAT_CNT = 0x0F; | |
+const unsigned char MIN_SOFT_MUTE = 0x00; | |
+const unsigned char MAX_SOFT_MUTE = 0x01; | |
+const unsigned char MIN_PEEK_ACCESS_LEN = 0x01; | |
+const unsigned char MAX_PEEK_ACCESS_LEN = 0xF9; | |
+const unsigned char MIN_RESET_CNTR = 0x00; | |
+const unsigned char MAX_RESET_CNTR = 0x01; | |
+const unsigned char MIN_HLSI = 0x00; | |
+const unsigned char MAX_HLSI = 0x02; | |
+const unsigned char MIN_NOTCH_FILTER = 0x00; | |
+const unsigned char MAX_NOTCH_FILTER = 0x02; | |
+const unsigned char MIN_INTF_DET_OUT_LW_TH = 0x00; | |
+const unsigned char MAX_INTF_DET_OUT_LW_TH = 0xFF; | |
+const unsigned char MIN_INTF_DET_OUT_HG_TH = 0x00; | |
+const unsigned char MAX_INTF_DET_OUT_HG_TH = 0xFF; | |
+const signed char MIN_SINR_TH = -128; | |
+const signed char MAX_SINR_TH = 127; | |
+const unsigned char MIN_SINR_SAMPLES = 0x01; | |
+const unsigned char MAX_SINR_SAMPLES = 0xFF; | |
+const signed char MIN_BLEND_HI = -128; | |
+const signed char MAX_BLEND_HI = 127; | |
+ | |
+ | |
+/* ---- HCI Packet structures ---- */ | |
+#define RADIO_HCI_COMMAND_HDR_SIZE sizeof(struct radio_hci_command_hdr) | |
+#define RADIO_HCI_EVENT_HDR_SIZE sizeof(struct radio_hci_event_hdr) | |
+ | |
+/* HCI data types */ | |
+#define RADIO_HCI_COMMAND_PKT 0x11 | |
+#define RADIO_HCI_EVENT_PKT 0x14 | |
+/*HCI reponce packets*/ | |
+#define MAX_RIVA_PEEK_RSP_SIZE 251 | |
+/* default data access */ | |
+#define DEFAULT_DATA_OFFSET 2 | |
+#define DEFAULT_DATA_SIZE 249 | |
+/* Power levels are 0-7, but SOC will expect values from 0-255 | |
+ * So the each level step size will be 255/7 = 36 */ | |
+#define FM_TX_PWR_LVL_STEP_SIZE 36 | |
+#define FM_TX_PWR_LVL_0 0 /* Lowest power lvl that can be set for Tx */ | |
+#define FM_TX_PWR_LVL_MAX 7 /* Max power lvl for Tx */ | |
+#define FM_TX_PHY_CFG_MODE 0x3c | |
+#define FM_TX_PHY_CFG_LEN 0x10 | |
+#define FM_TX_PWR_GAIN_OFFSET 14 | |
+/**RDS CONFIG MODE**/ | |
+#define FM_RDS_CNFG_MODE 0x0f | |
+#define FM_RDS_CNFG_LEN 0x10 | |
+#define AF_RMSSI_TH_LSB_OFFSET 10 | |
+#define AF_RMSSI_TH_MSB_OFFSET 11 | |
+#define AF_RMSSI_SAMPLES_OFFSET 15 | |
+/**RX CONFIG MODE**/ | |
+#define FM_RX_CONFG_MODE 0x15 | |
+#define FM_RX_CNFG_LEN 0x20 | |
+#define GD_CH_RMSSI_TH_OFFSET 12 | |
+#define MAX_GD_CH_RMSSI_TH 127 | |
+#define SRCH_ALGO_TYPE_OFFSET 25 | |
+#define SINRFIRSTSTAGE_OFFSET 26 | |
+#define RMSSIFIRSTSTAGE_OFFSET 27 | |
+#define CF0TH12_BYTE1_OFFSET 8 | |
+#define CF0TH12_BYTE2_OFFSET 9 | |
+#define CF0TH12_BYTE3_OFFSET 10 | |
+#define CF0TH12_BYTE4_OFFSET 11 | |
+#define MAX_SINR_FIRSTSTAGE 127 | |
+#define MAX_RMSSI_FIRSTSTAGE 127 | |
+#define RDS_PS0_XFR_MODE 0x01 | |
+#define RDS_PS0_LEN 6 | |
+#define RX_REPEATE_BYTE_OFFSET 5 | |
+#define FM_SPUR_TBL_SIZE 240 | |
+#define SPUR_DATA_LEN 16 | |
+#define ENTRIES_EACH_CMD 15 | |
+#define SPUR_DATA_INDEX 2 | |
+#define FM_AF_LIST_MAX_SIZE 200 | |
+#define AF_LIST_MAX (FM_AF_LIST_MAX_SIZE / 4) /* Each AF frequency consist | |
+ of sizeof(int) bytes */ | |
+#define MAX_BLEND_INDEX 49 | |
+/* HCI timeouts */ | |
+#define RADIO_HCI_TIMEOUT (10000) /* 10 seconds */ | |
+ | |
+#define TUNE_PARAM 16 | |
+struct radio_hci_command_hdr { | |
+ __le16 opcode; /* OCF & OGF */ | |
+ __u8 plen; | |
+} __packed; | |
+ | |
+struct radio_hci_event_hdr { | |
+ __u8 evt; | |
+ __u8 plen; | |
+} __packed; | |
+ | |
+struct radio_hci_dev { | |
+ char name[8]; | |
+ unsigned long flags; | |
+ __u16 id; | |
+ __u8 bus; | |
+ __u8 dev_type; | |
+ __u8 dev_name[248]; | |
+ __u8 dev_class[3]; | |
+ __u8 features[8]; | |
+ __u8 commands[64]; | |
+ | |
+ unsigned int data_block_len; | |
+ unsigned long cmd_last_tx; | |
+ | |
+ struct sk_buff *sent_cmd; | |
+ | |
+ __u32 req_status; | |
+ __u32 req_result; | |
+ atomic_t cmd_cnt; | |
+ | |
+ struct tasklet_struct cmd_task; | |
+ struct tasklet_struct rx_task; | |
+ struct tasklet_struct tx_task; | |
+ | |
+ struct sk_buff_head rx_q; | |
+ struct sk_buff_head raw_q; | |
+ struct sk_buff_head cmd_q; | |
+ | |
+ struct mutex req_lock; | |
+ wait_queue_head_t req_wait_q; | |
+ | |
+ int (*open)(struct radio_hci_dev *hdev); | |
+ int (*close)(struct radio_hci_dev *hdev); | |
+ int (*flush)(struct radio_hci_dev *hdev); | |
+ int (*send)(struct sk_buff *skb); | |
+ void (*destruct)(struct radio_hci_dev *hdev); | |
+ void (*notify)(struct radio_hci_dev *hdev, unsigned int evt); | |
+}; | |
+ | |
+int radio_hci_register_dev(struct radio_hci_dev *hdev); | |
+int radio_hci_unregister_dev(struct radio_hci_dev *hdev); | |
+int radio_hci_recv_frame(struct sk_buff *skb); | |
+int radio_hci_send_cmd(struct radio_hci_dev *hdev, __u16 opcode, __u32 plen, | |
+ void *param); | |
+void radio_hci_event_packet(struct radio_hci_dev *hdev, struct sk_buff *skb); | |
+ | |
+/* Opcode OCF */ | |
+/* HCI recv control commands opcode */ | |
+#define HCI_OCF_FM_ENABLE_RECV_REQ 0x0001 | |
+#define HCI_OCF_FM_DISABLE_RECV_REQ 0x0002 | |
+#define HCI_OCF_FM_GET_RECV_CONF_REQ 0x0003 | |
+#define HCI_OCF_FM_SET_RECV_CONF_REQ 0x0004 | |
+#define HCI_OCF_FM_SET_MUTE_MODE_REQ 0x0005 | |
+#define HCI_OCF_FM_SET_STEREO_MODE_REQ 0x0006 | |
+#define HCI_OCF_FM_SET_ANTENNA 0x0007 | |
+#define HCI_OCF_FM_SET_SIGNAL_THRESHOLD 0x0008 | |
+#define HCI_OCF_FM_GET_SIGNAL_THRESHOLD 0x0009 | |
+#define HCI_OCF_FM_GET_STATION_PARAM_REQ 0x000A | |
+#define HCI_OCF_FM_GET_PROGRAM_SERVICE_REQ 0x000B | |
+#define HCI_OCF_FM_GET_RADIO_TEXT_REQ 0x000C | |
+#define HCI_OCF_FM_GET_AF_LIST_REQ 0x000D | |
+#define HCI_OCF_FM_SEARCH_STATIONS 0x000E | |
+#define HCI_OCF_FM_SEARCH_RDS_STATIONS 0x000F | |
+#define HCI_OCF_FM_SEARCH_STATIONS_LIST 0x0010 | |
+#define HCI_OCF_FM_CANCEL_SEARCH 0x0011 | |
+#define HCI_OCF_FM_RDS_GRP 0x0012 | |
+#define HCI_OCF_FM_RDS_GRP_PROCESS 0x0013 | |
+#define HCI_OCF_FM_EN_WAN_AVD_CTRL 0x0014 | |
+#define HCI_OCF_FM_EN_NOTCH_CTRL 0x0015 | |
+#define HCI_OCF_FM_SET_EVENT_MASK 0x0016 | |
+#define HCI_OCF_FM_SET_CH_DET_THRESHOLD 0x0017 | |
+#define HCI_OCF_FM_GET_CH_DET_THRESHOLD 0x0018 | |
+#define HCI_OCF_FM_SET_BLND_TBL 0x001B | |
+#define HCI_OCF_FM_GET_BLND_TBL 0x001C | |
+/* HCI trans control commans opcode*/ | |
+#define HCI_OCF_FM_ENABLE_TRANS_REQ 0x0001 | |
+#define HCI_OCF_FM_DISABLE_TRANS_REQ 0x0002 | |
+#define HCI_OCF_FM_GET_TRANS_CONF_REQ 0x0003 | |
+#define HCI_OCF_FM_SET_TRANS_CONF_REQ 0x0004 | |
+#define HCI_OCF_FM_RDS_RT_REQ 0x0008 | |
+#define HCI_OCF_FM_RDS_PS_REQ 0x0009 | |
+ | |
+ | |
+/* HCI common control commands opcode */ | |
+#define HCI_OCF_FM_TUNE_STATION_REQ 0x0001 | |
+#define HCI_OCF_FM_DEFAULT_DATA_READ 0x0002 | |
+#define HCI_OCF_FM_DEFAULT_DATA_WRITE 0x0003 | |
+#define HCI_OCF_FM_RESET 0x0004 | |
+#define HCI_OCF_FM_GET_FEATURE_LIST 0x0005 | |
+#define HCI_OCF_FM_DO_CALIBRATION 0x0006 | |
+#define HCI_OCF_FM_SET_CALIBRATION 0x0007 | |
+#define HCI_OCF_FM_SET_SPUR_TABLE 0x0008 | |
+#define HCI_OCF_FM_GET_SPUR_TABLE 0x0009 | |
+ | |
+/*HCI Status parameters commands*/ | |
+#define HCI_OCF_FM_READ_GRP_COUNTERS 0x0001 | |
+ | |
+/*HCI Diagnostic commands*/ | |
+#define HCI_OCF_FM_PEEK_DATA 0x0002 | |
+#define HCI_OCF_FM_POKE_DATA 0x0003 | |
+#define HCI_OCF_FM_SSBI_PEEK_REG 0x0004 | |
+#define HCI_OCF_FM_SSBI_POKE_REG 0x0005 | |
+#define HCI_OCF_FM_STATION_DBG_PARAM 0x0007 | |
+#define HCI_FM_SET_INTERNAL_TONE_GENRATOR 0x0008 | |
+ | |
+/* Opcode OGF */ | |
+#define HCI_OGF_FM_RECV_CTRL_CMD_REQ 0x0013 | |
+#define HCI_OGF_FM_TRANS_CTRL_CMD_REQ 0x0014 | |
+#define HCI_OGF_FM_COMMON_CTRL_CMD_REQ 0x0015 | |
+#define HCI_OGF_FM_STATUS_PARAMETERS_CMD_REQ 0x0016 | |
+#define HCI_OGF_FM_TEST_CMD_REQ 0x0017 | |
+#define HCI_OGF_FM_DIAGNOSTIC_CMD_REQ 0x003F | |
+ | |
+/* Command opcode pack/unpack */ | |
+#define hci_opcode_pack(ogf, ocf) (__u16) ((ocf & 0x03ff)|(ogf << 10)) | |
+#define hci_opcode_ogf(op) (op >> 10) | |
+#define hci_opcode_ocf(op) (op & 0x03ff) | |
+#define hci_recv_ctrl_cmd_op_pack(ocf) \ | |
+ (__u16) hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ, ocf) | |
+#define hci_trans_ctrl_cmd_op_pack(ocf) \ | |
+ (__u16) hci_opcode_pack(HCI_OGF_FM_TRANS_CTRL_CMD_REQ, ocf) | |
+#define hci_common_cmd_op_pack(ocf) \ | |
+ (__u16) hci_opcode_pack(HCI_OGF_FM_COMMON_CTRL_CMD_REQ, ocf) | |
+#define hci_status_param_op_pack(ocf) \ | |
+ (__u16) hci_opcode_pack(HCI_OGF_FM_STATUS_PARAMETERS_CMD_REQ, ocf) | |
+#define hci_diagnostic_cmd_op_pack(ocf) \ | |
+ (__u16) hci_opcode_pack(HCI_OGF_FM_DIAGNOSTIC_CMD_REQ, ocf) | |
+ | |
+ | |
+/* HCI commands with no arguments*/ | |
+#define HCI_FM_ENABLE_RECV_CMD 1 | |
+#define HCI_FM_DISABLE_RECV_CMD 2 | |
+#define HCI_FM_GET_RECV_CONF_CMD 3 | |
+#define HCI_FM_GET_STATION_PARAM_CMD 4 | |
+#define HCI_FM_GET_SIGNAL_TH_CMD 5 | |
+#define HCI_FM_GET_PROGRAM_SERVICE_CMD 6 | |
+#define HCI_FM_GET_RADIO_TEXT_CMD 7 | |
+#define HCI_FM_GET_AF_LIST_CMD 8 | |
+#define HCI_FM_CANCEL_SEARCH_CMD 9 | |
+#define HCI_FM_RESET_CMD 10 | |
+#define HCI_FM_GET_FEATURES_CMD 11 | |
+#define HCI_FM_STATION_DBG_PARAM_CMD 12 | |
+#define HCI_FM_ENABLE_TRANS_CMD 13 | |
+#define HCI_FM_DISABLE_TRANS_CMD 14 | |
+#define HCI_FM_GET_TX_CONFIG 15 | |
+#define HCI_FM_GET_DET_CH_TH_CMD 16 | |
+#define HCI_FM_GET_BLND_TBL_CMD 17 | |
+ | |
+/* Defines for FM TX*/ | |
+#define TX_PS_DATA_LENGTH 108 | |
+#define TX_RT_DATA_LENGTH 64 | |
+#define PS_STRING_LEN 9 | |
+ | |
+/* ----- HCI Command request ----- */ | |
+struct hci_fm_recv_conf_req { | |
+ __u8 emphasis; | |
+ __u8 ch_spacing; | |
+ __u8 rds_std; | |
+ __u8 hlsi; | |
+ __u32 band_low_limit; | |
+ __u32 band_high_limit; | |
+} __packed; | |
+ | |
+/* ----- HCI Command request ----- */ | |
+struct hci_fm_trans_conf_req_struct { | |
+ __u8 emphasis; | |
+ __u8 rds_std; | |
+ __u32 band_low_limit; | |
+ __u32 band_high_limit; | |
+} __packed; | |
+ | |
+ | |
+/* ----- HCI Command request ----- */ | |
+struct hci_fm_tx_ps { | |
+ __u8 ps_control; | |
+ __u16 pi; | |
+ __u8 pty; | |
+ __u8 ps_repeatcount; | |
+ __u8 ps_num; | |
+ __u8 ps_data[TX_PS_DATA_LENGTH]; | |
+} __packed; | |
+ | |
+struct hci_fm_tx_rt { | |
+ __u8 rt_control; | |
+ __u16 pi; | |
+ __u8 pty; | |
+ __u8 rt_len; | |
+ __u8 rt_data[TX_RT_DATA_LENGTH]; | |
+} __packed; | |
+ | |
+struct hci_fm_mute_mode_req { | |
+ __u8 hard_mute; | |
+ __u8 soft_mute; | |
+} __packed; | |
+ | |
+struct hci_fm_stereo_mode_req { | |
+ __u8 stereo_mode; | |
+ __u8 sig_blend; | |
+ __u8 intf_blend; | |
+ __u8 most_switch; | |
+} __packed; | |
+ | |
+struct hci_fm_search_station_req { | |
+ __u8 srch_mode; | |
+ __u8 scan_time; | |
+ __u8 srch_dir; | |
+} __packed; | |
+ | |
+struct hci_fm_search_rds_station_req { | |
+ struct hci_fm_search_station_req srch_station; | |
+ __u8 srch_pty; | |
+ __u16 srch_pi; | |
+} __packed; | |
+ | |
+struct hci_fm_search_station_list_req { | |
+ __u8 srch_list_mode; | |
+ __u8 srch_list_dir; | |
+ __u32 srch_list_max; | |
+ __u8 srch_pty; | |
+} __packed; | |
+ | |
+struct hci_fm_rds_grp_req { | |
+ __u32 rds_grp_enable_mask; | |
+ __u32 rds_buf_size; | |
+ __u8 en_rds_change_filter; | |
+} __packed; | |
+ | |
+struct hci_fm_en_avd_ctrl_req { | |
+ __u8 no_freqs; | |
+ __u8 freq_index; | |
+ __u8 lo_shft; | |
+ __u16 freq_min; | |
+ __u16 freq_max; | |
+} __packed; | |
+ | |
+struct hci_fm_def_data_rd_req { | |
+ __u8 mode; | |
+ __u8 length; | |
+ __u8 param_len; | |
+ __u8 param; | |
+} __packed; | |
+ | |
+struct hci_fm_def_data_wr_req { | |
+ __u8 mode; | |
+ __u8 length; | |
+ __u8 data[DEFAULT_DATA_SIZE]; | |
+} __packed; | |
+ | |
+struct hci_fm_riva_data { | |
+ __u8 subopcode; | |
+ __u32 start_addr; | |
+ __u8 length; | |
+} __packed; | |
+ | |
+struct hci_fm_riva_poke { | |
+ struct hci_fm_riva_data cmd_params; | |
+ __u8 data[MAX_RIVA_PEEK_RSP_SIZE]; | |
+} __packed; | |
+ | |
+struct hci_fm_ssbi_req { | |
+ __u16 start_addr; | |
+ __u8 data; | |
+} __packed; | |
+struct hci_fm_ssbi_peek { | |
+ __u16 start_address; | |
+} __packed; | |
+ | |
+struct hci_fm_ch_det_threshold { | |
+ char sinr; | |
+ __u8 sinr_samples; | |
+ __u8 low_th; | |
+ __u8 high_th; | |
+ | |
+} __packed; | |
+ | |
+struct hci_fm_blend_table { | |
+ __u8 ucBlendType; | |
+ __u8 ucBlendRampRateUp; | |
+ __u8 ucBlendDebounceNumSampleUp; | |
+ __u8 ucBlendDebounceIdxUp; | |
+ __u8 ucBlendSinrIdxSkipStep; | |
+ __u8 scBlendSinrHi; | |
+ __u8 scBlendRmssiHi; | |
+ __u8 ucBlendIndexHi; | |
+ __u8 ucBlendIndex[MAX_BLEND_INDEX]; | |
+} __packed; | |
+ | |
+/*HCI events*/ | |
+#define HCI_EV_TUNE_STATUS 0x01 | |
+#define HCI_EV_RDS_LOCK_STATUS 0x02 | |
+#define HCI_EV_STEREO_STATUS 0x03 | |
+#define HCI_EV_SERVICE_AVAILABLE 0x04 | |
+#define HCI_EV_SEARCH_PROGRESS 0x05 | |
+#define HCI_EV_SEARCH_RDS_PROGRESS 0x06 | |
+#define HCI_EV_SEARCH_LIST_PROGRESS 0x07 | |
+#define HCI_EV_RDS_RX_DATA 0x08 | |
+#define HCI_EV_PROGRAM_SERVICE 0x09 | |
+#define HCI_EV_RADIO_TEXT 0x0A | |
+#define HCI_EV_FM_AF_LIST 0x0B | |
+#define HCI_EV_TX_RDS_GRP_AVBLE 0x0C | |
+#define HCI_EV_TX_RDS_GRP_COMPL 0x0D | |
+#define HCI_EV_TX_RDS_CONT_GRP_COMPL 0x0E | |
+#define HCI_EV_CMD_COMPLETE 0x0F | |
+#define HCI_EV_CMD_STATUS 0x10 | |
+#define HCI_EV_TUNE_COMPLETE 0x11 | |
+#define HCI_EV_SEARCH_COMPLETE 0x12 | |
+#define HCI_EV_SEARCH_RDS_COMPLETE 0x13 | |
+#define HCI_EV_SEARCH_LIST_COMPLETE 0x14 | |
+ | |
+#define HCI_REQ_DONE 0 | |
+#define HCI_REQ_PEND 1 | |
+#define HCI_REQ_CANCELED 2 | |
+#define HCI_REQ_STATUS 3 | |
+ | |
+#define MAX_RAW_RDS_GRPS 21 | |
+ | |
+#define RDSGRP_DATA_OFFSET 0x1 | |
+ | |
+/*RT PLUS*/ | |
+#define DUMMY_CLASS 0 | |
+#define RT_PLUS_LEN_1_TAG 3 | |
+#define RT_ERT_FLAG_BIT 5 | |
+ | |
+/*TAG1*/ | |
+#define TAG1_MSB_OFFSET 3 | |
+#define TAG1_MSB_MASK 7 | |
+#define TAG1_LSB_OFFSET 5 | |
+#define TAG1_POS_MSB_MASK 31 | |
+#define TAG1_POS_MSB_OFFSET 1 | |
+#define TAG1_POS_LSB_OFFSET 7 | |
+#define TAG1_LEN_OFFSET 1 | |
+#define TAG1_LEN_MASK 63 | |
+ | |
+/*TAG2*/ | |
+#define TAG2_MSB_OFFSET 5 | |
+#define TAG2_MSB_MASK 1 | |
+#define TAG2_LSB_OFFSET 3 | |
+#define TAG2_POS_MSB_MASK 7 | |
+#define TAG2_POS_MSB_OFFSET 3 | |
+#define TAG2_POS_LSB_OFFSET 5 | |
+#define TAG2_LEN_MASK 31 | |
+ | |
+#define AGT_MASK 31 | |
+/*Extract 5 left most bits of lsb of 2nd block*/ | |
+#define AGT(x) (x & AGT_MASK) | |
+/*16 bits of 4th block*/ | |
+#define AID(lsb, msb) ((msb << 8) | (lsb)) | |
+/*Extract 5 right most bits of msb of 2nd block*/ | |
+#define GTC(blk2msb) (blk2msb >> 3) | |
+ | |
+#define GRP_3A 0x6 | |
+#define RT_PLUS_AID 0x4bd7 | |
+ | |
+/*ERT*/ | |
+#define ERT_AID 0x6552 | |
+#define CARRIAGE_RETURN 0x000D | |
+#define MAX_ERT_SEGMENT 31 | |
+#define ERT_FORMAT_DIR_BIT 1 | |
+ | |
+#define EXTRACT_BIT(data, bit_pos) ((data & (1 << bit_pos)) >> bit_pos) | |
+ | |
+struct hci_ev_tune_status { | |
+ __u8 sub_event; | |
+ __le32 station_freq; | |
+ __u8 serv_avble; | |
+ char rssi; | |
+ __u8 stereo_prg; | |
+ __u8 rds_sync_status; | |
+ __u8 mute_mode; | |
+ char sinr; | |
+ __u8 intf_det_th; | |
+} __packed; | |
+ | |
+struct rds_blk_data { | |
+ __u8 rdsMsb; | |
+ __u8 rdsLsb; | |
+ __u8 blockStatus; | |
+} __packed; | |
+ | |
+struct rds_grp_data { | |
+ struct rds_blk_data rdsBlk[4]; | |
+} __packed; | |
+ | |
+struct hci_ev_rds_rx_data { | |
+ __u8 num_rds_grps; | |
+ struct rds_grp_data rds_grp_data[MAX_RAW_RDS_GRPS]; | |
+} __packed; | |
+ | |
+struct hci_ev_prg_service { | |
+ __le16 pi_prg_id; | |
+ __u8 pty_prg_type; | |
+ __u8 ta_prg_code_type; | |
+ __u8 ta_ann_code_flag; | |
+ __u8 ms_switch_code_flag; | |
+ __u8 dec_id_ctrl_code_flag; | |
+ __u8 ps_num; | |
+ __u8 prg_service_name[119]; | |
+} __packed; | |
+ | |
+struct hci_ev_radio_text { | |
+ __le16 pi_prg_id; | |
+ __u8 pty_prg_type; | |
+ __u8 ta_prg_code_type; | |
+ __u8 txt_ab_flag; | |
+ __u8 radio_txt[64]; | |
+} __packed; | |
+ | |
+struct hci_ev_af_list { | |
+ __le32 tune_freq; | |
+ __le16 pi_code; | |
+ __u8 af_size; | |
+ __u8 af_list[FM_AF_LIST_MAX_SIZE]; | |
+} __packed; | |
+ | |
+struct hci_ev_cmd_complete { | |
+ __u8 num_hci_cmd_pkts; | |
+ __le16 cmd_opcode; | |
+} __packed; | |
+ | |
+struct hci_ev_cmd_status { | |
+ __u8 status; | |
+ __u8 num_hci_cmd_pkts; | |
+ __le16 status_opcode; | |
+} __packed; | |
+ | |
+struct hci_ev_srch_st { | |
+ __le32 station_freq; | |
+ __u8 rds_cap; | |
+ __u8 pty; | |
+ __le16 status_opcode; | |
+} __packed; | |
+ | |
+struct hci_ev_rel_freq { | |
+ __u8 rel_freq_msb; | |
+ __u8 rel_freq_lsb; | |
+ | |
+} __packed; | |
+struct hci_ev_srch_list_compl { | |
+ __u8 num_stations_found; | |
+ struct hci_ev_rel_freq rel_freq[20]; | |
+} __packed; | |
+ | |
+/* ----- HCI Event Response ----- */ | |
+struct hci_fm_conf_rsp { | |
+ __u8 status; | |
+ struct hci_fm_recv_conf_req recv_conf_rsp; | |
+} __packed; | |
+ | |
+struct hci_fm_get_trans_conf_rsp { | |
+ __u8 status; | |
+ struct hci_fm_trans_conf_req_struct trans_conf_rsp; | |
+} __packed; | |
+struct hci_fm_sig_threshold_rsp { | |
+ __u8 status; | |
+ __u8 sig_threshold; | |
+} __packed; | |
+ | |
+struct hci_fm_station_rsp { | |
+ struct hci_ev_tune_status station_rsp; | |
+} __packed; | |
+ | |
+struct hci_fm_prgm_srv_rsp { | |
+ __u8 status; | |
+ struct hci_ev_prg_service prg_srv; | |
+} __packed; | |
+ | |
+struct hci_fm_radio_txt_rsp { | |
+ __u8 status; | |
+ struct hci_ev_radio_text rd_txt; | |
+} __packed; | |
+ | |
+struct hci_fm_af_list_rsp { | |
+ __u8 status; | |
+ struct hci_ev_af_list rd_txt; | |
+} __packed; | |
+ | |
+struct hci_fm_data_rd_rsp { | |
+ __u8 status; | |
+ __u8 ret_data_len; | |
+ __u8 data[DEFAULT_DATA_SIZE]; | |
+} __packed; | |
+ | |
+struct hci_fm_feature_list_rsp { | |
+ __u8 status; | |
+ __u8 feature_mask; | |
+} __packed; | |
+ | |
+struct hci_fm_dbg_param_rsp { | |
+ __u8 status; | |
+ __u8 blend; | |
+ __u8 soft_mute; | |
+ __u8 inf_blend; | |
+ __u8 inf_soft_mute; | |
+ __u8 pilot_pil; | |
+ __u8 io_verc; | |
+ __u8 in_det_out; | |
+} __packed; | |
+ | |
+#define CLKSPURID_INDEX0 0 | |
+#define CLKSPURID_INDEX1 5 | |
+#define CLKSPURID_INDEX2 10 | |
+#define CLKSPURID_INDEX3 15 | |
+#define CLKSPURID_INDEX4 20 | |
+#define CLKSPURID_INDEX5 25 | |
+ | |
+#define MAX_SPUR_FREQ_LIMIT 30 | |
+#define CKK_SPUR 0x3B | |
+#define SPUR_DATA_SIZE 0x4 | |
+#define SPUR_ENTRIES_PER_ID 0x5 | |
+ | |
+#define COMPUTE_SPUR(val) ((((val) - (76000)) / (50))) | |
+#define GET_FREQ(val, bit) ((bit == 1) ? ((val) >> 8) : ((val) & 0xFF)) | |
+#define GET_SPUR_ENTRY_LEVEL(val) ((val) / (5)) | |
+ | |
+struct hci_fm_spur_data { | |
+ __u32 freq[MAX_SPUR_FREQ_LIMIT]; | |
+ __s8 rmssi[MAX_SPUR_FREQ_LIMIT]; | |
+ __u8 enable[MAX_SPUR_FREQ_LIMIT]; | |
+} __packed; | |
+ | |
+ | |
+/* HCI dev events */ | |
+#define RADIO_HCI_DEV_REG 1 | |
+#define RADIO_HCI_DEV_WRITE 2 | |
+ | |
+#define hci_req_lock(d) mutex_lock(&d->req_lock) | |
+#define hci_req_unlock(d) mutex_unlock(&d->req_lock) | |
+ | |
+/* FM RDS */ | |
+#define RDS_PTYPE 2 | |
+#define RDS_PID_LOWER 1 | |
+#define RDS_PID_HIGHER 0 | |
+#define RDS_OFFSET 5 | |
+#define RDS_PS_LENGTH_OFFSET 7 | |
+#define RDS_STRING 8 | |
+#define RDS_PS_DATA_OFFSET 8 | |
+#define RDS_CONFIG_OFFSET 3 | |
+#define RDS_AF_JUMP_OFFSET 4 | |
+#define PI_CODE_OFFSET 4 | |
+#define AF_SIZE_OFFSET 6 | |
+#define AF_LIST_OFFSET 7 | |
+#define RT_A_B_FLAG_OFFSET 4 | |
+/*FM states*/ | |
+ | |
+enum radio_state_t { | |
+ FM_OFF, | |
+ FM_RECV, | |
+ FM_TRANS, | |
+ FM_RESET, | |
+ FM_CALIB, | |
+ FM_TURNING_OFF, | |
+ FM_RECV_TURNING_ON, | |
+ FM_TRANS_TURNING_ON, | |
+ FM_MAX_NO_STATES, | |
+}; | |
+ | |
+enum emphasis_type { | |
+ FM_RX_EMP75 = 0x0, | |
+ FM_RX_EMP50 = 0x1 | |
+}; | |
+ | |
+enum channel_space_type { | |
+ FM_RX_SPACE_200KHZ = 0x0, | |
+ FM_RX_SPACE_100KHZ = 0x1, | |
+ FM_RX_SPACE_50KHZ = 0x2 | |
+}; | |
+ | |
+enum high_low_injection { | |
+ AUTO_HI_LO_INJECTION = 0x0, | |
+ LOW_SIDE_INJECTION = 0x1, | |
+ HIGH_SIDE_INJECTION = 0x2 | |
+}; | |
+ | |
+enum fm_rds_type { | |
+ FM_RX_RDBS_SYSTEM = 0x0, | |
+ FM_RX_RDS_SYSTEM = 0x1 | |
+}; | |
+ | |
+enum iris_region_t { | |
+ IRIS_REGION_US, | |
+ IRIS_REGION_EU, | |
+ IRIS_REGION_JAPAN, | |
+ IRIS_REGION_JAPAN_WIDE, | |
+ IRIS_REGION_OTHER | |
+}; | |
+ | |
+#define STD_BUF_SIZE (256) | |
+ | |
+enum iris_buf_t { | |
+ IRIS_BUF_SRCH_LIST, | |
+ IRIS_BUF_EVENTS, | |
+ IRIS_BUF_RT_RDS, | |
+ IRIS_BUF_PS_RDS, | |
+ IRIS_BUF_RAW_RDS, | |
+ IRIS_BUF_AF_LIST, | |
+ IRIS_BUF_PEEK, | |
+ IRIS_BUF_SSBI_PEEK, | |
+ IRIS_BUF_RDS_CNTRS, | |
+ IRIS_BUF_RD_DEFAULT, | |
+ IRIS_BUF_CAL_DATA, | |
+ IRIS_BUF_RT_PLUS, | |
+ IRIS_BUF_ERT, | |
+ IRIS_BUF_SPUR, | |
+ IRIS_BUF_MAX, | |
+}; | |
+ | |
+enum iris_xfr_t { | |
+ IRIS_XFR_SYNC, | |
+ IRIS_XFR_ERROR, | |
+ IRIS_XFR_SRCH_LIST, | |
+ IRIS_XFR_RT_RDS, | |
+ IRIS_XFR_PS_RDS, | |
+ IRIS_XFR_AF_LIST, | |
+ IRIS_XFR_MAX | |
+}; | |
+ | |
+#undef FMDBG | |
+#ifdef FM_DEBUG | |
+#define FMDBG(fmt, args...) pr_info("iris_radio: " fmt, ##args) | |
+#else | |
+#define FMDBG(fmt, args...) | |
+#endif | |
+ | |
+#undef FMDERR | |
+#define FMDERR(fmt, args...) pr_err("iris_radio: " fmt, ##args) | |
+ | |
+/* Search options */ | |
+enum search_t { | |
+ SEEK, | |
+ SCAN, | |
+ SCAN_FOR_STRONG, | |
+ SCAN_FOR_WEAK, | |
+ RDS_SEEK_PTY, | |
+ RDS_SCAN_PTY, | |
+ RDS_SEEK_PI, | |
+ RDS_AF_JUMP, | |
+}; | |
+ | |
+enum spur_entry_levels { | |
+ ENTRY_0, | |
+ ENTRY_1, | |
+ ENTRY_2, | |
+ ENTRY_3, | |
+ ENTRY_4, | |
+ ENTRY_5, | |
+}; | |
+ | |
+/* Band limits */ | |
+#define REGION_US_EU_BAND_LOW 87500 | |
+#define REGION_US_EU_BAND_HIGH 108000 | |
+#define REGION_JAPAN_STANDARD_BAND_LOW 76000 | |
+#define REGION_JAPAN_STANDARD_BAND_HIGH 90000 | |
+#define REGION_JAPAN_WIDE_BAND_LOW 90000 | |
+#define REGION_JAPAN_WIDE_BAND_HIGH 108000 | |
+ | |
+#define SRCH_MODE 0x07 | |
+#define SRCH_DIR 0x08 /* 0-up 1-down */ | |
+#define SCAN_DWELL 0x70 | |
+#define SRCH_ON 0x80 | |
+ | |
+/* I/O Control */ | |
+#define IOC_HRD_MUTE 0x03 | |
+#define IOC_SFT_MUTE 0x01 | |
+#define IOC_MON_STR 0x01 | |
+#define IOC_SIG_BLND 0x01 | |
+#define IOC_INTF_BLND 0x01 | |
+#define IOC_ANTENNA 0x01 | |
+ | |
+/* RDS Control */ | |
+#define RDS_ON 0x01 | |
+#define RDS_BUF_SZ 100 | |
+ | |
+/* constants */ | |
+#define RDS_BLOCKS_NUM (4) | |
+#define BYTES_PER_BLOCK (3) | |
+#define MAX_PS_LENGTH (108) | |
+#define MAX_RT_LENGTH (64) | |
+#define RDS_GRP_CNTR_LEN (36) | |
+#define RX_RT_DATA_LENGTH (63) | |
+/* Search direction */ | |
+#define SRCH_DIR_UP (0) | |
+#define SRCH_DIR_DOWN (1) | |
+ | |
+/*Search RDS stations*/ | |
+#define SEARCH_RDS_STNS_MODE_OFFSET 4 | |
+ | |
+/*Search Station list */ | |
+#define PARAMS_PER_STATION 0x08 | |
+#define STN_NUM_OFFSET 0x01 | |
+#define STN_FREQ_OFFSET 0x02 | |
+#define KHZ_TO_MHZ 1000 | |
+#define GET_MSB(x)((x >> 8) & 0xFF) | |
+#define GET_LSB(x)((x) & 0xFF) | |
+ | |
+/* control options */ | |
+#define CTRL_ON (1) | |
+#define CTRL_OFF (0) | |
+ | |
+/*Diagnostic commands*/ | |
+ | |
+#define RIVA_PEEK_OPCODE 0x0D | |
+#define RIVA_POKE_OPCODE 0x0C | |
+ | |
+#define PEEK_DATA_OFSET 0x1 | |
+#define RIVA_PEEK_PARAM 0x6 | |
+#define RIVA_PEEK_LEN_OFSET 0x6 | |
+#define SSBI_PEEK_LEN 0x01 | |
+/*Calibration data*/ | |
+#define PROCS_CALIB_MODE 1 | |
+#define PROCS_CALIB_SIZE 23 | |
+#define DC_CALIB_MODE 2 | |
+#define DC_CALIB_SIZE 48 | |
+#define RSB_CALIB_MODE 3 | |
+#define RSB_CALIB_SIZE 4 | |
+#define CALIB_DATA_OFSET 2 | |
+#define CALIB_MODE_OFSET 1 | |
+#define MAX_CALIB_SIZE 75 | |
+ | |
+/* Channel validity */ | |
+#define INVALID_CHANNEL (0) | |
+#define VALID_CHANNEL (1) | |
+ | |
+struct hci_fm_set_cal_req_proc { | |
+ __u8 mode; | |
+ /*Max process calibration data size*/ | |
+ __u8 data[PROCS_CALIB_SIZE]; | |
+} __packed; | |
+ | |
+struct hci_fm_set_cal_req_dc { | |
+ __u8 mode; | |
+ /*Max DC calibration data size*/ | |
+ __u8 data[DC_CALIB_SIZE]; | |
+} __packed; | |
+ | |
+struct hci_cc_do_calibration_rsp { | |
+ __u8 status; | |
+ __u8 mode; | |
+ __u8 data[MAX_CALIB_SIZE]; | |
+} __packed; | |
+ | |
+struct hci_fm_set_spur_table_req { | |
+ __u8 mode; | |
+ __u8 no_of_freqs_entries; | |
+ u8 spur_data[FM_SPUR_TBL_SIZE]; | |
+} __packed; | |
+/* Low Power mode*/ | |
+#define SIG_LEVEL_INTR (1 << 0) | |
+#define RDS_SYNC_INTR (1 << 1) | |
+#define AUDIO_CTRL_INTR (1 << 2) | |
+#define AF_JUMP_ENABLE (1 << 4) | |
+ | |
+int hci_def_data_read(struct hci_fm_def_data_rd_req *arg, | |
+ struct radio_hci_dev *hdev); | |
+int hci_def_data_write(struct hci_fm_def_data_wr_req *arg, | |
+ struct radio_hci_dev *hdev); | |
+int hci_fm_do_calibration(__u8 *arg, struct radio_hci_dev *hdev); | |
+int hci_fm_do_calibration(__u8 *arg, struct radio_hci_dev *hdev); | |
+ | |
+static inline int is_valid_tone(int tone) | |
+{ | |
+ if ((tone >= MIN_TX_TONE_VAL) && | |
+ (tone <= MAX_TX_TONE_VAL)) | |
+ return 1; | |
+ else | |
+ return 0; | |
+} | |
+ | |
+static inline int is_valid_hard_mute(int hard_mute) | |
+{ | |
+ if ((hard_mute >= MIN_HARD_MUTE_VAL) && | |
+ (hard_mute <= MAX_HARD_MUTE_VAL)) | |
+ return 1; | |
+ else | |
+ return 0; | |
+} | |
+ | |
+static inline int is_valid_srch_mode(int srch_mode) | |
+{ | |
+ if ((srch_mode >= MIN_SRCH_MODE) && | |
+ (srch_mode <= MAX_SRCH_MODE)) | |
+ return 1; | |
+ else | |
+ return 0; | |
+} | |
+ | |
+static inline int is_valid_scan_dwell_prd(int scan_dwell_prd) | |
+{ | |
+ if ((scan_dwell_prd >= MIN_SCAN_DWELL) && | |
+ (scan_dwell_prd <= MAX_SCAN_DWELL)) | |
+ return 1; | |
+ else | |
+ return 0; | |
+} | |
+ | |
+static inline int is_valid_sig_th(int sig_th) | |
+{ | |
+ if ((sig_th >= MIN_SIG_TH) && | |
+ (sig_th <= MAX_SIG_TH)) | |
+ return 1; | |
+ else | |
+ return 0; | |
+} | |
+ | |
+static inline int is_valid_pty(int pty) | |
+{ | |
+ if ((pty >= MIN_PTY) && | |
+ (pty <= MAX_PTY)) | |
+ return 1; | |
+ else | |
+ return 0; | |
+} | |
+ | |
+static inline int is_valid_pi(int pi) | |
+{ | |
+ if ((pi >= MIN_PI) && | |
+ (pi <= MAX_PI)) | |
+ return 1; | |
+ else | |
+ return 0; | |
+} | |
+ | |
+static inline int is_valid_srch_station_cnt(int cnt) | |
+{ | |
+ if ((cnt >= MIN_SRCH_STATIONS_CNT) && | |
+ (cnt <= MAX_SRCH_STATIONS_CNT)) | |
+ return 1; | |
+ else | |
+ return 0; | |
+} | |
+ | |
+static inline int is_valid_chan_spacing(int spacing) | |
+{ | |
+ if ((spacing >= MIN_CHAN_SPACING) && | |
+ (spacing <= MAX_CHAN_SPACING)) | |
+ return 1; | |
+ else | |
+ return 0; | |
+} | |
+ | |
+static inline int is_valid_emphasis(int emphasis) | |
+{ | |
+ if ((emphasis >= MIN_EMPHASIS) && | |
+ (emphasis <= MAX_EMPHASIS)) | |
+ return 1; | |
+ else | |
+ return 0; | |
+} | |
+ | |
+static inline int is_valid_rds_std(int rds_std) | |
+{ | |
+ if ((rds_std >= MIN_RDS_STD) && | |
+ (rds_std <= MAX_RDS_STD)) | |
+ return 1; | |
+ else | |
+ return 0; | |
+} | |
+ | |
+static inline int is_valid_antenna(int antenna_type) | |
+{ | |
+ if ((antenna_type >= MIN_ANTENNA_VAL) && | |
+ (antenna_type <= MAX_ANTENNA_VAL)) | |
+ return 1; | |
+ else | |
+ return 0; | |
+} | |
+ | |
+static inline int is_valid_ps_repeat_cnt(int cnt) | |
+{ | |
+ if ((cnt >= MIN_TX_PS_REPEAT_CNT) && | |
+ (cnt <= MAX_TX_PS_REPEAT_CNT)) | |
+ return 1; | |
+ else | |
+ return 0; | |
+} | |
+ | |
+static inline int is_valid_soft_mute(int soft_mute) | |
+{ | |
+ if ((soft_mute >= MIN_SOFT_MUTE) && | |
+ (soft_mute <= MAX_SOFT_MUTE)) | |
+ return 1; | |
+ else | |
+ return 0; | |
+} | |
+ | |
+static inline int is_valid_peek_len(int len) | |
+{ | |
+ if ((len >= MIN_PEEK_ACCESS_LEN) && | |
+ (len <= MAX_PEEK_ACCESS_LEN)) | |
+ return 1; | |
+ else | |
+ return 0; | |
+} | |
+ | |
+static inline int is_valid_reset_cntr(int cntr) | |
+{ | |
+ if ((cntr >= MIN_RESET_CNTR) && | |
+ (cntr <= MAX_RESET_CNTR)) | |
+ return 1; | |
+ else | |
+ return 0; | |
+} | |
+ | |
+static inline int is_valid_hlsi(int hlsi) | |
+{ | |
+ if ((hlsi >= MIN_HLSI) && | |
+ (hlsi <= MAX_HLSI)) | |
+ return 1; | |
+ else | |
+ return 0; | |
+} | |
+ | |
+static inline int is_valid_notch_filter(int filter) | |
+{ | |
+ if ((filter >= MIN_NOTCH_FILTER) && | |
+ (filter <= MAX_NOTCH_FILTER)) | |
+ return 1; | |
+ else | |
+ return 0; | |
+} | |
+ | |
+static inline int is_valid_intf_det_low_th(int th) | |
+{ | |
+ if ((th >= MIN_INTF_DET_OUT_LW_TH) && | |
+ (th <= MAX_INTF_DET_OUT_LW_TH)) | |
+ return 1; | |
+ else | |
+ return 0; | |
+} | |
+ | |
+static inline int is_valid_intf_det_hgh_th(int th) | |
+{ | |
+ if ((th >= MIN_INTF_DET_OUT_HG_TH) && | |
+ (th <= MAX_INTF_DET_OUT_HG_TH)) | |
+ return 1; | |
+ else | |
+ return 0; | |
+} | |
+ | |
+static inline int is_valid_sinr_th(int th) | |
+{ | |
+ if ((th >= MIN_SINR_TH) && | |
+ (th <= MAX_SINR_TH)) | |
+ return 1; | |
+ else | |
+ return 0; | |
+} | |
+ | |
+static inline int is_valid_sinr_samples(int samples_cnt) | |
+{ | |
+ if ((samples_cnt >= MIN_SINR_SAMPLES) && | |
+ (samples_cnt <= MAX_SINR_SAMPLES)) | |
+ return 1; | |
+ else | |
+ return 0; | |
+} | |
+ | |
+static inline int is_valid_fm_state(int state) | |
+{ | |
+ if ((state >= 0) && (state < FM_MAX_NO_STATES)) | |
+ return 1; | |
+ else | |
+ return 0; | |
+} | |
+ | |
+static inline int is_valid_blend_value(int val) | |
+{ | |
+ if ((val >= MIN_BLEND_HI) && (val <= MAX_BLEND_HI)) | |
+ return 1; | |
+ else | |
+ return 0; | |
+} | |
+ | |
+#endif /* __UAPI_RADIO_HCI_CORE_H */ | |
-- | |
2.13.3 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment