diff options
Diffstat (limited to 'net/qrtr/smd.c')
-rw-r--r-- | net/qrtr/smd.c | 117 |
1 files changed, 117 insertions, 0 deletions
diff --git a/net/qrtr/smd.c b/net/qrtr/smd.c new file mode 100644 index 000000000000..84ebce73aa23 --- /dev/null +++ b/net/qrtr/smd.c | |||
@@ -0,0 +1,117 @@ | |||
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 | }; | ||
25 | |||
26 | /* from smd to qrtr */ | ||
27 | static int qcom_smd_qrtr_callback(struct qcom_smd_device *sdev, | ||
28 | const void *data, size_t len) | ||
29 | { | ||
30 | struct qrtr_smd_dev *qdev = dev_get_drvdata(&sdev->dev); | ||
31 | int rc; | ||
32 | |||
33 | if (!qdev) | ||
34 | return -EAGAIN; | ||
35 | |||
36 | rc = qrtr_endpoint_post(&qdev->ep, data, len); | ||
37 | if (rc == -EINVAL) { | ||
38 | dev_err(&sdev->dev, "invalid ipcrouter packet\n"); | ||
39 | /* return 0 to let smd drop the packet */ | ||
40 | rc = 0; | ||
41 | } | ||
42 | |||
43 | return rc; | ||
44 | } | ||
45 | |||
46 | /* from qrtr to smd */ | ||
47 | static int qcom_smd_qrtr_send(struct qrtr_endpoint *ep, struct sk_buff *skb) | ||
48 | { | ||
49 | struct qrtr_smd_dev *qdev = container_of(ep, struct qrtr_smd_dev, ep); | ||
50 | int rc; | ||
51 | |||
52 | rc = skb_linearize(skb); | ||
53 | if (rc) | ||
54 | goto out; | ||
55 | |||
56 | rc = qcom_smd_send(qdev->channel, skb->data, skb->len); | ||
57 | |||
58 | out: | ||
59 | if (rc) | ||
60 | kfree_skb(skb); | ||
61 | else | ||
62 | consume_skb(skb); | ||
63 | return rc; | ||
64 | } | ||
65 | |||
66 | static int qcom_smd_qrtr_probe(struct qcom_smd_device *sdev) | ||
67 | { | ||
68 | struct qrtr_smd_dev *qdev; | ||
69 | int rc; | ||
70 | |||
71 | qdev = devm_kzalloc(&sdev->dev, sizeof(*qdev), GFP_KERNEL); | ||
72 | if (!qdev) | ||
73 | return -ENOMEM; | ||
74 | |||
75 | qdev->channel = sdev->channel; | ||
76 | qdev->ep.xmit = qcom_smd_qrtr_send; | ||
77 | |||
78 | rc = qrtr_endpoint_register(&qdev->ep, QRTR_EP_NID_AUTO); | ||
79 | if (rc) | ||
80 | return rc; | ||
81 | |||
82 | dev_set_drvdata(&sdev->dev, qdev); | ||
83 | |||
84 | dev_dbg(&sdev->dev, "Qualcomm SMD QRTR driver probed\n"); | ||
85 | |||
86 | return 0; | ||
87 | } | ||
88 | |||
89 | static void qcom_smd_qrtr_remove(struct qcom_smd_device *sdev) | ||
90 | { | ||
91 | struct qrtr_smd_dev *qdev = dev_get_drvdata(&sdev->dev); | ||
92 | |||
93 | qrtr_endpoint_unregister(&qdev->ep); | ||
94 | |||
95 | dev_set_drvdata(&sdev->dev, NULL); | ||
96 | } | ||
97 | |||
98 | static const struct qcom_smd_id qcom_smd_qrtr_smd_match[] = { | ||
99 | { "IPCRTR" }, | ||
100 | {} | ||
101 | }; | ||
102 | |||
103 | static struct qcom_smd_driver qcom_smd_qrtr_driver = { | ||
104 | .probe = qcom_smd_qrtr_probe, | ||
105 | .remove = qcom_smd_qrtr_remove, | ||
106 | .callback = qcom_smd_qrtr_callback, | ||
107 | .smd_match_table = qcom_smd_qrtr_smd_match, | ||
108 | .driver = { | ||
109 | .name = "qcom_smd_qrtr", | ||
110 | .owner = THIS_MODULE, | ||
111 | }, | ||
112 | }; | ||
113 | |||
114 | module_qcom_smd_driver(qcom_smd_qrtr_driver); | ||
115 | |||
116 | MODULE_DESCRIPTION("Qualcomm IPC-Router SMD interface driver"); | ||
117 | MODULE_LICENSE("GPL v2"); | ||