RPMSG driver changes for FM in msm-4.19

Implementing FM driver using RPMSG driver
framework based on smd for communication
with the SOC.

Change-Id: I904a8c33ec09e914a5e440ea4ba31291a4b7c6f1
Signed-off-by: phaneendra Reddy <phaneend@codeaurora.org>
This commit is contained in:
phaneendra Reddy 2021-02-19 10:08:13 +05:30
parent c96971d7d8
commit 8bbaf739b6
2 changed files with 107 additions and 90 deletions

View File

@ -14,15 +14,19 @@
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/skbuff.h>
#include <linux/rpmsg.h>
#include <linux/workqueue.h>
#include <soc/qcom/smd.h>
#include <media/radio-iris.h>
#include <linux/uaccess.h>
struct radio_data {
struct radio_hci_dev *hdev;
struct tasklet_struct rx_task;
struct smd_channel *fm_channel;
struct rpmsg_endpoint *fm_channel;
unsigned char *data;
int length;
};
struct radio_data hs;
DEFINE_MUTEX(fm_smd_enable);
@ -46,44 +50,29 @@ static void radio_hci_smd_recv_event(unsigned long temp)
int rc;
struct sk_buff *skb;
unsigned char *buf;
struct radio_data *hsmd = &hs;
len = smd_read_avail(hsmd->fm_channel);
while (len) {
FMDBG("smd_recv event: is called\n");
len = hs.length;
buf = hs.data;
skb = alloc_skb(len, GFP_ATOMIC);
if (!skb) {
FMDERR("Memory not allocated for the socket\n");
return;
}
buf = kmalloc(len, GFP_ATOMIC);
if (!buf) {
kfree_skb(skb);
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;
FMDBG("hci_send_frame: is called\n");
FM_INFO("skb %pK\n", skb);
FMDBG("skb %pK\n", skb);
len = smd_write(hs.fm_channel, skb->data, skb->len);
len = rpmsg_send(hs.fm_channel, skb->data, skb->len);
if (len < skb->len) {
FMDERR("Failed to write Data %d\n", len);
kfree_skb(skb);
@ -118,45 +107,12 @@ static void send_disable_event(struct work_struct *worker)
kfree(worker);
}
static void radio_hci_smd_notify_cmd(void *data, unsigned int event)
{
struct radio_hci_dev *hdev = (struct radio_hci_dev *)data;
FMDBG("data %p event %u\n", data, event);
if (!hdev) {
FMDERR("Frame for unknown HCI device (hdev=NULL)\n");
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) {
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;
FMDBG("hsmd: %pK\n", hsmd);
FMDBG("smd_register event: is called\n");
if (hsmd == NULL)
return -ENODEV;
hdev = kmalloc(sizeof(struct radio_hci_dev), GFP_KERNEL);
if (hdev == NULL)
return -ENODEV;
@ -167,58 +123,112 @@ static int radio_hci_smd_register_dev(struct radio_data *hsmd)
hdev->destruct = radio_hci_smd_destruct;
hdev->close_smd = radio_hci_smd_exit;
/* 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\n");
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\n");
smd_close(hsmd->fm_channel);
hsmd->hdev = NULL;
kfree(hdev);
return -ENODEV;
}
hsmd->hdev = hdev;
return 0;
}
static void radio_hci_smd_deregister(void)
{
FM_INFO("smd_deregister: is called\n");
radio_hci_unregister_dev();
kfree(hs.hdev);
hs.hdev = NULL;
smd_close(hs.fm_channel);
hs.fm_channel = 0;
fmsmd_set = 0;
}
static int radio_hci_smd_init(void)
static int qcom_smd_fm_callback(struct rpmsg_device *rpdev,
void *data, int len, void *priv, u32 addr)
{
FM_INFO("fm_callback: is called\n");
if (!len) {
FMDERR("length received is NULL\n");
return -EINVAL;
}
hs.data = kmemdup((unsigned char *)data, len, GFP_ATOMIC);
if (!hs.data) {
FMDERR("Memory not allocated\n");
return -ENOMEM;
}
hs.length = len;
tasklet_schedule(&hs.rx_task);
return 0;
}
static int qcom_smd_fm_probe(struct rpmsg_device *rpdev)
{
int ret;
FM_INFO("fm_probe: is called\n");
if (chan_opened) {
FMDBG("Channel is already opened\n");
return 0;
}
/* this should be called with fm_smd_enable lock held */
hs.fm_channel = rpdev->ept;
ret = radio_hci_smd_register_dev(&hs);
if (ret < 0) {
FMDERR("Failed to register smd device\n");
FMDERR("Failed to register with rpmsg device\n");
chan_opened = false;
return ret;
}
FMDBG("probe succeeded\n");
chan_opened = true;
return ret;
}
static void qcom_smd_fm_remove(struct rpmsg_device *rpdev)
{
FM_INFO("fm_remove: is called\n");
reset_worker = kzalloc(sizeof(*reset_worker), GFP_ATOMIC);
if (reset_worker) {
INIT_WORK(reset_worker, send_disable_event);
schedule_work(reset_worker);
}
}
static const struct rpmsg_device_id qcom_smd_fm_match[] = {
{ "APPS_FM" },
{}
};
static struct rpmsg_driver qcom_smd_fm_driver = {
.probe = qcom_smd_fm_probe,
.remove = qcom_smd_fm_remove,
.callback = qcom_smd_fm_callback,
.id_table = qcom_smd_fm_match,
.drv = {
.name = "qcom_smd_fm",
},
};
static int radio_hci_smd_init(void)
{
int ret = 0;
FM_INFO("smd_init : is called\n");
if (chan_opened) {
FMDBG("Channel is already opened\n");
return 0;
}
ret = register_rpmsg_driver(&qcom_smd_fm_driver);
if (ret < 0) {
FMDERR("%s: Failed to register with rpmsg\n", __func__);
return ret;
}
chan_opened = true;
return ret;
}
@ -232,6 +242,7 @@ static void radio_hci_smd_exit(void)
/* this should be called with fm_smd_enable lock held */
radio_hci_smd_deregister();
unregister_rpmsg_driver(&qcom_smd_fm_driver);
chan_opened = false;
}
@ -256,7 +267,11 @@ static int hcismd_fm_set_enable(const char *val, const struct kernel_param *kp)
}
done:
mutex_unlock(&fm_smd_enable);
return ret;
}
MODULE_ALIAS("rpmsg:APPS_FM");
MODULE_DESCRIPTION("FM SMD driver");
MODULE_LICENSE("GPL v2");

View File

@ -71,7 +71,7 @@ void radio_hci_event_packet(struct radio_hci_dev *hdev, struct sk_buff *skb);
#undef FMDBG
#ifdef FM_DEBUG
#define FMDBG(fmt, args...) pr_info("iris_radio: " fmt, ##args)
#define FMDBG(fmt, args...) pr_debug("iris_radio: " fmt, ##args)
#else
#define FMDBG(fmt, args...)
#endif
@ -79,6 +79,8 @@ void radio_hci_event_packet(struct radio_hci_dev *hdev, struct sk_buff *skb);
#undef FMDERR
#define FMDERR(fmt, args...) pr_err("iris_radio: " fmt, ##args)
#define FM_INFO(fmt, args...) pr_info("iris_transport: " fmt, ##args)
/* HCI timeouts */
#define RADIO_HCI_TIMEOUT (10000) /* 10 seconds */