1 /* 2 * Copyright (c) 2015, Sony Mobile Communications Inc. 3 * Copyright (c) 2013, The Linux Foundation. All rights reserved. 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License version 2 and 7 * only version 2 as published by the Free Software Foundation. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 */ 14 15 #include <linux/module.h> 16 #include <linux/skbuff.h> 17 #include <linux/rpmsg.h> 18 19 #include "qrtr.h" 20 21 struct qrtr_smd_dev { 22 struct qrtr_endpoint ep; 23 struct rpmsg_endpoint *channel; 24 struct device *dev; 25 }; 26 27 /* from smd to qrtr */ 28 static int qcom_smd_qrtr_callback(struct rpmsg_device *rpdev, 29 void *data, int len, void *priv, u32 addr) 30 { 31 struct qrtr_smd_dev *qdev = dev_get_drvdata(&rpdev->dev); 32 int rc; 33 34 if (!qdev) 35 return -EAGAIN; 36 37 rc = qrtr_endpoint_post(&qdev->ep, data, len); 38 if (rc == -EINVAL) { 39 dev_err(qdev->dev, "invalid ipcrouter packet\n"); 40 /* return 0 to let smd drop the packet */ 41 rc = 0; 42 } 43 44 return rc; 45 } 46 47 /* from qrtr to smd */ 48 static int qcom_smd_qrtr_send(struct qrtr_endpoint *ep, struct sk_buff *skb) 49 { 50 struct qrtr_smd_dev *qdev = container_of(ep, struct qrtr_smd_dev, ep); 51 int rc; 52 53 rc = skb_linearize(skb); 54 if (rc) 55 goto out; 56 57 rc = rpmsg_send(qdev->channel, skb->data, skb->len); 58 59 out: 60 if (rc) 61 kfree_skb(skb); 62 else 63 consume_skb(skb); 64 return rc; 65 } 66 67 static int qcom_smd_qrtr_probe(struct rpmsg_device *rpdev) 68 { 69 struct qrtr_smd_dev *qdev; 70 int rc; 71 72 qdev = devm_kzalloc(&rpdev->dev, sizeof(*qdev), GFP_KERNEL); 73 if (!qdev) 74 return -ENOMEM; 75 76 qdev->channel = rpdev->ept; 77 qdev->dev = &rpdev->dev; 78 qdev->ep.xmit = qcom_smd_qrtr_send; 79 80 rc = qrtr_endpoint_register(&qdev->ep, QRTR_EP_NID_AUTO); 81 if (rc) 82 return rc; 83 84 dev_set_drvdata(&rpdev->dev, qdev); 85 86 dev_dbg(&rpdev->dev, "Qualcomm SMD QRTR driver probed\n"); 87 88 return 0; 89 } 90 91 static void qcom_smd_qrtr_remove(struct rpmsg_device *rpdev) 92 { 93 struct qrtr_smd_dev *qdev = dev_get_drvdata(&rpdev->dev); 94 95 qrtr_endpoint_unregister(&qdev->ep); 96 97 dev_set_drvdata(&rpdev->dev, NULL); 98 } 99 100 static const struct rpmsg_device_id qcom_smd_qrtr_smd_match[] = { 101 { "IPCRTR" }, 102 {} 103 }; 104 105 static struct rpmsg_driver qcom_smd_qrtr_driver = { 106 .probe = qcom_smd_qrtr_probe, 107 .remove = qcom_smd_qrtr_remove, 108 .callback = qcom_smd_qrtr_callback, 109 .id_table = qcom_smd_qrtr_smd_match, 110 .drv = { 111 .name = "qcom_smd_qrtr", 112 }, 113 }; 114 115 module_rpmsg_driver(qcom_smd_qrtr_driver); 116 117 MODULE_DESCRIPTION("Qualcomm IPC-Router SMD interface driver"); 118 MODULE_LICENSE("GPL v2"); 119