blob: 2d42e17a94616055049123b17c4f6a17c92274e0 [file] [log] [blame]
/* Copyright (c) 2011, Code Aurora Forum. 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/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;
module_param(rds_buf, uint, 0);
MODULE_PARM_DESC(rds_buf, "RDS buffer entries: *100*");
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;
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 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;
};
static struct video_device *priv_videodev;
static int iris_do_calibration(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,
},
};
static void iris_q_event(struct iris_device *radio,
enum iris_evt_t event)
{
struct kfifo *data_b = &radio->data_buf[IRIS_BUF_EVENTS];
unsigned char evt = event;
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 = (struct radio_hci_dev *) skb->dev;
if (!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 (!(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;
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);
if (!radio)
FMDERR(":radio is null");
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 = (struct radio_hci_dev *) skb->dev;
if (!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;
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;
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_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;
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;
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;
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;
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;
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;
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;
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;
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;
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_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;
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;
opcode = hci_opcode_pack(HCI_OGF_FM_COMMON_CTRL_CMD_REQ,
HCI_OCF_FM_DEFAULT_DATA_WRITE);
return radio_hci_send_cmd(hdev, opcode, sizeof((*def_data_wr)),
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),
&notch_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;
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;
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;
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;
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 radio_hci_err(__u16 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);
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))
return -EINTR;
switch (hdev->req_status) {
case HCI_REQ_DONE:
case HCI_REQ_STATUS:
err = radio_hci_err(hdev->req_result);
break;
case HCI_REQ_CANCELED:
err = -hdev->req_result;
break;
default:
err = -ETIMEDOUT;
break;
}
hdev->req_status = hdev->req_result = 0;
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 = *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 = *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 = *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 = *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)
{
return 0;
}
static int hci_fm_rds_grps_process(__u32 *arg, struct radio_hci_dev *hdev)
{
int ret = 0;
__u32 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 = *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 = *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 = *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_fm_set_cal_req(struct radio_hci_dev *hdev,
unsigned long param)
{
u16 opcode = 0;
struct hci_fm_set_cal_req *cal_req =
(struct hci_fm_set_cal_req *)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((*hci_fm_set_cal_req)),
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_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;
default:
ret = -EINVAL;
break;
}
return ret;
}
static void radio_hci_req_complete(struct radio_hci_dev *hdev, int result)
{
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)
{
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 = *((__u8 *) skb->data);
if (status)
return;
radio_hci_req_complete(hdev, status);
}
static void hci_cc_fm_disable_rsp(struct radio_hci_dev *hdev,
struct sk_buff *skb)
{
__u8 status = *((__u8 *) skb->data);
struct iris_device *radio = video_get_drvdata(video_get_dev());
if (status)
return;
iris_q_event(radio, IRIS_EVT_RADIO_READY);
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 = (void *)skb->data;
struct iris_device *radio = video_get_drvdata(video_get_dev());
if (rsp->status)
return;
radio->recv_conf = rsp->recv_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 = (void *)skb->data;
struct iris_device *radio = video_get_drvdata(video_get_dev());
if (rsp->status)
return;
iris_q_event(radio, IRIS_EVT_RADIO_READY);
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 = (void *)skb->data;
struct iris_device *radio = video_get_drvdata(video_get_dev());
if (rsp->status)
return;
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 = (void *)skb->data;
struct iris_device *radio = video_get_drvdata(video_get_dev());
struct v4l2_control *v4l_ctl = radio->g_ctl;
if (rsp->status)
return;
v4l_ctl->value = rsp->sig_threshold;
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 = (void *)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 = (void *)skb->data;
if (rsp->status)
return;
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 = (void *)skb->data;
if (rsp->status)
return;
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 = (void *)skb->data;
if (rsp->status)
return;
radio_hci_req_complete(hdev, rsp->status);
}
static void hci_cc_feature_list_rsp(struct radio_hci_dev *hdev,
struct sk_buff *skb)
{
struct hci_fm_feature_list_rsp *rsp = (void *)skb->data;
struct iris_device *radio = video_get_drvdata(video_get_dev());
struct v4l2_capability *v4l_cap = radio->g_cap;
if (rsp->status)
return;
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 = (void *)skb->data;
radio->st_dbg_param = *(rsp);
if (radio->st_dbg_param.status)
return;
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 = &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 = *((__u8 *) skb->data);
int len;
char *data;
if (status)
return;
len = skb->data[RIVA_PEEK_LEN_OFSET] + RIVA_PEEK_PARAM;
data = kmalloc(len, GFP_ATOMIC);
if (!data) {
FMDERR("Memory allocation failed");
return;
}
memcpy(data, &skb->data[PEEK_DATA_OFSET], len);
iris_q_evt_data(radio, data, len, IRIS_BUF_PEEK);
radio_hci_req_complete(hdev, status);
kfree(data);
}
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 *) skb->data);
__u8 len;
char *data;
if (status)
return;
len = skb->data[1];
data = kmalloc(len+2, GFP_ATOMIC);
if (!data) {
FMDERR("Memory allocation failed");
return;
}
data[0] = status;
data[1] = len;
memcpy(&data[2], &skb->data[DEFAULT_DATA_OFFSET], len);
iris_q_evt_data(radio, data, len+2, IRIS_BUF_RD_DEFAULT);
radio_hci_req_complete(hdev, status);
kfree(data);
}
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 = *((__u8 *) skb->data);
char *data;
if (status)
return;
data = kmalloc(SSBI_PEEK_LEN, GFP_ATOMIC);
if (!data) {
FMDERR("Memory allocation failed");
return;
}
data[0] = skb->data[PEEK_DATA_OFSET];
iris_q_evt_data(radio, data, SSBI_PEEK_LEN, IRIS_BUF_SSBI_PEEK);
radio_hci_req_complete(hdev, status);
kfree(data);
}
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 = *((__u8 *) skb->data);
char *data;
if (status)
return;
data = kmalloc(RDS_GRP_CNTR_LEN, GFP_ATOMIC);
if (!data) {
FMDERR("memory allocation failed");
return;
}
memcpy(data, &skb->data[1], RDS_GRP_CNTR_LEN);
iris_q_evt_data(radio, data, RDS_GRP_CNTR_LEN, IRIS_BUF_RDS_CNTRS);
radio_hci_req_complete(hdev, status);
kfree(data);
}
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 ;
rsp.status = skb->data[0];
rsp.mode = skb->data[CALIB_MODE_OFSET];
if (rsp.status) {
FMDERR("status = %d", rsp.status);
return;
}
if (rsp.mode == PROCS_CALIB_MODE) {
memcpy(&rsp.data[0], &skb->data[CALIB_DATA_OFSET],
PROCS_CALIB_SIZE);
} else if (rsp.mode == DC_CALIB_MODE) {
memcpy(&rsp.data[PROCS_CALIB_SIZE],
&skb->data[CALIB_DATA_OFSET], DC_CALIB_SIZE);
iris_q_evt_data(radio, rsp.data, (PROCS_CALIB_SIZE +
DC_CALIB_SIZE), IRIS_BUF_CAL_DATA);
}
radio_hci_req_complete(hdev, rsp.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 = (void *) skb->data;
__u16 opcode;
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):
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):
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_trans_ctrl_cmd_op_pack(HCI_OCF_FM_ENABLE_TRANS_REQ):
case hci_trans_ctrl_cmd_op_pack(HCI_OCF_FM_DISABLE_TRANS_REQ):
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):
hci_cc_rsp(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;
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;
int len;
struct iris_device *radio = video_get_drvdata(video_get_dev());
struct hci_fm_station_rsp *rsp;
len = sizeof(struct hci_fm_station_rsp);
rsp = (struct hci_fm_station_rsp *)skb_pull(skb, len);
if (rsp == NULL)
return;
memcpy(&radio->fm_st_rsp.station_rsp, rsp, len);
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.rssi)
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);
if (radio->fm_st_rsp.station_rsp.mute_mode)
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());
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;
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 = *((__u8 *) skb->data);
if (st_status)
iris_q_event(radio, IRIS_EVT_STEREO);
else
iris_q_event(radio, IRIS_EVT_MONO);
}
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;
iris_q_event(radio, IRIS_EVT_NEW_RT_RDS);
while (skb->data[len+RDS_OFFSET] != 0x0d)
len++;
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] = 0;
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;
ev.tune_freq = *((int *) &skb->data[0]);
ev.pi_code = *((__le16 *) &skb->data[PI_CODE_OFFSET]);
ev.af_size = skb->data[AF_SIZE_OFFSET];
memcpy(&ev.af_list[0], &skb->data[AF_LIST_OFFSET], ev.af_size);
iris_q_event(radio, IRIS_EVT_NEW_AF_LIST);
iris_q_evt_data(radio, (char *)&ev, sizeof(ev), 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 = 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());
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);
}
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:
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 = radio->g_search_mode & SRCH_MODE;
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;
radio->srch_st_list.srch_list_max = 0;
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);
}
return retval;
}
static int set_low_power_mode(struct iris_device *radio, int power_mode)
{
int rds_grps_proc = 0x00;
int retval = 0;
if (radio->power_mode != power_mode) {
if (power_mode) {
radio->event_mask = 0x00;
rds_grps_proc = 0x00 | AF_JUMP_ENABLE ;
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;
radio->region = req_region;
switch (radio->region) {
case IRIS_REGION_US:
radio->recv_conf.band_low_limit =
REGION_US_EU_BAND_LOW;
radio->recv_conf.band_high_limit =
REGION_US_EU_BAND_HIGH;
break;
case IRIS_REGION_EU:
radio->recv_conf.band_low_limit =
REGION_US_EU_BAND_LOW;
radio->recv_conf.band_high_limit =
REGION_US_EU_BAND_HIGH;
break;
case IRIS_REGION_JAPAN:
radio->recv_conf.band_low_limit =
REGION_JAPAN_STANDARD_BAND_LOW;
radio->recv_conf.band_high_limit =
REGION_JAPAN_STANDARD_BAND_HIGH;
break;
case IRIS_REGION_JAPAN_WIDE:
radio->recv_conf.band_low_limit =
REGION_JAPAN_WIDE_BAND_LOW;
radio->recv_conf.band_high_limit =
REGION_JAPAN_WIDE_BAND_HIGH;
break;
default:
/* The user specifies the value.
So nothing needs to be done */
break;
}
retval = hci_set_fm_recv_conf(
&radio->recv_conf,
radio->fm_hdev);
return retval;
}
static int iris_trans_set_region(struct iris_device *radio, int req_region)
{
int retval;
radio->region = req_region;
switch (radio->region) {
case IRIS_REGION_US:
radio->trans_conf.band_low_limit =
REGION_US_EU_BAND_LOW;
radio->trans_conf.band_high_limit =
REGION_US_EU_BAND_HIGH;
break;
case IRIS_REGION_EU:
radio->trans_conf.band_low_limit =
REGION_US_EU_BAND_LOW;
radio->trans_conf.band_high_limit =
REGION_US_EU_BAND_HIGH;
break;
case IRIS_REGION_JAPAN:
radio->trans_conf.band_low_limit =
REGION_JAPAN_STANDARD_BAND_LOW;
radio->trans_conf.band_high_limit =
REGION_JAPAN_STANDARD_BAND_HIGH;
break;
case IRIS_REGION_JAPAN_WIDE:
radio->recv_conf.band_low_limit =
REGION_JAPAN_WIDE_BAND_LOW;
radio->recv_conf.band_high_limit =
REGION_JAPAN_WIDE_BAND_HIGH;
default:
break;
}
retval = hci_set_fm_trans_conf(
&radio->trans_conf,
radio->fm_hdev);
return retval;
}
static int iris_set_freq(struct iris_device *radio, unsigned int freq)
{
int retval;
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;
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;
cal_mode = PROCS_CALIB_MODE;
retval = hci_cmd(HCI_FM_ENABLE_RECV_CMD,
radio->fm_hdev);
if (retval < 0) {
FMDERR("Enable failed before calibration %x", retval);
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);
return retval;
}
cal_mode = DC_CALIB_MODE;
retval = radio_hci_request(radio->fm_hdev, hci_fm_do_cal_req,
(unsigned long)cal_mode, RADIO_HCI_TIMEOUT);
if (retval < 0) {
FMDERR("Do DC calibration failed %x", retval);
return retval;
}
retval = hci_cmd(HCI_FM_DISABLE_RECV_CMD,
radio->fm_hdev);
if (retval < 0)
FMDERR("Disable Failed after calibration %d", retval);
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;
switch (ctrl->id) {
case V4L2_CID_AUDIO_VOLUME:
break;
case V4L2_CID_AUDIO_MUTE:
ctrl->value = radio->mute_mode.hard_mute;
break;
case V4L2_CID_PRIVATE_IRIS_SRCHMODE:
ctrl->value = radio->g_search_mode;
break;
case V4L2_CID_PRIVATE_IRIS_SCANDWELL:
ctrl->value = radio->g_scan_time;
break;
case V4L2_CID_PRIVATE_IRIS_SRCHON:
ctrl->value = radio->search_on;
break;
case V4L2_CID_PRIVATE_IRIS_STATE:
ctrl->value = radio->mode;
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)
return retval;
ctrl->value = radio->st_dbg_param.in_det_out;
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);
break;
case V4L2_CID_PRIVATE_IRIS_SRCH_PTY:
ctrl->value = radio->srch_rds.srch_pty;
break;
case V4L2_CID_PRIVATE_IRIS_SRCH_PI:
ctrl->value = radio->srch_rds.srch_pi;
break;
case V4L2_CID_PRIVATE_IRIS_SRCH_CNT:
ctrl->value = radio->srch_st_result.num_stations_found;
break;
case V4L2_CID_PRIVATE_IRIS_EMPHASIS:
if (radio->mode == FM_RECV) {
ctrl->value = radio->recv_conf.emphasis;
} else if (radio->mode == FM_TRANS) {
ctrl->value = radio->trans_conf.emphasis;
} else {
FMDERR("Error in radio mode"
" %d\n", retval);
return -EINVAL;
}
break;
case V4L2_CID_PRIVATE_IRIS_RDS_STD:
if (radio->mode == FM_RECV) {
ctrl->value = radio->recv_conf.rds_std;
} else if (radio->mode == FM_TRANS) {
ctrl->value = radio->trans_conf.rds_std;
} else {
FMDERR("Error in radio mode"
" %d\n", retval);
return -EINVAL;
}
break;
case V4L2_CID_PRIVATE_IRIS_SPACING:
if (radio->mode == FM_RECV) {
ctrl->value = radio->recv_conf.ch_spacing;
} else {
FMDERR("Error in radio mode"
" %d\n", retval);
return -EINVAL;
}
break;
case V4L2_CID_PRIVATE_IRIS_RDSON:
if (radio->mode == FM_RECV) {
ctrl->value = radio->recv_conf.rds_std;
} else {
FMDERR("Error in radio mode"
" %d\n", retval);
return -EINVAL;
}
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:
ctrl->value = radio->mute_mode.soft_mute;
break;
case V4L2_CID_PRIVATE_IRIS_DO_CALIBRATION:
retval = iris_do_calibration(radio);
break;
default:
retval = -EINVAL;
}
if (retval < 0)
FMDERR("get control failed with %d, id: %d\n",
retval, 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;
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)))
return -EFAULT;
retval = hci_def_data_read(&default_data_rd, radio->fm_hdev);
break;
default:
retval = -EINVAL;
}
return retval;
}
static int iris_vidioc_s_ext_ctrls(struct file *file, void *priv,
struct v4l2_ext_controls *ctrl)
{
int retval = 0;
int 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 cal_req;
struct iris_device *radio = video_get_drvdata(video_devdata(file));
char *data = NULL;
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((int)(ctrl->controls[0]).size,
MAX_PS_LENGTH);
data = (ctrl->controls[0]).string;
if (copy_from_user(tx_ps.ps_data,
data, bytes_to_copy))
return -EFAULT;
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_len = bytes_to_copy;
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((int)(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))
return -EFAULT;
tx_rt.rt_control = 0x01;
tx_rt.pi = radio->pi;
tx_rt.pty = radio->pty;
tx_rt.ps_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));
if (copy_from_user(&default_data, data, sizeof(default_data)))
return -EFAULT;
retval = hci_def_data_write(&default_data, radio->fm_hdev);
break;
case V4L2_CID_PRIVATE_IRIS_SET_CALIBRATION:
FMDERR("In Set Calibration");
data = (ctrl->controls[0]).string;
bytes_to_copy = (ctrl->controls[0]).size;
memset(cal_req.data, 0, MAX_CALIB_SIZE);
cal_req.mode = PROCS_CALIB_MODE;
if (copy_from_user(&cal_req.data[0],
data, PROCS_CALIB_SIZE))
return -EFAULT;
retval = radio_hci_request(radio->fm_hdev, hci_fm_set_cal_req,
(unsigned long)&cal_req, RADIO_HCI_TIMEOUT);
if (retval < 0)
FMDERR("Set Process calibration failed %d", retval);
if (copy_from_user(&cal_req.data[PROCS_CALIB_SIZE],
data, DC_CALIB_SIZE))
return -EFAULT;
cal_req.mode = DC_CALIB_MODE;
retval = radio_hci_request(radio->fm_hdev, hci_fm_set_cal_req,
(unsigned long)&cal_req, RADIO_HCI_TIMEOUT);
if (retval < 0)
FMDERR("Set DC calibration failed %d", retval);
break;
default:
FMDBG("Shouldn't reach here\n");
retval = -1;
}
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;
unsigned long arg = 0;
struct hci_fm_tx_ps tx_ps = {0};
struct hci_fm_tx_rt tx_rt = {0};
switch (ctrl->id) {
case V4L2_CID_PRIVATE_IRIS_TX_TONE:
radio->tone_freq = ctrl->value;
retval = radio_hci_request(radio->fm_hdev,
hci_fm_tone_generator, arg,
msecs_to_jiffies(RADIO_HCI_TIMEOUT));
break;
case V4L2_CID_AUDIO_VOLUME:
break;
case V4L2_CID_AUDIO_MUTE:
radio->mute_mode.hard_mute = ctrl->value;
radio->mute_mode.soft_mute = IOC_SFT_MUTE;
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);
break;
case V4L2_CID_PRIVATE_IRIS_SRCHMODE:
radio->g_search_mode = ctrl->value;
break;
case V4L2_CID_PRIVATE_IRIS_SCANDWELL:
radio->g_scan_time = ctrl->value;
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:
retval = hci_cmd(HCI_FM_ENABLE_RECV_CMD,
radio->fm_hdev);
radio->mode = FM_RECV;
if (retval < 0)
FMDERR("Error while enabling RECV FM"
" %d\n", retval);
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 = CTRL_ON;
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;
}
retval = hci_cmd(HCI_FM_GET_RECV_CONF_CMD,
radio->fm_hdev);
if (retval < 0)
FMDERR("Failed to get the Recv Config\n");
break;
case FM_TRANS:
retval = hci_cmd(HCI_FM_ENABLE_TRANS_CMD,
radio->fm_hdev);
radio->mode = FM_TRANS;
if (retval < 0)
FMDERR("Error while enabling TRANS FM"
" %d\n", retval);
break;
case FM_OFF:
switch (radio->mode) {
case FM_RECV:
retval = hci_cmd(HCI_FM_DISABLE_RECV_CMD,
radio->fm_hdev);
if (retval < 0)
FMDERR("Err on disable recv FM"
" %d\n", retval);
break;
case FM_TRANS:
retval = hci_cmd(HCI_FM_DISABLE_TRANS_CMD,
radio->fm_hdev);
if (retval < 0)
FMDERR("Err disabling trans FM"
" %d\n", retval);
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
retval = -EINVAL;
}
break;
case V4L2_CID_PRIVATE_IRIS_SIGNAL_TH:
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");
break;
}
break;
case V4L2_CID_PRIVATE_IRIS_SRCH_PTY:
radio->srch_rds.srch_pty = ctrl->value;
radio->srch_st_list.srch_pty = ctrl->value;
break;
case V4L2_CID_PRIVATE_IRIS_SRCH_PI:
radio->srch_rds.srch_pi = ctrl->value;
break;
case V4L2_CID_PRIVATE_IRIS_SRCH_CNT:
break;
case V4L2_CID_PRIVATE_IRIS_SPACING:
radio->recv_conf.ch_spacing = ctrl->value;
break;
case V4L2_CID_PRIVATE_IRIS_EMPHASIS:
switch (radio->mode) {
case FM_RECV:
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");
break;
case FM_TRANS:
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");
break;
default:
retval = -EINVAL;
}
break;
case V4L2_CID_PRIVATE_IRIS_RDS_STD:
switch (radio->mode) {
case FM_RECV:
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");
break;
case FM_TRANS:
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");
break;
default:
retval = -EINVAL;
}
break;
case V4L2_CID_PRIVATE_IRIS_RDSON:
switch (radio->mode) {
case FM_RECV:
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");
break;
case FM_TRANS:
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");
break;
default:
retval = -EINVAL;
}
break;
case V4L2_CID_PRIVATE_IRIS_RDSGROUP_MASK:
radio->rds_grp.rds_grp_enable_mask = ctrl->value;
retval = hci_fm_rds_grp(&radio->rds_grp, radio->fm_hdev);
break;
case V4L2_CID_PRIVATE_IRIS_RDSGROUP_PROC:
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);
break;
case V4L2_CID_PRIVATE_IRIS_RDSD_BUF:
radio->rds_grp.rds_buf_size = ctrl->value;
break;
case V4L2_CID_PRIVATE_IRIS_PSALL:
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);
break;
case V4L2_CID_PRIVATE_IRIS_AF_JUMP:
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);
break;
case V4L2_CID_PRIVATE_IRIS_LP_MODE:
set_low_power_mode(radio, ctrl->value);
break;
case V4L2_CID_PRIVATE_IRIS_ANTENNA:
temp_val = ctrl->value;
retval = hci_fm_set_antenna(&temp_val, radio->fm_hdev);
break;
case V4L2_CID_RDS_TX_PTY:
radio->pty = ctrl->value;
break;
case V4L2_CID_RDS_TX_PI:
radio->pi = ctrl->value;
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:
radio->ps_repeatcount = ctrl->value;
break;
case V4L2_CID_TUNE_POWER_LEVEL:
break;
case V4L2_CID_PRIVATE_IRIS_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);
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:
radio->riva_data_req.cmd_params.length = ctrl->value;
break;
case V4L2_CID_PRIVATE_IRIS_RIVA_POKE:
memcpy(radio->riva_data_req.data, (void *)ctrl->value,
radio->riva_data_req.cmd_params.length);
radio->riva_data_req.cmd_params.subopcode = RIVA_POKE_OPCODE;
retval = hci_poke_data(&radio->riva_data_req , radio->fm_hdev);
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:
temp_val = ctrl->value;
hci_read_grp_counters(&temp_val, radio->fm_hdev);
break;
case V4L2_CID_PRIVATE_IRIS_HLSI:
retval = hci_cmd(HCI_FM_GET_RECV_CONF_CMD,
radio->fm_hdev);
if (retval)
break;
radio->recv_conf.hlsi = ctrl->value;
retval = hci_set_fm_recv_conf(
&radio->recv_conf,
radio->fm_hdev);
break;
case V4L2_CID_PRIVATE_IRIS_SET_NOTCH_FILTER:
temp_val = ctrl->value;
retval = hci_set_notch_filter(&temp_val, radio->fm_hdev);
break;
default:
retval = -EINVAL;
}
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 (tuner->index > 0)
return -EINVAL;
retval = hci_cmd(HCI_FM_GET_STATION_PARAM_CMD, radio->fm_hdev);
if (retval < 0)
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;
return 0;
}
static int iris_vidioc_s_tuner(struct file *file, void *priv,
struct v4l2_tuner *tuner)
{
struct iris_device *radio = video_get_drvdata(video_devdata(file));
int retval = 0;
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));
int retval;
freq->type = V4L2_TUNER_RADIO;
retval = hci_cmd(HCI_FM_GET_STATION_PARAM_CMD, radio->fm_hdev);
if (retval < 0)
FMDERR("get frequency failed %d\n", retval);
else
freq->frequency =
radio->fm_st_rsp.station_rsp.station_freq * TUNE_PARAM;
return retval;
}
static int iris_vidioc_s_frequency(struct file *file, void *priv,
struct v4l2_frequency *freq)
{
struct iris_device *radio = video_get_drvdata(video_devdata(file));
int retval = -1;
freq->frequency = freq->frequency / TUNE_PARAM;
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, freq->frequency);
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_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 = buffer->index;
struct kfifo *data_fifo;
unsigned char *buf = (unsigned char *)buffer->m.userptr;
unsigned int len = buffer->length;
if (!access_ok(VERIFY_WRITE, buf, len))
return -EFAULT;
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;
}
buffer->bytesused = kfifo_out_locked(data_fifo, buf, len,
&radio->buf_lock[buf_type]);
return 0;
}
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,
struct v4l2_hw_freq_seek *seek)
{
struct iris_device *radio = video_get_drvdata(video_devdata(file));
int dir;
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));
strlcpy(capability->driver, DRIVER_NAME, sizeof(capability->driver));
strlcpy(capability->card, DRIVER_CARD, sizeof(capability->card));
radio->g_cap = capability;
return 0;
}
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,
};
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
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]);
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);
memcpy(priv_videodev, radio->videodev,
sizeof(struct video_device));
}
return 0;
}
static int __devexit iris_remove(struct platform_device *pdev)
{
int i;
struct iris_device *radio = platform_get_drvdata(pdev);
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 struct platform_driver iris_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "iris_fm",
},
.remove = __devexit_p(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);