blob: f0d1102a6fe61ced75a4d74c8c5c2064fa8e8d22 [file] [log] [blame]
Skylar Chang9fbce062017-07-25 16:20:42 -07001/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
Amir Levy9659e592016-10-27 18:08:27 +03002 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13#include <linux/debugfs.h>
14#include <linux/export.h>
15#include <linux/fs.h>
16#include <linux/if_ether.h>
17#include <linux/ioctl.h>
18#include <linux/kernel.h>
19#include <linux/module.h>
20#include <linux/msm_ipa.h>
21#include <linux/mutex.h>
22#include <linux/skbuff.h>
23#include <linux/types.h>
24#include <linux/ipv6.h>
25#include <net/addrconf.h>
26#include <linux/ipa.h>
27#include <linux/cdev.h>
28#include <linux/ipa_odu_bridge.h>
29#include "../ipa_common_i.h"
Michael Adisumarta3e350812017-09-18 14:54:36 -070030#include "../ipa_v3/ipa_pm.h"
Amir Levy9659e592016-10-27 18:08:27 +030031
32#define ODU_BRIDGE_DRV_NAME "odu_ipa_bridge"
33
34#define ODU_BRIDGE_DBG(fmt, args...) \
35 do { \
36 pr_debug(ODU_BRIDGE_DRV_NAME " %s:%d " fmt, \
37 __func__, __LINE__, ## args); \
38 IPA_IPC_LOGGING(ipa_get_ipc_logbuf(), \
39 ODU_BRIDGE_DRV_NAME " %s:%d " fmt, ## args); \
40 IPA_IPC_LOGGING(ipa_get_ipc_logbuf_low(), \
41 ODU_BRIDGE_DRV_NAME " %s:%d " fmt, ## args); \
42 } while (0)
43#define ODU_BRIDGE_DBG_LOW(fmt, args...) \
44 do { \
45 pr_debug(ODU_BRIDGE_DRV_NAME " %s:%d " fmt, \
46 __func__, __LINE__, ## args); \
47 IPA_IPC_LOGGING(ipa_get_ipc_logbuf_low(), \
48 ODU_BRIDGE_DRV_NAME " %s:%d " fmt, ## args); \
49 } while (0)
50#define ODU_BRIDGE_ERR(fmt, args...) \
51 do { \
52 pr_err(ODU_BRIDGE_DRV_NAME " %s:%d " fmt, \
53 __func__, __LINE__, ## args); \
54 IPA_IPC_LOGGING(ipa_get_ipc_logbuf(), \
55 ODU_BRIDGE_DRV_NAME " %s:%d " fmt, ## args); \
56 IPA_IPC_LOGGING(ipa_get_ipc_logbuf_low(), \
57 ODU_BRIDGE_DRV_NAME " %s:%d " fmt, ## args); \
58 } while (0)
59
60#define ODU_BRIDGE_FUNC_ENTRY() \
61 ODU_BRIDGE_DBG_LOW("ENTRY\n")
62#define ODU_BRIDGE_FUNC_EXIT() \
63 ODU_BRIDGE_DBG_LOW("EXIT\n")
64
65
66#define ODU_BRIDGE_IS_QMI_ADDR(daddr) \
67 (memcmp(&(daddr), &odu_bridge_ctx->llv6_addr, sizeof((daddr))) \
68 == 0)
69
70#define ODU_BRIDGE_IPV4_HDR_NAME "odu_br_ipv4"
71#define ODU_BRIDGE_IPV6_HDR_NAME "odu_br_ipv6"
72
73#define IPA_ODU_SYS_DESC_FIFO_SZ 0x800
74
75#ifdef CONFIG_COMPAT
76#define ODU_BRIDGE_IOC_SET_LLV6_ADDR32 _IOW(ODU_BRIDGE_IOC_MAGIC, \
77 ODU_BRIDGE_IOCTL_SET_LLV6_ADDR, \
78 compat_uptr_t)
79#endif
80
81#define IPA_ODU_VER_CHECK() \
82 do { \
83 ret = 0;\
84 if (ipa_get_hw_type() == IPA_HW_None) { \
85 pr_err("IPA HW is unknown\n"); \
86 ret = -EFAULT; \
87 } \
88 else if (ipa_get_hw_type() < IPA_HW_v3_0) \
89 ret = 1; \
90 } while (0)
91
92/**
93 * struct stats - driver statistics, viewable using debugfs
94 * @num_ul_packets: number of packets bridged in uplink direction
95 * @num_dl_packets: number of packets bridged in downink direction
96 * bridge
97 * @num_lan_packets: number of packets bridged to APPS on bridge mode
98 */
99struct stats {
100 u64 num_ul_packets;
101 u64 num_dl_packets;
102 u64 num_lan_packets;
103};
104
105/**
106 * struct odu_bridge_ctx - ODU bridge driver context information
107 * @class: kernel class pointer
108 * @dev_num: kernel device number
109 * @dev: kernel device struct pointer
110 * @cdev: kernel character device struct
111 * @netdev_name: network interface name
112 * @device_ethaddr: network interface ethernet address
113 * @priv: client's private data. to be used in client's callbacks
114 * @tx_dp_notify: client callback for handling IPA ODU_PROD callback
115 * @send_dl_skb: client callback for sending skb in downlink direction
116 * @stats: statistics, how many packets were transmitted using the SW bridge
117 * @is_conencted: is bridge connected ?
Skylar Chang9fbce062017-07-25 16:20:42 -0700118 * @is_suspended: is bridge suspended ?
Amir Levy9659e592016-10-27 18:08:27 +0300119 * @mode: ODU mode (router/bridge)
120 * @lock: for the initialization, connect and disconnect synchronization
121 * @llv6_addr: link local IPv6 address of ODU network interface
122 * @odu_br_ipv4_hdr_hdl: handle for partial ipv4 ethernet header
123 * @odu_br_ipv6_hdr_hdl: handle for partial ipv6 ethernet header
124 * @odu_prod_hdl: handle for IPA_CLIENT_ODU_PROD pipe
125 * @odu_emb_cons_hdl: handle for IPA_CLIENT_ODU_EMB_CONS pipe
126 * @odu_teth_cons_hdl: handle for IPA_CLIENT_ODU_TETH_CONS pipe
Skylar Chang9fbce062017-07-25 16:20:42 -0700127 * @rm_comp: completion object for IP RM
128 * @wakeup_request: client callback to wakeup
Amir Levy9659e592016-10-27 18:08:27 +0300129 */
130struct odu_bridge_ctx {
131 struct class *class;
132 dev_t dev_num;
133 struct device *dev;
134 struct cdev cdev;
135 char netdev_name[IPA_RESOURCE_NAME_MAX];
136 u8 device_ethaddr[ETH_ALEN];
137 void *priv;
138 ipa_notify_cb tx_dp_notify;
139 int (*send_dl_skb)(void *priv, struct sk_buff *skb);
140 struct stats stats;
141 bool is_connected;
Skylar Chang9fbce062017-07-25 16:20:42 -0700142 bool is_suspended;
Amir Levy9659e592016-10-27 18:08:27 +0300143 enum odu_bridge_mode mode;
144 struct mutex lock;
145 struct in6_addr llv6_addr;
146 uint32_t odu_br_ipv4_hdr_hdl;
147 uint32_t odu_br_ipv6_hdr_hdl;
148 u32 odu_prod_hdl;
149 u32 odu_emb_cons_hdl;
150 u32 odu_teth_cons_hdl;
151 u32 ipa_sys_desc_size;
152 void *logbuf;
153 void *logbuf_low;
Skylar Chang9fbce062017-07-25 16:20:42 -0700154 struct completion rm_comp;
155 void (*wakeup_request)(void *);
Michael Adisumarta3e350812017-09-18 14:54:36 -0700156 u32 pm_hdl;
Amir Levy9659e592016-10-27 18:08:27 +0300157};
158static struct odu_bridge_ctx *odu_bridge_ctx;
159
160#ifdef CONFIG_DEBUG_FS
161#define ODU_MAX_MSG_LEN 512
162static char dbg_buff[ODU_MAX_MSG_LEN];
163#endif
164
165static void odu_bridge_emb_cons_cb(void *priv, enum ipa_dp_evt_type evt,
166 unsigned long data)
167{
168 ODU_BRIDGE_FUNC_ENTRY();
169 if (evt != IPA_RECEIVE) {
170 ODU_BRIDGE_ERR("unexpected event\n");
171 WARN_ON(1);
172 return;
173 }
174 odu_bridge_ctx->send_dl_skb(priv, (struct sk_buff *)data);
175 odu_bridge_ctx->stats.num_dl_packets++;
176 ODU_BRIDGE_FUNC_EXIT();
177}
178
179static void odu_bridge_teth_cons_cb(void *priv, enum ipa_dp_evt_type evt,
180 unsigned long data)
181{
182 struct ipv6hdr *ipv6hdr;
183 struct sk_buff *skb = (struct sk_buff *)data;
184 struct sk_buff *skb_copied;
185
186 ODU_BRIDGE_FUNC_ENTRY();
187 if (evt != IPA_RECEIVE) {
188 ODU_BRIDGE_ERR("unexpected event\n");
189 WARN_ON(1);
190 return;
191 }
192
193 ipv6hdr = (struct ipv6hdr *)(skb->data + ETH_HLEN);
194 if (ipv6hdr->version == 6 &&
195 ipv6_addr_is_multicast(&ipv6hdr->daddr)) {
196 ODU_BRIDGE_DBG_LOW("Multicast pkt, send to APPS and adapter\n");
197 skb_copied = skb_clone(skb, GFP_KERNEL);
198 if (skb_copied) {
199 odu_bridge_ctx->tx_dp_notify(odu_bridge_ctx->priv,
200 IPA_RECEIVE,
201 (unsigned long) skb_copied);
202 odu_bridge_ctx->stats.num_lan_packets++;
203 } else {
204 ODU_BRIDGE_ERR("No memory\n");
205 }
206 }
207
208 odu_bridge_ctx->send_dl_skb(priv, skb);
209 odu_bridge_ctx->stats.num_dl_packets++;
210 ODU_BRIDGE_FUNC_EXIT();
211}
212
213static int odu_bridge_connect_router(void)
214{
215 struct ipa_sys_connect_params odu_prod_params;
216 struct ipa_sys_connect_params odu_emb_cons_params;
217 int res;
218
219 ODU_BRIDGE_FUNC_ENTRY();
220
221 memset(&odu_prod_params, 0, sizeof(odu_prod_params));
222 memset(&odu_emb_cons_params, 0, sizeof(odu_emb_cons_params));
223
224 /* configure RX (ODU->IPA) EP */
225 odu_prod_params.client = IPA_CLIENT_ODU_PROD;
226 odu_prod_params.ipa_ep_cfg.hdr.hdr_len = ETH_HLEN;
227 odu_prod_params.ipa_ep_cfg.nat.nat_en = IPA_SRC_NAT;
228 odu_prod_params.desc_fifo_sz = odu_bridge_ctx->ipa_sys_desc_size;
229 odu_prod_params.priv = odu_bridge_ctx->priv;
230 odu_prod_params.notify = odu_bridge_ctx->tx_dp_notify;
231 res = ipa_setup_sys_pipe(&odu_prod_params,
232 &odu_bridge_ctx->odu_prod_hdl);
233 if (res) {
234 ODU_BRIDGE_ERR("fail to setup sys pipe ODU_PROD %d\n", res);
235 goto fail_odu_prod;
236 }
237
238 /* configure TX (IPA->ODU) EP */
239 odu_emb_cons_params.client = IPA_CLIENT_ODU_EMB_CONS;
240 odu_emb_cons_params.ipa_ep_cfg.hdr.hdr_len = ETH_HLEN;
241 odu_emb_cons_params.ipa_ep_cfg.nat.nat_en = IPA_BYPASS_NAT;
242 odu_emb_cons_params.desc_fifo_sz = odu_bridge_ctx->ipa_sys_desc_size;
243 odu_emb_cons_params.priv = odu_bridge_ctx->priv;
244 odu_emb_cons_params.notify = odu_bridge_emb_cons_cb;
245 res = ipa_setup_sys_pipe(&odu_emb_cons_params,
246 &odu_bridge_ctx->odu_emb_cons_hdl);
247 if (res) {
248 ODU_BRIDGE_ERR("fail to setup sys pipe ODU_EMB_CONS %d\n", res);
249 goto fail_odu_emb_cons;
250 }
251
252 ODU_BRIDGE_DBG("odu_prod_hdl = %d, odu_emb_cons_hdl = %d\n",
253 odu_bridge_ctx->odu_prod_hdl, odu_bridge_ctx->odu_emb_cons_hdl);
254
255 ODU_BRIDGE_FUNC_EXIT();
256
257 return 0;
258
259fail_odu_emb_cons:
260 ipa_teardown_sys_pipe(odu_bridge_ctx->odu_prod_hdl);
261 odu_bridge_ctx->odu_prod_hdl = 0;
262fail_odu_prod:
263 return res;
264}
265
266static int odu_bridge_connect_bridge(void)
267{
268 struct ipa_sys_connect_params odu_prod_params;
269 struct ipa_sys_connect_params odu_emb_cons_params;
270 struct ipa_sys_connect_params odu_teth_cons_params;
271 int res;
272
273 ODU_BRIDGE_FUNC_ENTRY();
274
275 memset(&odu_prod_params, 0, sizeof(odu_prod_params));
276 memset(&odu_emb_cons_params, 0, sizeof(odu_emb_cons_params));
277
Michael Adisumarta3e350812017-09-18 14:54:36 -0700278 if (!ipa_pm_is_used()) {
279 /* Build IPA Resource manager dependency graph */
280 ODU_BRIDGE_DBG_LOW("build dependency graph\n");
281 res = ipa_rm_add_dependency(IPA_RM_RESOURCE_ODU_ADAPT_PROD,
Amir Levy9659e592016-10-27 18:08:27 +0300282 IPA_RM_RESOURCE_Q6_CONS);
Michael Adisumarta3e350812017-09-18 14:54:36 -0700283 if (res && res != -EINPROGRESS) {
284 ODU_BRIDGE_ERR("ipa_rm_add_dependency() failed\n");
285 goto fail_add_dependency_1;
286 }
Amir Levy9659e592016-10-27 18:08:27 +0300287
Michael Adisumarta3e350812017-09-18 14:54:36 -0700288 res = ipa_rm_add_dependency(IPA_RM_RESOURCE_Q6_PROD,
Amir Levy9659e592016-10-27 18:08:27 +0300289 IPA_RM_RESOURCE_ODU_ADAPT_CONS);
Michael Adisumarta3e350812017-09-18 14:54:36 -0700290 if (res && res != -EINPROGRESS) {
291 ODU_BRIDGE_ERR("ipa_rm_add_dependency() failed\n");
292 goto fail_add_dependency_2;
293 }
Amir Levy9659e592016-10-27 18:08:27 +0300294 }
295
296 /* configure RX (ODU->IPA) EP */
297 odu_prod_params.client = IPA_CLIENT_ODU_PROD;
298 odu_prod_params.desc_fifo_sz = IPA_ODU_SYS_DESC_FIFO_SZ;
299 odu_prod_params.priv = odu_bridge_ctx->priv;
300 odu_prod_params.notify = odu_bridge_ctx->tx_dp_notify;
301 odu_prod_params.skip_ep_cfg = true;
302 res = ipa_setup_sys_pipe(&odu_prod_params,
303 &odu_bridge_ctx->odu_prod_hdl);
304 if (res) {
305 ODU_BRIDGE_ERR("fail to setup sys pipe ODU_PROD %d\n", res);
306 goto fail_odu_prod;
307 }
308
309 /* configure TX tethered (IPA->ODU) EP */
310 odu_teth_cons_params.client = IPA_CLIENT_ODU_TETH_CONS;
311 odu_teth_cons_params.desc_fifo_sz = IPA_ODU_SYS_DESC_FIFO_SZ;
312 odu_teth_cons_params.priv = odu_bridge_ctx->priv;
313 odu_teth_cons_params.notify = odu_bridge_teth_cons_cb;
314 odu_teth_cons_params.skip_ep_cfg = true;
315 res = ipa_setup_sys_pipe(&odu_teth_cons_params,
316 &odu_bridge_ctx->odu_teth_cons_hdl);
317 if (res) {
318 ODU_BRIDGE_ERR("fail to setup sys pipe ODU_TETH_CONS %d\n",
319 res);
320 goto fail_odu_teth_cons;
321 }
322
323 /* configure TX embedded(IPA->ODU) EP */
324 odu_emb_cons_params.client = IPA_CLIENT_ODU_EMB_CONS;
325 odu_emb_cons_params.ipa_ep_cfg.hdr.hdr_len = ETH_HLEN;
326 odu_emb_cons_params.ipa_ep_cfg.nat.nat_en = IPA_BYPASS_NAT;
327 odu_emb_cons_params.desc_fifo_sz = IPA_ODU_SYS_DESC_FIFO_SZ;
328 odu_emb_cons_params.priv = odu_bridge_ctx->priv;
329 odu_emb_cons_params.notify = odu_bridge_emb_cons_cb;
330 res = ipa_setup_sys_pipe(&odu_emb_cons_params,
331 &odu_bridge_ctx->odu_emb_cons_hdl);
332 if (res) {
333 ODU_BRIDGE_ERR("fail to setup sys pipe ODU_EMB_CONS %d\n", res);
334 goto fail_odu_emb_cons;
335 }
336
337 ODU_BRIDGE_DBG_LOW("odu_prod_hdl = %d, odu_emb_cons_hdl = %d\n",
338 odu_bridge_ctx->odu_prod_hdl, odu_bridge_ctx->odu_emb_cons_hdl);
339 ODU_BRIDGE_DBG_LOW("odu_teth_cons_hdl = %d\n",
340 odu_bridge_ctx->odu_teth_cons_hdl);
341
342 ODU_BRIDGE_FUNC_EXIT();
343
344 return 0;
345
346fail_odu_emb_cons:
347 ipa_teardown_sys_pipe(odu_bridge_ctx->odu_teth_cons_hdl);
348 odu_bridge_ctx->odu_teth_cons_hdl = 0;
349fail_odu_teth_cons:
350 ipa_teardown_sys_pipe(odu_bridge_ctx->odu_prod_hdl);
351 odu_bridge_ctx->odu_prod_hdl = 0;
352fail_odu_prod:
Michael Adisumarta3e350812017-09-18 14:54:36 -0700353 if (!ipa_pm_is_used())
354 ipa_rm_delete_dependency(IPA_RM_RESOURCE_Q6_PROD,
Amir Levy9659e592016-10-27 18:08:27 +0300355 IPA_RM_RESOURCE_ODU_ADAPT_CONS);
356fail_add_dependency_2:
Michael Adisumarta3e350812017-09-18 14:54:36 -0700357 if (!ipa_pm_is_used())
358 ipa_rm_delete_dependency(IPA_RM_RESOURCE_ODU_ADAPT_PROD,
Amir Levy9659e592016-10-27 18:08:27 +0300359 IPA_RM_RESOURCE_Q6_CONS);
360fail_add_dependency_1:
361 return res;
362}
363
364static int odu_bridge_disconnect_router(void)
365{
366 int res;
367
368 ODU_BRIDGE_FUNC_ENTRY();
369
370 res = ipa_teardown_sys_pipe(odu_bridge_ctx->odu_prod_hdl);
371 if (res)
372 ODU_BRIDGE_ERR("teardown ODU PROD failed\n");
373 odu_bridge_ctx->odu_prod_hdl = 0;
374
375 res = ipa_teardown_sys_pipe(odu_bridge_ctx->odu_emb_cons_hdl);
376 if (res)
377 ODU_BRIDGE_ERR("teardown ODU EMB CONS failed\n");
378 odu_bridge_ctx->odu_emb_cons_hdl = 0;
379
380 ODU_BRIDGE_FUNC_EXIT();
381
382 return 0;
383}
384
385static int odu_bridge_disconnect_bridge(void)
386{
387 int res;
388
389 ODU_BRIDGE_FUNC_ENTRY();
390
391 res = ipa_teardown_sys_pipe(odu_bridge_ctx->odu_prod_hdl);
392 if (res)
393 ODU_BRIDGE_ERR("teardown ODU PROD failed\n");
394 odu_bridge_ctx->odu_prod_hdl = 0;
395
396 res = ipa_teardown_sys_pipe(odu_bridge_ctx->odu_teth_cons_hdl);
397 if (res)
398 ODU_BRIDGE_ERR("teardown ODU TETH CONS failed\n");
399 odu_bridge_ctx->odu_teth_cons_hdl = 0;
400
401 res = ipa_teardown_sys_pipe(odu_bridge_ctx->odu_emb_cons_hdl);
402 if (res)
403 ODU_BRIDGE_ERR("teardown ODU EMB CONS failed\n");
404 odu_bridge_ctx->odu_emb_cons_hdl = 0;
405
Michael Adisumarta3e350812017-09-18 14:54:36 -0700406 if (!ipa_pm_is_used()) {
407 /* Delete IPA Resource manager dependency graph */
408 ODU_BRIDGE_DBG("deleting dependency graph\n");
409 res = ipa_rm_delete_dependency(IPA_RM_RESOURCE_ODU_ADAPT_PROD,
410 IPA_RM_RESOURCE_Q6_CONS);
411 if (res && res != -EINPROGRESS)
412 ODU_BRIDGE_ERR("ipa_rm_delete_dependency() failed\n");
Amir Levy9659e592016-10-27 18:08:27 +0300413
Michael Adisumarta3e350812017-09-18 14:54:36 -0700414 res = ipa_rm_delete_dependency(IPA_RM_RESOURCE_Q6_PROD,
415 IPA_RM_RESOURCE_ODU_ADAPT_CONS);
416 if (res && res != -EINPROGRESS)
417 ODU_BRIDGE_ERR("ipa_rm_delete_dependency() failed\n");
418 }
Amir Levy9659e592016-10-27 18:08:27 +0300419
420 return 0;
421}
422
423/**
424 * odu_bridge_disconnect() - Disconnect odu bridge
425 *
426 * Disconnect all pipes and deletes IPA RM dependencies on bridge mode
427 *
428 * Return codes: 0- success, error otherwise
429 */
430int odu_bridge_disconnect(void)
431{
432 int res;
433
434 ODU_BRIDGE_FUNC_ENTRY();
435
436 if (!odu_bridge_ctx) {
437 ODU_BRIDGE_ERR("Not initialized\n");
438 return -EFAULT;
439 }
440
441 if (!odu_bridge_ctx->is_connected) {
442 ODU_BRIDGE_ERR("Not connected\n");
443 return -EFAULT;
444 }
445
446 mutex_lock(&odu_bridge_ctx->lock);
447 if (odu_bridge_ctx->mode == ODU_BRIDGE_MODE_ROUTER) {
448 res = odu_bridge_disconnect_router();
449 if (res) {
450 ODU_BRIDGE_ERR("disconnect_router failed %d\n", res);
451 goto out;
452 }
453 } else {
454 res = odu_bridge_disconnect_bridge();
455 if (res) {
456 ODU_BRIDGE_ERR("disconnect_bridge failed %d\n", res);
457 goto out;
458 }
459 }
460
461 odu_bridge_ctx->is_connected = false;
462 res = 0;
463out:
464 mutex_unlock(&odu_bridge_ctx->lock);
465 ODU_BRIDGE_FUNC_EXIT();
466 return res;
467}
468EXPORT_SYMBOL(odu_bridge_disconnect);
469
470/**
471 * odu_bridge_connect() - Connect odu bridge.
472 *
473 * Call to the mode-specific connect function for connection IPA pipes
474 * and adding IPA RM dependencies
475
476 * Return codes: 0: success
477 * -EINVAL: invalid parameters
478 * -EPERM: Operation not permitted as the bridge is already
479 * connected
480 */
481int odu_bridge_connect(void)
482{
483 int res;
484
485 ODU_BRIDGE_FUNC_ENTRY();
486
487 if (!odu_bridge_ctx) {
488 ODU_BRIDGE_ERR("Not initialized\n");
489 return -EFAULT;
490 }
491
492 if (odu_bridge_ctx->is_connected) {
493 ODU_BRIDGE_ERR("already connected\n");
494 return -EFAULT;
495 }
496
497 mutex_lock(&odu_bridge_ctx->lock);
498 if (odu_bridge_ctx->mode == ODU_BRIDGE_MODE_ROUTER) {
499 res = odu_bridge_connect_router();
500 if (res) {
501 ODU_BRIDGE_ERR("connect_router failed\n");
502 goto bail;
503 }
504 } else {
505 res = odu_bridge_connect_bridge();
506 if (res) {
507 ODU_BRIDGE_ERR("connect_bridge failed\n");
508 goto bail;
509 }
510 }
511
512 odu_bridge_ctx->is_connected = true;
513 res = 0;
514bail:
515 mutex_unlock(&odu_bridge_ctx->lock);
516 ODU_BRIDGE_FUNC_EXIT();
517 return res;
518}
519EXPORT_SYMBOL(odu_bridge_connect);
520
521/**
522 * odu_bridge_set_mode() - Set bridge mode to Router/Bridge
523 * @mode: mode to be set
524 */
525static int odu_bridge_set_mode(enum odu_bridge_mode mode)
526{
527 int res;
528
529 ODU_BRIDGE_FUNC_ENTRY();
530
531 if (mode < 0 || mode >= ODU_BRIDGE_MODE_MAX) {
532 ODU_BRIDGE_ERR("Unsupported mode: %d\n", mode);
533 return -EFAULT;
534 }
535
536 ODU_BRIDGE_DBG_LOW("setting mode: %d\n", mode);
537 mutex_lock(&odu_bridge_ctx->lock);
538
539 if (odu_bridge_ctx->mode == mode) {
540 ODU_BRIDGE_DBG_LOW("same mode\n");
541 res = 0;
542 goto bail;
543 }
544
545 if (odu_bridge_ctx->is_connected) {
546 /* first disconnect the old configuration */
547 if (odu_bridge_ctx->mode == ODU_BRIDGE_MODE_ROUTER) {
548 res = odu_bridge_disconnect_router();
549 if (res) {
550 ODU_BRIDGE_ERR("disconnect_router failed\n");
551 goto bail;
552 }
553 } else {
554 res = odu_bridge_disconnect_bridge();
555 if (res) {
556 ODU_BRIDGE_ERR("disconnect_bridge failed\n");
557 goto bail;
558 }
559 }
560
561 /* connect the new configuration */
562 if (mode == ODU_BRIDGE_MODE_ROUTER) {
563 res = odu_bridge_connect_router();
564 if (res) {
565 ODU_BRIDGE_ERR("connect_router failed\n");
566 goto bail;
567 }
568 } else {
569 res = odu_bridge_connect_bridge();
570 if (res) {
571 ODU_BRIDGE_ERR("connect_bridge failed\n");
572 goto bail;
573 }
574 }
575 }
576 odu_bridge_ctx->mode = mode;
577 res = 0;
578bail:
579 mutex_unlock(&odu_bridge_ctx->lock);
580 ODU_BRIDGE_FUNC_EXIT();
581 return res;
582};
583
584/**
585 * odu_bridge_set_llv6_addr() - Set link local ipv6 address
586 * @llv6_addr: odu network interface link local address
587 *
588 * This function sets the link local ipv6 address provided by IOCTL
589 */
590static int odu_bridge_set_llv6_addr(struct in6_addr *llv6_addr)
591{
592 struct in6_addr llv6_addr_host;
593
594 ODU_BRIDGE_FUNC_ENTRY();
595
596 llv6_addr_host.s6_addr32[0] = ntohl(llv6_addr->s6_addr32[0]);
597 llv6_addr_host.s6_addr32[1] = ntohl(llv6_addr->s6_addr32[1]);
598 llv6_addr_host.s6_addr32[2] = ntohl(llv6_addr->s6_addr32[2]);
599 llv6_addr_host.s6_addr32[3] = ntohl(llv6_addr->s6_addr32[3]);
600
601 memcpy(&odu_bridge_ctx->llv6_addr, &llv6_addr_host,
602 sizeof(odu_bridge_ctx->llv6_addr));
603 ODU_BRIDGE_DBG_LOW("LLV6 addr: %pI6c\n", &odu_bridge_ctx->llv6_addr);
604
605 ODU_BRIDGE_FUNC_EXIT();
606
607 return 0;
608};
609
610static long odu_bridge_ioctl(struct file *filp,
611 unsigned int cmd,
612 unsigned long arg)
613{
614 int res = 0;
615 struct in6_addr llv6_addr;
616
617 ODU_BRIDGE_DBG("cmd=%x nr=%d\n", cmd, _IOC_NR(cmd));
618
619 if ((_IOC_TYPE(cmd) != ODU_BRIDGE_IOC_MAGIC) ||
620 (_IOC_NR(cmd) >= ODU_BRIDGE_IOCTL_MAX)) {
621 ODU_BRIDGE_ERR("Invalid ioctl\n");
622 return -ENOIOCTLCMD;
623 }
624
625 switch (cmd) {
626 case ODU_BRIDGE_IOC_SET_MODE:
627 ODU_BRIDGE_DBG("ODU_BRIDGE_IOC_SET_MODE ioctl called\n");
628 res = odu_bridge_set_mode(arg);
629 if (res) {
630 ODU_BRIDGE_ERR("Error, res = %d\n", res);
631 break;
632 }
633 break;
634
635 case ODU_BRIDGE_IOC_SET_LLV6_ADDR:
636 ODU_BRIDGE_DBG("ODU_BRIDGE_IOC_SET_LLV6_ADDR ioctl called\n");
637 res = copy_from_user(&llv6_addr,
638 (struct in6_addr *)arg,
639 sizeof(llv6_addr));
640 if (res) {
641 ODU_BRIDGE_ERR("Error, res = %d\n", res);
642 res = -EFAULT;
643 break;
644 }
645
646 res = odu_bridge_set_llv6_addr(&llv6_addr);
647 if (res) {
648 ODU_BRIDGE_ERR("Error, res = %d\n", res);
649 break;
650 }
651 break;
652
653 default:
654 ODU_BRIDGE_ERR("Unknown ioctl: %d\n", cmd);
655 WARN_ON(1);
656 }
657
658 return res;
659}
660
661#ifdef CONFIG_COMPAT
662static long compat_odu_bridge_ioctl(struct file *file,
663 unsigned int cmd, unsigned long arg)
664{
665 switch (cmd) {
666 case ODU_BRIDGE_IOC_SET_LLV6_ADDR32:
667 cmd = ODU_BRIDGE_IOC_SET_LLV6_ADDR;
668 break;
669 case ODU_BRIDGE_IOC_SET_MODE:
670 break;
671 default:
672 return -ENOIOCTLCMD;
673 }
674 return odu_bridge_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
675}
676#endif
677
678#ifdef CONFIG_DEBUG_FS
679static struct dentry *dent;
680static struct dentry *dfile_stats;
681static struct dentry *dfile_mode;
682
683static ssize_t odu_debugfs_stats(struct file *file,
684 char __user *ubuf,
685 size_t count,
686 loff_t *ppos)
687{
688 int nbytes = 0;
689
690 nbytes += scnprintf(&dbg_buff[nbytes],
691 ODU_MAX_MSG_LEN - nbytes,
692 "UL packets: %lld\n",
693 odu_bridge_ctx->stats.num_ul_packets);
694 nbytes += scnprintf(&dbg_buff[nbytes],
695 ODU_MAX_MSG_LEN - nbytes,
696 "DL packets: %lld\n",
697 odu_bridge_ctx->stats.num_dl_packets);
698 nbytes += scnprintf(&dbg_buff[nbytes],
699 ODU_MAX_MSG_LEN - nbytes,
700 "LAN packets: %lld\n",
701 odu_bridge_ctx->stats.num_lan_packets);
702 return simple_read_from_buffer(ubuf, count, ppos, dbg_buff, nbytes);
703}
704
705static ssize_t odu_debugfs_hw_bridge_mode_write(struct file *file,
706 const char __user *ubuf,
707 size_t count,
708 loff_t *ppos)
709{
710 unsigned long missing;
711 enum odu_bridge_mode mode;
712
713 if (sizeof(dbg_buff) < count + 1)
714 return -EFAULT;
715
716 missing = copy_from_user(dbg_buff, ubuf, count);
717 if (missing)
718 return -EFAULT;
719
720 if (count > 0)
721 dbg_buff[count-1] = '\0';
722
723 if (strcmp(dbg_buff, "router") == 0) {
724 mode = ODU_BRIDGE_MODE_ROUTER;
725 } else if (strcmp(dbg_buff, "bridge") == 0) {
726 mode = ODU_BRIDGE_MODE_BRIDGE;
727 } else {
728 ODU_BRIDGE_ERR("Bad mode, got %s,\n"
729 "Use <router> or <bridge>.\n", dbg_buff);
730 return count;
731 }
732
733 odu_bridge_set_mode(mode);
734 return count;
735}
736
737static ssize_t odu_debugfs_hw_bridge_mode_read(struct file *file,
738 char __user *ubuf,
739 size_t count,
740 loff_t *ppos)
741{
742 int nbytes = 0;
743
744 switch (odu_bridge_ctx->mode) {
745 case ODU_BRIDGE_MODE_ROUTER:
746 nbytes += scnprintf(&dbg_buff[nbytes],
747 ODU_MAX_MSG_LEN - nbytes,
748 "router\n");
749 break;
750 case ODU_BRIDGE_MODE_BRIDGE:
751 nbytes += scnprintf(&dbg_buff[nbytes],
752 ODU_MAX_MSG_LEN - nbytes,
753 "bridge\n");
754 break;
755 default:
756 nbytes += scnprintf(&dbg_buff[nbytes],
757 ODU_MAX_MSG_LEN - nbytes,
758 "mode error\n");
759 break;
760
761 }
762
763 return simple_read_from_buffer(ubuf, count, ppos, dbg_buff, nbytes);
764}
765
766const struct file_operations odu_stats_ops = {
767 .read = odu_debugfs_stats,
768};
769
770const struct file_operations odu_hw_bridge_mode_ops = {
771 .read = odu_debugfs_hw_bridge_mode_read,
772 .write = odu_debugfs_hw_bridge_mode_write,
773};
774
775static void odu_debugfs_init(void)
776{
777 const mode_t read_only_mode = S_IRUSR | S_IRGRP | S_IROTH;
778 const mode_t read_write_mode = S_IRUSR | S_IRGRP | S_IROTH |
779 S_IWUSR | S_IWGRP | S_IWOTH;
780
781 dent = debugfs_create_dir("odu_ipa_bridge", 0);
782 if (IS_ERR(dent)) {
783 ODU_BRIDGE_ERR("fail to create folder odu_ipa_bridge\n");
784 return;
785 }
786
787 dfile_stats =
788 debugfs_create_file("stats", read_only_mode, dent,
789 0, &odu_stats_ops);
790 if (!dfile_stats || IS_ERR(dfile_stats)) {
791 ODU_BRIDGE_ERR("fail to create file stats\n");
792 goto fail;
793 }
794
795 dfile_mode =
796 debugfs_create_file("mode", read_write_mode,
797 dent, 0, &odu_hw_bridge_mode_ops);
798 if (!dfile_mode ||
799 IS_ERR(dfile_mode)) {
800 ODU_BRIDGE_ERR("fail to create file dfile_mode\n");
801 goto fail;
802 }
803
804 return;
805fail:
806 debugfs_remove_recursive(dent);
807}
808
809static void odu_debugfs_destroy(void)
810{
811 debugfs_remove_recursive(dent);
812}
813
814#else
815static void odu_debugfs_init(void) {}
816static void odu_debugfs_destroy(void) {}
817#endif /* CONFIG_DEBUG_FS */
818
819
820static const struct file_operations odu_bridge_drv_fops = {
821 .owner = THIS_MODULE,
822 .unlocked_ioctl = odu_bridge_ioctl,
823#ifdef CONFIG_COMPAT
824 .compat_ioctl = compat_odu_bridge_ioctl,
825#endif
826};
827
828/**
829 * odu_bridge_tx_dp() - Send skb to ODU bridge
830 * @skb: skb to send
831 * @metadata: metadata on packet
832 *
833 * This function handles uplink packet.
834 * In Router Mode:
835 * packet is sent directly to IPA.
836 * In Router Mode:
837 * packet is classified if it should arrive to network stack.
838 * QMI IP packet should arrive to APPS network stack
839 * IPv6 Multicast packet should arrive to APPS network stack and Q6
840 *
841 * Return codes: 0- success, error otherwise
842 */
843int odu_bridge_tx_dp(struct sk_buff *skb, struct ipa_tx_meta *metadata)
844{
845 struct sk_buff *skb_copied = NULL;
846 struct ipv6hdr *ipv6hdr;
847 int res;
848
849 ODU_BRIDGE_FUNC_ENTRY();
850
851 switch (odu_bridge_ctx->mode) {
852 case ODU_BRIDGE_MODE_ROUTER:
853 /* Router mode - pass skb to IPA */
854 res = ipa_tx_dp(IPA_CLIENT_ODU_PROD, skb, metadata);
855 if (res) {
856 ODU_BRIDGE_DBG("tx dp failed %d\n", res);
857 goto out;
858 }
859 odu_bridge_ctx->stats.num_ul_packets++;
860 goto out;
861
862 case ODU_BRIDGE_MODE_BRIDGE:
863 ipv6hdr = (struct ipv6hdr *)(skb->data + ETH_HLEN);
864 if (ipv6hdr->version == 6 &&
865 ODU_BRIDGE_IS_QMI_ADDR(ipv6hdr->daddr)) {
866 ODU_BRIDGE_DBG_LOW("QMI packet\n");
867 skb_copied = skb_clone(skb, GFP_KERNEL);
868 if (!skb_copied) {
869 ODU_BRIDGE_ERR("No memory\n");
870 return -ENOMEM;
871 }
872 odu_bridge_ctx->tx_dp_notify(odu_bridge_ctx->priv,
873 IPA_RECEIVE,
874 (unsigned long)skb_copied);
875 odu_bridge_ctx->tx_dp_notify(odu_bridge_ctx->priv,
876 IPA_WRITE_DONE,
877 (unsigned long)skb);
878 odu_bridge_ctx->stats.num_ul_packets++;
879 odu_bridge_ctx->stats.num_lan_packets++;
880 res = 0;
881 goto out;
882 }
883
884 if (ipv6hdr->version == 6 &&
885 ipv6_addr_is_multicast(&ipv6hdr->daddr)) {
886 ODU_BRIDGE_DBG_LOW(
887 "Multicast pkt, send to APPS and IPA\n");
888 skb_copied = skb_clone(skb, GFP_KERNEL);
889 if (!skb_copied) {
890 ODU_BRIDGE_ERR("No memory\n");
891 return -ENOMEM;
892 }
893
894 res = ipa_tx_dp(IPA_CLIENT_ODU_PROD, skb, metadata);
895 if (res) {
896 ODU_BRIDGE_DBG("tx dp failed %d\n", res);
897 dev_kfree_skb(skb_copied);
898 goto out;
899 }
900
901 odu_bridge_ctx->tx_dp_notify(odu_bridge_ctx->priv,
902 IPA_RECEIVE,
903 (unsigned long)skb_copied);
904 odu_bridge_ctx->stats.num_ul_packets++;
905 odu_bridge_ctx->stats.num_lan_packets++;
906 goto out;
907 }
908
909 res = ipa_tx_dp(IPA_CLIENT_ODU_PROD, skb, metadata);
910 if (res) {
911 ODU_BRIDGE_DBG("tx dp failed %d\n", res);
912 goto out;
913 }
914 odu_bridge_ctx->stats.num_ul_packets++;
915 goto out;
916
917 default:
918 ODU_BRIDGE_ERR("Unsupported mode: %d\n", odu_bridge_ctx->mode);
919 WARN_ON(1);
920 res = -EFAULT;
921
922 }
923out:
924 ODU_BRIDGE_FUNC_EXIT();
925 return res;
926}
927EXPORT_SYMBOL(odu_bridge_tx_dp);
928
929static int odu_bridge_add_hdrs(void)
930{
931 struct ipa_ioc_add_hdr *hdrs;
932 struct ipa_hdr_add *ipv4_hdr;
933 struct ipa_hdr_add *ipv6_hdr;
934 struct ethhdr *eth_ipv4;
935 struct ethhdr *eth_ipv6;
936 int res;
937
938 ODU_BRIDGE_FUNC_ENTRY();
939 hdrs = kzalloc(sizeof(*hdrs) + sizeof(*ipv4_hdr) + sizeof(*ipv6_hdr),
940 GFP_KERNEL);
941 if (!hdrs) {
942 ODU_BRIDGE_ERR("no mem\n");
943 res = -ENOMEM;
944 goto out;
945 }
946 ipv4_hdr = &hdrs->hdr[0];
947 eth_ipv4 = (struct ethhdr *)(ipv4_hdr->hdr);
948 ipv6_hdr = &hdrs->hdr[1];
949 eth_ipv6 = (struct ethhdr *)(ipv6_hdr->hdr);
950 strlcpy(ipv4_hdr->name, ODU_BRIDGE_IPV4_HDR_NAME,
951 IPA_RESOURCE_NAME_MAX);
952 memcpy(eth_ipv4->h_source, odu_bridge_ctx->device_ethaddr, ETH_ALEN);
953 eth_ipv4->h_proto = htons(ETH_P_IP);
954 ipv4_hdr->hdr_len = ETH_HLEN;
955 ipv4_hdr->is_partial = 1;
956 ipv4_hdr->is_eth2_ofst_valid = 1;
957 ipv4_hdr->eth2_ofst = 0;
958 strlcpy(ipv6_hdr->name, ODU_BRIDGE_IPV6_HDR_NAME,
959 IPA_RESOURCE_NAME_MAX);
960 memcpy(eth_ipv6->h_source, odu_bridge_ctx->device_ethaddr, ETH_ALEN);
961 eth_ipv6->h_proto = htons(ETH_P_IPV6);
962 ipv6_hdr->hdr_len = ETH_HLEN;
963 ipv6_hdr->is_partial = 1;
964 ipv6_hdr->is_eth2_ofst_valid = 1;
965 ipv6_hdr->eth2_ofst = 0;
966 hdrs->commit = 1;
967 hdrs->num_hdrs = 2;
968 res = ipa_add_hdr(hdrs);
969 if (res) {
970 ODU_BRIDGE_ERR("Fail on Header-Insertion(%d)\n", res);
971 goto out_free_mem;
972 }
973 if (ipv4_hdr->status) {
974 ODU_BRIDGE_ERR("Fail on Header-Insertion ipv4(%d)\n",
975 ipv4_hdr->status);
976 res = ipv4_hdr->status;
977 goto out_free_mem;
978 }
979 if (ipv6_hdr->status) {
980 ODU_BRIDGE_ERR("Fail on Header-Insertion ipv6(%d)\n",
981 ipv6_hdr->status);
982 res = ipv6_hdr->status;
983 goto out_free_mem;
984 }
985 odu_bridge_ctx->odu_br_ipv4_hdr_hdl = ipv4_hdr->hdr_hdl;
986 odu_bridge_ctx->odu_br_ipv6_hdr_hdl = ipv6_hdr->hdr_hdl;
987
988 res = 0;
989out_free_mem:
990 kfree(hdrs);
991out:
992 ODU_BRIDGE_FUNC_EXIT();
993 return res;
994}
995
996static void odu_bridge_del_hdrs(void)
997{
998 struct ipa_ioc_del_hdr *del_hdr;
999 struct ipa_hdr_del *ipv4;
1000 struct ipa_hdr_del *ipv6;
1001 int result;
1002
1003 del_hdr = kzalloc(sizeof(*del_hdr) + sizeof(*ipv4) +
1004 sizeof(*ipv6), GFP_KERNEL);
1005 if (!del_hdr)
1006 return;
1007 del_hdr->commit = 1;
1008 del_hdr->num_hdls = 2;
1009 ipv4 = &del_hdr->hdl[0];
1010 ipv4->hdl = odu_bridge_ctx->odu_br_ipv4_hdr_hdl;
1011 ipv6 = &del_hdr->hdl[1];
1012 ipv6->hdl = odu_bridge_ctx->odu_br_ipv6_hdr_hdl;
1013 result = ipa_del_hdr(del_hdr);
1014 if (result || ipv4->status || ipv6->status)
1015 ODU_BRIDGE_ERR("ipa_del_hdr failed");
1016 kfree(del_hdr);
1017}
1018
1019/**
1020 * odu_bridge_register_properties() - set Tx/Rx properties for ipacm
1021 *
1022 * Register the network interface interface with Tx and Rx properties
1023 * Tx properties are for data flowing from IPA to adapter, they
1024 * have Header-Insertion properties both for Ipv4 and Ipv6 Ethernet framing.
1025 * Rx properties are for data flowing from adapter to IPA, they have
1026 * simple rule which always "hit".
1027 *
1028 */
1029static int odu_bridge_register_properties(void)
1030{
1031 struct ipa_tx_intf tx_properties = {0};
1032 struct ipa_ioc_tx_intf_prop properties[2] = { {0}, {0} };
1033 struct ipa_ioc_tx_intf_prop *ipv4_property;
1034 struct ipa_ioc_tx_intf_prop *ipv6_property;
1035 struct ipa_ioc_rx_intf_prop rx_ioc_properties[2] = { {0}, {0} };
1036 struct ipa_rx_intf rx_properties = {0};
1037 struct ipa_ioc_rx_intf_prop *rx_ipv4_property;
1038 struct ipa_ioc_rx_intf_prop *rx_ipv6_property;
1039 int res = 0;
1040
1041 ODU_BRIDGE_FUNC_ENTRY();
1042
1043 tx_properties.prop = properties;
1044 ipv4_property = &tx_properties.prop[0];
1045 ipv4_property->ip = IPA_IP_v4;
1046 ipv4_property->dst_pipe = IPA_CLIENT_ODU_EMB_CONS;
1047 ipv4_property->hdr_l2_type = IPA_HDR_L2_ETHERNET_II;
1048 strlcpy(ipv4_property->hdr_name, ODU_BRIDGE_IPV4_HDR_NAME,
1049 IPA_RESOURCE_NAME_MAX);
1050 ipv6_property = &tx_properties.prop[1];
1051 ipv6_property->ip = IPA_IP_v6;
1052 ipv6_property->dst_pipe = IPA_CLIENT_ODU_EMB_CONS;
1053 ipv6_property->hdr_l2_type = IPA_HDR_L2_ETHERNET_II;
1054 strlcpy(ipv6_property->hdr_name, ODU_BRIDGE_IPV6_HDR_NAME,
1055 IPA_RESOURCE_NAME_MAX);
1056 tx_properties.num_props = 2;
1057
1058 rx_properties.prop = rx_ioc_properties;
1059 rx_ipv4_property = &rx_properties.prop[0];
1060 rx_ipv4_property->ip = IPA_IP_v4;
1061 rx_ipv4_property->attrib.attrib_mask = 0;
1062 rx_ipv4_property->src_pipe = IPA_CLIENT_ODU_PROD;
1063 rx_ipv4_property->hdr_l2_type = IPA_HDR_L2_ETHERNET_II;
1064 rx_ipv6_property = &rx_properties.prop[1];
1065 rx_ipv6_property->ip = IPA_IP_v6;
1066 rx_ipv6_property->attrib.attrib_mask = 0;
1067 rx_ipv6_property->src_pipe = IPA_CLIENT_ODU_PROD;
1068 rx_ipv6_property->hdr_l2_type = IPA_HDR_L2_ETHERNET_II;
1069 rx_properties.num_props = 2;
1070
1071 res = ipa_register_intf(odu_bridge_ctx->netdev_name, &tx_properties,
1072 &rx_properties);
1073 if (res) {
1074 ODU_BRIDGE_ERR("fail on Tx/Rx properties registration %d\n",
1075 res);
1076 }
1077
1078 ODU_BRIDGE_FUNC_EXIT();
1079
1080 return res;
1081}
1082
1083static void odu_bridge_deregister_properties(void)
1084{
1085 int res;
1086
1087 ODU_BRIDGE_FUNC_ENTRY();
1088 res = ipa_deregister_intf(odu_bridge_ctx->netdev_name);
1089 if (res)
1090 ODU_BRIDGE_ERR("Fail on Tx prop deregister %d\n", res);
1091 ODU_BRIDGE_FUNC_EXIT();
1092}
1093
1094/**
1095 * odu_bridge_init() - Initialize the ODU bridge driver
1096 * @params: initialization parameters
1097 *
1098 * This function initialize all bridge internal data and register odu bridge to
1099 * kernel for IOCTL and debugfs.
1100 * Header addition and properties are registered to IPA driver.
1101 *
1102 * Return codes: 0: success,
1103 * -EINVAL - Bad parameter
1104 * Other negative value - Failure
1105 */
1106int odu_bridge_init(struct odu_bridge_params *params)
1107{
1108 int res;
1109
1110 ODU_BRIDGE_FUNC_ENTRY();
1111
1112 if (!params) {
1113 ODU_BRIDGE_ERR("null pointer params\n");
1114 return -EINVAL;
1115 }
1116 if (!params->netdev_name) {
1117 ODU_BRIDGE_ERR("null pointer params->netdev_name\n");
1118 return -EINVAL;
1119 }
1120 if (!params->tx_dp_notify) {
1121 ODU_BRIDGE_ERR("null pointer params->tx_dp_notify\n");
1122 return -EINVAL;
1123 }
1124 if (!params->send_dl_skb) {
1125 ODU_BRIDGE_ERR("null pointer params->send_dl_skb\n");
1126 return -EINVAL;
1127 }
1128 if (odu_bridge_ctx) {
1129 ODU_BRIDGE_ERR("Already initialized\n");
1130 return -EFAULT;
1131 }
1132 if (!ipa_is_ready()) {
1133 ODU_BRIDGE_ERR("IPA is not ready\n");
1134 return -EFAULT;
1135 }
1136
1137 ODU_BRIDGE_DBG("device_ethaddr=%pM\n", params->device_ethaddr);
1138
1139 odu_bridge_ctx = kzalloc(sizeof(*odu_bridge_ctx), GFP_KERNEL);
1140 if (!odu_bridge_ctx) {
1141 ODU_BRIDGE_ERR("kzalloc err.\n");
1142 return -ENOMEM;
1143 }
1144
1145 odu_bridge_ctx->class = class_create(THIS_MODULE, ODU_BRIDGE_DRV_NAME);
1146 if (!odu_bridge_ctx->class) {
1147 ODU_BRIDGE_ERR("Class_create err.\n");
1148 res = -ENODEV;
1149 goto fail_class_create;
1150 }
1151
1152 res = alloc_chrdev_region(&odu_bridge_ctx->dev_num, 0, 1,
1153 ODU_BRIDGE_DRV_NAME);
1154 if (res) {
1155 ODU_BRIDGE_ERR("alloc_chrdev_region err.\n");
1156 res = -ENODEV;
1157 goto fail_alloc_chrdev_region;
1158 }
1159
1160 odu_bridge_ctx->dev = device_create(odu_bridge_ctx->class, NULL,
1161 odu_bridge_ctx->dev_num, odu_bridge_ctx, ODU_BRIDGE_DRV_NAME);
1162 if (IS_ERR(odu_bridge_ctx->dev)) {
1163 ODU_BRIDGE_ERR(":device_create err.\n");
1164 res = -ENODEV;
1165 goto fail_device_create;
1166 }
1167
1168 cdev_init(&odu_bridge_ctx->cdev, &odu_bridge_drv_fops);
1169 odu_bridge_ctx->cdev.owner = THIS_MODULE;
1170 odu_bridge_ctx->cdev.ops = &odu_bridge_drv_fops;
1171
1172 res = cdev_add(&odu_bridge_ctx->cdev, odu_bridge_ctx->dev_num, 1);
1173 if (res) {
1174 ODU_BRIDGE_ERR(":cdev_add err=%d\n", -res);
1175 res = -ENODEV;
1176 goto fail_cdev_add;
1177 }
1178
1179 odu_debugfs_init();
1180
1181 strlcpy(odu_bridge_ctx->netdev_name, params->netdev_name,
1182 IPA_RESOURCE_NAME_MAX);
1183 odu_bridge_ctx->priv = params->priv;
1184 odu_bridge_ctx->tx_dp_notify = params->tx_dp_notify;
1185 odu_bridge_ctx->send_dl_skb = params->send_dl_skb;
1186 memcpy(odu_bridge_ctx->device_ethaddr, params->device_ethaddr,
1187 ETH_ALEN);
1188 odu_bridge_ctx->ipa_sys_desc_size = params->ipa_desc_size;
1189 odu_bridge_ctx->mode = ODU_BRIDGE_MODE_ROUTER;
1190
1191 mutex_init(&odu_bridge_ctx->lock);
1192
1193 res = odu_bridge_add_hdrs();
1194 if (res) {
1195 ODU_BRIDGE_ERR("fail on odu_bridge_add_hdr %d\n", res);
1196 goto fail_add_hdrs;
1197 }
1198
1199 res = odu_bridge_register_properties();
1200 if (res) {
1201 ODU_BRIDGE_ERR("fail on register properties %d\n", res);
1202 goto fail_register_properties;
1203 }
1204
1205 ODU_BRIDGE_FUNC_EXIT();
1206 return 0;
1207
1208fail_register_properties:
1209 odu_bridge_del_hdrs();
1210fail_add_hdrs:
1211 odu_debugfs_destroy();
1212fail_cdev_add:
1213 device_destroy(odu_bridge_ctx->class, odu_bridge_ctx->dev_num);
1214fail_device_create:
1215 unregister_chrdev_region(odu_bridge_ctx->dev_num, 1);
1216fail_alloc_chrdev_region:
1217 class_destroy(odu_bridge_ctx->class);
1218fail_class_create:
1219 kfree(odu_bridge_ctx);
1220 odu_bridge_ctx = NULL;
1221 return res;
1222}
1223EXPORT_SYMBOL(odu_bridge_init);
1224
1225/**
1226 * odu_bridge_cleanup() - De-Initialize the ODU bridge driver
1227 *
1228 * Return codes: 0: success,
1229 * -EINVAL - Bad parameter
1230 * Other negative value - Failure
1231 */
1232int odu_bridge_cleanup(void)
1233{
1234 ODU_BRIDGE_FUNC_ENTRY();
1235
1236 if (!odu_bridge_ctx) {
1237 ODU_BRIDGE_ERR("Not initialized\n");
1238 return -EFAULT;
1239 }
1240
1241 if (odu_bridge_ctx->is_connected) {
1242 ODU_BRIDGE_ERR("cannot deinit while bridge is conncetd\n");
1243 return -EFAULT;
1244 }
1245
1246 odu_bridge_deregister_properties();
1247 odu_bridge_del_hdrs();
1248 odu_debugfs_destroy();
1249 cdev_del(&odu_bridge_ctx->cdev);
1250 device_destroy(odu_bridge_ctx->class, odu_bridge_ctx->dev_num);
1251 unregister_chrdev_region(odu_bridge_ctx->dev_num, 1);
1252 class_destroy(odu_bridge_ctx->class);
1253 ipc_log_context_destroy(odu_bridge_ctx->logbuf);
1254 ipc_log_context_destroy(odu_bridge_ctx->logbuf_low);
1255 kfree(odu_bridge_ctx);
1256 odu_bridge_ctx = NULL;
1257
1258 ODU_BRIDGE_FUNC_EXIT();
1259 return 0;
1260}
1261EXPORT_SYMBOL(odu_bridge_cleanup);
1262
Skylar Chang9fbce062017-07-25 16:20:42 -07001263/* IPA Bridge implementation */
1264#ifdef CONFIG_IPA3
1265
1266static void ipa_br_rm_notify(void *user_data, enum ipa_rm_event event,
1267 unsigned long data)
1268{
1269 if (event == IPA_RM_RESOURCE_GRANTED)
1270 complete(&odu_bridge_ctx->rm_comp);
1271}
1272
1273static int ipa_br_request_prod(void)
1274{
1275 int res;
1276
1277 ODU_BRIDGE_FUNC_ENTRY();
1278
1279 reinit_completion(&odu_bridge_ctx->rm_comp);
1280 ODU_BRIDGE_DBG("requesting odu prod\n");
1281 res = ipa_rm_request_resource(IPA_RM_RESOURCE_ODU_ADAPT_PROD);
1282 if (res) {
1283 if (res != -EINPROGRESS) {
1284 ODU_BRIDGE_ERR("failed to request prod %d\n", res);
1285 return res;
1286 }
1287 wait_for_completion(&odu_bridge_ctx->rm_comp);
1288 }
1289
1290 ODU_BRIDGE_FUNC_EXIT();
1291 return 0;
1292
1293}
1294
1295static int ipa_br_release_prod(void)
1296{
1297 int res;
1298
1299 ODU_BRIDGE_FUNC_ENTRY();
1300
1301 reinit_completion(&odu_bridge_ctx->rm_comp);
1302 ODU_BRIDGE_DBG("requesting odu prod\n");
1303 res = ipa_rm_release_resource(IPA_RM_RESOURCE_ODU_ADAPT_PROD);
1304 if (res) {
1305 ODU_BRIDGE_ERR("failed to release prod %d\n", res);
1306 return res;
1307 }
1308
1309 ODU_BRIDGE_FUNC_EXIT();
1310 return 0;
1311
1312}
1313
1314static int ipa_br_cons_request(void)
1315{
1316 ODU_BRIDGE_FUNC_ENTRY();
1317 if (odu_bridge_ctx->is_suspended)
1318 odu_bridge_ctx->wakeup_request(odu_bridge_ctx->priv);
1319 ODU_BRIDGE_FUNC_EXIT();
1320 return 0;
1321}
1322
1323static int ipa_br_cons_release(void)
1324{
1325 ODU_BRIDGE_FUNC_ENTRY();
1326 ODU_BRIDGE_FUNC_EXIT();
1327 return 0;
1328}
1329
Michael Adisumarta3e350812017-09-18 14:54:36 -07001330static void ipa_br_pm_cb(void *p, enum ipa_pm_cb_event event)
1331{
1332 ODU_BRIDGE_FUNC_ENTRY();
1333 if (event != IPA_PM_REQUEST_WAKEUP) {
1334 ODU_BRIDGE_ERR("Unexpected event %d\n", event);
1335 WARN_ON(1);
1336 return;
1337 }
1338
1339 if (odu_bridge_ctx->is_suspended)
1340 odu_bridge_ctx->wakeup_request(odu_bridge_ctx->priv);
1341 ODU_BRIDGE_FUNC_EXIT();
1342}
1343
1344static int ipa_br_register_pm(void)
1345{
1346 struct ipa_pm_register_params reg_params;
1347 int ret;
1348
1349 memset(&reg_params, 0, sizeof(reg_params));
1350 reg_params.name = "ODU Bridge";
1351 reg_params.callback = ipa_br_pm_cb;
1352 reg_params.group = IPA_PM_GROUP_DEFAULT;
1353
1354 ret = ipa_pm_register(&reg_params,
1355 &odu_bridge_ctx->pm_hdl);
1356 if (ret) {
1357 ODU_BRIDGE_ERR("fail to register with PM %d\n", ret);
1358 goto fail_pm_reg;
1359 }
1360
1361 ret = ipa_pm_associate_ipa_cons_to_client(odu_bridge_ctx->pm_hdl,
1362 IPA_CLIENT_ODU_EMB_CONS);
1363 if (ret) {
1364 ODU_BRIDGE_ERR("fail to associate cons with PM %d\n", ret);
1365 goto fail_pm_cons;
1366 }
1367
1368 return 0;
1369
1370fail_pm_cons:
1371 ipa_pm_deregister(odu_bridge_ctx->pm_hdl);
1372 odu_bridge_ctx->pm_hdl = ~0;
1373fail_pm_reg:
1374 return ret;
1375}
1376
1377static int ipa_br_create_rm_resources(void)
Skylar Chang9fbce062017-07-25 16:20:42 -07001378{
1379 int ret;
1380 struct ipa_rm_create_params create_params;
1381
Skylar Chang9fbce062017-07-25 16:20:42 -07001382 /* create IPA RM resources for power management */
1383 init_completion(&odu_bridge_ctx->rm_comp);
1384 memset(&create_params, 0, sizeof(create_params));
1385 create_params.name = IPA_RM_RESOURCE_ODU_ADAPT_PROD;
1386 create_params.reg_params.user_data = odu_bridge_ctx;
1387 create_params.reg_params.notify_cb = ipa_br_rm_notify;
1388 create_params.floor_voltage = IPA_VOLTAGE_SVS;
1389 ret = ipa_rm_create_resource(&create_params);
1390 if (ret) {
1391 ODU_BRIDGE_ERR("failed to create RM prod %d\n", ret);
1392 goto fail_rm_prod;
1393 }
1394
1395 ret = ipa_rm_add_dependency_sync(IPA_RM_RESOURCE_ODU_ADAPT_PROD,
1396 IPA_RM_RESOURCE_APPS_CONS);
1397 if (ret) {
1398 ODU_BRIDGE_ERR("failed to add ODU->APPS dependency %d\n", ret);
1399 goto fail_add_dep;
1400 }
1401
1402 memset(&create_params, 0, sizeof(create_params));
1403 create_params.name = IPA_RM_RESOURCE_ODU_ADAPT_CONS;
1404 create_params.request_resource = ipa_br_cons_request;
1405 create_params.release_resource = ipa_br_cons_release;
1406 create_params.floor_voltage = IPA_VOLTAGE_SVS;
1407 ret = ipa_rm_create_resource(&create_params);
1408 if (ret) {
1409 ODU_BRIDGE_ERR("failed to create RM cons %d\n", ret);
1410 goto fail_rm_cons;
1411 }
1412
Skylar Chang9fbce062017-07-25 16:20:42 -07001413 return 0;
1414
1415fail_rm_cons:
1416 ipa_rm_delete_dependency(IPA_RM_RESOURCE_ODU_ADAPT_PROD,
1417 IPA_RM_RESOURCE_APPS_CONS);
1418fail_add_dep:
1419 ipa_rm_delete_resource(IPA_RM_RESOURCE_ODU_ADAPT_PROD);
1420fail_rm_prod:
Michael Adisumarta3e350812017-09-18 14:54:36 -07001421 return ret;
1422}
1423
1424/* IPA Bridge API is the new API which will replaces old odu_bridge API */
1425int ipa_bridge_init(struct ipa_bridge_init_params *params, u32 *hdl)
1426{
1427 int ret;
1428
1429 if (!params || !params->wakeup_request || !hdl) {
1430 ODU_BRIDGE_ERR("NULL arg\n");
1431 return -EINVAL;
1432 }
1433
1434
1435 ret = odu_bridge_init(&params->info);
1436 if (ret)
1437 return ret;
1438
1439 odu_bridge_ctx->wakeup_request = params->wakeup_request;
1440
1441 if (ipa_pm_is_used())
1442 ret = ipa_br_register_pm();
1443 else
1444 ret = ipa_br_create_rm_resources();
1445 if (ret) {
1446 ODU_BRIDGE_ERR("fail to register woth RM/PM %d\n", ret);
1447 goto fail_pm;
1448 }
1449
1450 /* handle is ignored for now */
1451 *hdl = 0;
1452
1453 return 0;
1454
1455fail_pm:
Skylar Chang9fbce062017-07-25 16:20:42 -07001456 odu_bridge_cleanup();
1457 return ret;
1458}
1459EXPORT_SYMBOL(ipa_bridge_init);
1460
1461int ipa_bridge_connect(u32 hdl)
1462{
1463 int ret;
1464
1465 if (!odu_bridge_ctx) {
1466 ODU_BRIDGE_ERR("Not initialized\n");
1467 return -EFAULT;
1468 }
1469
1470 if (odu_bridge_ctx->is_connected) {
1471 ODU_BRIDGE_ERR("already connected\n");
1472 return -EFAULT;
1473 }
1474
Michael Adisumarta3e350812017-09-18 14:54:36 -07001475 if (ipa_pm_is_used())
1476 ret = ipa_pm_activate_sync(odu_bridge_ctx->pm_hdl);
1477 else
1478 ret = ipa_br_request_prod();
Skylar Chang9fbce062017-07-25 16:20:42 -07001479 if (ret)
1480 return ret;
1481
1482 return odu_bridge_connect();
1483}
1484EXPORT_SYMBOL(ipa_bridge_connect);
1485
1486int ipa_bridge_set_perf_profile(u32 hdl, u32 bandwidth)
1487{
1488 struct ipa_rm_perf_profile profile = {0};
1489 int ret;
1490
Michael Adisumarta3e350812017-09-18 14:54:36 -07001491 if (ipa_pm_is_used())
1492 return ipa_pm_set_perf_profile(odu_bridge_ctx->pm_hdl,
1493 bandwidth);
1494
Skylar Chang9fbce062017-07-25 16:20:42 -07001495 profile.max_supported_bandwidth_mbps = bandwidth;
1496 ret = ipa_rm_set_perf_profile(IPA_RM_RESOURCE_ODU_ADAPT_PROD, &profile);
1497 if (ret) {
1498 ODU_BRIDGE_ERR("failed to set perf profile to prod %d\n", ret);
1499 return ret;
1500 }
1501
1502 ret = ipa_rm_set_perf_profile(IPA_RM_RESOURCE_ODU_ADAPT_CONS, &profile);
1503 if (ret) {
1504 ODU_BRIDGE_ERR("failed to set perf profile to cons %d\n", ret);
1505 return ret;
1506 }
1507
1508 return 0;
1509}
1510EXPORT_SYMBOL(ipa_bridge_set_perf_profile);
1511
1512int ipa_bridge_disconnect(u32 hdl)
1513{
1514 int ret;
1515
1516 ret = odu_bridge_disconnect();
1517 if (ret)
1518 return ret;
1519
Michael Adisumarta3e350812017-09-18 14:54:36 -07001520 if (ipa_pm_is_used())
1521 ret = ipa_pm_deactivate_sync(odu_bridge_ctx->pm_hdl);
1522 else
1523 ret = ipa_br_release_prod();
Skylar Chang9fbce062017-07-25 16:20:42 -07001524 if (ret)
1525 return ret;
1526
1527 return 0;
1528}
1529EXPORT_SYMBOL(ipa_bridge_disconnect);
1530
1531int ipa_bridge_suspend(u32 hdl)
1532{
1533 int ret;
1534
1535 if (!odu_bridge_ctx) {
1536 ODU_BRIDGE_ERR("Not initialized\n");
1537 return -EFAULT;
1538 }
1539
1540 if (!odu_bridge_ctx->is_connected) {
1541 ODU_BRIDGE_ERR("bridge is disconnected\n");
1542 return -EFAULT;
1543 }
1544
1545 if (odu_bridge_ctx->is_suspended) {
1546 ODU_BRIDGE_ERR("bridge is already suspended\n");
1547 return -EFAULT;
1548 }
1549
1550 /* stop cons channel to prevent downlink data during suspend */
1551 ret = ipa_stop_gsi_channel(odu_bridge_ctx->odu_emb_cons_hdl);
1552 if (ret) {
1553 ODU_BRIDGE_ERR("failed to stop CONS channel %d\n", ret);
1554 return ret;
1555 }
1556
Michael Adisumarta3e350812017-09-18 14:54:36 -07001557 if (ipa_pm_is_used())
1558 ret = ipa_pm_deactivate_sync(odu_bridge_ctx->pm_hdl);
1559 else
1560 ret = ipa_br_release_prod();
Skylar Chang9fbce062017-07-25 16:20:42 -07001561 if (ret) {
1562 ODU_BRIDGE_ERR("failed to release prod %d\n", ret);
1563 ipa_start_gsi_channel(odu_bridge_ctx->odu_emb_cons_hdl);
1564 return ret;
1565 }
1566 odu_bridge_ctx->is_suspended = true;
1567
1568 return 0;
1569}
1570EXPORT_SYMBOL(ipa_bridge_suspend);
1571
1572int ipa_bridge_resume(u32 hdl)
1573{
1574 int ret;
1575
1576 if (!odu_bridge_ctx) {
1577 ODU_BRIDGE_ERR("Not initialized\n");
1578 return -EFAULT;
1579 }
1580
1581 if (!odu_bridge_ctx->is_connected) {
1582 ODU_BRIDGE_ERR("bridge is disconnected\n");
1583 return -EFAULT;
1584 }
1585
1586 if (!odu_bridge_ctx->is_suspended) {
1587 ODU_BRIDGE_ERR("bridge is not suspended\n");
1588 return -EFAULT;
1589 }
1590
Michael Adisumarta3e350812017-09-18 14:54:36 -07001591 if (ipa_pm_is_used())
1592 ret = ipa_pm_activate_sync(odu_bridge_ctx->pm_hdl);
1593 else
1594 ret = ipa_br_request_prod();
Skylar Chang9fbce062017-07-25 16:20:42 -07001595 if (ret)
1596 return ret;
1597
1598 ret = ipa_start_gsi_channel(odu_bridge_ctx->odu_emb_cons_hdl);
1599 if (ret) {
1600 ODU_BRIDGE_ERR("failed to start CONS channel %d\n", ret);
1601 return ret;
1602 }
1603 odu_bridge_ctx->is_suspended = false;
1604
1605 return 0;
1606}
1607EXPORT_SYMBOL(ipa_bridge_resume);
1608
1609int ipa_bridge_tx_dp(u32 hdl, struct sk_buff *skb,
1610 struct ipa_tx_meta *metadata)
1611{
1612 return odu_bridge_tx_dp(skb, metadata);
1613}
1614EXPORT_SYMBOL(ipa_bridge_tx_dp);
1615
Michael Adisumarta3e350812017-09-18 14:54:36 -07001616static void ipa_br_delete_rm_resources(void)
Skylar Chang9fbce062017-07-25 16:20:42 -07001617{
1618 ipa_rm_delete_dependency(IPA_RM_RESOURCE_ODU_ADAPT_PROD,
1619 IPA_RM_RESOURCE_APPS_CONS);
1620 ipa_rm_delete_resource(IPA_RM_RESOURCE_ODU_ADAPT_PROD);
1621 ipa_rm_delete_resource(IPA_RM_RESOURCE_ODU_ADAPT_CONS);
Michael Adisumarta3e350812017-09-18 14:54:36 -07001622}
1623
1624static void ipa_br_deregister_pm(void)
1625{
1626 ipa_pm_deactivate_sync(odu_bridge_ctx->pm_hdl);
1627 ipa_pm_deregister(odu_bridge_ctx->pm_hdl);
1628 odu_bridge_ctx->pm_hdl = ~0;
1629}
1630
1631int ipa_bridge_cleanup(u32 hdl)
1632{
1633 if (ipa_pm_is_used())
1634 ipa_br_deregister_pm();
1635 else
1636 ipa_br_delete_rm_resources();
Skylar Chang9fbce062017-07-25 16:20:42 -07001637 return odu_bridge_cleanup();
1638}
1639EXPORT_SYMBOL(ipa_bridge_cleanup);
1640
1641#endif /* CONFIG_IPA3 */
Amir Levy9659e592016-10-27 18:08:27 +03001642
1643MODULE_LICENSE("GPL v2");
1644MODULE_DESCRIPTION("ODU bridge driver");