1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* Copyright (c) 2021, Stephan Gerhold <stephan@gerhold.net> */ |
3 | #include <linux/kernel.h> |
4 | #include <linux/mod_devicetable.h> |
5 | #include <linux/module.h> |
6 | #include <linux/platform_device.h> |
7 | #include <linux/rpmsg.h> |
8 | #include <linux/wwan.h> |
9 | |
10 | struct rpmsg_wwan_dev { |
11 | /* Lower level is a rpmsg dev, upper level is a wwan port */ |
12 | struct rpmsg_device *rpdev; |
13 | struct wwan_port *wwan_port; |
14 | struct rpmsg_endpoint *ept; |
15 | }; |
16 | |
17 | static int rpmsg_wwan_ctrl_callback(struct rpmsg_device *rpdev, |
18 | void *buf, int len, void *priv, u32 src) |
19 | { |
20 | struct rpmsg_wwan_dev *rpwwan = priv; |
21 | struct sk_buff *skb; |
22 | |
23 | skb = alloc_skb(size: len, GFP_ATOMIC); |
24 | if (!skb) |
25 | return -ENOMEM; |
26 | |
27 | skb_put_data(skb, data: buf, len); |
28 | wwan_port_rx(port: rpwwan->wwan_port, skb); |
29 | return 0; |
30 | } |
31 | |
32 | static int rpmsg_wwan_ctrl_start(struct wwan_port *port) |
33 | { |
34 | struct rpmsg_wwan_dev *rpwwan = wwan_port_get_drvdata(port); |
35 | struct rpmsg_channel_info chinfo = { |
36 | .src = rpwwan->rpdev->src, |
37 | .dst = RPMSG_ADDR_ANY, |
38 | }; |
39 | |
40 | strscpy(chinfo.name, rpwwan->rpdev->id.name, sizeof(chinfo.name)); |
41 | rpwwan->ept = rpmsg_create_ept(rpwwan->rpdev, cb: rpmsg_wwan_ctrl_callback, |
42 | priv: rpwwan, chinfo); |
43 | if (!rpwwan->ept) |
44 | return -EREMOTEIO; |
45 | |
46 | return 0; |
47 | } |
48 | |
49 | static void rpmsg_wwan_ctrl_stop(struct wwan_port *port) |
50 | { |
51 | struct rpmsg_wwan_dev *rpwwan = wwan_port_get_drvdata(port); |
52 | |
53 | rpmsg_destroy_ept(rpwwan->ept); |
54 | rpwwan->ept = NULL; |
55 | } |
56 | |
57 | static int rpmsg_wwan_ctrl_tx(struct wwan_port *port, struct sk_buff *skb) |
58 | { |
59 | struct rpmsg_wwan_dev *rpwwan = wwan_port_get_drvdata(port); |
60 | int ret; |
61 | |
62 | ret = rpmsg_trysend(ept: rpwwan->ept, data: skb->data, len: skb->len); |
63 | if (ret) |
64 | return ret; |
65 | |
66 | consume_skb(skb); |
67 | return 0; |
68 | } |
69 | |
70 | static int rpmsg_wwan_ctrl_tx_blocking(struct wwan_port *port, struct sk_buff *skb) |
71 | { |
72 | struct rpmsg_wwan_dev *rpwwan = wwan_port_get_drvdata(port); |
73 | int ret; |
74 | |
75 | ret = rpmsg_send(ept: rpwwan->ept, data: skb->data, len: skb->len); |
76 | if (ret) |
77 | return ret; |
78 | |
79 | consume_skb(skb); |
80 | return 0; |
81 | } |
82 | |
83 | static __poll_t rpmsg_wwan_ctrl_tx_poll(struct wwan_port *port, |
84 | struct file *filp, poll_table *wait) |
85 | { |
86 | struct rpmsg_wwan_dev *rpwwan = wwan_port_get_drvdata(port); |
87 | |
88 | return rpmsg_poll(ept: rpwwan->ept, filp, wait); |
89 | } |
90 | |
91 | static const struct wwan_port_ops rpmsg_wwan_pops = { |
92 | .start = rpmsg_wwan_ctrl_start, |
93 | .stop = rpmsg_wwan_ctrl_stop, |
94 | .tx = rpmsg_wwan_ctrl_tx, |
95 | .tx_blocking = rpmsg_wwan_ctrl_tx_blocking, |
96 | .tx_poll = rpmsg_wwan_ctrl_tx_poll, |
97 | }; |
98 | |
99 | static struct device *rpmsg_wwan_find_parent(struct device *dev) |
100 | { |
101 | /* Select first platform device as parent for the WWAN ports. |
102 | * On Qualcomm platforms this is usually the platform device that |
103 | * represents the modem remote processor. This might need to be |
104 | * adjusted when adding device IDs for other platforms. |
105 | */ |
106 | for (dev = dev->parent; dev; dev = dev->parent) { |
107 | if (dev_is_platform(dev)) |
108 | return dev; |
109 | } |
110 | return NULL; |
111 | } |
112 | |
113 | static int rpmsg_wwan_ctrl_probe(struct rpmsg_device *rpdev) |
114 | { |
115 | struct rpmsg_wwan_dev *rpwwan; |
116 | struct wwan_port *port; |
117 | struct device *parent; |
118 | |
119 | parent = rpmsg_wwan_find_parent(dev: &rpdev->dev); |
120 | if (!parent) |
121 | return -ENODEV; |
122 | |
123 | rpwwan = devm_kzalloc(dev: &rpdev->dev, size: sizeof(*rpwwan), GFP_KERNEL); |
124 | if (!rpwwan) |
125 | return -ENOMEM; |
126 | |
127 | rpwwan->rpdev = rpdev; |
128 | dev_set_drvdata(dev: &rpdev->dev, data: rpwwan); |
129 | |
130 | /* Register as a wwan port, id.driver_data contains wwan port type */ |
131 | port = wwan_create_port(parent, type: rpdev->id.driver_data, |
132 | ops: &rpmsg_wwan_pops, NULL, drvdata: rpwwan); |
133 | if (IS_ERR(ptr: port)) |
134 | return PTR_ERR(ptr: port); |
135 | |
136 | rpwwan->wwan_port = port; |
137 | |
138 | return 0; |
139 | }; |
140 | |
141 | static void rpmsg_wwan_ctrl_remove(struct rpmsg_device *rpdev) |
142 | { |
143 | struct rpmsg_wwan_dev *rpwwan = dev_get_drvdata(dev: &rpdev->dev); |
144 | |
145 | wwan_remove_port(port: rpwwan->wwan_port); |
146 | } |
147 | |
148 | static const struct rpmsg_device_id rpmsg_wwan_ctrl_id_table[] = { |
149 | /* RPMSG channels for Qualcomm SoCs with integrated modem */ |
150 | { .name = "DATA5_CNTL" , .driver_data = WWAN_PORT_QMI }, |
151 | { .name = "DATA4" , .driver_data = WWAN_PORT_AT }, |
152 | { .name = "DATA1" , .driver_data = WWAN_PORT_AT }, |
153 | {}, |
154 | }; |
155 | MODULE_DEVICE_TABLE(rpmsg, rpmsg_wwan_ctrl_id_table); |
156 | |
157 | static struct rpmsg_driver rpmsg_wwan_ctrl_driver = { |
158 | .drv.name = "rpmsg_wwan_ctrl" , |
159 | .id_table = rpmsg_wwan_ctrl_id_table, |
160 | .probe = rpmsg_wwan_ctrl_probe, |
161 | .remove = rpmsg_wwan_ctrl_remove, |
162 | }; |
163 | module_rpmsg_driver(rpmsg_wwan_ctrl_driver); |
164 | |
165 | MODULE_LICENSE("GPL v2" ); |
166 | MODULE_DESCRIPTION("RPMSG WWAN CTRL Driver" ); |
167 | MODULE_AUTHOR("Stephan Gerhold <stephan@gerhold.net>" ); |
168 | |