blob: 32284103ec4d11a20f14bfb36a567d8f22d7b926 [file] [log] [blame]
Shihuan Liufbc51862018-02-27 23:21:14 -08001/* Copyright (c) 2014-2018, 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"
Mohammed Javid4c4037e2017-11-27 16:23:35 +053030#ifdef CONFIG_IPA3
Michael Adisumarta3e350812017-09-18 14:54:36 -070031#include "../ipa_v3/ipa_pm.h"
Mohammed Javid4c4037e2017-11-27 16:23:35 +053032#endif
Amir Levy9659e592016-10-27 18:08:27 +030033
34#define ODU_BRIDGE_DRV_NAME "odu_ipa_bridge"
35
36#define ODU_BRIDGE_DBG(fmt, args...) \
37 do { \
38 pr_debug(ODU_BRIDGE_DRV_NAME " %s:%d " fmt, \
39 __func__, __LINE__, ## args); \
40 IPA_IPC_LOGGING(ipa_get_ipc_logbuf(), \
41 ODU_BRIDGE_DRV_NAME " %s:%d " fmt, ## args); \
42 IPA_IPC_LOGGING(ipa_get_ipc_logbuf_low(), \
43 ODU_BRIDGE_DRV_NAME " %s:%d " fmt, ## args); \
44 } while (0)
45#define ODU_BRIDGE_DBG_LOW(fmt, args...) \
46 do { \
47 pr_debug(ODU_BRIDGE_DRV_NAME " %s:%d " fmt, \
48 __func__, __LINE__, ## args); \
49 IPA_IPC_LOGGING(ipa_get_ipc_logbuf_low(), \
50 ODU_BRIDGE_DRV_NAME " %s:%d " fmt, ## args); \
51 } while (0)
52#define ODU_BRIDGE_ERR(fmt, args...) \
53 do { \
54 pr_err(ODU_BRIDGE_DRV_NAME " %s:%d " fmt, \
55 __func__, __LINE__, ## args); \
56 IPA_IPC_LOGGING(ipa_get_ipc_logbuf(), \
57 ODU_BRIDGE_DRV_NAME " %s:%d " fmt, ## args); \
58 IPA_IPC_LOGGING(ipa_get_ipc_logbuf_low(), \
59 ODU_BRIDGE_DRV_NAME " %s:%d " fmt, ## args); \
60 } while (0)
61
62#define ODU_BRIDGE_FUNC_ENTRY() \
63 ODU_BRIDGE_DBG_LOW("ENTRY\n")
64#define ODU_BRIDGE_FUNC_EXIT() \
65 ODU_BRIDGE_DBG_LOW("EXIT\n")
66
67
68#define ODU_BRIDGE_IS_QMI_ADDR(daddr) \
69 (memcmp(&(daddr), &odu_bridge_ctx->llv6_addr, sizeof((daddr))) \
70 == 0)
71
72#define ODU_BRIDGE_IPV4_HDR_NAME "odu_br_ipv4"
73#define ODU_BRIDGE_IPV6_HDR_NAME "odu_br_ipv6"
74
75#define IPA_ODU_SYS_DESC_FIFO_SZ 0x800
76
77#ifdef CONFIG_COMPAT
78#define ODU_BRIDGE_IOC_SET_LLV6_ADDR32 _IOW(ODU_BRIDGE_IOC_MAGIC, \
79 ODU_BRIDGE_IOCTL_SET_LLV6_ADDR, \
80 compat_uptr_t)
81#endif
82
83#define IPA_ODU_VER_CHECK() \
84 do { \
85 ret = 0;\
86 if (ipa_get_hw_type() == IPA_HW_None) { \
87 pr_err("IPA HW is unknown\n"); \
88 ret = -EFAULT; \
89 } \
90 else if (ipa_get_hw_type() < IPA_HW_v3_0) \
91 ret = 1; \
92 } while (0)
93
94/**
95 * struct stats - driver statistics, viewable using debugfs
96 * @num_ul_packets: number of packets bridged in uplink direction
97 * @num_dl_packets: number of packets bridged in downink direction
98 * bridge
99 * @num_lan_packets: number of packets bridged to APPS on bridge mode
100 */
101struct stats {
102 u64 num_ul_packets;
103 u64 num_dl_packets;
104 u64 num_lan_packets;
105};
106
107/**
108 * struct odu_bridge_ctx - ODU bridge driver context information
109 * @class: kernel class pointer
110 * @dev_num: kernel device number
111 * @dev: kernel device struct pointer
112 * @cdev: kernel character device struct
113 * @netdev_name: network interface name
114 * @device_ethaddr: network interface ethernet address
115 * @priv: client's private data. to be used in client's callbacks
116 * @tx_dp_notify: client callback for handling IPA ODU_PROD callback
117 * @send_dl_skb: client callback for sending skb in downlink direction
118 * @stats: statistics, how many packets were transmitted using the SW bridge
119 * @is_conencted: is bridge connected ?
Skylar Chang9fbce062017-07-25 16:20:42 -0700120 * @is_suspended: is bridge suspended ?
Amir Levy9659e592016-10-27 18:08:27 +0300121 * @mode: ODU mode (router/bridge)
122 * @lock: for the initialization, connect and disconnect synchronization
123 * @llv6_addr: link local IPv6 address of ODU network interface
124 * @odu_br_ipv4_hdr_hdl: handle for partial ipv4 ethernet header
125 * @odu_br_ipv6_hdr_hdl: handle for partial ipv6 ethernet header
126 * @odu_prod_hdl: handle for IPA_CLIENT_ODU_PROD pipe
127 * @odu_emb_cons_hdl: handle for IPA_CLIENT_ODU_EMB_CONS pipe
128 * @odu_teth_cons_hdl: handle for IPA_CLIENT_ODU_TETH_CONS pipe
Skylar Chang9fbce062017-07-25 16:20:42 -0700129 * @rm_comp: completion object for IP RM
130 * @wakeup_request: client callback to wakeup
Amir Levy9659e592016-10-27 18:08:27 +0300131 */
132struct odu_bridge_ctx {
133 struct class *class;
134 dev_t dev_num;
135 struct device *dev;
136 struct cdev cdev;
137 char netdev_name[IPA_RESOURCE_NAME_MAX];
138 u8 device_ethaddr[ETH_ALEN];
139 void *priv;
140 ipa_notify_cb tx_dp_notify;
141 int (*send_dl_skb)(void *priv, struct sk_buff *skb);
142 struct stats stats;
143 bool is_connected;
Skylar Chang9fbce062017-07-25 16:20:42 -0700144 bool is_suspended;
Amir Levy9659e592016-10-27 18:08:27 +0300145 enum odu_bridge_mode mode;
146 struct mutex lock;
147 struct in6_addr llv6_addr;
148 uint32_t odu_br_ipv4_hdr_hdl;
149 uint32_t odu_br_ipv6_hdr_hdl;
150 u32 odu_prod_hdl;
151 u32 odu_emb_cons_hdl;
152 u32 odu_teth_cons_hdl;
153 u32 ipa_sys_desc_size;
154 void *logbuf;
155 void *logbuf_low;
Skylar Chang9fbce062017-07-25 16:20:42 -0700156 struct completion rm_comp;
157 void (*wakeup_request)(void *);
Michael Adisumarta3e350812017-09-18 14:54:36 -0700158 u32 pm_hdl;
Amir Levy9659e592016-10-27 18:08:27 +0300159};
160static struct odu_bridge_ctx *odu_bridge_ctx;
161
162#ifdef CONFIG_DEBUG_FS
163#define ODU_MAX_MSG_LEN 512
164static char dbg_buff[ODU_MAX_MSG_LEN];
165#endif
166
167static void odu_bridge_emb_cons_cb(void *priv, enum ipa_dp_evt_type evt,
168 unsigned long data)
169{
170 ODU_BRIDGE_FUNC_ENTRY();
171 if (evt != IPA_RECEIVE) {
172 ODU_BRIDGE_ERR("unexpected event\n");
173 WARN_ON(1);
174 return;
175 }
176 odu_bridge_ctx->send_dl_skb(priv, (struct sk_buff *)data);
177 odu_bridge_ctx->stats.num_dl_packets++;
178 ODU_BRIDGE_FUNC_EXIT();
179}
180
181static void odu_bridge_teth_cons_cb(void *priv, enum ipa_dp_evt_type evt,
182 unsigned long data)
183{
184 struct ipv6hdr *ipv6hdr;
185 struct sk_buff *skb = (struct sk_buff *)data;
186 struct sk_buff *skb_copied;
187
188 ODU_BRIDGE_FUNC_ENTRY();
189 if (evt != IPA_RECEIVE) {
190 ODU_BRIDGE_ERR("unexpected event\n");
191 WARN_ON(1);
192 return;
193 }
194
195 ipv6hdr = (struct ipv6hdr *)(skb->data + ETH_HLEN);
196 if (ipv6hdr->version == 6 &&
197 ipv6_addr_is_multicast(&ipv6hdr->daddr)) {
198 ODU_BRIDGE_DBG_LOW("Multicast pkt, send to APPS and adapter\n");
199 skb_copied = skb_clone(skb, GFP_KERNEL);
200 if (skb_copied) {
201 odu_bridge_ctx->tx_dp_notify(odu_bridge_ctx->priv,
202 IPA_RECEIVE,
203 (unsigned long) skb_copied);
204 odu_bridge_ctx->stats.num_lan_packets++;
205 } else {
206 ODU_BRIDGE_ERR("No memory\n");
207 }
208 }
209
210 odu_bridge_ctx->send_dl_skb(priv, skb);
211 odu_bridge_ctx->stats.num_dl_packets++;
212 ODU_BRIDGE_FUNC_EXIT();
213}
214
215static int odu_bridge_connect_router(void)
216{
217 struct ipa_sys_connect_params odu_prod_params;
218 struct ipa_sys_connect_params odu_emb_cons_params;
219 int res;
220
221 ODU_BRIDGE_FUNC_ENTRY();
222
223 memset(&odu_prod_params, 0, sizeof(odu_prod_params));
224 memset(&odu_emb_cons_params, 0, sizeof(odu_emb_cons_params));
225
226 /* configure RX (ODU->IPA) EP */
227 odu_prod_params.client = IPA_CLIENT_ODU_PROD;
228 odu_prod_params.ipa_ep_cfg.hdr.hdr_len = ETH_HLEN;
229 odu_prod_params.ipa_ep_cfg.nat.nat_en = IPA_SRC_NAT;
230 odu_prod_params.desc_fifo_sz = odu_bridge_ctx->ipa_sys_desc_size;
231 odu_prod_params.priv = odu_bridge_ctx->priv;
232 odu_prod_params.notify = odu_bridge_ctx->tx_dp_notify;
233 res = ipa_setup_sys_pipe(&odu_prod_params,
234 &odu_bridge_ctx->odu_prod_hdl);
235 if (res) {
236 ODU_BRIDGE_ERR("fail to setup sys pipe ODU_PROD %d\n", res);
237 goto fail_odu_prod;
238 }
239
240 /* configure TX (IPA->ODU) EP */
241 odu_emb_cons_params.client = IPA_CLIENT_ODU_EMB_CONS;
242 odu_emb_cons_params.ipa_ep_cfg.hdr.hdr_len = ETH_HLEN;
243 odu_emb_cons_params.ipa_ep_cfg.nat.nat_en = IPA_BYPASS_NAT;
244 odu_emb_cons_params.desc_fifo_sz = odu_bridge_ctx->ipa_sys_desc_size;
245 odu_emb_cons_params.priv = odu_bridge_ctx->priv;
246 odu_emb_cons_params.notify = odu_bridge_emb_cons_cb;
247 res = ipa_setup_sys_pipe(&odu_emb_cons_params,
248 &odu_bridge_ctx->odu_emb_cons_hdl);
249 if (res) {
250 ODU_BRIDGE_ERR("fail to setup sys pipe ODU_EMB_CONS %d\n", res);
251 goto fail_odu_emb_cons;
252 }
253
254 ODU_BRIDGE_DBG("odu_prod_hdl = %d, odu_emb_cons_hdl = %d\n",
255 odu_bridge_ctx->odu_prod_hdl, odu_bridge_ctx->odu_emb_cons_hdl);
256
257 ODU_BRIDGE_FUNC_EXIT();
258
259 return 0;
260
261fail_odu_emb_cons:
262 ipa_teardown_sys_pipe(odu_bridge_ctx->odu_prod_hdl);
263 odu_bridge_ctx->odu_prod_hdl = 0;
264fail_odu_prod:
265 return res;
266}
267
268static int odu_bridge_connect_bridge(void)
269{
270 struct ipa_sys_connect_params odu_prod_params;
271 struct ipa_sys_connect_params odu_emb_cons_params;
272 struct ipa_sys_connect_params odu_teth_cons_params;
273 int res;
274
275 ODU_BRIDGE_FUNC_ENTRY();
276
277 memset(&odu_prod_params, 0, sizeof(odu_prod_params));
278 memset(&odu_emb_cons_params, 0, sizeof(odu_emb_cons_params));
279
Michael Adisumarta3e350812017-09-18 14:54:36 -0700280 if (!ipa_pm_is_used()) {
281 /* Build IPA Resource manager dependency graph */
282 ODU_BRIDGE_DBG_LOW("build dependency graph\n");
283 res = ipa_rm_add_dependency(IPA_RM_RESOURCE_ODU_ADAPT_PROD,
Amir Levy9659e592016-10-27 18:08:27 +0300284 IPA_RM_RESOURCE_Q6_CONS);
Michael Adisumarta3e350812017-09-18 14:54:36 -0700285 if (res && res != -EINPROGRESS) {
286 ODU_BRIDGE_ERR("ipa_rm_add_dependency() failed\n");
287 goto fail_add_dependency_1;
288 }
Amir Levy9659e592016-10-27 18:08:27 +0300289
Michael Adisumarta3e350812017-09-18 14:54:36 -0700290 res = ipa_rm_add_dependency(IPA_RM_RESOURCE_Q6_PROD,
Amir Levy9659e592016-10-27 18:08:27 +0300291 IPA_RM_RESOURCE_ODU_ADAPT_CONS);
Michael Adisumarta3e350812017-09-18 14:54:36 -0700292 if (res && res != -EINPROGRESS) {
293 ODU_BRIDGE_ERR("ipa_rm_add_dependency() failed\n");
294 goto fail_add_dependency_2;
295 }
Amir Levy9659e592016-10-27 18:08:27 +0300296 }
297
298 /* configure RX (ODU->IPA) EP */
299 odu_prod_params.client = IPA_CLIENT_ODU_PROD;
300 odu_prod_params.desc_fifo_sz = IPA_ODU_SYS_DESC_FIFO_SZ;
301 odu_prod_params.priv = odu_bridge_ctx->priv;
302 odu_prod_params.notify = odu_bridge_ctx->tx_dp_notify;
303 odu_prod_params.skip_ep_cfg = true;
304 res = ipa_setup_sys_pipe(&odu_prod_params,
305 &odu_bridge_ctx->odu_prod_hdl);
306 if (res) {
307 ODU_BRIDGE_ERR("fail to setup sys pipe ODU_PROD %d\n", res);
308 goto fail_odu_prod;
309 }
310
311 /* configure TX tethered (IPA->ODU) EP */
312 odu_teth_cons_params.client = IPA_CLIENT_ODU_TETH_CONS;
313 odu_teth_cons_params.desc_fifo_sz = IPA_ODU_SYS_DESC_FIFO_SZ;
314 odu_teth_cons_params.priv = odu_bridge_ctx->priv;
315 odu_teth_cons_params.notify = odu_bridge_teth_cons_cb;
316 odu_teth_cons_params.skip_ep_cfg = true;
317 res = ipa_setup_sys_pipe(&odu_teth_cons_params,
318 &odu_bridge_ctx->odu_teth_cons_hdl);
319 if (res) {
320 ODU_BRIDGE_ERR("fail to setup sys pipe ODU_TETH_CONS %d\n",
321 res);
322 goto fail_odu_teth_cons;
323 }
324
325 /* configure TX embedded(IPA->ODU) EP */
326 odu_emb_cons_params.client = IPA_CLIENT_ODU_EMB_CONS;
327 odu_emb_cons_params.ipa_ep_cfg.hdr.hdr_len = ETH_HLEN;
328 odu_emb_cons_params.ipa_ep_cfg.nat.nat_en = IPA_BYPASS_NAT;
329 odu_emb_cons_params.desc_fifo_sz = IPA_ODU_SYS_DESC_FIFO_SZ;
330 odu_emb_cons_params.priv = odu_bridge_ctx->priv;
331 odu_emb_cons_params.notify = odu_bridge_emb_cons_cb;
332 res = ipa_setup_sys_pipe(&odu_emb_cons_params,
333 &odu_bridge_ctx->odu_emb_cons_hdl);
334 if (res) {
335 ODU_BRIDGE_ERR("fail to setup sys pipe ODU_EMB_CONS %d\n", res);
336 goto fail_odu_emb_cons;
337 }
338
339 ODU_BRIDGE_DBG_LOW("odu_prod_hdl = %d, odu_emb_cons_hdl = %d\n",
340 odu_bridge_ctx->odu_prod_hdl, odu_bridge_ctx->odu_emb_cons_hdl);
341 ODU_BRIDGE_DBG_LOW("odu_teth_cons_hdl = %d\n",
342 odu_bridge_ctx->odu_teth_cons_hdl);
343
344 ODU_BRIDGE_FUNC_EXIT();
345
346 return 0;
347
348fail_odu_emb_cons:
349 ipa_teardown_sys_pipe(odu_bridge_ctx->odu_teth_cons_hdl);
350 odu_bridge_ctx->odu_teth_cons_hdl = 0;
351fail_odu_teth_cons:
352 ipa_teardown_sys_pipe(odu_bridge_ctx->odu_prod_hdl);
353 odu_bridge_ctx->odu_prod_hdl = 0;
354fail_odu_prod:
Michael Adisumarta3e350812017-09-18 14:54:36 -0700355 if (!ipa_pm_is_used())
356 ipa_rm_delete_dependency(IPA_RM_RESOURCE_Q6_PROD,
Amir Levy9659e592016-10-27 18:08:27 +0300357 IPA_RM_RESOURCE_ODU_ADAPT_CONS);
358fail_add_dependency_2:
Michael Adisumarta3e350812017-09-18 14:54:36 -0700359 if (!ipa_pm_is_used())
360 ipa_rm_delete_dependency(IPA_RM_RESOURCE_ODU_ADAPT_PROD,
Amir Levy9659e592016-10-27 18:08:27 +0300361 IPA_RM_RESOURCE_Q6_CONS);
362fail_add_dependency_1:
363 return res;
364}
365
366static int odu_bridge_disconnect_router(void)
367{
368 int res;
369
370 ODU_BRIDGE_FUNC_ENTRY();
371
372 res = ipa_teardown_sys_pipe(odu_bridge_ctx->odu_prod_hdl);
373 if (res)
374 ODU_BRIDGE_ERR("teardown ODU PROD failed\n");
375 odu_bridge_ctx->odu_prod_hdl = 0;
376
377 res = ipa_teardown_sys_pipe(odu_bridge_ctx->odu_emb_cons_hdl);
378 if (res)
379 ODU_BRIDGE_ERR("teardown ODU EMB CONS failed\n");
380 odu_bridge_ctx->odu_emb_cons_hdl = 0;
381
382 ODU_BRIDGE_FUNC_EXIT();
383
384 return 0;
385}
386
387static int odu_bridge_disconnect_bridge(void)
388{
389 int res;
390
391 ODU_BRIDGE_FUNC_ENTRY();
392
393 res = ipa_teardown_sys_pipe(odu_bridge_ctx->odu_prod_hdl);
394 if (res)
395 ODU_BRIDGE_ERR("teardown ODU PROD failed\n");
396 odu_bridge_ctx->odu_prod_hdl = 0;
397
398 res = ipa_teardown_sys_pipe(odu_bridge_ctx->odu_teth_cons_hdl);
399 if (res)
400 ODU_BRIDGE_ERR("teardown ODU TETH CONS failed\n");
401 odu_bridge_ctx->odu_teth_cons_hdl = 0;
402
403 res = ipa_teardown_sys_pipe(odu_bridge_ctx->odu_emb_cons_hdl);
404 if (res)
405 ODU_BRIDGE_ERR("teardown ODU EMB CONS failed\n");
406 odu_bridge_ctx->odu_emb_cons_hdl = 0;
407
Michael Adisumarta3e350812017-09-18 14:54:36 -0700408 if (!ipa_pm_is_used()) {
409 /* Delete IPA Resource manager dependency graph */
410 ODU_BRIDGE_DBG("deleting dependency graph\n");
411 res = ipa_rm_delete_dependency(IPA_RM_RESOURCE_ODU_ADAPT_PROD,
412 IPA_RM_RESOURCE_Q6_CONS);
413 if (res && res != -EINPROGRESS)
414 ODU_BRIDGE_ERR("ipa_rm_delete_dependency() failed\n");
Amir Levy9659e592016-10-27 18:08:27 +0300415
Michael Adisumarta3e350812017-09-18 14:54:36 -0700416 res = ipa_rm_delete_dependency(IPA_RM_RESOURCE_Q6_PROD,
417 IPA_RM_RESOURCE_ODU_ADAPT_CONS);
418 if (res && res != -EINPROGRESS)
419 ODU_BRIDGE_ERR("ipa_rm_delete_dependency() failed\n");
420 }
Amir Levy9659e592016-10-27 18:08:27 +0300421
422 return 0;
423}
424
425/**
426 * odu_bridge_disconnect() - Disconnect odu bridge
427 *
428 * Disconnect all pipes and deletes IPA RM dependencies on bridge mode
429 *
430 * Return codes: 0- success, error otherwise
431 */
432int odu_bridge_disconnect(void)
433{
434 int res;
435
436 ODU_BRIDGE_FUNC_ENTRY();
437
438 if (!odu_bridge_ctx) {
439 ODU_BRIDGE_ERR("Not initialized\n");
440 return -EFAULT;
441 }
442
443 if (!odu_bridge_ctx->is_connected) {
444 ODU_BRIDGE_ERR("Not connected\n");
445 return -EFAULT;
446 }
447
448 mutex_lock(&odu_bridge_ctx->lock);
449 if (odu_bridge_ctx->mode == ODU_BRIDGE_MODE_ROUTER) {
450 res = odu_bridge_disconnect_router();
451 if (res) {
452 ODU_BRIDGE_ERR("disconnect_router failed %d\n", res);
453 goto out;
454 }
455 } else {
456 res = odu_bridge_disconnect_bridge();
457 if (res) {
458 ODU_BRIDGE_ERR("disconnect_bridge failed %d\n", res);
459 goto out;
460 }
461 }
462
463 odu_bridge_ctx->is_connected = false;
464 res = 0;
465out:
466 mutex_unlock(&odu_bridge_ctx->lock);
467 ODU_BRIDGE_FUNC_EXIT();
468 return res;
469}
470EXPORT_SYMBOL(odu_bridge_disconnect);
471
472/**
473 * odu_bridge_connect() - Connect odu bridge.
474 *
475 * Call to the mode-specific connect function for connection IPA pipes
476 * and adding IPA RM dependencies
477
478 * Return codes: 0: success
479 * -EINVAL: invalid parameters
480 * -EPERM: Operation not permitted as the bridge is already
481 * connected
482 */
483int odu_bridge_connect(void)
484{
485 int res;
486
487 ODU_BRIDGE_FUNC_ENTRY();
488
489 if (!odu_bridge_ctx) {
490 ODU_BRIDGE_ERR("Not initialized\n");
491 return -EFAULT;
492 }
493
494 if (odu_bridge_ctx->is_connected) {
495 ODU_BRIDGE_ERR("already connected\n");
496 return -EFAULT;
497 }
498
499 mutex_lock(&odu_bridge_ctx->lock);
500 if (odu_bridge_ctx->mode == ODU_BRIDGE_MODE_ROUTER) {
501 res = odu_bridge_connect_router();
502 if (res) {
503 ODU_BRIDGE_ERR("connect_router failed\n");
504 goto bail;
505 }
506 } else {
507 res = odu_bridge_connect_bridge();
508 if (res) {
509 ODU_BRIDGE_ERR("connect_bridge failed\n");
510 goto bail;
511 }
512 }
513
514 odu_bridge_ctx->is_connected = true;
515 res = 0;
516bail:
517 mutex_unlock(&odu_bridge_ctx->lock);
518 ODU_BRIDGE_FUNC_EXIT();
519 return res;
520}
521EXPORT_SYMBOL(odu_bridge_connect);
522
523/**
524 * odu_bridge_set_mode() - Set bridge mode to Router/Bridge
525 * @mode: mode to be set
526 */
527static int odu_bridge_set_mode(enum odu_bridge_mode mode)
528{
529 int res;
530
531 ODU_BRIDGE_FUNC_ENTRY();
532
533 if (mode < 0 || mode >= ODU_BRIDGE_MODE_MAX) {
534 ODU_BRIDGE_ERR("Unsupported mode: %d\n", mode);
535 return -EFAULT;
536 }
537
538 ODU_BRIDGE_DBG_LOW("setting mode: %d\n", mode);
539 mutex_lock(&odu_bridge_ctx->lock);
540
541 if (odu_bridge_ctx->mode == mode) {
542 ODU_BRIDGE_DBG_LOW("same mode\n");
543 res = 0;
544 goto bail;
545 }
546
547 if (odu_bridge_ctx->is_connected) {
548 /* first disconnect the old configuration */
549 if (odu_bridge_ctx->mode == ODU_BRIDGE_MODE_ROUTER) {
550 res = odu_bridge_disconnect_router();
551 if (res) {
552 ODU_BRIDGE_ERR("disconnect_router failed\n");
553 goto bail;
554 }
555 } else {
556 res = odu_bridge_disconnect_bridge();
557 if (res) {
558 ODU_BRIDGE_ERR("disconnect_bridge failed\n");
559 goto bail;
560 }
561 }
562
563 /* connect the new configuration */
564 if (mode == ODU_BRIDGE_MODE_ROUTER) {
565 res = odu_bridge_connect_router();
566 if (res) {
567 ODU_BRIDGE_ERR("connect_router failed\n");
568 goto bail;
569 }
570 } else {
571 res = odu_bridge_connect_bridge();
572 if (res) {
573 ODU_BRIDGE_ERR("connect_bridge failed\n");
574 goto bail;
575 }
576 }
577 }
578 odu_bridge_ctx->mode = mode;
579 res = 0;
580bail:
581 mutex_unlock(&odu_bridge_ctx->lock);
582 ODU_BRIDGE_FUNC_EXIT();
583 return res;
584};
585
586/**
587 * odu_bridge_set_llv6_addr() - Set link local ipv6 address
588 * @llv6_addr: odu network interface link local address
589 *
590 * This function sets the link local ipv6 address provided by IOCTL
591 */
592static int odu_bridge_set_llv6_addr(struct in6_addr *llv6_addr)
593{
594 struct in6_addr llv6_addr_host;
595
596 ODU_BRIDGE_FUNC_ENTRY();
597
598 llv6_addr_host.s6_addr32[0] = ntohl(llv6_addr->s6_addr32[0]);
599 llv6_addr_host.s6_addr32[1] = ntohl(llv6_addr->s6_addr32[1]);
600 llv6_addr_host.s6_addr32[2] = ntohl(llv6_addr->s6_addr32[2]);
601 llv6_addr_host.s6_addr32[3] = ntohl(llv6_addr->s6_addr32[3]);
602
603 memcpy(&odu_bridge_ctx->llv6_addr, &llv6_addr_host,
604 sizeof(odu_bridge_ctx->llv6_addr));
605 ODU_BRIDGE_DBG_LOW("LLV6 addr: %pI6c\n", &odu_bridge_ctx->llv6_addr);
606
607 ODU_BRIDGE_FUNC_EXIT();
608
609 return 0;
610};
611
612static long odu_bridge_ioctl(struct file *filp,
613 unsigned int cmd,
614 unsigned long arg)
615{
616 int res = 0;
617 struct in6_addr llv6_addr;
618
619 ODU_BRIDGE_DBG("cmd=%x nr=%d\n", cmd, _IOC_NR(cmd));
620
621 if ((_IOC_TYPE(cmd) != ODU_BRIDGE_IOC_MAGIC) ||
622 (_IOC_NR(cmd) >= ODU_BRIDGE_IOCTL_MAX)) {
623 ODU_BRIDGE_ERR("Invalid ioctl\n");
624 return -ENOIOCTLCMD;
625 }
626
627 switch (cmd) {
628 case ODU_BRIDGE_IOC_SET_MODE:
629 ODU_BRIDGE_DBG("ODU_BRIDGE_IOC_SET_MODE ioctl called\n");
630 res = odu_bridge_set_mode(arg);
631 if (res) {
632 ODU_BRIDGE_ERR("Error, res = %d\n", res);
633 break;
634 }
635 break;
636
637 case ODU_BRIDGE_IOC_SET_LLV6_ADDR:
638 ODU_BRIDGE_DBG("ODU_BRIDGE_IOC_SET_LLV6_ADDR ioctl called\n");
639 res = copy_from_user(&llv6_addr,
640 (struct in6_addr *)arg,
641 sizeof(llv6_addr));
642 if (res) {
643 ODU_BRIDGE_ERR("Error, res = %d\n", res);
644 res = -EFAULT;
645 break;
646 }
647
648 res = odu_bridge_set_llv6_addr(&llv6_addr);
649 if (res) {
650 ODU_BRIDGE_ERR("Error, res = %d\n", res);
651 break;
652 }
653 break;
654
655 default:
656 ODU_BRIDGE_ERR("Unknown ioctl: %d\n", cmd);
657 WARN_ON(1);
658 }
659
660 return res;
661}
662
663#ifdef CONFIG_COMPAT
664static long compat_odu_bridge_ioctl(struct file *file,
665 unsigned int cmd, unsigned long arg)
666{
667 switch (cmd) {
668 case ODU_BRIDGE_IOC_SET_LLV6_ADDR32:
669 cmd = ODU_BRIDGE_IOC_SET_LLV6_ADDR;
670 break;
671 case ODU_BRIDGE_IOC_SET_MODE:
672 break;
673 default:
674 return -ENOIOCTLCMD;
675 }
676 return odu_bridge_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
677}
678#endif
679
680#ifdef CONFIG_DEBUG_FS
681static struct dentry *dent;
682static struct dentry *dfile_stats;
683static struct dentry *dfile_mode;
684
685static ssize_t odu_debugfs_stats(struct file *file,
686 char __user *ubuf,
687 size_t count,
688 loff_t *ppos)
689{
690 int nbytes = 0;
691
692 nbytes += scnprintf(&dbg_buff[nbytes],
693 ODU_MAX_MSG_LEN - nbytes,
694 "UL packets: %lld\n",
695 odu_bridge_ctx->stats.num_ul_packets);
696 nbytes += scnprintf(&dbg_buff[nbytes],
697 ODU_MAX_MSG_LEN - nbytes,
698 "DL packets: %lld\n",
699 odu_bridge_ctx->stats.num_dl_packets);
700 nbytes += scnprintf(&dbg_buff[nbytes],
701 ODU_MAX_MSG_LEN - nbytes,
702 "LAN packets: %lld\n",
703 odu_bridge_ctx->stats.num_lan_packets);
704 return simple_read_from_buffer(ubuf, count, ppos, dbg_buff, nbytes);
705}
706
707static ssize_t odu_debugfs_hw_bridge_mode_write(struct file *file,
708 const char __user *ubuf,
709 size_t count,
710 loff_t *ppos)
711{
712 unsigned long missing;
713 enum odu_bridge_mode mode;
714
715 if (sizeof(dbg_buff) < count + 1)
716 return -EFAULT;
717
718 missing = copy_from_user(dbg_buff, ubuf, count);
719 if (missing)
720 return -EFAULT;
721
722 if (count > 0)
723 dbg_buff[count-1] = '\0';
724
725 if (strcmp(dbg_buff, "router") == 0) {
726 mode = ODU_BRIDGE_MODE_ROUTER;
727 } else if (strcmp(dbg_buff, "bridge") == 0) {
728 mode = ODU_BRIDGE_MODE_BRIDGE;
729 } else {
730 ODU_BRIDGE_ERR("Bad mode, got %s,\n"
731 "Use <router> or <bridge>.\n", dbg_buff);
732 return count;
733 }
734
735 odu_bridge_set_mode(mode);
736 return count;
737}
738
739static ssize_t odu_debugfs_hw_bridge_mode_read(struct file *file,
740 char __user *ubuf,
741 size_t count,
742 loff_t *ppos)
743{
744 int nbytes = 0;
745
746 switch (odu_bridge_ctx->mode) {
747 case ODU_BRIDGE_MODE_ROUTER:
748 nbytes += scnprintf(&dbg_buff[nbytes],
749 ODU_MAX_MSG_LEN - nbytes,
750 "router\n");
751 break;
752 case ODU_BRIDGE_MODE_BRIDGE:
753 nbytes += scnprintf(&dbg_buff[nbytes],
754 ODU_MAX_MSG_LEN - nbytes,
755 "bridge\n");
756 break;
757 default:
758 nbytes += scnprintf(&dbg_buff[nbytes],
759 ODU_MAX_MSG_LEN - nbytes,
760 "mode error\n");
761 break;
762
763 }
764
765 return simple_read_from_buffer(ubuf, count, ppos, dbg_buff, nbytes);
766}
767
768const struct file_operations odu_stats_ops = {
769 .read = odu_debugfs_stats,
770};
771
772const struct file_operations odu_hw_bridge_mode_ops = {
773 .read = odu_debugfs_hw_bridge_mode_read,
774 .write = odu_debugfs_hw_bridge_mode_write,
775};
776
777static void odu_debugfs_init(void)
778{
779 const mode_t read_only_mode = S_IRUSR | S_IRGRP | S_IROTH;
780 const mode_t read_write_mode = S_IRUSR | S_IRGRP | S_IROTH |
781 S_IWUSR | S_IWGRP | S_IWOTH;
782
783 dent = debugfs_create_dir("odu_ipa_bridge", 0);
784 if (IS_ERR(dent)) {
785 ODU_BRIDGE_ERR("fail to create folder odu_ipa_bridge\n");
786 return;
787 }
788
789 dfile_stats =
790 debugfs_create_file("stats", read_only_mode, dent,
791 0, &odu_stats_ops);
792 if (!dfile_stats || IS_ERR(dfile_stats)) {
793 ODU_BRIDGE_ERR("fail to create file stats\n");
794 goto fail;
795 }
796
797 dfile_mode =
798 debugfs_create_file("mode", read_write_mode,
799 dent, 0, &odu_hw_bridge_mode_ops);
800 if (!dfile_mode ||
801 IS_ERR(dfile_mode)) {
802 ODU_BRIDGE_ERR("fail to create file dfile_mode\n");
803 goto fail;
804 }
805
806 return;
807fail:
808 debugfs_remove_recursive(dent);
809}
810
811static void odu_debugfs_destroy(void)
812{
813 debugfs_remove_recursive(dent);
814}
815
816#else
817static void odu_debugfs_init(void) {}
818static void odu_debugfs_destroy(void) {}
819#endif /* CONFIG_DEBUG_FS */
820
821
822static const struct file_operations odu_bridge_drv_fops = {
823 .owner = THIS_MODULE,
824 .unlocked_ioctl = odu_bridge_ioctl,
825#ifdef CONFIG_COMPAT
826 .compat_ioctl = compat_odu_bridge_ioctl,
827#endif
828};
829
830/**
831 * odu_bridge_tx_dp() - Send skb to ODU bridge
832 * @skb: skb to send
833 * @metadata: metadata on packet
834 *
835 * This function handles uplink packet.
836 * In Router Mode:
837 * packet is sent directly to IPA.
838 * In Router Mode:
839 * packet is classified if it should arrive to network stack.
840 * QMI IP packet should arrive to APPS network stack
841 * IPv6 Multicast packet should arrive to APPS network stack and Q6
842 *
843 * Return codes: 0- success, error otherwise
844 */
845int odu_bridge_tx_dp(struct sk_buff *skb, struct ipa_tx_meta *metadata)
846{
847 struct sk_buff *skb_copied = NULL;
848 struct ipv6hdr *ipv6hdr;
849 int res;
850
851 ODU_BRIDGE_FUNC_ENTRY();
852
853 switch (odu_bridge_ctx->mode) {
854 case ODU_BRIDGE_MODE_ROUTER:
855 /* Router mode - pass skb to IPA */
856 res = ipa_tx_dp(IPA_CLIENT_ODU_PROD, skb, metadata);
857 if (res) {
858 ODU_BRIDGE_DBG("tx dp failed %d\n", res);
859 goto out;
860 }
861 odu_bridge_ctx->stats.num_ul_packets++;
862 goto out;
863
864 case ODU_BRIDGE_MODE_BRIDGE:
865 ipv6hdr = (struct ipv6hdr *)(skb->data + ETH_HLEN);
866 if (ipv6hdr->version == 6 &&
867 ODU_BRIDGE_IS_QMI_ADDR(ipv6hdr->daddr)) {
868 ODU_BRIDGE_DBG_LOW("QMI packet\n");
869 skb_copied = skb_clone(skb, GFP_KERNEL);
870 if (!skb_copied) {
871 ODU_BRIDGE_ERR("No memory\n");
872 return -ENOMEM;
873 }
874 odu_bridge_ctx->tx_dp_notify(odu_bridge_ctx->priv,
875 IPA_RECEIVE,
876 (unsigned long)skb_copied);
877 odu_bridge_ctx->tx_dp_notify(odu_bridge_ctx->priv,
878 IPA_WRITE_DONE,
879 (unsigned long)skb);
880 odu_bridge_ctx->stats.num_ul_packets++;
881 odu_bridge_ctx->stats.num_lan_packets++;
882 res = 0;
883 goto out;
884 }
885
886 if (ipv6hdr->version == 6 &&
887 ipv6_addr_is_multicast(&ipv6hdr->daddr)) {
888 ODU_BRIDGE_DBG_LOW(
889 "Multicast pkt, send to APPS and IPA\n");
890 skb_copied = skb_clone(skb, GFP_KERNEL);
891 if (!skb_copied) {
892 ODU_BRIDGE_ERR("No memory\n");
893 return -ENOMEM;
894 }
895
896 res = ipa_tx_dp(IPA_CLIENT_ODU_PROD, skb, metadata);
897 if (res) {
898 ODU_BRIDGE_DBG("tx dp failed %d\n", res);
899 dev_kfree_skb(skb_copied);
900 goto out;
901 }
902
903 odu_bridge_ctx->tx_dp_notify(odu_bridge_ctx->priv,
904 IPA_RECEIVE,
905 (unsigned long)skb_copied);
906 odu_bridge_ctx->stats.num_ul_packets++;
907 odu_bridge_ctx->stats.num_lan_packets++;
908 goto out;
909 }
910
911 res = ipa_tx_dp(IPA_CLIENT_ODU_PROD, skb, metadata);
912 if (res) {
913 ODU_BRIDGE_DBG("tx dp failed %d\n", res);
914 goto out;
915 }
916 odu_bridge_ctx->stats.num_ul_packets++;
917 goto out;
918
919 default:
920 ODU_BRIDGE_ERR("Unsupported mode: %d\n", odu_bridge_ctx->mode);
921 WARN_ON(1);
922 res = -EFAULT;
923
924 }
925out:
926 ODU_BRIDGE_FUNC_EXIT();
927 return res;
928}
929EXPORT_SYMBOL(odu_bridge_tx_dp);
930
931static int odu_bridge_add_hdrs(void)
932{
933 struct ipa_ioc_add_hdr *hdrs;
934 struct ipa_hdr_add *ipv4_hdr;
935 struct ipa_hdr_add *ipv6_hdr;
936 struct ethhdr *eth_ipv4;
937 struct ethhdr *eth_ipv6;
938 int res;
939
940 ODU_BRIDGE_FUNC_ENTRY();
941 hdrs = kzalloc(sizeof(*hdrs) + sizeof(*ipv4_hdr) + sizeof(*ipv6_hdr),
942 GFP_KERNEL);
943 if (!hdrs) {
944 ODU_BRIDGE_ERR("no mem\n");
945 res = -ENOMEM;
946 goto out;
947 }
948 ipv4_hdr = &hdrs->hdr[0];
949 eth_ipv4 = (struct ethhdr *)(ipv4_hdr->hdr);
950 ipv6_hdr = &hdrs->hdr[1];
951 eth_ipv6 = (struct ethhdr *)(ipv6_hdr->hdr);
952 strlcpy(ipv4_hdr->name, ODU_BRIDGE_IPV4_HDR_NAME,
953 IPA_RESOURCE_NAME_MAX);
954 memcpy(eth_ipv4->h_source, odu_bridge_ctx->device_ethaddr, ETH_ALEN);
955 eth_ipv4->h_proto = htons(ETH_P_IP);
956 ipv4_hdr->hdr_len = ETH_HLEN;
957 ipv4_hdr->is_partial = 1;
958 ipv4_hdr->is_eth2_ofst_valid = 1;
959 ipv4_hdr->eth2_ofst = 0;
960 strlcpy(ipv6_hdr->name, ODU_BRIDGE_IPV6_HDR_NAME,
961 IPA_RESOURCE_NAME_MAX);
962 memcpy(eth_ipv6->h_source, odu_bridge_ctx->device_ethaddr, ETH_ALEN);
963 eth_ipv6->h_proto = htons(ETH_P_IPV6);
964 ipv6_hdr->hdr_len = ETH_HLEN;
965 ipv6_hdr->is_partial = 1;
966 ipv6_hdr->is_eth2_ofst_valid = 1;
967 ipv6_hdr->eth2_ofst = 0;
968 hdrs->commit = 1;
969 hdrs->num_hdrs = 2;
970 res = ipa_add_hdr(hdrs);
971 if (res) {
972 ODU_BRIDGE_ERR("Fail on Header-Insertion(%d)\n", res);
973 goto out_free_mem;
974 }
975 if (ipv4_hdr->status) {
976 ODU_BRIDGE_ERR("Fail on Header-Insertion ipv4(%d)\n",
977 ipv4_hdr->status);
978 res = ipv4_hdr->status;
979 goto out_free_mem;
980 }
981 if (ipv6_hdr->status) {
982 ODU_BRIDGE_ERR("Fail on Header-Insertion ipv6(%d)\n",
983 ipv6_hdr->status);
984 res = ipv6_hdr->status;
985 goto out_free_mem;
986 }
987 odu_bridge_ctx->odu_br_ipv4_hdr_hdl = ipv4_hdr->hdr_hdl;
988 odu_bridge_ctx->odu_br_ipv6_hdr_hdl = ipv6_hdr->hdr_hdl;
989
990 res = 0;
991out_free_mem:
992 kfree(hdrs);
993out:
994 ODU_BRIDGE_FUNC_EXIT();
995 return res;
996}
997
998static void odu_bridge_del_hdrs(void)
999{
1000 struct ipa_ioc_del_hdr *del_hdr;
1001 struct ipa_hdr_del *ipv4;
1002 struct ipa_hdr_del *ipv6;
1003 int result;
1004
1005 del_hdr = kzalloc(sizeof(*del_hdr) + sizeof(*ipv4) +
1006 sizeof(*ipv6), GFP_KERNEL);
1007 if (!del_hdr)
1008 return;
1009 del_hdr->commit = 1;
1010 del_hdr->num_hdls = 2;
1011 ipv4 = &del_hdr->hdl[0];
1012 ipv4->hdl = odu_bridge_ctx->odu_br_ipv4_hdr_hdl;
1013 ipv6 = &del_hdr->hdl[1];
1014 ipv6->hdl = odu_bridge_ctx->odu_br_ipv6_hdr_hdl;
1015 result = ipa_del_hdr(del_hdr);
1016 if (result || ipv4->status || ipv6->status)
1017 ODU_BRIDGE_ERR("ipa_del_hdr failed");
1018 kfree(del_hdr);
1019}
1020
1021/**
1022 * odu_bridge_register_properties() - set Tx/Rx properties for ipacm
1023 *
1024 * Register the network interface interface with Tx and Rx properties
1025 * Tx properties are for data flowing from IPA to adapter, they
1026 * have Header-Insertion properties both for Ipv4 and Ipv6 Ethernet framing.
1027 * Rx properties are for data flowing from adapter to IPA, they have
1028 * simple rule which always "hit".
1029 *
1030 */
1031static int odu_bridge_register_properties(void)
1032{
1033 struct ipa_tx_intf tx_properties = {0};
1034 struct ipa_ioc_tx_intf_prop properties[2] = { {0}, {0} };
1035 struct ipa_ioc_tx_intf_prop *ipv4_property;
1036 struct ipa_ioc_tx_intf_prop *ipv6_property;
1037 struct ipa_ioc_rx_intf_prop rx_ioc_properties[2] = { {0}, {0} };
1038 struct ipa_rx_intf rx_properties = {0};
1039 struct ipa_ioc_rx_intf_prop *rx_ipv4_property;
1040 struct ipa_ioc_rx_intf_prop *rx_ipv6_property;
1041 int res = 0;
1042
1043 ODU_BRIDGE_FUNC_ENTRY();
1044
1045 tx_properties.prop = properties;
1046 ipv4_property = &tx_properties.prop[0];
1047 ipv4_property->ip = IPA_IP_v4;
1048 ipv4_property->dst_pipe = IPA_CLIENT_ODU_EMB_CONS;
1049 ipv4_property->hdr_l2_type = IPA_HDR_L2_ETHERNET_II;
1050 strlcpy(ipv4_property->hdr_name, ODU_BRIDGE_IPV4_HDR_NAME,
1051 IPA_RESOURCE_NAME_MAX);
1052 ipv6_property = &tx_properties.prop[1];
1053 ipv6_property->ip = IPA_IP_v6;
1054 ipv6_property->dst_pipe = IPA_CLIENT_ODU_EMB_CONS;
1055 ipv6_property->hdr_l2_type = IPA_HDR_L2_ETHERNET_II;
1056 strlcpy(ipv6_property->hdr_name, ODU_BRIDGE_IPV6_HDR_NAME,
1057 IPA_RESOURCE_NAME_MAX);
1058 tx_properties.num_props = 2;
1059
1060 rx_properties.prop = rx_ioc_properties;
1061 rx_ipv4_property = &rx_properties.prop[0];
1062 rx_ipv4_property->ip = IPA_IP_v4;
1063 rx_ipv4_property->attrib.attrib_mask = 0;
1064 rx_ipv4_property->src_pipe = IPA_CLIENT_ODU_PROD;
1065 rx_ipv4_property->hdr_l2_type = IPA_HDR_L2_ETHERNET_II;
1066 rx_ipv6_property = &rx_properties.prop[1];
1067 rx_ipv6_property->ip = IPA_IP_v6;
1068 rx_ipv6_property->attrib.attrib_mask = 0;
1069 rx_ipv6_property->src_pipe = IPA_CLIENT_ODU_PROD;
1070 rx_ipv6_property->hdr_l2_type = IPA_HDR_L2_ETHERNET_II;
1071 rx_properties.num_props = 2;
1072
1073 res = ipa_register_intf(odu_bridge_ctx->netdev_name, &tx_properties,
1074 &rx_properties);
1075 if (res) {
1076 ODU_BRIDGE_ERR("fail on Tx/Rx properties registration %d\n",
1077 res);
1078 }
1079
1080 ODU_BRIDGE_FUNC_EXIT();
1081
1082 return res;
1083}
1084
1085static void odu_bridge_deregister_properties(void)
1086{
1087 int res;
1088
1089 ODU_BRIDGE_FUNC_ENTRY();
1090 res = ipa_deregister_intf(odu_bridge_ctx->netdev_name);
1091 if (res)
1092 ODU_BRIDGE_ERR("Fail on Tx prop deregister %d\n", res);
1093 ODU_BRIDGE_FUNC_EXIT();
1094}
1095
1096/**
1097 * odu_bridge_init() - Initialize the ODU bridge driver
1098 * @params: initialization parameters
1099 *
1100 * This function initialize all bridge internal data and register odu bridge to
1101 * kernel for IOCTL and debugfs.
1102 * Header addition and properties are registered to IPA driver.
1103 *
1104 * Return codes: 0: success,
1105 * -EINVAL - Bad parameter
1106 * Other negative value - Failure
1107 */
1108int odu_bridge_init(struct odu_bridge_params *params)
1109{
1110 int res;
1111
1112 ODU_BRIDGE_FUNC_ENTRY();
1113
1114 if (!params) {
1115 ODU_BRIDGE_ERR("null pointer params\n");
1116 return -EINVAL;
1117 }
1118 if (!params->netdev_name) {
1119 ODU_BRIDGE_ERR("null pointer params->netdev_name\n");
1120 return -EINVAL;
1121 }
1122 if (!params->tx_dp_notify) {
1123 ODU_BRIDGE_ERR("null pointer params->tx_dp_notify\n");
1124 return -EINVAL;
1125 }
1126 if (!params->send_dl_skb) {
1127 ODU_BRIDGE_ERR("null pointer params->send_dl_skb\n");
1128 return -EINVAL;
1129 }
1130 if (odu_bridge_ctx) {
1131 ODU_BRIDGE_ERR("Already initialized\n");
1132 return -EFAULT;
1133 }
1134 if (!ipa_is_ready()) {
1135 ODU_BRIDGE_ERR("IPA is not ready\n");
1136 return -EFAULT;
1137 }
1138
1139 ODU_BRIDGE_DBG("device_ethaddr=%pM\n", params->device_ethaddr);
1140
1141 odu_bridge_ctx = kzalloc(sizeof(*odu_bridge_ctx), GFP_KERNEL);
1142 if (!odu_bridge_ctx) {
1143 ODU_BRIDGE_ERR("kzalloc err.\n");
1144 return -ENOMEM;
1145 }
1146
1147 odu_bridge_ctx->class = class_create(THIS_MODULE, ODU_BRIDGE_DRV_NAME);
1148 if (!odu_bridge_ctx->class) {
1149 ODU_BRIDGE_ERR("Class_create err.\n");
1150 res = -ENODEV;
1151 goto fail_class_create;
1152 }
1153
1154 res = alloc_chrdev_region(&odu_bridge_ctx->dev_num, 0, 1,
1155 ODU_BRIDGE_DRV_NAME);
1156 if (res) {
1157 ODU_BRIDGE_ERR("alloc_chrdev_region err.\n");
1158 res = -ENODEV;
1159 goto fail_alloc_chrdev_region;
1160 }
1161
1162 odu_bridge_ctx->dev = device_create(odu_bridge_ctx->class, NULL,
1163 odu_bridge_ctx->dev_num, odu_bridge_ctx, ODU_BRIDGE_DRV_NAME);
1164 if (IS_ERR(odu_bridge_ctx->dev)) {
1165 ODU_BRIDGE_ERR(":device_create err.\n");
1166 res = -ENODEV;
1167 goto fail_device_create;
1168 }
1169
1170 cdev_init(&odu_bridge_ctx->cdev, &odu_bridge_drv_fops);
1171 odu_bridge_ctx->cdev.owner = THIS_MODULE;
1172 odu_bridge_ctx->cdev.ops = &odu_bridge_drv_fops;
1173
1174 res = cdev_add(&odu_bridge_ctx->cdev, odu_bridge_ctx->dev_num, 1);
1175 if (res) {
1176 ODU_BRIDGE_ERR(":cdev_add err=%d\n", -res);
1177 res = -ENODEV;
1178 goto fail_cdev_add;
1179 }
1180
1181 odu_debugfs_init();
1182
1183 strlcpy(odu_bridge_ctx->netdev_name, params->netdev_name,
1184 IPA_RESOURCE_NAME_MAX);
1185 odu_bridge_ctx->priv = params->priv;
1186 odu_bridge_ctx->tx_dp_notify = params->tx_dp_notify;
1187 odu_bridge_ctx->send_dl_skb = params->send_dl_skb;
1188 memcpy(odu_bridge_ctx->device_ethaddr, params->device_ethaddr,
1189 ETH_ALEN);
1190 odu_bridge_ctx->ipa_sys_desc_size = params->ipa_desc_size;
1191 odu_bridge_ctx->mode = ODU_BRIDGE_MODE_ROUTER;
1192
1193 mutex_init(&odu_bridge_ctx->lock);
1194
1195 res = odu_bridge_add_hdrs();
1196 if (res) {
1197 ODU_BRIDGE_ERR("fail on odu_bridge_add_hdr %d\n", res);
1198 goto fail_add_hdrs;
1199 }
1200
1201 res = odu_bridge_register_properties();
1202 if (res) {
1203 ODU_BRIDGE_ERR("fail on register properties %d\n", res);
1204 goto fail_register_properties;
1205 }
1206
1207 ODU_BRIDGE_FUNC_EXIT();
1208 return 0;
1209
1210fail_register_properties:
1211 odu_bridge_del_hdrs();
1212fail_add_hdrs:
1213 odu_debugfs_destroy();
1214fail_cdev_add:
1215 device_destroy(odu_bridge_ctx->class, odu_bridge_ctx->dev_num);
1216fail_device_create:
1217 unregister_chrdev_region(odu_bridge_ctx->dev_num, 1);
1218fail_alloc_chrdev_region:
1219 class_destroy(odu_bridge_ctx->class);
1220fail_class_create:
1221 kfree(odu_bridge_ctx);
1222 odu_bridge_ctx = NULL;
1223 return res;
1224}
1225EXPORT_SYMBOL(odu_bridge_init);
1226
1227/**
1228 * odu_bridge_cleanup() - De-Initialize the ODU bridge driver
1229 *
1230 * Return codes: 0: success,
1231 * -EINVAL - Bad parameter
1232 * Other negative value - Failure
1233 */
1234int odu_bridge_cleanup(void)
1235{
1236 ODU_BRIDGE_FUNC_ENTRY();
1237
1238 if (!odu_bridge_ctx) {
1239 ODU_BRIDGE_ERR("Not initialized\n");
1240 return -EFAULT;
1241 }
1242
1243 if (odu_bridge_ctx->is_connected) {
1244 ODU_BRIDGE_ERR("cannot deinit while bridge is conncetd\n");
1245 return -EFAULT;
1246 }
1247
1248 odu_bridge_deregister_properties();
1249 odu_bridge_del_hdrs();
1250 odu_debugfs_destroy();
1251 cdev_del(&odu_bridge_ctx->cdev);
1252 device_destroy(odu_bridge_ctx->class, odu_bridge_ctx->dev_num);
1253 unregister_chrdev_region(odu_bridge_ctx->dev_num, 1);
1254 class_destroy(odu_bridge_ctx->class);
1255 ipc_log_context_destroy(odu_bridge_ctx->logbuf);
1256 ipc_log_context_destroy(odu_bridge_ctx->logbuf_low);
1257 kfree(odu_bridge_ctx);
1258 odu_bridge_ctx = NULL;
1259
1260 ODU_BRIDGE_FUNC_EXIT();
1261 return 0;
1262}
1263EXPORT_SYMBOL(odu_bridge_cleanup);
1264
Amir Levy9659e592016-10-27 18:08:27 +03001265MODULE_LICENSE("GPL v2");
1266MODULE_DESCRIPTION("ODU bridge driver");