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/soc/qcom/smd.h> 18 19 #include "qrtr.h" 20 21 struct qrtr_smd_dev { 22 struct qrtr_endpoint ep; 23 struct qcom_smd_channel *channel; 24 struct device *dev; 25 }; 26 27 /* from smd to qrtr */ 28 static int qcom_smd_qrtr_callback(struct qcom_smd_channel *channel, 29 const void *data, size_t len) 30 { 31 struct qrtr_smd_dev *qdev = qcom_smd_get_drvdata(channel); 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 = qcom_smd_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 qcom_smd_device *sdev) 68 { 69 struct qrtr_smd_dev *qdev; 70 int rc; 71 72 qdev = devm_kzalloc(&sdev->dev, sizeof(*qdev), GFP_KERNEL); 73 if (!qdev) 74 return -ENOMEM; 75 76 qdev->channel = sdev->channel; 77 qdev->dev = &sdev->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 qcom_smd_set_drvdata(sdev->channel, qdev); 85 dev_set_drvdata(&sdev->dev, qdev); 86 87 dev_dbg(&sdev->dev, "Qualcomm SMD QRTR driver probed\n"); 88 89 return 0; 90 } 91 92 static void qcom_smd_qrtr_remove(struct qcom_smd_device *sdev) 93 { 94 struct qrtr_smd_dev *qdev = dev_get_drvdata(&sdev->dev); 95 96 qrtr_endpoint_unregister(&qdev->ep); 97 98 dev_set_drvdata(&sdev->dev, NULL); 99 } 100 101 static const struct qcom_smd_id qcom_smd_qrtr_smd_match[] = { 102 { "IPCRTR" }, 103 {} 104 }; 105 106 static struct qcom_smd_driver qcom_smd_qrtr_driver = { 107 .probe = qcom_smd_qrtr_probe, 108 .remove = qcom_smd_qrtr_remove, 109 .callback = qcom_smd_qrtr_callback, 110 .smd_match_table = qcom_smd_qrtr_smd_match, 111 .driver = { 112 .name = "qcom_smd_qrtr", 113 .owner = THIS_MODULE, 114 }, 115 }; 116 117 module_qcom_smd_driver(qcom_smd_qrtr_driver); 118 119 MODULE_DESCRIPTION("Qualcomm IPC-Router SMD interface driver"); 120 MODULE_LICENSE("GPL v2"); 121